diff options
Diffstat (limited to 'core/java/android/server/BluetoothDeviceService.java')
-rw-r--r-- | core/java/android/server/BluetoothDeviceService.java | 1263 |
1 files changed, 0 insertions, 1263 deletions
diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java deleted file mode 100644 index 8c843ef..0000000 --- a/core/java/android/server/BluetoothDeviceService.java +++ /dev/null @@ -1,1263 +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. - */ - -/** - * TODO: Move this to - * java/services/com/android/server/BluetoothDeviceService.java - * and make the contructor package private again. - * - * @hide - */ - -package android.server; - -import android.bluetooth.BluetoothClass; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothError; -import android.bluetooth.BluetoothHeadset; -import android.bluetooth.BluetoothIntent; -import android.bluetooth.IBluetoothDevice; -import android.bluetooth.IBluetoothDeviceCallback; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Binder; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemService; -import android.provider.Settings; -import android.util.Log; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import com.android.internal.app.IBatteryStats; - -public class BluetoothDeviceService extends IBluetoothDevice.Stub { - private static final String TAG = "BluetoothDeviceService"; - private static final boolean DBG = true; - - private int mNativeData; - private BluetoothEventLoop mEventLoop; - private IntentFilter mIntentFilter; - private boolean mIsAirplaneSensitive; - private int mBluetoothState; - private boolean mRestart = false; // need to call enable() after disable() - - private final BondState mBondState = new BondState(); // local cache of bondings - private boolean mIsDiscovering; - private final IBatteryStats mBatteryStats; - - private final Context mContext; - - 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 int MESSAGE_REGISTER_SDP_RECORDS = 1; - private static final int MESSAGE_FINISH_DISABLE = 2; - - static { - classInitNative(); - } - private native static void classInitNative(); - - public BluetoothDeviceService(Context context) { - mContext = context; - - // Need to do this in place of: - // mBatteryStats = BatteryStatsService.getService(); - // Since we can not import BatteryStatsService from here. This class really needs to be - // moved to java/services/com/android/server/ - mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); - } - - /** Must be called after construction, and before any other method. - */ - public synchronized void init() { - initializeNativeDataNative(); - - if (isEnabledNative() == 1) { - Log.w(TAG, "Bluetooth daemons already running - runtime restart? "); - disableNative(); - } - - setBluetoothState(BluetoothDevice.BLUETOOTH_STATE_OFF); - mIsDiscovering = false; - mEventLoop = new BluetoothEventLoop(mContext, this); - registerForAirplaneMode(); - } - private native void initializeNativeDataNative(); - - @Override - protected void finalize() throws Throwable { - if (mIsAirplaneSensitive) { - mContext.unregisterReceiver(mReceiver); - } - try { - cleanupNativeDataNative(); - } finally { - super.finalize(); - } - } - private native void cleanupNativeDataNative(); - - public boolean isEnabled() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBluetoothState == BluetoothDevice.BLUETOOTH_STATE_ON; - } - private native int isEnabledNative(); - - public int getBluetoothState() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBluetoothState; - } - - - /** - * Bring down bluetooth and disable BT in settings. Returns true on success. - */ - public boolean disable() { - return disable(true); - } - - /** - * Bring down bluetooth. Returns true on success. - * - * @param saveSetting If true, disable BT in settings - */ - public synchronized boolean disable(boolean saveSetting) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - - switch (mBluetoothState) { - case BluetoothDevice.BLUETOOTH_STATE_OFF: - return true; - case BluetoothDevice.BLUETOOTH_STATE_ON: - break; - default: - return false; - } - if (mEnableThread != null && mEnableThread.isAlive()) { - return false; - } - setBluetoothState(BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF); - - // Allow 3 seconds for profiles to gracefully disconnect - // TODO: Introduce a callback mechanism so that each profile can notify - // BluetoothDeviceService when it is done shutting down - mHandler.sendMessageDelayed( - mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000); - return true; - } - - - private synchronized void finishDisable(boolean saveSetting) { - if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF) { - return; - } - mEventLoop.stop(); - disableNative(); - - // mark in progress bondings as cancelled - for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) { - mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED, - BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); - } - - // Remove remoteServiceChannelCallbacks - HashMap<String, IBluetoothDeviceCallback> callbacksMap = - mEventLoop.getRemoteServiceChannelCallbacks(); - - for (Iterator<String> i = callbacksMap.keySet().iterator(); i.hasNext();) { - String address = i.next(); - IBluetoothDeviceCallback callback = callbacksMap.get(address); - try { - callback.onGetRemoteServiceChannelResult(address, BluetoothError.ERROR_DISABLED); - } catch (RemoteException e) {} - i.remove(); - } - - // update mode - Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION); - intent.putExtra(BluetoothIntent.SCAN_MODE, BluetoothDevice.SCAN_MODE_NONE); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - - mIsDiscovering = false; - - if (saveSetting) { - persistBluetoothOnSetting(false); - } - - setBluetoothState(BluetoothDevice.BLUETOOTH_STATE_OFF); - - // Log bluetooth off to battery stats. - long ident = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteBluetoothOff(); - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(ident); - } - - if (mRestart) { - mRestart = false; - enable(); - } - } - - /** Bring up BT and persist BT on in settings */ - public boolean enable() { - return enable(true); - } - - /** - * Enable this Bluetooth device, asynchronously. - * This turns on/off the underlying hardware. - * - * @param saveSetting If true, persist the new state of BT in settings - * @return True on success (so far) - */ - public synchronized boolean enable(boolean saveSetting) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - - // Airplane mode can prevent Bluetooth radio from being turned on. - if (mIsAirplaneSensitive && isAirplaneModeOn()) { - return false; - } - if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_OFF) { - return false; - } - if (mEnableThread != null && mEnableThread.isAlive()) { - return false; - } - setBluetoothState(BluetoothDevice.BLUETOOTH_STATE_TURNING_ON); - mEnableThread = new EnableThread(saveSetting); - mEnableThread.start(); - return true; - } - - /** Forcibly restart Bluetooth if it is on */ - /* package */ synchronized void restart() { - if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_ON) { - return; - } - mRestart = true; - if (!disable(false)) { - mRestart = false; - } - } - - private synchronized void setBluetoothState(int state) { - if (state == mBluetoothState) { - return; - } - - if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state); - - Intent intent = new Intent(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION); - intent.putExtra(BluetoothIntent.BLUETOOTH_PREVIOUS_STATE, mBluetoothState); - intent.putExtra(BluetoothIntent.BLUETOOTH_STATE, state); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - - mBluetoothState = state; - - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_REGISTER_SDP_RECORDS: - //TODO: Don't assume HSP/HFP is running, don't use sdptool, - if (isEnabled()) { - SystemService.start("hsag"); - SystemService.start("hfag"); - } - break; - case MESSAGE_FINISH_DISABLE: - finishDisable(msg.arg1 != 0); - break; - } - } - }; - - private EnableThread mEnableThread; - - private class EnableThread extends Thread { - private final boolean mSaveSetting; - public EnableThread(boolean saveSetting) { - mSaveSetting = saveSetting; - } - public void run() { - boolean res = (enableNative() == 0); - if (res) { - int retryCount = 2; - boolean running = false; - while ((retryCount-- > 0) && !running) { - mEventLoop.start(); - // it may take a momement for the other thread to do its - // thing. Check periodically for a while. - int pollCount = 5; - while ((pollCount-- > 0) && !running) { - if (mEventLoop.isEventLoopRunning()) { - running = true; - break; - } - try { - Thread.sleep(100); - } catch (InterruptedException e) {} - } - } - if (!running) { - log("bt EnableThread giving up"); - res = false; - disableNative(); - } - } - - - if (res) { - if (mSaveSetting) { - persistBluetoothOnSetting(true); - } - mIsDiscovering = false; - mBondState.loadBondState(); - mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS), - 3000); - - // Log bluetooth on to battery stats. - long ident = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteBluetoothOn(); - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - mEnableThread = null; - - setBluetoothState(res ? - BluetoothDevice.BLUETOOTH_STATE_ON : - BluetoothDevice.BLUETOOTH_STATE_OFF); - - if (res) { - // Update mode - mEventLoop.onModeChanged(getModeNative()); - } - - if (mIsAirplaneSensitive && isAirplaneModeOn()) { - disable(false); - } - - } - } - - private void persistBluetoothOnSetting(boolean bluetoothOn) { - long origCallerIdentityToken = Binder.clearCallingIdentity(); - Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON, - bluetoothOn ? 1 : 0); - Binder.restoreCallingIdentity(origCallerIdentityToken); - } - - private native int enableNative(); - private native int disableNative(); - - /* package */ BondState getBondState() { - return mBondState; - } - - /** local cache of bonding state. - /* we keep our own state to track the intermediate state BONDING, which - /* bluez does not track. - * All addreses must be passed in upper case. - */ - public class BondState { - private final HashMap<String, Integer> mState = new HashMap<String, Integer>(); - private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>(); - private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>(); - // List of all the vendor_id prefix of Bluetooth addresses for - // which auto pairing is not attempted. - // The following companies are included in the list below: - // ALPS (lexus), Murata (Prius 2007, Nokia 616), TEMIC SDS (Porsche, Audi), - // Parrot, Zhongshan General K-mate Electronics, Great Well - // Electronics, Flaircomm Electronics, Jatty Electronics, Delphi, - // Clarion, Novero, Denso (Lexus, Toyota), Johnson Controls (Acura), - // Continental Automotive, Harman/Becker - private final ArrayList<String> mAutoPairingBlacklisted = - new ArrayList<String>(Arrays.asList( - "00:02:C7", "00:16:FE", "00:19:C1", "00:1B:FB", "00:1E:3D", "00:21:4F", - "00:23:06", "00:24:33", "00:A0:79", "00:0E:6D", "00:13:E0", "00:21:E8", - "00:60:57", "00:0E:9F", "00:12:1C", "00:18:91", "00:18:96", "00:13:04", - "00:16:FD", "00:22:A0", "00:0B:4C", "00:60:6F", "00:23:3D", "00:C0:59", - "00:0A:30", "00:1E:AE", "00:1C:D7" - )); - - public synchronized void loadBondState() { - if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_TURNING_ON) { - return; - } - String[] bonds = listBondingsNative(); - if (bonds == null) { - return; - } - mState.clear(); - if (DBG) log("found " + bonds.length + " bonded devices"); - for (String address : bonds) { - mState.put(address.toUpperCase(), BluetoothDevice.BOND_BONDED); - } - } - - public synchronized void setBondState(String address, int state) { - setBondState(address, state, 0); - } - - /** reason is ignored unless state == BOND_NOT_BONDED */ - public synchronized void setBondState(String address, int state, int reason) { - int oldState = getBondState(address); - if (oldState == state) { - return; - } - if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" + - reason + ")"); - Intent intent = new Intent(BluetoothIntent.BOND_STATE_CHANGED_ACTION); - intent.putExtra(BluetoothIntent.ADDRESS, address); - intent.putExtra(BluetoothIntent.BOND_STATE, state); - intent.putExtra(BluetoothIntent.BOND_PREVIOUS_STATE, oldState); - if (state == BluetoothDevice.BOND_NOT_BONDED) { - if (reason <= 0) { - Log.w(TAG, "setBondState() called to unbond device, but reason code is " + - "invalid. Overriding reason code with BOND_RESULT_REMOVED"); - reason = BluetoothDevice.UNBOND_REASON_REMOVED; - } - intent.putExtra(BluetoothIntent.REASON, reason); - mState.remove(address); - } else { - mState.put(address, state); - } - - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - } - - public boolean isAutoPairingBlacklisted(String address) { - for (String blacklistAddress : mAutoPairingBlacklisted) { - if (address.startsWith(blacklistAddress)) return true; - } - return false; - } - - public synchronized int getBondState(String address) { - Integer state = mState.get(address); - if (state == null) { - return BluetoothDevice.BOND_NOT_BONDED; - } - return state.intValue(); - } - - private synchronized String[] listInState(int state) { - ArrayList<String> result = new ArrayList<String>(mState.size()); - for (Map.Entry<String, Integer> e : mState.entrySet()) { - if (e.getValue().intValue() == state) { - result.add(e.getKey()); - } - } - return result.toArray(new String[result.size()]); - } - - public synchronized void addAutoPairingFailure(String address) { - if (!mAutoPairingFailures.contains(address)) { - mAutoPairingFailures.add(address); - } - } - - public synchronized boolean isAutoPairingAttemptsInProgress(String address) { - return getAttempt(address) != 0; - } - - public synchronized void clearPinAttempts(String address) { - mPinAttempt.remove(address); - } - - public synchronized boolean hasAutoPairingFailed(String address) { - return mAutoPairingFailures.contains(address); - } - - public synchronized int getAttempt(String address) { - Integer attempt = mPinAttempt.get(address); - if (attempt == null) { - return 0; - } - return attempt.intValue(); - } - - public synchronized void attempt(String address) { - Integer attempt = mPinAttempt.get(address); - int newAttempt; - if (attempt == null) { - newAttempt = 1; - } else { - newAttempt = attempt.intValue() + 1; - } - mPinAttempt.put(address, new Integer(newAttempt)); - } - - } - private native String[] listBondingsNative(); - - private static String toBondStateString(int bondState) { - switch (bondState) { - case BluetoothDevice.BOND_NOT_BONDED: - return "not bonded"; - case BluetoothDevice.BOND_BONDING: - return "bonding"; - case BluetoothDevice.BOND_BONDED: - return "bonded"; - default: - return "??????"; - } - } - - public synchronized String getAddress() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getAddressNative(); - } - private native String getAddressNative(); - - public synchronized String getName() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getNameNative(); - } - private native String getNameNative(); - - public synchronized boolean setName(String name) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (name == null) { - return false; - } - // hcid handles persistance of the bluetooth name - return setNameNative(name); - } - private native boolean setNameNative(String name); - - /** - * Returns the user-friendly name of a remote device. This value is - * retrned from our local cache, which is updated during device discovery. - * Do not expect to retrieve the updated remote name immediately after - * changing the name on the remote device. - * - * @param address Bluetooth address of remote device. - * - * @return The user-friendly name of the specified remote device. - */ - public synchronized String getRemoteName(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteNameNative(address); - } - private native String getRemoteNameNative(String address); - - /* pacakge */ native String getAdapterPathNative(); - - public synchronized boolean startDiscovery(boolean resolveNames) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return startDiscoveryNative(resolveNames); - } - private native boolean startDiscoveryNative(boolean resolveNames); - - public synchronized boolean cancelDiscovery() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return cancelDiscoveryNative(); - } - private native boolean cancelDiscoveryNative(); - - public synchronized boolean isDiscovering() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mIsDiscovering; - } - - /* package */ void setIsDiscovering(boolean isDiscovering) { - mIsDiscovering = isDiscovering; - } - - public synchronized boolean startPeriodicDiscovery() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return startPeriodicDiscoveryNative(); - } - private native boolean startPeriodicDiscoveryNative(); - - public synchronized boolean stopPeriodicDiscovery() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return stopPeriodicDiscoveryNative(); - } - private native boolean stopPeriodicDiscoveryNative(); - - public synchronized boolean isPeriodicDiscovery() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return isPeriodicDiscoveryNative(); - } - private native boolean isPeriodicDiscoveryNative(); - - /** - * Set the discoverability window for the device. A timeout of zero - * makes the device permanently discoverable (if the device is - * discoverable). Setting the timeout to a nonzero value does not make - * a device discoverable; you need to call setMode() to make the device - * explicitly discoverable. - * - * @param timeout_s The discoverable timeout in seconds. - */ - public synchronized boolean setDiscoverableTimeout(int timeout) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - return setDiscoverableTimeoutNative(timeout); - } - private native boolean setDiscoverableTimeoutNative(int timeout_s); - - /** - * Get the discoverability window for the device. A timeout of zero - * means that the device is permanently discoverable (if the device is - * in the discoverable mode). - * - * @return The discoverability window of the device, in seconds. A negative - * value indicates an error. - */ - public synchronized int getDiscoverableTimeout() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getDiscoverableTimeoutNative(); - } - private native int getDiscoverableTimeoutNative(); - - public synchronized boolean isAclConnected(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - return isConnectedNative(address); - } - private native boolean isConnectedNative(String address); - - public synchronized int getScanMode() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return bluezStringToScanMode(getModeNative()); - } - private native String getModeNative(); - - public synchronized boolean setScanMode(int mode) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - String bluezMode = scanModeToBluezString(mode); - if (bluezMode != null) { - return setModeNative(bluezMode); - } - return false; - } - private native boolean setModeNative(String mode); - - public synchronized boolean disconnectRemoteDeviceAcl(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - return disconnectRemoteDeviceNative(address); - } - private native boolean disconnectRemoteDeviceNative(String address); - - public synchronized boolean createBond(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - address = address.toUpperCase(); - - String[] bonding = mBondState.listInState(BluetoothDevice.BOND_BONDING); - if (bonding.length > 0 && !bonding[0].equals(address)) { - log("Ignoring createBond(): another device is bonding"); - // a different device is currently bonding, fail - return false; - } - - // Check for bond state only if we are not performing auto - // pairing exponential back-off attempts. - if (!mBondState.isAutoPairingAttemptsInProgress(address) && - mBondState.getBondState(address) != BluetoothDevice.BOND_NOT_BONDED) { - log("Ignoring createBond(): this device is already bonding or bonded"); - return false; - } - - if (!createBondingNative(address, 60000 /* 1 minute */)) { - return false; - } - - mBondState.setBondState(address, BluetoothDevice.BOND_BONDING); - return true; - } - private native boolean createBondingNative(String address, int timeout_ms); - - public synchronized boolean cancelBondProcess(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - address = address.toUpperCase(); - if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) { - return false; - } - - mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED, - BluetoothDevice.UNBOND_REASON_AUTH_CANCELED); - cancelBondingProcessNative(address); - return true; - } - private native boolean cancelBondingProcessNative(String address); - - public synchronized boolean removeBond(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - return removeBondingNative(address); - } - private native boolean removeBondingNative(String address); - - public synchronized String[] listBonds() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return mBondState.listInState(BluetoothDevice.BOND_BONDED); - } - - public synchronized int getBondState(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return BluetoothError.ERROR; - } - return mBondState.getBondState(address.toUpperCase()); - } - - public synchronized String[] listAclConnections() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return listConnectionsNative(); - } - private native String[] listConnectionsNative(); - - /** - * This method lists all remote devices that this adapter is aware of. - * This is a list not only of all most-recently discovered devices, but of - * all devices discovered by this adapter up to some point in the past. - * Note that many of these devices may not be in the neighborhood anymore, - * and attempting to connect to them will result in an error. - * - * @return An array of strings representing the Bluetooth addresses of all - * remote devices that this adapter is aware of. - */ - public synchronized String[] listRemoteDevices() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return listRemoteDevicesNative(); - } - private native String[] listRemoteDevicesNative(); - - /** - * Returns the version of the Bluetooth chip. This version is compiled from - * the LMP version. In case of EDR the features attribute must be checked. - * Example: "Bluetooth 2.0 + EDR". - * - * @return a String representation of the this Adapter's underlying - * Bluetooth-chip version. - */ - public synchronized String getVersion() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getVersionNative(); - } - private native String getVersionNative(); - - /** - * Returns the revision of the Bluetooth chip. This is a vendor-specific - * value and in most cases it represents the firmware version. This might - * derive from the HCI revision and LMP subversion values or via extra - * vendord specific commands. - * In case the revision of a chip is not available. This method should - * return the LMP subversion value as a string. - * Example: "HCI 19.2" - * - * @return The HCI revision of this adapter. - */ - public synchronized String getRevision() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getRevisionNative(); - } - private native String getRevisionNative(); - - /** - * Returns the manufacturer of the Bluetooth chip. If the company id is not - * known the sting "Company ID %d" where %d should be replaced with the - * numeric value from the manufacturer field. - * Example: "Cambridge Silicon Radio" - * - * @return Manufacturer name. - */ - public synchronized String getManufacturer() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getManufacturerNative(); - } - private native String getManufacturerNative(); - - /** - * Returns the company name from the OUI database of the Bluetooth device - * address. This function will need a valid and up-to-date oui.txt from - * the IEEE. This value will be different from the manufacturer string in - * the most cases. - * If the oui.txt file is not present or the OUI part of the Bluetooth - * address is not listed, it should return the string "OUI %s" where %s is - * the actual OUI. - * - * Example: "Apple Computer" - * - * @return company name - */ - public synchronized String getCompany() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return getCompanyNative(); - } - private native String getCompanyNative(); - - /** - * Like getVersion(), but for a remote device. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device Bluetooth version - * - * @see #getVersion - */ - public synchronized String getRemoteVersion(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteVersionNative(address); - } - private native String getRemoteVersionNative(String address); - - /** - * Like getRevision(), but for a remote device. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device HCI revision - * - * @see #getRevision - */ - public synchronized String getRemoteRevision(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteRevisionNative(address); - } - private native String getRemoteRevisionNative(String address); - - /** - * Like getManufacturer(), but for a remote device. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device Bluetooth chip manufacturer - * - * @see #getManufacturer - */ - public synchronized String getRemoteManufacturer(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteManufacturerNative(address); - } - private native String getRemoteManufacturerNative(String address); - - /** - * Like getCompany(), but for a remote device. - * - * @param address The Bluetooth address of the remote device. - * - * @return remote-device company - * - * @see #getCompany - */ - public synchronized String getRemoteCompany(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteCompanyNative(address); - } - private native String getRemoteCompanyNative(String address); - - /** - * Returns the date and time when the specified remote device has been seen - * by a discover procedure. - * Example: "2006-02-08 12:00:00 GMT" - * - * @return a String with the timestamp. - */ - public synchronized String lastSeen(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return lastSeenNative(address); - } - private native String lastSeenNative(String address); - - /** - * Returns the date and time when the specified remote device has last been - * connected to - * Example: "2006-02-08 12:00:00 GMT" - * - * @return a String with the timestamp. - */ - public synchronized String lastUsed(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return lastUsedNative(address); - } - private native String lastUsedNative(String address); - - /** - * Gets the remote major, minor, and service classes encoded as a 32-bit - * integer. - * - * Note: this value is retrieved from cache, because we get it during - * remote-device discovery. - * - * @return 32-bit integer encoding the remote major, minor, and service - * classes. - * - * @see #getRemoteMajorClass - * @see #getRemoteMinorClass - * @see #getRemoteServiceClasses - */ - public synchronized int getRemoteClass(String address) { - if (!BluetoothDevice.checkBluetoothAddress(address)) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return BluetoothClass.ERROR; - } - return getRemoteClassNative(address); - } - private native int getRemoteClassNative(String address); - - /** - * Gets the remote features encoded as bit mask. - * - * Note: This method may be obsoleted soon. - * - * @return byte array of features. - */ - public synchronized byte[] getRemoteFeatures(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteFeaturesNative(address); - } - private native byte[] getRemoteFeaturesNative(String address); - - /** - * This method and {@link #getRemoteServiceRecord} query the SDP service - * on a remote device. They do not interpret the data, but simply return - * it raw to the user. To read more about SDP service handles and records, - * consult the Bluetooth core documentation (www.bluetooth.com). - * - * @param address Bluetooth address of remote device. - * @param match a String match to narrow down the service-handle search. - * The only supported value currently is "hsp" for the headset - * profile. To retrieve all service handles, simply pass an empty - * match string. - * - * @return all service handles corresponding to the string match. - * - * @see #getRemoteServiceRecord - */ - public synchronized int[] getRemoteServiceHandles(String address, String match) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - if (match == null) { - match = ""; - } - return getRemoteServiceHandlesNative(address, match); - } - private native int[] getRemoteServiceHandlesNative(String address, String match); - - /** - * This method retrieves the service records corresponding to a given - * service handle (method {@link #getRemoteServiceHandles} retrieves the - * service handles.) - * - * This method and {@link #getRemoteServiceHandles} do not interpret their - * data, but simply return it raw to the user. To read more about SDP - * service handles and records, consult the Bluetooth core documentation - * (www.bluetooth.com). - * - * @param address Bluetooth address of remote device. - * @param handle Service handle returned by {@link #getRemoteServiceHandles} - * - * @return a byte array of all service records corresponding to the - * specified service handle. - * - * @see #getRemoteServiceHandles - */ - public synchronized byte[] getRemoteServiceRecord(String address, int handle) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return null; - } - return getRemoteServiceRecordNative(address, handle); - } - private native byte[] getRemoteServiceRecordNative(String address, int handle); - - private static final int MAX_OUTSTANDING_ASYNC = 32; - - // AIDL does not yet support short's - public synchronized boolean getRemoteServiceChannel(String address, int uuid16, - IBluetoothDeviceCallback callback) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - HashMap<String, IBluetoothDeviceCallback> callbacks = - mEventLoop.getRemoteServiceChannelCallbacks(); - if (callbacks.containsKey(address)) { - Log.w(TAG, "SDP request already in progress for " + address); - return false; - } - // Protect from malicious clients - only allow 32 bonding requests per minute. - if (callbacks.size() > MAX_OUTSTANDING_ASYNC) { - Log.w(TAG, "Too many outstanding SDP requests, dropping request for " + address); - return false; - } - callbacks.put(address, callback); - - if (!getRemoteServiceChannelNative(address, (short)uuid16)) { - callbacks.remove(address); - return false; - } - return true; - } - private native boolean getRemoteServiceChannelNative(String address, short uuid16); - - public synchronized boolean setPin(String address, byte[] pin) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (pin == null || pin.length <= 0 || pin.length > 16 || - !BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - address = address.toUpperCase(); - Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); - if (data == null) { - Log.w(TAG, "setPin(" + address + ") called but no native data available, " + - "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" + - " or by bluez.\n"); - return false; - } - // bluez API wants pin as a string - String pinString; - try { - pinString = new String(pin, "UTF8"); - } catch (UnsupportedEncodingException uee) { - Log.e(TAG, "UTF8 not supported?!?"); - return false; - } - return setPinNative(address, pinString, data.intValue()); - } - private native boolean setPinNative(String address, String pin, int nativeData); - - public synchronized boolean cancelPin(String address) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (!BluetoothDevice.checkBluetoothAddress(address)) { - return false; - } - address = address.toUpperCase(); - Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address); - if (data == null) { - Log.w(TAG, "cancelPin(" + address + ") called but no native data available, " + - "ignoring. Maybe the PasskeyAgent Request was already cancelled by the remote " + - "or by bluez.\n"); - return false; - } - return cancelPinNative(address, data.intValue()); - } - private native boolean cancelPinNative(String address, int natveiData); - - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) { - ContentResolver resolver = context.getContentResolver(); - // Query the airplane mode from Settings.System just to make sure that - // some random app is not sending this intent and disabling bluetooth - boolean enabled = !isAirplaneModeOn(); - // If bluetooth is currently expected to be on, then enable or disable bluetooth - if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) { - if (enabled) { - enable(false); - } else { - disable(false); - } - } - } - } - }; - - private void registerForAirplaneMode() { - String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(), - Settings.System.AIRPLANE_MODE_RADIOS); - mIsAirplaneSensitive = airplaneModeRadios == null - ? true : airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH); - if (mIsAirplaneSensitive) { - mIntentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); - mContext.registerReceiver(mReceiver, mIntentFilter); - } - } - - /* Returns true if airplane mode is currently on */ - private final boolean isAirplaneModeOn() { - return Settings.System.getInt(mContext.getContentResolver(), - Settings.System.AIRPLANE_MODE_ON, 0) == 1; - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n"); - - switch(mBluetoothState) { - case BluetoothDevice.BLUETOOTH_STATE_OFF: - pw.println("\nBluetooth OFF\n"); - return; - case BluetoothDevice.BLUETOOTH_STATE_TURNING_ON: - pw.println("\nBluetooth TURNING ON\n"); - return; - case BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF: - pw.println("\nBluetooth TURNING OFF\n"); - return; - case BluetoothDevice.BLUETOOTH_STATE_ON: - pw.println("\nBluetooth ON\n"); - } - - pw.println("\nLocal address = " + getAddress()); - pw.println("\nLocal name = " + getName()); - pw.println("\nisDiscovering() = " + isDiscovering()); - - BluetoothHeadset headset = new BluetoothHeadset(mContext, null); - - String[] addresses = listRemoteDevices(); - - pw.println("\n--Known devices--"); - for (String address : addresses) { - pw.printf("%s %10s (%d) %s\n", address, - toBondStateString(mBondState.getBondState(address)), - mBondState.getAttempt(address), - getRemoteName(address)); - } - - addresses = listAclConnections(); - pw.println("\n--ACL connected devices--"); - for (String address : addresses) { - pw.println(address); - } - - // Rather not do this from here, but no-where else and I need this - // dump - pw.println("\n--Headset Service--"); - switch (headset.getState()) { - case BluetoothHeadset.STATE_DISCONNECTED: - pw.println("getState() = STATE_DISCONNECTED"); - break; - case BluetoothHeadset.STATE_CONNECTING: - pw.println("getState() = STATE_CONNECTING"); - break; - case BluetoothHeadset.STATE_CONNECTED: - pw.println("getState() = STATE_CONNECTED"); - break; - case BluetoothHeadset.STATE_ERROR: - pw.println("getState() = STATE_ERROR"); - break; - } - pw.println("getHeadsetAddress() = " + headset.getHeadsetAddress()); - pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint()); - - headset.close(); - } - - /* package */ static int bluezStringToScanMode(String mode) { - if (mode == null) { - return BluetoothError.ERROR; - } - mode = mode.toLowerCase(); - if (mode.equals("off")) { - return BluetoothDevice.SCAN_MODE_NONE; - } else if (mode.equals("connectable")) { - return BluetoothDevice.SCAN_MODE_CONNECTABLE; - } else if (mode.equals("discoverable")) { - return BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE; - } else { - return BluetoothError.ERROR; - } - } - - /* package */ static String scanModeToBluezString(int mode) { - switch (mode) { - case BluetoothDevice.SCAN_MODE_NONE: - return "off"; - case BluetoothDevice.SCAN_MODE_CONNECTABLE: - return "connectable"; - case BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE: - return "discoverable"; - } - return null; - } - - private static void log(String msg) { - Log.d(TAG, msg); - } -} |