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

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

Merge "HID profile."

parents 7cbd36f8 545e6708
Loading
Loading
Loading
Loading
+241 −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.bluetooth;

import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * Public API for controlling the Bluetooth HID (Input Device) Profile
 *
 * BluetoothInputDevice is a proxy object used to make calls to Bluetooth Service
 * which handles the HID profile.
 *
 * Creating a BluetoothInputDevice object will initiate a binding with the
 * Bluetooth service. Users of this object should call close() when they
 * are finished, so that this proxy object can unbind from the service.
 *
 * Currently the Bluetooth service runs in the system server and this
 * proxy object will be immediately bound to the service on construction.
 *
 *  @hide
 */
public final class BluetoothInputDevice {
    private static final String TAG = "BluetoothInputDevice";
    private static final boolean DBG = false;

    /** int extra for ACTION_INPUT_DEVICE_STATE_CHANGED */
    public static final String EXTRA_INPUT_DEVICE_STATE =
        "android.bluetooth.inputdevice.extra.INPUT_DEVICE_STATE";
    /** int extra for ACTION_INPUT_DEVICE_STATE_CHANGED */
    public static final String EXTRA_PREVIOUS_INPUT_DEVICE_STATE =
        "android.bluetooth.inputdevice.extra.PREVIOUS_INPUT_DEVICE_STATE";

    /** Indicates the state of an input device has changed.
     * This intent will always contain EXTRA_INPUT_DEVICE_STATE,
     * EXTRA_PREVIOUS_INPUT_DEVICE_STATE and BluetoothDevice.EXTRA_DEVICE
     * extras.
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_INPUT_DEVICE_STATE_CHANGED =
        "android.bluetooth.inputdevice.action.INPUT_DEVICE_STATE_CHANGED";

    public static final int STATE_DISCONNECTED = 0;
    public static final int STATE_CONNECTING   = 1;
    public static final int STATE_CONNECTED    = 2;
    public static final int STATE_DISCONNECTING = 3;

    /**
     * Auto connection, incoming and outgoing connection are allowed at this
     * priority level.
     */
    public static final int PRIORITY_AUTO_CONNECT = 1000;
    /**
     * Incoming and outgoing connection are allowed at this priority level
     */
    public static final int PRIORITY_ON = 100;
    /**
     * Connections to the device are not allowed at this priority level.
     */
    public static final int PRIORITY_OFF = 0;
    /**
     * Default priority level when the device is unpaired.
     */
    public static final int PRIORITY_UNDEFINED = -1;

    private final IBluetooth mService;
    private final Context mContext;

    /**
     * Create a BluetoothInputDevice proxy object for interacting with the local
     * Bluetooth Service which handle the HID profile.
     * @param c Context
     */
    public BluetoothInputDevice(Context c) {
        mContext = c;

        IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
        if (b != null) {
            mService = IBluetooth.Stub.asInterface(b);
        } else {
            Log.w(TAG, "Bluetooth Service not available!");

            // Instead of throwing an exception which prevents people from going
            // into Wireless settings in the emulator. Let it crash later when it is actually used.
            mService = null;
        }
    }

    /** Initiate a connection to an Input device.
     *
     *  This function returns false on error and true if the connection
     *  attempt is being made.
     *
     *  Listen for INPUT_DEVICE_STATE_CHANGED_ACTION to find out when the
     *  connection is completed.
     *  @param device Remote BT device.
     *  @return false on immediate error, true otherwise
     *  @hide
     */
    public boolean connectInputDevice(BluetoothDevice device) {
        if (DBG) log("connectInputDevice(" + device + ")");
        try {
            return mService.connectInputDevice(device);
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
            return false;
        }
    }

