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

Commit 86231ee0 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge changes I1bc365ab,I1f44a3ef am: 8ee21dee

parents 853add73 8ee21dee
Loading
Loading
Loading
Loading
+354 −55

File changed.

Preview size limit exceeded, changes collapsed.

+84 −33
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;

import com.android.bluetooth.BluetoothEventLogger;
import com.android.bluetooth.audio_util.MediaData;
import com.android.bluetooth.audio_util.MediaPlayerList;
import com.android.bluetooth.audio_util.MediaPlayerWrapper;
@@ -59,6 +60,9 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {
    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
    private final Context mContext;

    private static final int LOG_NB_EVENTS = 100;
    private final BluetoothEventLogger mEventLogger;

    // Media players data
    private MediaPlayerList mMediaPlayerList;
    private MediaData mCurrentData;
@@ -84,9 +88,16 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {
            boolean state = !MediaPlayerWrapper.playstateEquals(mCurrentData.state, data.state);
            boolean queue = !Objects.equals(mCurrentData.queue, data.queue);

            if (DBG) {
                Log.d(TAG, "onMediaUpdated: track_changed=" + metadata + " state=" + state
                        + " queue=" + queue);
            if (metadata || state || queue) {
                mEventLogger.logd(
                        DBG,
                        TAG,
                        "onMediaUpdated: track_changed="
                                + metadata
                                + " state_changed="
                                + state
                                + " queue_changed="
                                + queue);
            }

            mCurrentData = data;
@@ -98,10 +109,15 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {

        @Override
        public void run(boolean availablePlayers, boolean addressedPlayers, boolean uids) {
            if (DBG) {
                Log.d(TAG, "onFolderUpdated: available_players= " + availablePlayers
                        + " addressedPlayers=" + addressedPlayers + " uids=" + uids);
            }
            mEventLogger.logd(
                    DBG,
                    TAG,
                    "onFolderUpdated: available_players= "
                            + availablePlayers
                            + " addressedPlayers="
                            + addressedPlayers
                            + " uids="
                            + uids);
        }
    }

@@ -133,11 +149,11 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {

        if (stateChanged) {
            if (mCurrentData.state != null) {
                if (DBG) Log.d(TAG, "onCurrentPlayerStateUpdated state.");
                MediaState playback_state = playerState2McsState(mCurrentData.state.getState());
                state_map.put(PlayerStateField.PLAYBACK_STATE, playback_state);
                state_map.put(PlayerStateField.OPCODES_SUPPORTED,
                        playerActions2McsSupportedOpcodes(mCurrentData.state.getActions()));

                int opcodes = playerActions2McsSupportedOpcodes(mCurrentData.state.getActions());
                state_map.put(PlayerStateField.OPCODES_SUPPORTED, opcodes);

                if (playback_state != MediaState.INACTIVE) {
                    state_map.put(
@@ -147,6 +163,19 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {
                    state_map.put(PlayerStateField.TRACK_POSITION,
                            getDriftCorrectedTrackPosition(mCurrentData.state));
                }

                mEventLogger.logd(
                        DBG,
                        TAG,
                        "onCurrentPlayerStateUpdated state= "
                                + playback_state
                                + ", supported opcodes= "
                                + Request.SupportedOpcodes.toString(opcodes)
                                + ", track position= "
                                + getDriftCorrectedTrackPosition(mCurrentData.state)
                                + " ("
                                + mCurrentData.state.getPosition()
                                + ")");
            } else {
                // Just update the state and the service should set it's characteristics as required
                state_map.put(PlayerStateField.PLAYBACK_STATE, MediaState.INACTIVE);
@@ -155,11 +184,13 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {

        if (metadataChanged) {
            if (mCurrentData.metadata != null) {
                if (DBG) {
                    Log.d(TAG, "onCurrentPlayerStateUpdated metadata: title= "
                            + mCurrentData.metadata.title + " duration= "
                mEventLogger.logd(
                        DBG,
                        TAG,
                        "onCurrentPlayerStateUpdated metadata: title= '"
                                + mCurrentData.metadata.title
                                + "', duration= "
                                + mCurrentData.metadata.duration);
                }

                state_map.put(PlayerStateField.TRACK_DURATION,
                        mCurrentData.metadata.duration != null
@@ -215,18 +246,20 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {
        } else {
            mMediaPlayerList = new MediaPlayerList(Looper.myLooper(), mContext);
        }

        mEventLogger = new BluetoothEventLogger(LOG_NB_EVENTS, TAG + " event log");
    }

    @Override
    public void onServiceInstanceRegistered(ServiceStatus status,
            MediaControlGattServiceInterface service) {
        if (DBG) Log.d(TAG, "onServiceInstanceRegistered: status= " + status);
        mEventLogger.logd(DBG, TAG, "onServiceInstanceRegistered: status= " + status);
        mGMcsService = service;
    }

    @Override
    public void onServiceInstanceUnregistered(ServiceStatus status) {
        if (DBG) Log.d(TAG, "GMCS onServiceInstanceUnregistered: status= " + status);
        mEventLogger.logd(DBG, TAG, "onServiceInstanceUnregistered: status= " + status);
        mGMcsService = null;
    }

@@ -277,13 +310,13 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {

    @Override
    public long onGetCurrentTrackPosition() {
        if (DBG) Log.d(TAG, "getCurrentTrackPosition");
        mEventLogger.logd(DBG, TAG, "getCurrentTrackPosition");
        return getLatestTrackPosition();
    }

    @Override
    public void onTrackPositionSetRequest(long position) {
        if (DBG) Log.d(TAG, "GMCS onTrackPositionSetRequest");
        mEventLogger.logd(DBG, TAG, "GMCS onTrackPositionSetRequest");

        if (mMediaPlayerList.getActivePlayer() == null) return;
        if ((mCurrentData.state.getActions() & PlaybackState.ACTION_SEEK_TO) != 0) {
@@ -301,41 +334,44 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {

    @Override
    public void onCurrentTrackMetadataRequest() {
        if (DBG) Log.d(TAG, "GMCS onCurrentTrackMetadataRequest");
        mEventLogger.logd(DBG, TAG, "GMCS onCurrentTrackMetadataRequest");
        // FIXME: Seems to be not used right now
    }

    @Override
    public void onPlayingOrderSetRequest(int order) {
        if (DBG) Log.d(TAG, "GMCS onPlayingOrderSetRequest");
        mEventLogger.logd(DBG, TAG, "GMCS onPlayingOrderSetRequest");
        // Notice: MediaPlayerWrapper does not support play order control.
        // Ignore the request for now.
    }

    @Override
    public void onPlaybackSpeedSetRequest(float speed) {
        if (DBG) Log.d(TAG, "GMCS onPlaybackSpeedSetRequest");
        mEventLogger.logd(DBG, TAG, "GMCS onPlaybackSpeedSetRequest");
        if (mMediaPlayerList.getActivePlayer() == null) return;
        mMediaPlayerList.getActivePlayer().setPlaybackSpeed(speed);
    }

    @Override
    public void onSetObjectIdRequest(int objField, long objectId) {
        if (DBG) Log.d(TAG, "GMCS onSetObjectIdRequest");
        mEventLogger.logd(DBG, TAG, "GMCS onSetObjectIdRequest");
        // TODO: Implement once we have the Object Transfer Service
    }

    @Override
    public void onSearchRequest(SearchRequest request) {
        if (DBG) Log.d(TAG, "GMCS onSearchRequest");
        mEventLogger.logd(DBG, TAG, "GMCS onSearchRequest");
        // TODO: Implement once we have the Object Transfer Service
    }


    @Override
    public void onMediaControlRequest(Request request) {
        if (DBG) Log.d(TAG, "GMCS onMediaControlRequest: posted task");

        mEventLogger.logd(
                DBG,
                TAG,
                "GMCS onMediaControlRequest: opcode= "
                        + Request.Opcodes.toString(request.getOpcode()));
        Request.Results status = Request.Results.COMMAND_CANNOT_BE_COMPLETED;

        if (mMediaPlayerList.getActivePlayer() == null && mGMcsService != null) {
@@ -508,9 +544,6 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {
        // PlaybackState.ACTION_PREPARE_FROM_SEARCH
        // PlaybackState.ACTION_PREPARE_FROM_URI

        if (DBG) {
            Log.d(TAG, "updateSupportedOpcodes setting supported opcodes to: " + opcodesSupported);
        }
        return opcodesSupported;
    }

@@ -583,9 +616,15 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {
                        break;
                    case OPCODES_SUPPORTED:
                        if (mCurrentData.state != null) {
                            handled_request_map.put(settings_field,
                            int opcodes =
                                    playerActions2McsSupportedOpcodes(
                                            mCurrentData.state.getActions()));
                                            mCurrentData.state.getActions());
                            handled_request_map.put(settings_field, opcodes);
                            mEventLogger.logd(
                                    DBG,
                                    TAG,
                                    "updateSupportedOpcodes setting supported opcodes to: "
                                            + opcodes);
                        }
                        break;
                }
@@ -689,6 +728,14 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {
            // Only the bluetooth app is allowed to create generic media control service
            boolean isGenericMcs = appToken.equals(mContext.getPackageName());

            mEventLogger.logd(
                    DBG,
                    TAG,
                    "Register MediaControlGattService instance ccid= "
                            + ccid
                            + ", features= "
                            + ServiceFeature.featuresToString(SUPPORTED_FEATURES, "\n\t\t\t"));

            MediaControlGattService svc = new MediaControlGattService(mMcpService, this, ccid);
            svc.init(isGenericMcs ? BluetoothUuid.GENERIC_MEDIA_CONTROL.getUuid()
                    : BluetoothUuid.MEDIA_CONTROL.getUuid());
@@ -758,7 +805,7 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {
    private final Map<String, MediaControlGattServiceInterface> mServiceMap;

    public void unregisterServiceInstance(String appToken) {
        Log.d(TAG, "unregisterServiceInstance");
        mEventLogger.logd(DBG, TAG, "unregisterServiceInstance");

        synchronized (mServiceMap) {
            MediaControlGattServiceInterface service = mServiceMap.get(appToken);
@@ -787,6 +834,10 @@ public class MediaControlProfile implements MediaControlServiceCallbacks {
        sb.append("Media Control Service instance list:\n");
        for (MediaControlGattServiceInterface svc : mServiceMap.values()) {
            svc.dump(sb);
            sb.append("\n");
        }

        mEventLogger.dump(sb);
        sb.append("\n");
    }
}
+16 −0
Original line number Diff line number Diff line
@@ -37,4 +37,20 @@ public enum MediaState {
    public int getValue() {
        return mValue;
    }

    /** Converts the state to string. */
    public static String toString(int value) {
        switch (value) {
            case 0x00:
                return "INACTIVE(0x00)";
            case 0x01:
                return "PLAYING(0x01)";
            case 0x02:
                return "PAUSED(0x02)";
            case 0x03:
                return "SEEKING(0x03)";
            default:
                return "UNKNOWN(0x" + Integer.toHexString(value) + ")";
        }
    }
}
+69 −0
Original line number Diff line number Diff line
@@ -103,6 +103,75 @@ public final class Request {
        public static final int FIRST_GROUP = 0x040000;
        public static final int LAST_GROUP = 0x080000;
        public static final int GOTO_GROUP = 0x100000;

        static String toString(int opcodes) {
            StringBuilder sb = new StringBuilder();
            boolean is_complex = false;

            sb.append("0x" + Integer.toHexString(opcodes) + " (");
            for (int i = 1; i <= opcodes; i <<= 1) {
                int opcode = opcodes & i;
                if (opcode != 0) {
                    if (is_complex) {
                        sb.append(" | ");
                    } else {
                        is_complex = true;
                    }
                    sb.append(singleOpcodeToString(opcode));
                }
            }
            sb.append(")");
            return sb.toString();
        }

        private static String singleOpcodeToString(int opcode) {
            switch (opcode) {
                case 0x01:
                    return "PLAY";
                case 0x02:
                    return "PAUSE";
                case 0x04:
                    return "FAST_REWIND";
                case 0x08:
                    return "FAST_FORWARD";
                case 0x10:
                    return "STOP";
                case 0x20:
                    return "MOVE_RELATIVE";
                case 0x40:
                    return "PREVIOUS_SEGMENT";
                case 0x80:
                    return "NEXT_SEGMENT";
                case 0x0100:
                    return "FIRST_SEGMENT";
                case 0x0200:
                    return "LAST_SEGMENT";
                case 0x0400:
                    return "GOTO_SEGMENT";
                case 0x0800:
                    return "PREVIOUS_TRACK";
                case 0x1000:
                    return "NEXT_TRACK";
                case 0x2000:
                    return "FIRST_TRACK";
                case 0x4000:
                    return "LAST_TRACK";
                case 0x8000:
                    return "GOTO_TRACK";
                case 0x010000:
                    return "PREVIOUS_GROUP";
                case 0x020000:
                    return "NEXT_GROUP";
                case 0x040000:
                    return "FIRST_GROUP";
                case 0x080000:
                    return "LAST_GROUP";
                case 0x100000:
                    return "GOTO_GROUP";
                default:
                    return "0x" + Integer.toHexString(opcode);
            }
        }
    }

    /**
+116 −8
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.le_audio.LeAudioService;
@@ -113,15 +112,16 @@ public class MediaControlGattServiceTest {
    private void prepareConnectedDevice() {
        if (mCurrentDevice == null) {
            mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0);
            List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
            devices.add(mCurrentDevice);
            doReturn(devices).when(mMockGattServer).getConnectedDevices();
            doReturn(true).when(mMockGattServer).isDeviceConnected(eq(mCurrentDevice));
        }
    }

    private void prepareConnectedDevicesCccVal(
            BluetoothGattCharacteristic characteristic, byte[] value) {
        prepareConnectedDevice();
        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
        devices.add(mCurrentDevice);
        doReturn(devices).when(mMockGattServer).getConnectedDevices();
        mMcpService.setCcc(mCurrentDevice, characteristic.getUuid(), 0, value, true);
    }

@@ -1055,7 +1055,7 @@ public class MediaControlGattServiceTest {
    }

    @Test
    public void testCharacteristicReadUnauthorized() {
    public void testCharacteristicReadRejectedUnauthorized() {
        BluetoothGattService service = initAllFeaturesGattService();

        BluetoothGattCharacteristic characteristic =
@@ -1075,7 +1075,31 @@ public class MediaControlGattServiceTest {
    }

    @Test
    public void testCharacteristicWriteUnauthorized() {
    public void testCharacteristicReadUnknownUnauthorized() {
        BluetoothGattService service = initAllFeaturesGattService();

        BluetoothGattCharacteristic characteristic =
                service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);

        prepareConnectedDevice();
        doReturn(BluetoothDevice.ACCESS_UNKNOWN)
                .when(mMockMcpService)
                .getDeviceAuthorization(any(BluetoothDevice.class));

        mMcpService.mServerCallback.onCharacteristicReadRequest(
                mCurrentDevice, 1, 0, characteristic);
        verify(mMockMcpService, times(0)).onDeviceUnauthorized(eq(mCurrentDevice));
        verify(mMockGattServer, times(0))
                .sendResponse(
                        eq(mCurrentDevice),
                        eq(1),
                        eq(BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION),
                        eq(0),
                        any());
    }

    @Test
    public void testCharacteristicWriteRejectedUnauthorized() {
        BluetoothGattService service = initAllFeaturesGattService();
        int track_position = 100;

@@ -1100,7 +1124,29 @@ public class MediaControlGattServiceTest {
    }

    @Test
    public void testDescriptorReadUnauthorized() {
    public void testCharacteristicWriteUnknownUnauthorized() {
        BluetoothGattService service = initAllFeaturesGattService();
        int track_position = 100;

        BluetoothGattCharacteristic characteristic =
                service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);

        ByteBuffer bb = ByteBuffer.allocate(Integer.BYTES + 1).order(ByteOrder.LITTLE_ENDIAN);
        bb.putInt((int) track_position);
        bb.put((byte) 0);

        prepareConnectedDevice();
        doReturn(BluetoothDevice.ACCESS_UNKNOWN)
                .when(mMockMcpService)
                .getDeviceAuthorization(any(BluetoothDevice.class));

        mMcpService.mServerCallback.onCharacteristicWriteRequest(
                mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
        verify(mMockMcpService).onDeviceUnauthorized(eq(mCurrentDevice));
    }

    @Test
    public void testDescriptorReadRejectedUnauthorized() {
        BluetoothGattService service = initAllFeaturesGattService();

        BluetoothGattDescriptor descriptor =
@@ -1121,7 +1167,32 @@ public class MediaControlGattServiceTest {
    }

    @Test
    public void testDescriptorWriteUnauthorized() {
    public void testDescriptorReadUnknownUnauthorized() {
        BluetoothGattService service = initAllFeaturesGattService();

        BluetoothGattDescriptor descriptor =
                service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION)
                        .getDescriptor(UUID_CCCD);
        Assert.assertNotNull(descriptor);

        prepareConnectedDevice();
        doReturn(BluetoothDevice.ACCESS_UNKNOWN)
                .when(mMockMcpService)
                .getDeviceAuthorization(any(BluetoothDevice.class));

        mMcpService.mServerCallback.onDescriptorReadRequest(mCurrentDevice, 1, 0, descriptor);
        verify(mMockMcpService, times(0)).onDeviceUnauthorized(eq(mCurrentDevice));
        verify(mMockGattServer, times(0))
                .sendResponse(
                        eq(mCurrentDevice),
                        eq(1),
                        eq(BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION),
                        eq(0),
                        any());
    }

    @Test
    public void testDescriptorWriteRejectedUnauthorized() {
        BluetoothGattService service = initAllFeaturesGattService();

        BluetoothGattDescriptor descriptor =
@@ -1146,6 +1217,36 @@ public class MediaControlGattServiceTest {
                        eq(BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION), eq(0), any());
    }

    @Test
    public void testDescriptorWriteUnknownUnauthorized() {
        BluetoothGattService service = initAllFeaturesGattService();

        BluetoothGattDescriptor descriptor =
                service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION)
                        .getDescriptor(UUID_CCCD);
        Assert.assertNotNull(descriptor);

        prepareConnectedDevice();
        doReturn(BluetoothDevice.ACCESS_UNKNOWN)
                .when(mMockMcpService)
                .getDeviceAuthorization(any(BluetoothDevice.class));

        ByteBuffer bb = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN);
        bb.put((byte) 0);
        bb.put((byte) 1);

        mMcpService.mServerCallback.onDescriptorWriteRequest(
                mCurrentDevice, 1, descriptor, false, true, 0, bb.array());
        verify(mMockMcpService, times(0)).onDeviceUnauthorized(eq(mCurrentDevice));
        verify(mMockGattServer, times(0))
                .sendResponse(
                        eq(mCurrentDevice),
                        eq(1),
                        eq(BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION),
                        eq(0),
                        any());
    }

    @Test
    public void testUpdatePlayerNameFromNull() {
        BluetoothGattService service = initAllFeaturesGattService();
@@ -1187,4 +1288,11 @@ public class MediaControlGattServiceTest {
        state_map.put(PlayerStateField.PLAYBACK_STATE, playback_state);
        mMcpService.updatePlayerState(state_map);
    }

    @Test
    public void testDumpDoesNotCrash() {
        mMcpService.dump(new StringBuilder());
        BluetoothGattService service = initAllFeaturesGattService();
        mMcpService.dump(new StringBuilder());
    }
}