Loading core/java/android/bluetooth/BluetoothDeviceProfileState.java +0 −1 Original line number Diff line number Diff line Loading @@ -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(); Loading core/java/android/server/BluetoothA2dpService.java +31 −26 Original line number Diff line number Diff line Loading @@ -16,7 +16,7 @@ /** * TODO: Move this to services.jar * and make the contructor package private again. * and make the constructor package private again. * @hide */ Loading Loading @@ -57,8 +57,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private static final String PROPERTY_STATE = "State"; private static int mSinkCount; private final Context mContext; private final IntentFilter mIntentFilter; private HashMap<BluetoothDevice, Integer> mAudioDevices; Loading Loading @@ -128,7 +126,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } }; private boolean isPhoneDocked(BluetoothDevice device) { // This works only because these broadcast intents are "sticky" Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT)); Loading Loading @@ -184,7 +181,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } } private int convertBluezSinkStringtoState(String value) { private int convertBluezSinkStringToState(String value) { if (value.equalsIgnoreCase("disconnected")) return BluetoothA2dp.STATE_DISCONNECTED; if (value.equalsIgnoreCase("connecting")) Loading Loading @@ -215,7 +212,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { // Properties are name-value pairs for (int i = 0; i < propValues.length; i+=2) { if (propValues[i].equals(PROPERTY_STATE)) { state = new Integer(convertBluezSinkStringtoState(propValues[i+1])); state = new Integer(convertBluezSinkStringToState(propValues[i+1])); break; } } Loading @@ -226,7 +223,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private synchronized void onBluetoothEnable() { String devices = mBluetoothService.getProperty("Devices"); mSinkCount = 0; if (devices != null) { String [] paths = devices.split(","); for (String path: paths) { Loading Loading @@ -467,6 +463,14 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority); } /** * Called by native code on a PropertyChanged signal from * org.bluez.AudioSink. * * @param path the object path for the changed device * @param propValues a string array containing the key and one or more * values. */ private synchronized void onSinkPropertyChanged(String path, String[] propValues) { if (!mBluetoothService.isEnabled()) { return; Loading @@ -482,7 +486,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { BluetoothDevice device = mAdapter.getRemoteDevice(address); if (name.equals(PROPERTY_STATE)) { int state = convertBluezSinkStringtoState(propValues[1]); int state = convertBluezSinkStringToState(propValues[1]); log("A2DP: onSinkPropertyChanged newState is: " + state + "mPlayingA2dpDevice: " + mPlayingA2dpDevice); if (mAudioDevices.get(device) == null) { Loading @@ -508,12 +512,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private void handleSinkStateChange(BluetoothDevice device, int prevState, int state) { if (state != prevState) { if (state == BluetoothA2dp.STATE_DISCONNECTED || state == BluetoothA2dp.STATE_DISCONNECTING) { mSinkCount--; } else if (state == BluetoothA2dp.STATE_CONNECTED) { mSinkCount ++; } mAudioDevices.put(device, state); checkSinkSuspendState(state); Loading Loading @@ -577,6 +575,13 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return result; } /** * Called by native code for the async response to a Connect * method call to org.bluez.AudioSink. * * @param deviceObjectPath the object path for the connecting device * @param result true on success; false on error */ private void onConnectSinkResult(String deviceObjectPath, boolean result) { // If the call was a success, ignore we will update the state // when we a Sink Property Change Loading core/java/android/server/BluetoothAdapterProperties.java 0 → 100644 +106 −0 Original line number Diff line number Diff line /* * 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.server; import android.content.Context; import android.util.Log; import java.util.HashMap; import java.util.Map; class BluetoothAdapterProperties { private static final String TAG = "BluetoothAdapterProperties"; private final Map<String, String> mPropertiesMap; private final Context mContext; private final BluetoothService mService; BluetoothAdapterProperties(Context context, BluetoothService service) { mPropertiesMap = new HashMap<String, String>(); mContext = context; mService = service; } synchronized String getProperty(String name) { if (mPropertiesMap.isEmpty()) { getAllProperties(); } return mPropertiesMap.get(name); } String getObjectPath() { return getProperty("ObjectPath"); } synchronized void clear() { mPropertiesMap.clear(); } synchronized boolean isEmpty() { return mPropertiesMap.isEmpty(); } synchronized void setProperty(String name, String value) { mPropertiesMap.put(name, value); } synchronized void getAllProperties() { mContext.enforceCallingOrSelfPermission( BluetoothService.BLUETOOTH_PERM, "Need BLUETOOTH permission"); mPropertiesMap.clear(); String properties[] = (String[]) mService .getAdapterPropertiesNative(); // The String Array consists of key-value pairs. if (properties == null) { Log.e(TAG, "*Error*: GetAdapterProperties returned NULL"); return; } for (int i = 0; i < properties.length; i++) { String name = properties[i]; String newValue = null; int len; if (name == null) { Log.e(TAG, "Error:Adapter Property at index " + i + " is null"); continue; } if (name.equals("Devices") || name.equals("UUIDs")) { StringBuilder str = new StringBuilder(); len = Integer.valueOf(properties[++i]); for (int j = 0; j < len; j++) { str.append(properties[++i]); str.append(","); } if (len > 0) { newValue = str.toString(); } } else { newValue = properties[++i]; } mPropertiesMap.put(name, newValue); } // Add adapter object path property. String adapterPath = mService.getAdapterPathNative(); if (adapterPath != null) { mPropertiesMap.put("ObjectPath", adapterPath + "/dev_"); } } } core/java/android/server/BluetoothBondState.java 0 → 100644 +368 −0 Original line number Diff line number Diff line /* * 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.server; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; import android.util.Log; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; /** * Local cache of bonding state. * We keep our own state to track the intermediate state BONDING, which * bluez does not track. * All addresses must be passed in upper case. */ class BluetoothBondState { private static final String TAG = "BluetoothBondState"; private static final boolean DBG = true; private final HashMap<String, Integer> mState = new HashMap<String, Integer>(); private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>(); private static final String AUTO_PAIRING_BLACKLIST = "/etc/bluetooth/auto_pairing.conf"; private static final String DYNAMIC_AUTO_PAIRING_BLACKLIST = "/data/misc/bluetooth/dynamic_auto_pairing.conf"; private ArrayList<String> mAutoPairingAddressBlacklist; private ArrayList<String> mAutoPairingExactNameBlacklist; private ArrayList<String> mAutoPairingPartialNameBlacklist; private ArrayList<String> mAutoPairingFixedPinZerosKeyboardList; // Addresses added to blacklist dynamically based on usage. private ArrayList<String> mAutoPairingDynamicAddressBlacklist; // If this is an outgoing connection, store the address. // There can be only 1 pending outgoing connection at a time, private String mPendingOutgoingBonding; private final Context mContext; private final BluetoothService mService; private final BluetoothInputProfileHandler mBluetoothInputProfileHandler; BluetoothBondState(Context context, BluetoothService service) { mContext = context; mService = service; mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, mService); } synchronized void setPendingOutgoingBonding(String address) { mPendingOutgoingBonding = address; } public synchronized String getPendingOutgoingBonding() { return mPendingOutgoingBonding; } public synchronized void loadBondState() { if (mService.getBluetoothStateInternal() != BluetoothAdapter.STATE_TURNING_ON) { return; } String val = mService.getAdapterProperties().getProperty("Devices"); if (val == null) { return; } String[] bonds = val.split(","); if (bonds == null) { return; } mState.clear(); if (DBG) Log.d(TAG, "found " + bonds.length + " bonded devices"); for (String device : bonds) { mState.put(mService.getAddressFromObjectPath(device).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; } // Check if this was an pending outgoing bonding. // If yes, reset the state. if (oldState == BluetoothDevice.BOND_BONDING) { if (address.equals(mPendingOutgoingBonding)) { mPendingOutgoingBonding = null; } } if (state == BluetoothDevice.BOND_BONDED) { mService.addProfileState(address); } else if (state == BluetoothDevice.BOND_NONE) { mService.removeProfileState(address); } // HID is handled by BluetoothService, other profiles // will be handled by their respective services. mBluetoothInputProfileHandler.setInitialInputDevicePriority( mService.getRemoteDevice(address), state); if (DBG) { Log.d(TAG, address + " bond state " + oldState + " -> " + state + " (" + reason + ")"); } Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mService.getRemoteDevice(address)); intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state); intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState); if (state == BluetoothDevice.BOND_NONE) { 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(BluetoothDevice.EXTRA_REASON, reason); mState.remove(address); } else { mState.put(address, state); } mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); } public boolean isAutoPairingBlacklisted(String address) { if (mAutoPairingAddressBlacklist != null) { for (String blacklistAddress : mAutoPairingAddressBlacklist) { if (address.startsWith(blacklistAddress)) return true; } } if (mAutoPairingDynamicAddressBlacklist != null) { for (String blacklistAddress: mAutoPairingDynamicAddressBlacklist) { if (address.equals(blacklistAddress)) return true; } } String name = mService.getRemoteName(address); if (name != null) { if (mAutoPairingExactNameBlacklist != null) { for (String blacklistName : mAutoPairingExactNameBlacklist) { if (name.equals(blacklistName)) return true; } } if (mAutoPairingPartialNameBlacklist != null) { for (String blacklistName : mAutoPairingPartialNameBlacklist) { if (name.startsWith(blacklistName)) return true; } } } return false; } public boolean isFixedPinZerosAutoPairKeyboard(String address) { // Note: the meaning of blacklist is reversed in this case. // If its in the list, we can go ahead and auto pair since // by default keyboard should have a variable PIN that we don't // auto pair using 0000. if (mAutoPairingFixedPinZerosKeyboardList != null) { for (String blacklistAddress : mAutoPairingFixedPinZerosKeyboardList) { 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_NONE; } return state.intValue(); } /*package*/ 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 (mAutoPairingDynamicAddressBlacklist == null) { mAutoPairingDynamicAddressBlacklist = new ArrayList<String>(); } updateAutoPairingData(address); mAutoPairingDynamicAddressBlacklist.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) { if (mAutoPairingDynamicAddressBlacklist == null) return false; return mAutoPairingDynamicAddressBlacklist.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 void copyAutoPairingData() { FileInputStream in = null; FileOutputStream out = null; try { File file = new File(DYNAMIC_AUTO_PAIRING_BLACKLIST); if (file.exists()) return; in = new FileInputStream(AUTO_PAIRING_BLACKLIST); out= new FileOutputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST); byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException: copyAutoPairingData " + e); } catch (IOException e) { Log.e(TAG, "IOException: copyAutoPairingData " + e); } finally { try { if (in != null) in.close(); if (out != null) out.close(); } catch (IOException e) {} } } synchronized public void readAutoPairingData() { if (mAutoPairingAddressBlacklist != null) return; copyAutoPairingData(); FileInputStream fstream = null; try { fstream = new FileInputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST); DataInputStream in = new DataInputStream(fstream); BufferedReader file = new BufferedReader(new InputStreamReader(in)); String line; while((line = file.readLine()) != null) { line = line.trim(); if (line.length() == 0 || line.startsWith("//")) continue; String[] value = line.split("="); if (value != null && value.length == 2) { String[] val = value[1].split(","); if (value[0].equalsIgnoreCase("AddressBlacklist")) { mAutoPairingAddressBlacklist = new ArrayList<String>(Arrays.asList(val)); } else if (value[0].equalsIgnoreCase("ExactNameBlacklist")) { mAutoPairingExactNameBlacklist = new ArrayList<String>(Arrays.asList(val)); } else if (value[0].equalsIgnoreCase("PartialNameBlacklist")) { mAutoPairingPartialNameBlacklist = new ArrayList<String>(Arrays.asList(val)); } else if (value[0].equalsIgnoreCase("FixedPinZerosKeyboardBlacklist")) { mAutoPairingFixedPinZerosKeyboardList = new ArrayList<String>(Arrays.asList(val)); } else if (value[0].equalsIgnoreCase("DynamicAddressBlacklist")) { mAutoPairingDynamicAddressBlacklist = new ArrayList<String>(Arrays.asList(val)); } else { Log.e(TAG, "Error parsing Auto pairing blacklist file"); } } } } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException: readAutoPairingData " + e); } catch (IOException e) { Log.e(TAG, "IOException: readAutoPairingData " + e); } finally { if (fstream != null) { try { fstream.close(); } catch (IOException e) { // Ignore } } } } // This function adds a bluetooth address to the auto pairing blacklist // file. These addresses are added to DynamicAddressBlacklistSection private void updateAutoPairingData(String address) { BufferedWriter out = null; try { out = new BufferedWriter(new FileWriter(DYNAMIC_AUTO_PAIRING_BLACKLIST, true)); StringBuilder str = new StringBuilder(); if (mAutoPairingDynamicAddressBlacklist.size() == 0) { str.append("DynamicAddressBlacklist="); } str.append(address); str.append(","); out.write(str.toString()); } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException: updateAutoPairingData " + e); } catch (IOException e) { Log.e(TAG, "IOException: updateAutoPairingData " + e); } finally { if (out != null) { try { out.close(); } catch (IOException e) { // Ignore } } } } } Loading
core/java/android/bluetooth/BluetoothDeviceProfileState.java +0 −1 Original line number Diff line number Diff line Loading @@ -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(); Loading
core/java/android/server/BluetoothA2dpService.java +31 −26 Original line number Diff line number Diff line Loading @@ -16,7 +16,7 @@ /** * TODO: Move this to services.jar * and make the contructor package private again. * and make the constructor package private again. * @hide */ Loading Loading @@ -57,8 +57,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private static final String PROPERTY_STATE = "State"; private static int mSinkCount; private final Context mContext; private final IntentFilter mIntentFilter; private HashMap<BluetoothDevice, Integer> mAudioDevices; Loading Loading @@ -128,7 +126,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } }; private boolean isPhoneDocked(BluetoothDevice device) { // This works only because these broadcast intents are "sticky" Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT)); Loading Loading @@ -184,7 +181,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { } } private int convertBluezSinkStringtoState(String value) { private int convertBluezSinkStringToState(String value) { if (value.equalsIgnoreCase("disconnected")) return BluetoothA2dp.STATE_DISCONNECTED; if (value.equalsIgnoreCase("connecting")) Loading Loading @@ -215,7 +212,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { // Properties are name-value pairs for (int i = 0; i < propValues.length; i+=2) { if (propValues[i].equals(PROPERTY_STATE)) { state = new Integer(convertBluezSinkStringtoState(propValues[i+1])); state = new Integer(convertBluezSinkStringToState(propValues[i+1])); break; } } Loading @@ -226,7 +223,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private synchronized void onBluetoothEnable() { String devices = mBluetoothService.getProperty("Devices"); mSinkCount = 0; if (devices != null) { String [] paths = devices.split(","); for (String path: paths) { Loading Loading @@ -467,6 +463,14 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority); } /** * Called by native code on a PropertyChanged signal from * org.bluez.AudioSink. * * @param path the object path for the changed device * @param propValues a string array containing the key and one or more * values. */ private synchronized void onSinkPropertyChanged(String path, String[] propValues) { if (!mBluetoothService.isEnabled()) { return; Loading @@ -482,7 +486,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { BluetoothDevice device = mAdapter.getRemoteDevice(address); if (name.equals(PROPERTY_STATE)) { int state = convertBluezSinkStringtoState(propValues[1]); int state = convertBluezSinkStringToState(propValues[1]); log("A2DP: onSinkPropertyChanged newState is: " + state + "mPlayingA2dpDevice: " + mPlayingA2dpDevice); if (mAudioDevices.get(device) == null) { Loading @@ -508,12 +512,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private void handleSinkStateChange(BluetoothDevice device, int prevState, int state) { if (state != prevState) { if (state == BluetoothA2dp.STATE_DISCONNECTED || state == BluetoothA2dp.STATE_DISCONNECTING) { mSinkCount--; } else if (state == BluetoothA2dp.STATE_CONNECTED) { mSinkCount ++; } mAudioDevices.put(device, state); checkSinkSuspendState(state); Loading Loading @@ -577,6 +575,13 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { return result; } /** * Called by native code for the async response to a Connect * method call to org.bluez.AudioSink. * * @param deviceObjectPath the object path for the connecting device * @param result true on success; false on error */ private void onConnectSinkResult(String deviceObjectPath, boolean result) { // If the call was a success, ignore we will update the state // when we a Sink Property Change Loading
core/java/android/server/BluetoothAdapterProperties.java 0 → 100644 +106 −0 Original line number Diff line number Diff line /* * 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.server; import android.content.Context; import android.util.Log; import java.util.HashMap; import java.util.Map; class BluetoothAdapterProperties { private static final String TAG = "BluetoothAdapterProperties"; private final Map<String, String> mPropertiesMap; private final Context mContext; private final BluetoothService mService; BluetoothAdapterProperties(Context context, BluetoothService service) { mPropertiesMap = new HashMap<String, String>(); mContext = context; mService = service; } synchronized String getProperty(String name) { if (mPropertiesMap.isEmpty()) { getAllProperties(); } return mPropertiesMap.get(name); } String getObjectPath() { return getProperty("ObjectPath"); } synchronized void clear() { mPropertiesMap.clear(); } synchronized boolean isEmpty() { return mPropertiesMap.isEmpty(); } synchronized void setProperty(String name, String value) { mPropertiesMap.put(name, value); } synchronized void getAllProperties() { mContext.enforceCallingOrSelfPermission( BluetoothService.BLUETOOTH_PERM, "Need BLUETOOTH permission"); mPropertiesMap.clear(); String properties[] = (String[]) mService .getAdapterPropertiesNative(); // The String Array consists of key-value pairs. if (properties == null) { Log.e(TAG, "*Error*: GetAdapterProperties returned NULL"); return; } for (int i = 0; i < properties.length; i++) { String name = properties[i]; String newValue = null; int len; if (name == null) { Log.e(TAG, "Error:Adapter Property at index " + i + " is null"); continue; } if (name.equals("Devices") || name.equals("UUIDs")) { StringBuilder str = new StringBuilder(); len = Integer.valueOf(properties[++i]); for (int j = 0; j < len; j++) { str.append(properties[++i]); str.append(","); } if (len > 0) { newValue = str.toString(); } } else { newValue = properties[++i]; } mPropertiesMap.put(name, newValue); } // Add adapter object path property. String adapterPath = mService.getAdapterPathNative(); if (adapterPath != null) { mPropertiesMap.put("ObjectPath", adapterPath + "/dev_"); } } }
core/java/android/server/BluetoothBondState.java 0 → 100644 +368 −0 Original line number Diff line number Diff line /* * 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.server; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; import android.util.Log; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; /** * Local cache of bonding state. * We keep our own state to track the intermediate state BONDING, which * bluez does not track. * All addresses must be passed in upper case. */ class BluetoothBondState { private static final String TAG = "BluetoothBondState"; private static final boolean DBG = true; private final HashMap<String, Integer> mState = new HashMap<String, Integer>(); private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>(); private static final String AUTO_PAIRING_BLACKLIST = "/etc/bluetooth/auto_pairing.conf"; private static final String DYNAMIC_AUTO_PAIRING_BLACKLIST = "/data/misc/bluetooth/dynamic_auto_pairing.conf"; private ArrayList<String> mAutoPairingAddressBlacklist; private ArrayList<String> mAutoPairingExactNameBlacklist; private ArrayList<String> mAutoPairingPartialNameBlacklist; private ArrayList<String> mAutoPairingFixedPinZerosKeyboardList; // Addresses added to blacklist dynamically based on usage. private ArrayList<String> mAutoPairingDynamicAddressBlacklist; // If this is an outgoing connection, store the address. // There can be only 1 pending outgoing connection at a time, private String mPendingOutgoingBonding; private final Context mContext; private final BluetoothService mService; private final BluetoothInputProfileHandler mBluetoothInputProfileHandler; BluetoothBondState(Context context, BluetoothService service) { mContext = context; mService = service; mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, mService); } synchronized void setPendingOutgoingBonding(String address) { mPendingOutgoingBonding = address; } public synchronized String getPendingOutgoingBonding() { return mPendingOutgoingBonding; } public synchronized void loadBondState() { if (mService.getBluetoothStateInternal() != BluetoothAdapter.STATE_TURNING_ON) { return; } String val = mService.getAdapterProperties().getProperty("Devices"); if (val == null) { return; } String[] bonds = val.split(","); if (bonds == null) { return; } mState.clear(); if (DBG) Log.d(TAG, "found " + bonds.length + " bonded devices"); for (String device : bonds) { mState.put(mService.getAddressFromObjectPath(device).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; } // Check if this was an pending outgoing bonding. // If yes, reset the state. if (oldState == BluetoothDevice.BOND_BONDING) { if (address.equals(mPendingOutgoingBonding)) { mPendingOutgoingBonding = null; } } if (state == BluetoothDevice.BOND_BONDED) { mService.addProfileState(address); } else if (state == BluetoothDevice.BOND_NONE) { mService.removeProfileState(address); } // HID is handled by BluetoothService, other profiles // will be handled by their respective services. mBluetoothInputProfileHandler.setInitialInputDevicePriority( mService.getRemoteDevice(address), state); if (DBG) { Log.d(TAG, address + " bond state " + oldState + " -> " + state + " (" + reason + ")"); } Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mService.getRemoteDevice(address)); intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state); intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState); if (state == BluetoothDevice.BOND_NONE) { 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(BluetoothDevice.EXTRA_REASON, reason); mState.remove(address); } else { mState.put(address, state); } mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); } public boolean isAutoPairingBlacklisted(String address) { if (mAutoPairingAddressBlacklist != null) { for (String blacklistAddress : mAutoPairingAddressBlacklist) { if (address.startsWith(blacklistAddress)) return true; } } if (mAutoPairingDynamicAddressBlacklist != null) { for (String blacklistAddress: mAutoPairingDynamicAddressBlacklist) { if (address.equals(blacklistAddress)) return true; } } String name = mService.getRemoteName(address); if (name != null) { if (mAutoPairingExactNameBlacklist != null) { for (String blacklistName : mAutoPairingExactNameBlacklist) { if (name.equals(blacklistName)) return true; } } if (mAutoPairingPartialNameBlacklist != null) { for (String blacklistName : mAutoPairingPartialNameBlacklist) { if (name.startsWith(blacklistName)) return true; } } } return false; } public boolean isFixedPinZerosAutoPairKeyboard(String address) { // Note: the meaning of blacklist is reversed in this case. // If its in the list, we can go ahead and auto pair since // by default keyboard should have a variable PIN that we don't // auto pair using 0000. if (mAutoPairingFixedPinZerosKeyboardList != null) { for (String blacklistAddress : mAutoPairingFixedPinZerosKeyboardList) { 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_NONE; } return state.intValue(); } /*package*/ 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 (mAutoPairingDynamicAddressBlacklist == null) { mAutoPairingDynamicAddressBlacklist = new ArrayList<String>(); } updateAutoPairingData(address); mAutoPairingDynamicAddressBlacklist.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) { if (mAutoPairingDynamicAddressBlacklist == null) return false; return mAutoPairingDynamicAddressBlacklist.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 void copyAutoPairingData() { FileInputStream in = null; FileOutputStream out = null; try { File file = new File(DYNAMIC_AUTO_PAIRING_BLACKLIST); if (file.exists()) return; in = new FileInputStream(AUTO_PAIRING_BLACKLIST); out= new FileOutputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST); byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); } } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException: copyAutoPairingData " + e); } catch (IOException e) { Log.e(TAG, "IOException: copyAutoPairingData " + e); } finally { try { if (in != null) in.close(); if (out != null) out.close(); } catch (IOException e) {} } } synchronized public void readAutoPairingData() { if (mAutoPairingAddressBlacklist != null) return; copyAutoPairingData(); FileInputStream fstream = null; try { fstream = new FileInputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST); DataInputStream in = new DataInputStream(fstream); BufferedReader file = new BufferedReader(new InputStreamReader(in)); String line; while((line = file.readLine()) != null) { line = line.trim(); if (line.length() == 0 || line.startsWith("//")) continue; String[] value = line.split("="); if (value != null && value.length == 2) { String[] val = value[1].split(","); if (value[0].equalsIgnoreCase("AddressBlacklist")) { mAutoPairingAddressBlacklist = new ArrayList<String>(Arrays.asList(val)); } else if (value[0].equalsIgnoreCase("ExactNameBlacklist")) { mAutoPairingExactNameBlacklist = new ArrayList<String>(Arrays.asList(val)); } else if (value[0].equalsIgnoreCase("PartialNameBlacklist")) { mAutoPairingPartialNameBlacklist = new ArrayList<String>(Arrays.asList(val)); } else if (value[0].equalsIgnoreCase("FixedPinZerosKeyboardBlacklist")) { mAutoPairingFixedPinZerosKeyboardList = new ArrayList<String>(Arrays.asList(val)); } else if (value[0].equalsIgnoreCase("DynamicAddressBlacklist")) { mAutoPairingDynamicAddressBlacklist = new ArrayList<String>(Arrays.asList(val)); } else { Log.e(TAG, "Error parsing Auto pairing blacklist file"); } } } } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException: readAutoPairingData " + e); } catch (IOException e) { Log.e(TAG, "IOException: readAutoPairingData " + e); } finally { if (fstream != null) { try { fstream.close(); } catch (IOException e) { // Ignore } } } } // This function adds a bluetooth address to the auto pairing blacklist // file. These addresses are added to DynamicAddressBlacklistSection private void updateAutoPairingData(String address) { BufferedWriter out = null; try { out = new BufferedWriter(new FileWriter(DYNAMIC_AUTO_PAIRING_BLACKLIST, true)); StringBuilder str = new StringBuilder(); if (mAutoPairingDynamicAddressBlacklist.size() == 0) { str.append("DynamicAddressBlacklist="); } str.append(address); str.append(","); out.write(str.toString()); } catch (FileNotFoundException e) { Log.e(TAG, "FileNotFoundException: updateAutoPairingData " + e); } catch (IOException e) { Log.e(TAG, "IOException: updateAutoPairingData " + e); } finally { if (out != null) { try { out.close(); } catch (IOException e) { // Ignore } } } } }