/* * Copyright (C) 2012 Google Inc. */ /** * @hide */ package com.android.bluetooth.btservice; import android.app.Application; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetooth; 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.IBinder; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; import android.util.Log; import android.util.Pair; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.hid.HidService; import com.android.bluetooth.hfp.HeadsetService; import com.android.bluetooth.hdp.HealthService; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties; import java.io.FileDescriptor; import java.io.IOException; import java.util.HashMap; import java.util.Set; public class AdapterService extends Application { private static final String TAG = "BluetoothAdapterService"; private static final boolean DBG = true; static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; private AdapterProperties mAdapterProperties; private int mAdapterState; private Context mContext; private boolean mIsAirplaneSensitive; private boolean mIsAirplaneToggleable; private static AdapterService sAdapterService; private BluetoothAdapter mAdapter; private AdapterState mAdapterStateMachine; private BondStateMachine mBondStateMachine; private JniCallbacks mJniCallbacks; private RemoteDevices mRemoteDevices; static { System.loadLibrary("bluetooth_jni"); classInitNative(); } @Override public void onCreate() { super.onCreate(); ServiceManager.addService(Context.BLUETOOTH_SERVICE, mBinder); mAdapter = BluetoothAdapter.getDefaultAdapter(); mContext = this; sAdapterService = this; IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); registerForAirplaneMode(filter); registerReceiver(mReceiver, filter); mRemoteDevices = RemoteDevices.getInstance(this, mContext); mAdapterProperties = AdapterProperties.getInstance(this, mContext); mAdapterStateMachine = new AdapterState(this, mContext, mAdapterProperties); mBondStateMachine = new BondStateMachine(this, mContext, mAdapterProperties); mJniCallbacks = JniCallbacks.getInstance(mRemoteDevices, mAdapterProperties, mAdapterStateMachine, mBondStateMachine); initNative(); mAdapterStateMachine.start(); mBondStateMachine.start(); //TODO(BT): Remove this when BT is no longer a persitent process. int bluetoothOn = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON, 0); if (!isAirplaneModeOn() && bluetoothOn != 0) mAdapter.enable(); startService(new Intent(this, HeadsetService.class)); startService(new Intent(this, A2dpService.class)); startService(new Intent(this, HidService.class)); startService(new Intent(this, HealthService.class)); } @Override protected void finalize() throws Throwable { mContext.unregisterReceiver(mReceiver); try { cleanupNative(); } finally { super.finalize(); } } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent == null) return; 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 if (isAirplaneModeOn()) { mAdapterStateMachine.sendMessage(AdapterState.AIRPLANE_MODE_ON); } else { mAdapterStateMachine.sendMessage(AdapterState.AIRPLANE_MODE_OFF); } } } }; private void registerForAirplaneMode(IntentFilter filter) { final ContentResolver resolver = mContext.getContentResolver(); final String airplaneModeRadios = Settings.System.getString(resolver, Settings.System.AIRPLANE_MODE_RADIOS); final String toggleableRadios = Settings.System.getString(resolver, Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS); mIsAirplaneSensitive = airplaneModeRadios == null ? true : airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH); mIsAirplaneToggleable = toggleableRadios == null ? false : toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH); if (mIsAirplaneSensitive) { filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); } } /* 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; } /** * Handlers for incoming service calls */ private final IBluetooth.Stub mBinder = new IBluetooth.Stub() { public boolean isEnabled() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mAdapterProperties.getState() == BluetoothAdapter.STATE_ON; } public int getState() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mAdapterProperties.getState(); } public boolean enable() { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); // Persist the setting Message m = mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_ON); m.arg1 = 1; mAdapterStateMachine.sendMessage(m); return true; } public boolean disable(boolean persist) { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); int val = (persist ? 1 : 0); Message m = mAdapterStateMachine.obtainMessage(AdapterState.USER_TURN_OFF); m.arg1 = val; mAdapterStateMachine.sendMessage(m); return true; } public String getAddress() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); String addrString = null; byte[] address = mAdapterProperties.getAddress(); return Utils.getAddressStringFromByte(address); } public ParcelUuid[] getUuids() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mAdapterProperties.getUuids(); } public String getName() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mAdapterProperties.getName(); } public boolean setName(String name) { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); return mAdapterProperties.setName(name); } public int getScanMode() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mAdapterProperties.getScanMode(); } public boolean setScanMode(int mode, int duration) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); setDiscoverableTimeout(duration); int newMode = convertScanModeToHal(mode); return mAdapterProperties.setScanMode(newMode); } public int getDiscoverableTimeout() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mAdapterProperties.getDiscoverableTimeout(); } public boolean setDiscoverableTimeout(int timeout) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mAdapterProperties.setDiscoverableTimeout(timeout); } public boolean startDiscovery() { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); return startDiscoveryNative(); } public boolean cancelDiscovery() { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); return cancelDiscoveryNative(); } public boolean isDiscovering() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mAdapterProperties.isDiscovering(); } public BluetoothDevice[] getBondedDevices() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); debugLog("Get Bonded Devices being called"); return mAdapterProperties.getBondedDevices(); } public int getAdapterConnectionState() { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mAdapterProperties.getConnectionState(); } public int getProfileConnectionState(int profile) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return mAdapterProperties.getProfileConnectionState(profile); } public boolean createBond(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp != null && deviceProp.getBondState() != BluetoothDevice.BOND_NONE) { return false; } Message msg = mBondStateMachine.obtainMessage(BondStateMachine.CREATE_BOND); msg.obj = device; mBondStateMachine.sendMessage(msg); return true; } public boolean cancelBondProcess(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); byte[] addr = Utils.getBytesFromAddress(device.getAddress()); return cancelBondNative(addr); } public boolean removeBond(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDED) { return false; } Message msg = mBondStateMachine.obtainMessage(BondStateMachine.REMOVE_BOND); msg.obj = device; mBondStateMachine.sendMessage(msg); return true; } public int getBondState(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null) { return BluetoothDevice.BOND_NONE; } return deviceProp.getBondState(); } public String getRemoteName(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null) return null; return deviceProp.getName(); } public String getRemoteAlias(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null) return null; return deviceProp.getAlias(); } public boolean setRemoteAlias(BluetoothDevice device, String name) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null) return false; deviceProp.setAlias(name); return true; } public int getRemoteClass(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null) return 0; return deviceProp.getBluetoothClass(); } public ParcelUuid[] getRemoteUuids(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null) return null; return deviceProp.getUuids(); } public boolean fetchRemoteUuids(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); mRemoteDevices.performSdp(device); return true; } public boolean setPin(BluetoothDevice device, boolean accept, int len, byte[] pinCode) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDING) { return false; } byte[] addr = Utils.getBytesFromAddress(device.getAddress()); return pinReplyNative(addr, accept, len, pinCode); } public boolean setPasskey(BluetoothDevice device, boolean accept, int len, byte[] passkey) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDING) { return false; } byte[] addr = Utils.getBytesFromAddress(device.getAddress()); return sspReplyNative(addr, AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY, accept, Utils.byteArrayToInt(passkey)); } public boolean setPairingConfirmation(BluetoothDevice device, boolean accept) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp == null || deviceProp.getBondState() != BluetoothDevice.BOND_BONDING) { return false; } byte[] addr = Utils.getBytesFromAddress(device.getAddress()); return sspReplyNative(addr, AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION, accept, 0); } public void sendConnectionStateChange(BluetoothDevice device, int profile, int state, int prevState) { // TODO(BT) permission check? // Since this is a binder call check if Bluetooth is on still if (getState() == BluetoothAdapter.STATE_OFF) return; mAdapterProperties.sendConnectionStateChange(device, profile, state, prevState); } public ParcelFileDescriptor connectSocket(BluetoothDevice device, int type, ParcelUuid uuid, int port, int flag) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); int fd = connectSocketNative(Utils.getBytesFromAddress(device.getAddress()), type, Utils.uuidToByteArray(uuid), port, flag); if (fd < 0) { errorLog("Failed to connect socket"); return null; } return ParcelFileDescriptor.adoptFd(fd); } public ParcelFileDescriptor createSocketChannel(int type, String serviceName, ParcelUuid uuid, int port, int flag) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); int fd = createSocketChannelNative(type, serviceName, Utils.uuidToByteArray(uuid), port, flag); if (fd < 0) { errorLog("Failed to create socket channel"); return null; } return ParcelFileDescriptor.adoptFd(fd); } }; private int convertScanModeToHal(int mode) { switch (mode) { case BluetoothAdapter.SCAN_MODE_NONE: return AbstractionLayer.BT_SCAN_MODE_NONE; case BluetoothAdapter.SCAN_MODE_CONNECTABLE: return AbstractionLayer.BT_SCAN_MODE_CONNECTABLE; case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE: return AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE; } errorLog("Incorrect scan mode in convertScanModeToHal"); return -1; } int convertScanModeFromHal(int mode) { switch (mode) { case AbstractionLayer.BT_SCAN_MODE_NONE: return BluetoothAdapter.SCAN_MODE_NONE; case AbstractionLayer.BT_SCAN_MODE_CONNECTABLE: return BluetoothAdapter.SCAN_MODE_CONNECTABLE; case AbstractionLayer.BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE: return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; } errorLog("Incorrect scan mode in convertScanModeFromHal"); return -1; } private static void debugLog(String msg) { Log.d(TAG, msg); } private static void errorLog(String msg) { Log.e(TAG, msg); } void persistBluetoothSetting(boolean setOn) { long origCallerIdentityToken = Binder.clearCallingIdentity(); Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON, setOn ? 1 : 0); Binder.restoreCallingIdentity(origCallerIdentityToken); } boolean getBluetoothPersistedSetting() { ContentResolver contentResolver = mContext.getContentResolver(); return (Settings.Secure.getInt(contentResolver, Settings.Secure.BLUETOOTH_ON, 0) > 0); } private native static void classInitNative(); private native boolean initNative(); private native void cleanupNative(); /*package*/ native boolean enableNative(); /*package*/ native boolean disableNative(); /*package*/ native boolean setAdapterPropertyNative(int type, byte[] val); /*package*/ native boolean getAdapterPropertiesNative(); /*package*/ native boolean getAdapterPropertyNative(int type); /*package*/ native boolean setAdapterPropertyNative(int type); /*package*/ native boolean setDevicePropertyNative(byte[] address, int type, byte[] val); /*package*/ native boolean getDevicePropertyNative(byte[] address, int type); /*package*/ native boolean createBondNative(byte[] address); /*package*/ native boolean removeBondNative(byte[] address); /*package*/ native boolean cancelBondNative(byte[] address); private native boolean startDiscoveryNative(); private native boolean cancelDiscoveryNative(); private native boolean pinReplyNative(byte[] address, boolean accept, int len, byte[] pin); private native boolean sspReplyNative(byte[] address, int type, boolean accept, int passkey); // TODO(BT) move this to ../btsock dir private native int connectSocketNative(byte[] address, int type, byte[] uuid, int port, int flag); private native int createSocketChannelNative(int type, String serviceName, byte[] uuid, int port, int flag); }