    /** Initiate disconnect from an Input Device.
     *  This function return false on error and true if the disconnection
     *  attempt is being made.
     *
     *  Listen for INPUT_DEVICE_STATE_CHANGED_ACTION to find out when
     *  disconnect is completed.
     *
     *  @param device Remote BT device.
     *  @return false on immediate error, true otherwise
     *  @hide
     */
    public boolean disconnectInputDevice(BluetoothDevice device) {
        if (DBG) log("disconnectInputDevice(" + device + ")");
        try {
            return mService.disconnectInputDevice(device);
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
            return false;
        }
    }

    /** Check if a specified InputDevice is connected.
     *
     *  @param device Remote BT device.
     *  @return True if connected , false otherwise and on error.
     *  @hide
     */
    public boolean isInputDeviceConnected(BluetoothDevice device) {
        if (DBG) log("isInputDeviceConnected(" + device + ")");
        int state = getInputDeviceState(device);
        if (state == STATE_CONNECTED) return true;
        return false;
    }

    /** Check if any Input Device is connected.
     *
     * @return a unmodifiable set of connected Input Devices, or null on error.
     * @hide
     */
    public Set<BluetoothDevice> getConnectedInputDevices() {
        if (DBG) log("getConnectedInputDevices()");
        try {
            return Collections.unmodifiableSet(
                    new HashSet<BluetoothDevice>(
                        Arrays.asList(mService.getConnectedInputDevices())));
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
            return null;
        }
    }

    /** Get the state of an Input Device.
     *
     *  @param device Remote BT device.
     *  @return The current state of the Input Device
     *  @hide
     */
    public int getInputDeviceState(BluetoothDevice device) {
        if (DBG) log("getInputDeviceState(" + device + ")");
        try {
            return mService.getInputDeviceState(device);
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
            return STATE_DISCONNECTED;
        }
    }

    /**
     * Set priority of an input device.
     *
     * Priority is a non-negative integer. Priority can take the following
     * values:
     * {@link PRIORITY_ON}, {@link PRIORITY_OFF}, {@link PRIORITY_AUTO_CONNECT}
     *
     * @param device Paired device.
     * @param priority Integer priority
     * @return true if priority is set, false on error
     */
    public boolean setInputDevicePriority(BluetoothDevice device, int priority) {
        if (DBG) log("setInputDevicePriority(" + device + ", " + priority + ")");
        try {
            return mService.setInputDevicePriority(device, priority);
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
            return false;
        }
    }

    /**
     * Get the priority associated with an Input Device.
     *
     * @param device Input Device
     * @return non-negative priority, or negative error code on error.
     */
    public int getInputDevicePriority(BluetoothDevice device) {
        if (DBG) log("getInputDevicePriority(" + device + ")");
        try {
            return mService.getInputDevicePriority(device);
        } catch (RemoteException e) {
            Log.e(TAG, "", e);
            return PRIORITY_OFF;
        }
    }

