1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetooth.vc; 18 19 import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED; 20 import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_DEVICE_AVAILABLE; 21 import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED; 22 import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED; 23 import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_VOL_OFFSET_CHANGED; 24 import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED; 25 26 import static java.util.Objects.requireNonNull; 27 28 import android.bluetooth.AudioInputControl.AudioInputStatus; 29 import android.bluetooth.AudioInputControl.AudioInputType; 30 import android.bluetooth.AudioInputControl.GainMode; 31 import android.bluetooth.AudioInputControl.Mute; 32 import android.bluetooth.BluetoothDevice; 33 import android.util.Log; 34 35 import com.android.bluetooth.btservice.AdapterService; 36 import com.android.internal.annotations.VisibleForTesting; 37 38 import java.util.Arrays; 39 import java.util.function.Consumer; 40 41 class VolumeControlNativeCallback { 42 private static final String TAG = VolumeControlNativeCallback.class.getSimpleName(); 43 44 private final AdapterService mAdapterService; 45 private final VolumeControlService mVolumeControlService; 46 VolumeControlNativeCallback( AdapterService adapterService, VolumeControlService volumeControlService)47 VolumeControlNativeCallback( 48 AdapterService adapterService, VolumeControlService volumeControlService) { 49 mAdapterService = requireNonNull(adapterService); 50 mVolumeControlService = requireNonNull(volumeControlService); 51 } 52 getDevice(byte[] address)53 private BluetoothDevice getDevice(byte[] address) { 54 return mAdapterService.getDeviceFromByte(address); 55 } 56 sendMessageToService(Consumer<VolumeControlService> action)57 private void sendMessageToService(Consumer<VolumeControlService> action) { 58 if (!mVolumeControlService.isAvailable()) { 59 StringBuilder sb = new StringBuilder(); 60 Arrays.stream(new Throwable().getStackTrace()) 61 .skip(1) // skip the inlineStackTrace method in the outputted stack trace 62 .forEach(trace -> sb.append(" [at ").append(trace).append("]")); 63 Log.e(TAG, "Action ignored, service not available: " + sb.toString()); 64 return; 65 } 66 action.accept(mVolumeControlService); 67 } 68 69 @VisibleForTesting onConnectionStateChanged(int state, byte[] address)70 void onConnectionStateChanged(int state, byte[] address) { 71 VolumeControlStackEvent event = 72 new VolumeControlStackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 73 event.device = getDevice(address); 74 event.valueInt1 = state; 75 76 Log.d(TAG, "onConnectionStateChanged: " + event); 77 mVolumeControlService.messageFromNative(event); 78 } 79 80 @VisibleForTesting onVolumeStateChanged( int volume, boolean mute, int flags, byte[] address, boolean isAutonomous)81 void onVolumeStateChanged( 82 int volume, boolean mute, int flags, byte[] address, boolean isAutonomous) { 83 VolumeControlStackEvent event = 84 new VolumeControlStackEvent(EVENT_TYPE_VOLUME_STATE_CHANGED); 85 event.device = getDevice(address); 86 event.valueInt1 = -1; 87 event.valueInt2 = volume; 88 event.valueInt3 = flags; 89 event.valueBool1 = mute; 90 event.valueBool2 = isAutonomous; 91 92 Log.d(TAG, "onVolumeStateChanged: " + event); 93 mVolumeControlService.messageFromNative(event); 94 } 95 96 @VisibleForTesting onGroupVolumeStateChanged(int volume, boolean mute, int groupId, boolean isAutonomous)97 void onGroupVolumeStateChanged(int volume, boolean mute, int groupId, boolean isAutonomous) { 98 VolumeControlStackEvent event = 99 new VolumeControlStackEvent(EVENT_TYPE_VOLUME_STATE_CHANGED); 100 event.device = null; 101 event.valueInt1 = groupId; 102 event.valueInt2 = volume; 103 event.valueBool1 = mute; 104 event.valueBool2 = isAutonomous; 105 106 Log.d(TAG, "onGroupVolumeStateChanged: " + event); 107 mVolumeControlService.messageFromNative(event); 108 } 109 110 @VisibleForTesting onDeviceAvailable(int numOfExternalOutputs, int numOfExternalInputs, byte[] address)111 void onDeviceAvailable(int numOfExternalOutputs, int numOfExternalInputs, byte[] address) { 112 VolumeControlStackEvent event = new VolumeControlStackEvent(EVENT_TYPE_DEVICE_AVAILABLE); 113 event.device = getDevice(address); 114 event.valueInt1 = numOfExternalOutputs; 115 event.valueInt2 = numOfExternalInputs; 116 117 Log.d(TAG, "onDeviceAvailable: " + event); 118 mVolumeControlService.messageFromNative(event); 119 } 120 121 @VisibleForTesting onExtAudioOutVolumeOffsetChanged(int externalOutputId, int offset, byte[] address)122 void onExtAudioOutVolumeOffsetChanged(int externalOutputId, int offset, byte[] address) { 123 VolumeControlStackEvent event = 124 new VolumeControlStackEvent(EVENT_TYPE_EXT_AUDIO_OUT_VOL_OFFSET_CHANGED); 125 event.device = getDevice(address); 126 event.valueInt1 = externalOutputId; 127 event.valueInt2 = offset; 128 129 Log.d(TAG, "onExtAudioOutVolumeOffsetChanged: " + event); 130 mVolumeControlService.messageFromNative(event); 131 } 132 133 @VisibleForTesting onExtAudioOutLocationChanged(int externalOutputId, int location, byte[] address)134 void onExtAudioOutLocationChanged(int externalOutputId, int location, byte[] address) { 135 VolumeControlStackEvent event = 136 new VolumeControlStackEvent(EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED); 137 event.device = getDevice(address); 138 event.valueInt1 = externalOutputId; 139 event.valueInt2 = location; 140 141 Log.d(TAG, "onExtAudioOutLocationChanged: " + event); 142 mVolumeControlService.messageFromNative(event); 143 } 144 145 @VisibleForTesting onExtAudioOutDescriptionChanged(int externalOutputId, String descr, byte[] address)146 void onExtAudioOutDescriptionChanged(int externalOutputId, String descr, byte[] address) { 147 VolumeControlStackEvent event = 148 new VolumeControlStackEvent(EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED); 149 event.device = getDevice(address); 150 event.valueInt1 = externalOutputId; 151 event.valueString1 = descr; 152 153 Log.d(TAG, "onExtAudioOutLocationChanged: " + event); 154 mVolumeControlService.messageFromNative(event); 155 } 156 157 @VisibleForTesting onExtAudioInStateChanged( int id, int gainSetting, @Mute int mute, @GainMode int gainMode, byte[] address)158 void onExtAudioInStateChanged( 159 int id, int gainSetting, @Mute int mute, @GainMode int gainMode, byte[] address) { 160 sendMessageToService( 161 s -> 162 s.onExtAudioInStateChanged( 163 getDevice(address), id, gainSetting, mute, gainMode)); 164 } 165 166 @VisibleForTesting onExtAudioInSetGainSettingFailed(int id, byte[] address)167 void onExtAudioInSetGainSettingFailed(int id, byte[] address) { 168 sendMessageToService(s -> s.onExtAudioInSetGainSettingFailed(getDevice(address), id)); 169 } 170 171 @VisibleForTesting onExtAudioInSetMuteFailed(int id, byte[] address)172 void onExtAudioInSetMuteFailed(int id, byte[] address) { 173 sendMessageToService(s -> s.onExtAudioInSetMuteFailed(getDevice(address), id)); 174 } 175 176 @VisibleForTesting onExtAudioInSetGainModeFailed(int id, byte[] address)177 void onExtAudioInSetGainModeFailed(int id, byte[] address) { 178 sendMessageToService(s -> s.onExtAudioInSetGainModeFailed(getDevice(address), id)); 179 } 180 181 @VisibleForTesting onExtAudioInStatusChanged(int id, @AudioInputStatus int status, byte[] address)182 void onExtAudioInStatusChanged(int id, @AudioInputStatus int status, byte[] address) { 183 sendMessageToService(s -> s.onExtAudioInStatusChanged(getDevice(address), id, status)); 184 } 185 186 @VisibleForTesting onExtAudioInTypeChanged(int id, @AudioInputType int type, byte[] address)187 void onExtAudioInTypeChanged(int id, @AudioInputType int type, byte[] address) { 188 sendMessageToService(s -> s.onExtAudioInTypeChanged(getDevice(address), id, type)); 189 } 190 191 @VisibleForTesting onExtAudioInDescriptionChanged( int id, String description, boolean isWritable, byte[] address)192 void onExtAudioInDescriptionChanged( 193 int id, String description, boolean isWritable, byte[] address) { 194 sendMessageToService( 195 s -> 196 s.onExtAudioInDescriptionChanged( 197 getDevice(address), id, description, isWritable)); 198 } 199 200 @VisibleForTesting onExtAudioInGainSettingPropertiesChanged( int id, int unit, int min, int max, byte[] address)201 void onExtAudioInGainSettingPropertiesChanged( 202 int id, int unit, int min, int max, byte[] address) { 203 sendMessageToService( 204 s -> 205 s.onExtAudioInGainSettingPropertiesChanged( 206 getDevice(address), id, unit, min, max)); 207 } 208 } 209