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

Commit ba12af51 authored by Sanket Agarwal's avatar Sanket Agarwal
Browse files

Add support for AVRCP 1.3.

* Add metadata support.
* Add player settings support.
* Add playback support.

A2DP Settings App support.

Bluetooth: A2DP Sink support for Settings App

- add support for A2DP Sink in Settings App. This will enable connection
  initiation and updation on Settings App
- add framework Apis to support A2DP Sink. Any third party Apps can access
  A2DP Sink priority of device and playing state of device
- add support for key to set priority. This manages priority of device for
  A2DP Sink profile

Change-Id: If5f9139f37cdb9d200387877c7801075205c78a0
parent 2c2b5e85
Loading
Loading
Loading
Loading
+83 −0
Original line number Diff line number Diff line
@@ -370,6 +370,89 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
        return null;
    }

    /**
     * Set priority of the profile
     *
     * <p> The device should already be paired.
     *  Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
     * {@link #PRIORITY_OFF},
     *
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
     * permission.
     *
     * @param device Paired bluetooth device
     * @param priority
     * @return true if priority is set, false on error
     * @hide
     */
    public boolean setPriority(BluetoothDevice device, int priority) {
        if (DBG) log("setPriority(" + device + ", " + priority + ")");
        if (mService != null && isEnabled()
            && isValidDevice(device)) {
            if (priority != BluetoothProfile.PRIORITY_OFF &&
                priority != BluetoothProfile.PRIORITY_ON){
                return false;
            }
            try {
                return mService.setPriority(device, priority);
            } catch (RemoteException e) {
                   Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
                   return false;
            }
        }
        if (mService == null) Log.w(TAG, "Proxy not attached to service");
            return false;
    }

    /**
     * Get the priority of the profile.
     *
     * <p> The priority can be any of:
     * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
     * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
     *
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
     *
     * @param device Bluetooth device
     * @return priority of the device
     * @hide
     */
    public int getPriority(BluetoothDevice device) {
        if (VDBG) log("getPriority(" + device + ")");
        if (mService != null && isEnabled()
            && isValidDevice(device)) {
            try {
                return mService.getPriority(device);
            } catch (RemoteException e) {
                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
                return BluetoothProfile.PRIORITY_OFF;
            }
        }
        if (mService == null) Log.w(TAG, "Proxy not attached to service");
        return BluetoothProfile.PRIORITY_OFF;
    }

    /**
     * Check if A2DP profile is streaming music.
     *
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
     *
     * @param device BluetoothDevice device
     */
    public boolean isA2dpPlaying(BluetoothDevice device) {
        if (mService != null && isEnabled()
            && isValidDevice(device)) {
            try {
                return mService.isA2dpPlaying(device);
            } catch (RemoteException e) {
                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
                return false;
            }
        }
        if (mService == null) Log.w(TAG, "Proxy not attached to service");
        return false;
    }

    /**
     * Helper for converting a state to a string.
     *
+182 −26
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.media.MediaMetadata;
import android.media.session.PlaybackState;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -28,8 +30,8 @@ import java.util.ArrayList;
import java.util.List;

/**
 * This class provides the public APIs to control the Bluetooth AVRCP Controller
 * profile.
 * This class provides the public APIs to control the Bluetooth AVRCP Controller. It currently
 * supports player information, playback support and track metadata.
 *
 *<p>BluetoothAvrcpController is a proxy object for controlling the Bluetooth AVRCP
 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
@@ -39,7 +41,7 @@ import java.util.List;
 */
public final class BluetoothAvrcpController implements BluetoothProfile {
    private static final String TAG = "BluetoothAvrcpController";
    private static final boolean DBG = true;
    private static final boolean DBG = false;
    private static final boolean VDBG = false;

    /**
@@ -61,7 +63,63 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
     * receive.
     */
    public static final String ACTION_CONNECTION_STATE_CHANGED =
        "android.bluetooth.acrcp-controller.profile.action.CONNECTION_STATE_CHANGED";
        "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED";

    /**
     * Intent used to broadcast the change in metadata state of playing track on the AVRCP
     * AG.
     *
     * <p>This intent will have the two extras:
     * <ul>
     *    <li> {@link #EXTRA_METADATA} - {@link MediaMetadata} containing the current metadata.</li>
     *    <li> {@link #EXTRA_PLAYBACK} - {@link PlaybackState} containing the current playback
     *    state. </li>
     * </ul>
     */
    public static final String ACTION_TRACK_EVENT =
        "android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT";


    /**
     * Intent used to broadcast the change in player application setting state on AVRCP AG.
     *
     * <p>This intent will have the following extras:
     * <ul>
     *    <li> {@link #EXTRA_PLAYER_SETTING} - {@link BluetoothAvrcpPlayerSettings} containing the
     *    most recent player setting. </li>
     * </ul>
     */
    public static final String ACTION_PLAYER_SETTING =
        "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING";

    public static final String EXTRA_METADATA =
            "android.bluetooth.avrcp-controller.profile.extra.METADATA";

    public static final String EXTRA_PLAYBACK =
            "android.bluetooth.avrcp-controller.profile.extra.PLAYBACK";

    public static final String EXTRA_PLAYER_SETTING =
            "android.bluetooth.avrcp-controller.profile.extra.PLAYER_SETTING";

    /*
     * KeyCoded for Pass Through Commands
     */
    public static final int PASS_THRU_CMD_ID_PLAY = 0x44;
    public static final int PASS_THRU_CMD_ID_PAUSE = 0x46;
    public static final int PASS_THRU_CMD_ID_VOL_UP = 0x41;
    public static final int PASS_THRU_CMD_ID_VOL_DOWN = 0x42;
    public static final int PASS_THRU_CMD_ID_STOP = 0x45;
    public static final int PASS_THRU_CMD_ID_FF = 0x49;
    public static final int PASS_THRU_CMD_ID_REWIND = 0x48;
    public static final int PASS_THRU_CMD_ID_FORWARD = 0x4B;
    public static final int PASS_THRU_CMD_ID_BACKWARD = 0x4C;
    /* Key State Variables */
    public static final int KEY_STATE_PRESSED = 0;
    public static final int KEY_STATE_RELEASED = 1;
    /* Group Navigation Key Codes */
    public static final int PASS_THRU_CMD_ID_NEXT_GRP = 0x00;
    public static final int PASS_THRU_CMD_ID_PREV_GRP = 0x01;


    private Context mContext;
    private ServiceListener mServiceListener;
@@ -223,6 +281,104 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
        if (mService == null) Log.w(TAG, "Proxy not attached to service");
    }

    /**
     * Gets the player application settings.
     *
     * @return the {@link BluetoothAvrcpPlayerSettings} or {@link null} if there is an error.
     */
    public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
        if (DBG) Log.d(TAG, "getPlayerSettings");
        BluetoothAvrcpPlayerSettings settings = null;
        if (mService != null && isEnabled()) {
            try {
                settings = mService.getPlayerSettings(device);
            } catch (RemoteException e) {
                Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
                return null;
            }
        }
        return settings;
    }

    /**
     * Gets the metadata for the current track.
     *
     * This should be usually called when application UI needs to be updated, eg. when the track
     * changes or immediately after connecting and getting the current state.
     * @return the {@link MediaMetadata} or {@link null} if there is an error.
     */
    public MediaMetadata getMetadata(BluetoothDevice device) {
        if (DBG) Log.d(TAG, "getMetadata");
        MediaMetadata metadata = null;
        if (mService != null && isEnabled()) {
            try {
                metadata = mService.getMetadata(device);
            } catch (RemoteException e) {
                Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
                return null;
            }
        }
        return metadata;
    }

    /**
     * Gets the playback state for current track.
     *
     * When the application is first connecting it can use current track state to get playback info.
     * For all further updates it should listen to notifications.
     * @return the {@link PlaybackState} or {@link null} if there is an error.
     */
    public PlaybackState getPlaybackState(BluetoothDevice device) {
        if (DBG) Log.d(TAG, "getPlaybackState");
        PlaybackState playbackState = null;
        if (mService != null && isEnabled()) {
            try {
                playbackState = mService.getPlaybackState(device);
            } catch (RemoteException e) {
                Log.e(TAG,
                    "Error talking to BT service in getPlaybackState() " + e);
                return null;
            }
        }
        return playbackState;
    }

    /**
     * Sets the player app setting for current player.
     * returns true in case setting is supported by remote, false otherwise
     */
    public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
        if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
        if (mService != null && isEnabled()) {
            try {
                return mService.setPlayerApplicationSetting(plAppSetting);
            } catch (RemoteException e) {
                Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
                return false;
            }
        }
        if (mService == null) Log.w(TAG, "Proxy not attached to service");
        return false;
    }

    /*
     * Send Group Navigation Command to Remote.
     * possible keycode values: next_grp, previous_grp defined above
     */
    public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
        Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = " + keyState);
        if (mService != null && isEnabled()) {
            try {
                mService.sendGroupNavigationCmd(device, keyCode, keyState);
                return;
            } catch (RemoteException e) {
                Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
                return;
            }
        }
        if (mService == null) Log.w(TAG, "Proxy not attached to service");
    }

    private final ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            if (DBG) Log.d(TAG, "Proxy object connected");
+19 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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;

parcelable BluetoothAvrcpPlayerSettings;
+189 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

import java.util.HashMap;
import java.util.Map;

/**
 * Class used to identify settings associated with the player on AG.
 *
 * {@hide}
 */
public final class BluetoothAvrcpPlayerSettings implements Parcelable {
    public static final String TAG = "BluetoothAvrcpPlayerSettings";

    /**
     * Equalizer setting.
     */
    public static final int SETTING_EQUALIZER    = 0x01;

    /**
     * Repeat setting.
     */
    public static final int SETTING_REPEAT       = 0x02;

