Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7d4da25c authored by Jaikumar Ganesh's avatar Jaikumar Ganesh Committed by Android (Google) Code Review
Browse files

Merge "Refactor android.server.BluetoothService classes."

parents 0b52dbe3 9a62c9cd
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -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();
+31 −26
Original line number Diff line number Diff line
@@ -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
 */

@@ -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;
@@ -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));
@@ -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"))
@@ -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;
            }
        }
@@ -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) {
@@ -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;
@@ -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) {
@@ -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);
@@ -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
+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_");
        }
    }
}
+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
                }
            }
        }
    }
}
+128 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading