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

Commit 5dcfcd37 authored by zhenchao's avatar zhenchao Committed by Bruno Martins
Browse files

VCP: frameworks and settingsLib changes for VCP Profile

This change contains below implementations:
- Bluetooth VCP profile proxy object implementation.
- VcpProfile implementation in SettingsLib
- VCP Refactor changes in framework and settingsLib

CRs-fixed: 2857030
Change-Id: If7c607d51c92e4215a1bef58dab77a7479131a31
parent 375b13e3
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -3177,6 +3177,9 @@ public final class BluetoothAdapter {
        } else if (profile == BluetoothProfile.GROUP_CLIENT) {
            BluetoothDeviceGroup groupClient = new BluetoothDeviceGroup(context, listener);
            return true;
        } else if (profile == BluetoothProfile.VCP) {
            BluetoothVcp vcp = new BluetoothVcp(context, listener);
            return true;
        } else {
            return false;
        }
@@ -3279,6 +3282,10 @@ public final class BluetoothAdapter {
                BluetoothDeviceGroup groupClient = (BluetoothDeviceGroup) proxy;
                groupClient.close();
                break;
            case BluetoothProfile.VCP:
                BluetoothVcp vcp = (BluetoothVcp) proxy;
                vcp.close();
                break;
        }
    }

+12 −4
Original line number Diff line number Diff line
@@ -224,23 +224,29 @@ public interface BluetoothProfile {
     */
    public int BROADCAST = 24;

    /**
     * VCP
     * @hide
     */
    public static final int VCP = 25;

   /**
     * BC_PROFILE
     * @hide
     */
    public static final int BC_PROFILE = 25;
    public static final int BC_PROFILE = 26;

    /**
     * PC_PROFILE
     * @hide
     */
    public static final int PC_PROFILE = 26;
    public static final int PC_PROFILE = 27;

   /**
     * CC_SERVER
     * @hide
     */
    public static final int CC_SERVER = 27;
    public static final int CC_SERVER = 28;

    /**
     * Max profile ID. This value should be updated whenever a new profile is added to match
@@ -248,7 +254,7 @@ public interface BluetoothProfile {
     *
     * @hide
     */
    int MAX_PROFILE_ID = 27;
    int MAX_PROFILE_ID = 28;

    /**
     * Default priority for devices that we try to auto-connect to and
@@ -448,6 +454,8 @@ public interface BluetoothProfile {
                return "HEARING_AID";
            case BROADCAST:
                return "BROADCAST";
            case VCP:
                return "VCP";
            default:
                return "UNKNOWN_PROFILE";
        }
+337 −0
Original line number Diff line number Diff line
/*
 *Copyright (c) 2020, The Linux Foundation. All rights reserved.
 *Not a contribution
 */

/*
 * Copyright 2018 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.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

/**
 * This class provides the public APIs to control the Bluetooth VCP profile.
 *
 * <p>BluetoothVcp is a proxy object for controlling the Bluetooth VolumeControl
 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
 * the BluetoothVcp proxy object.
 *
 * {@hide}
 */
public final class BluetoothVcp implements BluetoothProfile {
    private static final String TAG = "BluetoothVcp";
    private static final boolean DBG = true;
    private static final boolean VDBG = true;

    /**
     * Intent used to broadcast the change in connection state of the VCP
     * profile.
     *
     * <p>This intent will have 3 extras:
     * <ul>
     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
     * </ul>
     *
     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
     *
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
     * receive.
     */
    public static final String ACTION_CONNECTION_STATE_CHANGED =
            "android.bluetooth.vcp.profile.action.CONNECTION_STATE_CHANGED";

    /**
     * Intent used to broadcast the volume change of the Volume Renderer device
     *
     * <p>This intent will have 1 extras:
     * <ul>
     * <li> {@link #EXTRA_VOLUME} - Current volume settings of Renderer device
     *      device. Range: 0 - 255.
     *
     * @hide
     */
    public static final String ACTION_VOLUME_CHANGED =
            "android.bluetooth.vcp.profile.action.VOLUME_CHANGED";

    /**
     * A int extra field in {@link #ACTION_VOLUME_CHANGED}
     * intents that contains the volume of the Volume Renderer device.
     */
    public static final String EXTRA_VOLUME =
            "android.bluetooth.vcp.profile.extra.VOLUME";

    /**
     * Intent used to broadcast the mute change of the Volume Renderer device
     *
     * <p>This intent will have 1 extras:
     * <ul>
     * <li> {@link #EXTRA_MUTE} - Current mute status of Renderer device
     *      device. False: unmute, True: mute.
     *
     * @hide
     */
    public static final String ACTION_MUTE_CHANGED =
            "android.bluetooth.vcp.profile.action.MUTE_CHANGED";

    /**
     * A boolean extra field in {@link #ACTION_MUTE_CHANGED}
     * intents that contains the mute status of the Volume Renderer device.
     */
    public static final String EXTRA_MUTE =
            "android.bluetooth.vcp.profile.extra.MUTE";

    /**
     * Intent used to broadcast the connection mode change of the VCP
     *
     * <p>This intent will have 1 extras:
     * <ul>
     * <li> {@link #EXTRA_MODE} - Current connection mode of VCP
     * can be any of {@link #MODE_NONE}, {@link #MODE_UNICAST},
     * {@link #MODE_BROADCAST}, {@link #MODE_UNICAST_BROADCAST},
     *
     * @hide
     */
    public static final String ACTION_CONNECTION_MODE_CHANGED =
            "android.bluetooth.vcp.profile.action.CONNECTION_MODE_CHANGED";

    /**
     * A int extra field in {@link #ACTION_CONNECTION_MODE_CHANGED}
     * intents that contains the connection mode of the VCP.
     */
    public static final String EXTRA_MODE =
            "android.bluetooth.vcp.profile.extra.MODE";

    /** None VCP connection */
    public static final int MODE_NONE = 0x00;
    /** VCP connection setup with unicast mode */
    public static final int MODE_UNICAST = 0x01;
    /** VCP connection setup with broadcast mode */
    public static final int MODE_BROADCAST = 0x02;
    /** VCP connection setup with unicast and broadcast mode */
    public static final int MODE_UNICAST_BROADCAST = 0x03;

    private BluetoothAdapter mAdapter;
    private final BluetoothProfileConnector<IBluetoothVcp> mProfileConnector =
            new BluetoothProfileConnector(this, BluetoothProfile.VCP,
                    "BluetoothVcp", IBluetoothVcp.class.getName()) {
                @Override
                public IBluetoothVcp getServiceInterface(IBinder service) {
                    return IBluetoothVcp.Stub.asInterface(
                            Binder.allowBlocking(service));
                }
    };

    /**
     * Create a BluetoothVcp proxy object for interacting with the local
     * Bluetooth VCP service.
     */
    /*package*/ BluetoothVcp(Context context, ServiceListener listener) {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mProfileConnector.connect(context, listener);
    }

    /*package*/ void close() {
        mProfileConnector.disconnect();
    }

    private IBluetoothVcp getService() {
        return mProfileConnector.getService();
    }

    @Override
    public void finalize() {
        close();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<BluetoothDevice> getConnectedDevices() {
        if (VDBG) log("getConnectedDevices()");
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
        if (VDBG) log("getDevicesMatchingStates()");
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getConnectionState(BluetoothDevice device) {
        if (VDBG) log("getConnectionState(" + device + ")");
        final IBluetoothVcp service =
                getService();
        if (service != null && isEnabled() && isValidDevice(device)) {
            try {
                return service.getConnectionState(device);
            } catch (RemoteException e) {
                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
                return BluetoothProfile.STATE_DISCONNECTED;
            }
        }
        if (service == null) Log.w(TAG, "Proxy not attached to service");
        return BluetoothProfile.STATE_DISCONNECTED;
    }

    /**
     * Get current VCP Connection mode
     *
     * @param device: remote device instance
     * @return current connection mode of VCP:
     * {@link #BluetoothVcp.MODE_NONE} if none VCP connection
     * {@link #BluetoothVcp.MODE_UNICAST} if VCP is connected for unicast
     * {@link #BluetoothVcp.MODE_BROADCAST} if VCP is connected for broadcast
     * {@link #BluetoothVcp.MODE_UNICAST_BROADCAST} if VCP
     * is connected for unicast and broadcast
     */
    public int getConnectionMode(BluetoothDevice device) {
        if (VDBG) log("getConnectionMode(" + device + ")");
        final IBluetoothVcp service =
                getService();
        if (service != null && isEnabled() && isValidDevice(device)) {
            try {
                return service.getConnectionMode(device);
            } catch (RemoteException e) {
                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
                return MODE_NONE;
            }
        }
        if (service == null) Log.w(TAG, "Proxy not attached to service");
        return MODE_NONE;
    }

    /**
     * Set absolute volume to remote device via VCP connection
     *
     * @param device: remote device instance
     * @prarm volume: requested volume settings for remote device
     */
    public void setAbsoluteVolume(BluetoothDevice device, int volume) {
        if (VDBG) log("setAbsoluteVolume(" + device + ")");
        final IBluetoothVcp service =
                getService();
        if (service != null && isEnabled() && isValidDevice(device)) {
            try {
                service.setAbsoluteVolume(device, volume);
                return;
            } catch (RemoteException e) {
                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
                return;
            }
        }
        if (service == null) Log.w(TAG, "Proxy not attached to service");
    }

    /**
     * Get current absolute volume of the remote device
     *
     * @param device: remote device instance
     * @return current absolute volume of the remote device
     */
    public int getAbsoluteVolume(BluetoothDevice device) {
        if (VDBG) log("getAbsoluteVolume(" + device + ")");
        final IBluetoothVcp service =
                getService();
        if (service != null && isEnabled() && isValidDevice(device)) {
            try {
                return service.getAbsoluteVolume(device);
            } catch (RemoteException e) {
                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
                return -1;
            }
        }
        if (service == null) Log.w(TAG, "Proxy not attached to service");
        return -1;
    }

    /**
     * Mute or unmute remote device via VCP connection
     *
     * @param device: remote device instance
     * @prarm enableMute: true if mute, false if unmute
     */
    public void setMute(BluetoothDevice device, boolean enableMute) {
        if (VDBG) log("setMute(" + device + ")" +" enableMute: " + enableMute);
        final IBluetoothVcp service =
                getService();
        if (service != null && isEnabled() && isValidDevice(device)) {
            try {
                service.setMute(device, enableMute);
                return;
            } catch (RemoteException e) {
                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
                return;
            }
        }
        if (service == null) Log.w(TAG, "Proxy not attached to service");
    }

    /**
     * Get mute status of remote device
     *
     * @param device: remote device instance
     * @return current mute status of the remote device
     * true if mute status, false if unmute status
     */
    public boolean isMute(BluetoothDevice device) {
        if (VDBG) log("isMute(" + device + ")");
        final IBluetoothVcp service =
                getService();
        if (service != null && isEnabled() && isValidDevice(device)) {
            try {
                return service.isMute(device);
            } catch (RemoteException e) {
                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
                return false;
            }
        }
        if (service == null) Log.w(TAG, "Proxy not attached to service");
        return false;
    }

    private boolean isEnabled() {
        return mAdapter.getState() == BluetoothAdapter.STATE_ON;
    }

    private static boolean isValidDevice(BluetoothDevice device) {
        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
    }

    private static void log(String msg) {
        Log.d(TAG, msg);
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -250,6 +250,9 @@
    <string name="bluetooth_profile_map">Text Messages</string>
    <!-- Bluetooth settings. The user-visible string that is used whenever referring to the SAP profile (sharing SIM card). -->
    <string name="bluetooth_profile_sap">SIM Access</string>
    <!-- Bluetooth settings. The user-visible string that is used
         whenever referring to the vcp profile. -->
    <string name="bluetooth_profile_vcp">Volume control</string>

    <!-- Bluetooth settings. The user-visible string for the setting controlling whether to use a high-quality codec if the device supports it, along with the name of the codec (eg AAC, LDAC, aptX) -->
    <string name="bluetooth_profile_a2dp_high_quality">HD audio: <xliff:g id="codec_name">%1$s</xliff:g></string>
+27 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothVcp;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -157,6 +158,9 @@ public class BluetoothEventManager {
                new BroadcastStateChangedHandler());
        addHandler("android.bluetooth.broadcast.profile.action.BROADCAST_ENCRYPTION_KEY_GENERATED",
                new BroadcastKeyGeneratedHandler());
        // VCP state changed broadcasts
        addHandler(BluetoothVcp.ACTION_CONNECTION_MODE_CHANGED, new VcpModeChangedHandler());
        addHandler(BluetoothVcp.ACTION_VOLUME_CHANGED, new VcpVolumeChangedHandler());

        registerAdapterIntentReceiver();
    }
@@ -645,6 +649,29 @@ public class BluetoothEventManager {
        }
    }

    private class VcpModeChangedHandler implements Handler {
        @Override
        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
            int mode = intent.getIntExtra(BluetoothVcp.EXTRA_MODE, 0);
            if (cachedDevice != null) {
                Log.i(TAG, cachedDevice + " Vcp connection mode change to " + mode);
                cachedDevice.refresh();
            }
        }
    }

    private class VcpVolumeChangedHandler implements Handler {
        @Override
        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
            if (cachedDevice != null) {
                Log.i(TAG, cachedDevice + " Vcp volume change");
                cachedDevice.refresh();
            }
        }
    }

    private void updateCacheDeviceInfo(int groupId, CachedBluetoothDevice cachedDevice) {
        BluetoothDevice device = cachedDevice.getDevice();
        boolean isGroup = cachedDevice.isGroupDevice();
Loading