    /**
     * Shuffle setting.
     */
    public static final int SETTING_SHUFFLE      = 0x04;

    /**
     * Scan mode setting.
     */
    public static final int SETTING_SCAN         = 0x08;

    /**
     * Invalid state.
     *
     * Used for returning error codes.
     */
    public static final int STATE_INVALID = -1;

    /**
     * OFF state.
     *
     * Denotes a general OFF state. Applies to all settings.
     */
    public static final int STATE_OFF = 0x00;

    /**
     * ON state.
     *
     * Applies to {@link SETTING_EQUALIZER}.
     */
    public static final int STATE_ON = 0x01;

    /**
     * Single track repeat.
     *
     * Applies only to {@link SETTING_REPEAT}.
     */
    public static final int STATE_SINGLE_TRACK = 0x02;

    /**
     * All track repeat/shuffle.
     *
     * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}.
     */
    public static final int STATE_ALL_TRACK    = 0x03;

    /**
     * Group repeat/shuffle.
     *
     * Applies to {@link SETTING_REPEAT}, {@link SETTING_SHUFFLE} and {@link SETTING_SCAN}.
     */
    public static final int STATE_GROUP        = 0x04;

    /**
     * List of supported settings ORed.
     */
    private int mSettings;

    /**
     * Hash map of current capability values.
     */
    private Map<Integer, Integer> mSettingsValue = new HashMap<Integer, Integer>();

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(mSettings);
        out.writeInt(mSettingsValue.size());
        for (int k : mSettingsValue.keySet()) {
            out.writeInt(k);
            out.writeInt(mSettingsValue.get(k));
        }
    }

    public static final Parcelable.Creator<BluetoothAvrcpPlayerSettings> CREATOR
            = new Parcelable.Creator<BluetoothAvrcpPlayerSettings>() {
        public BluetoothAvrcpPlayerSettings createFromParcel(Parcel in) {
            return new BluetoothAvrcpPlayerSettings(in);
        }

        public BluetoothAvrcpPlayerSettings[] newArray(int size) {
            return new BluetoothAvrcpPlayerSettings[size];
        }
    };

    private BluetoothAvrcpPlayerSettings(Parcel in) {
        mSettings = in.readInt();
        int numSettings = in.readInt();
        for (int i = 0; i < numSettings; i++) {
            mSettingsValue.put(in.readInt(), in.readInt());
        }
    }

    /**
     * Create a new player settings object.
     *
     * @param settings a ORed value of SETTINGS_* defined above.
     */
    public BluetoothAvrcpPlayerSettings(int settings) {
        mSettings = settings;
    }

    /**
     * Get the supported settings.
     *
     * @return int ORed value of supported settings.
     */
    public int getSettings() {
        return mSettings;
    }

    /**
     * Add a setting value.
     *
     * The setting must be part of possible settings in {@link getSettings()}.
     * @param setting setting config.
     * @param value value for the setting.
     * @throws IllegalStateException if the setting is not supported.
     */
    public void addSettingValue(int setting, int value) {
        if ((setting & mSettings) == 0) {
            Log.e(TAG, "Setting not supported: " + setting + " " + mSettings);
            throw new IllegalStateException("Setting not supported: " + setting);
        }
        mSettingsValue.put(setting, value);
    }

    /**
     * Get a setting value.
     *
     * The setting must be part of possible settings in {@link getSettings()}.
     * @param setting setting config.
     * @return value value for the setting.
     * @throws IllegalStateException if the setting is not supported.
     */
    public int getSettingValue(int setting) {
        if ((setting & mSettings) == 0) {
            Log.e(TAG, "Setting not supported: " + setting + " " + mSettings);
            throw new IllegalStateException("Setting not supported: " + setting);
        }
        Integer i = mSettingsValue.get(setting);
        if (i == null) return -1;
        return i;
    }
}
+17 −0
Original line number Diff line number Diff line
@@ -283,6 +283,8 @@ public final class BluetoothClass implements Parcelable {
    public static final int PROFILE_PANU = 4;
    /** @hide */
    public static final int PROFILE_NAP = 5;
    /** @hide */
    public static final int PROFILE_A2DP_SINK = 6;

    /**
     * Check class bits for possible bluetooth profile support.
@@ -310,6 +312,21 @@ public final class BluetoothClass implements Parcelable {
                default:
                    return false;
            }
        } else if (profile == PROFILE_A2DP_SINK) {
            if (hasService(Service.CAPTURE)) {
                return true;
            }
            // By the A2DP spec, srcs must indicate the CAPTURE service.
            // However if some device that do not, we try to
            // match on some other class bits.
            switch (getDeviceClass()) {
                case Device.AUDIO_VIDEO_HIFI_AUDIO:
                case Device.AUDIO_VIDEO_SET_TOP_BOX:
                case Device.AUDIO_VIDEO_VCR :
                    return true;
                default:
                    return false;
            }
        } else if (profile == PROFILE_HEADSET) {
            // The render service class is required by the spec for HFP, so is a
            // pretty good signal
Loading