    private static void log(String msg) {
        Log.d(TAG, msg);
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ public final class BluetoothUuid {
            ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
    public static final ParcelUuid ObexObjectPush =
            ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
    public static final ParcelUuid Hid =
      ParcelUuid.fromString("00000011-0000-1000-8000-00805f9b34fb");

    public static final ParcelUuid[] RESERVED_UUIDS = {
        AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
@@ -82,6 +84,10 @@ public final class BluetoothUuid {
        return uuid.equals(AvrcpTarget);
    }

    public static boolean isInputDevice(ParcelUuid uuid) {
        return uuid.equals(Hid);
    }

    /**
     * Returns true if ParcelUuid is present in uuidArray
     *
+9 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.bluetooth;

import android.bluetooth.IBluetoothCallback;
import android.bluetooth.BluetoothDevice;
import android.os.ParcelUuid;

/**
@@ -72,4 +73,12 @@ interface IBluetooth
    boolean connectHeadset(String address);
    boolean disconnectHeadset(String address);
    boolean notifyIncomingConnection(String address);

    // HID profile APIs
    boolean connectInputDevice(in BluetoothDevice device);
    boolean disconnectInputDevice(in BluetoothDevice device);
    BluetoothDevice[] getConnectedInputDevices();  // change to Set<> once AIDL supports
    int getInputDeviceState(in BluetoothDevice device);
    boolean setInputDevicePriority(in BluetoothDevice device, int priority);
    int getInputDevicePriority(in BluetoothDevice device);
}
+10 −2
Original line number Diff line number Diff line
@@ -2281,6 +2281,14 @@ public final class Settings {
            return ("bluetooth_a2dp_sink_priority_" + address.toUpperCase());
        }

        /**
         * Get the key that retrieves a bluetooth Input Device's priority.
         * @hide
         */
        public static final String getBluetoothInputDevicePriorityKey(String address) {
            return ("bluetooth_input_device_priority_" + address.toUpperCase());
        }

        /**
         * Whether or not data roaming is enabled. (0 = false, 1 = true)
         */
+45 −7
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.content.Intent;
@@ -427,6 +428,20 @@ class BluetoothEventLoop {
        }
    }

    private void onInputDevicePropertyChanged(String path, String[] propValues) {
        String address = mBluetoothService.getAddressFromObjectPath(path);
        if (address == null) {
            Log.e(TAG, "onInputDevicePropertyChanged: Address of the remote device in null");
            return;
        }
        log(" Input Device : Name of Property is:" + propValues[0]);
        boolean state = false;
        if (propValues[1].equals("true")) {
            state = true;
        }
        mBluetoothService.handleInputDevicePropertyChange(address, state);
    }

    private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
        String address = mBluetoothService.getAddressFromObjectPath(objectPath);
        if (address == null) {
@@ -573,6 +588,8 @@ class BluetoothEventLoop {
    }

    private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
        if (!mBluetoothService.isEnabled()) return false;

        String address = mBluetoothService.getAddressFromObjectPath(objectPath);
        if (address == null) {
            Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
@@ -581,15 +598,15 @@ class BluetoothEventLoop {

        boolean authorized = false;
        ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
        BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
        BluetoothDevice device = mAdapter.getRemoteDevice(address);

        // Bluez sends the UUID of the local service being accessed, _not_ the
        // remote service
        if (mBluetoothService.isEnabled() &&
                (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
        if ((BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
              || BluetoothUuid.isAdvAudioDist(uuid)) &&
              !isOtherSinkInNonDisconnectingState(address)) {
            BluetoothDevice device = mAdapter.getRemoteDevice(address);
            BluetoothA2dp a2dp = new BluetoothA2dp(mContext);

            authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
            if (authorized) {
                Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
@@ -597,6 +614,15 @@ class BluetoothEventLoop {
            } else {
                Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
            }
        } else if (BluetoothUuid.isInputDevice(uuid) && !isOtherInputDeviceConnected(address)) {
            BluetoothInputDevice inputDevice = new BluetoothInputDevice(mContext);
            authorized = inputDevice.getInputDevicePriority(device) >
                         BluetoothInputDevice.PRIORITY_OFF;
             if (authorized) {
                 Log.i(TAG, "Allowing incoming HID connection from " + address);
             } else {
                 Log.i(TAG, "Rejecting incoming HID connection from " + address);
             }
        } else {
            Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
        }
@@ -604,7 +630,19 @@ class BluetoothEventLoop {
        return authorized;
    }

    boolean isOtherSinkInNonDisconnectingState(String address) {
    private boolean isOtherInputDeviceConnected(String address) {
        Set<BluetoothDevice> devices =
            mBluetoothService.lookupInputDevicesMatchingStates(new int[] {
                                                BluetoothInputDevice.STATE_CONNECTING,
                                                BluetoothInputDevice.STATE_CONNECTED});

        for (BluetoothDevice device : devices) {
            if (!device.getAddress().equals(address)) return true;
        }
        return false;
    }

    private boolean isOtherSinkInNonDisconnectingState(String address) {
        BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
        Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks();
        if (devices.size() == 0) return false;
Loading