Loading AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/> <uses-permission android:name="android.permission.ACCESS_LAST_KNOWN_CELL_ID"/> <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" /> <permission android:name="android.permission.BROADCAST_CALLLOG_INFO" android:label="Broadcast the call type/duration information" Loading src/com/android/server/telecom/AudioRoute.java 0 → 100644 +285 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 com.android.server.telecom; import static com.android.server.telecom.CallAudioRouteAdapter.BT_AUDIO_CONNECTED; import static com.android.server.telecom.CallAudioRouteAdapter.BT_AUDIO_DISCONNECTED; import static com.android.server.telecom.CallAudioRouteAdapter.PENDING_ROUTE_FAILED; import static com.android.server.telecom.CallAudioRouteAdapter.SPEAKER_OFF; import static com.android.server.telecom.CallAudioRouteAdapter.SPEAKER_ON; import android.annotation.IntDef; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.telecom.Log; import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class AudioRoute { public static class Factory { private final ScheduledExecutorService mScheduledExecutorService = new ScheduledThreadPoolExecutor(1); private final CompletableFuture<AudioRoute> mAudioRouteFuture = new CompletableFuture<>(); public AudioRoute create(@AudioRouteType int type, String bluetoothAddress, AudioManager audioManager) throws RuntimeException { createRetry(type, bluetoothAddress, audioManager, MAX_CONNECTION_RETRIES); try { return mAudioRouteFuture.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException("Error when creating requested audio route"); } } private void createRetry(@AudioRouteType int type, String bluetoothAddress, AudioManager audioManager, int retryCount) { if (retryCount == 0) { mAudioRouteFuture.complete(null); } Log.i(this, "creating AudioRoute with type %s and address %s, retry count %d", DEVICE_TYPE_STRINGS.get(type), bluetoothAddress, retryCount); AudioDeviceInfo routeInfo = null; List<AudioDeviceInfo> infos = audioManager.getAvailableCommunicationDevices(); List<Integer> possibleInfoTypes = AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.get(type); for (AudioDeviceInfo info : infos) { Log.i(this, "type: " + info.getType()); if (possibleInfoTypes != null && possibleInfoTypes.contains(info.getType())) { if (BT_AUDIO_ROUTE_TYPES.contains(type)) { if (bluetoothAddress.equals(info.getAddress())) { routeInfo = info; break; } } else { routeInfo = info; break; } } } if (routeInfo == null) { CompletableFuture<Boolean> future = new CompletableFuture<>(); mScheduledExecutorService.schedule(new Runnable() { @Override public void run() { createRetry(type, bluetoothAddress, audioManager, retryCount - 1); } }, RETRY_TIME_DELAY, TimeUnit.MILLISECONDS); } else { mAudioRouteFuture.complete(new AudioRoute(type, bluetoothAddress, routeInfo)); } } } private static final long RETRY_TIME_DELAY = 500L; private static final int MAX_CONNECTION_RETRIES = 2; public static final int TYPE_INVALID = 0; public static final int TYPE_EARPIECE = 1; public static final int TYPE_WIRED = 2; public static final int TYPE_SPEAKER = 3; public static final int TYPE_DOCK = 4; public static final int TYPE_BLUETOOTH_SCO = 5; public static final int TYPE_BLUETOOTH_HA = 6; public static final int TYPE_BLUETOOTH_LE = 7; @IntDef(prefix = "TYPE", value = { TYPE_INVALID, TYPE_EARPIECE, TYPE_WIRED, TYPE_SPEAKER, TYPE_DOCK, TYPE_BLUETOOTH_SCO, TYPE_BLUETOOTH_HA, TYPE_BLUETOOTH_LE }) @Retention(RetentionPolicy.SOURCE) public @interface AudioRouteType {} private @AudioRouteType int mAudioRouteType; private String mBluetoothAddress; private AudioDeviceInfo mInfo; public static final Set<Integer> BT_AUDIO_DEVICE_INFO_TYPES = Set.of( AudioDeviceInfo.TYPE_BLE_HEADSET, AudioDeviceInfo.TYPE_BLE_SPEAKER, AudioDeviceInfo.TYPE_BLE_BROADCAST, AudioDeviceInfo.TYPE_HEARING_AID, AudioDeviceInfo.TYPE_BLUETOOTH_SCO ); public static final Set<Integer> BT_AUDIO_ROUTE_TYPES = Set.of( AudioRoute.TYPE_BLUETOOTH_SCO, AudioRoute.TYPE_BLUETOOTH_HA, AudioRoute.TYPE_BLUETOOTH_LE ); public static final HashMap<Integer, String> DEVICE_TYPE_STRINGS; static { DEVICE_TYPE_STRINGS = new HashMap<>(); DEVICE_TYPE_STRINGS.put(TYPE_EARPIECE, "TYPE_EARPIECE"); DEVICE_TYPE_STRINGS.put(TYPE_WIRED, "TYPE_WIRED_HEADSET"); DEVICE_TYPE_STRINGS.put(TYPE_SPEAKER, "TYPE_SPEAKER"); DEVICE_TYPE_STRINGS.put(TYPE_DOCK, "TYPE_DOCK"); DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_SCO, "TYPE_BLUETOOTH_SCO"); DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_HA, "TYPE_BLUETOOTH_HA"); DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_LE, "TYPE_BLUETOOTH_LE"); } public static final HashMap<Integer, Integer> DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE; static { DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE = new HashMap<>(); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, TYPE_EARPIECE); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, TYPE_SPEAKER); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_WIRED_HEADSET, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLUETOOTH_SCO, TYPE_BLUETOOTH_SCO); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_DEVICE, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_ACCESSORY, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_DOCK, TYPE_DOCK); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_HEADSET, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_HEARING_AID, TYPE_BLUETOOTH_HA); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_HEADSET, TYPE_BLUETOOTH_LE); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_SPEAKER, TYPE_BLUETOOTH_LE); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_BROADCAST, TYPE_BLUETOOTH_LE); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_DOCK_ANALOG, TYPE_DOCK); } private static final HashMap<Integer, List<Integer>> AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE; static { AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE = new HashMap<>(); List<Integer> earpieceDeviceInfoTypes = new ArrayList<>(); earpieceDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_EARPIECE, earpieceDeviceInfoTypes); List<Integer> wiredDeviceInfoTypes = new ArrayList<>(); wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_WIRED_HEADSET); wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_WIRED_HEADPHONES); wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_USB_DEVICE); wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_USB_ACCESSORY); wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_USB_HEADSET); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_WIRED, wiredDeviceInfoTypes); List<Integer> speakerDeviceInfoTypes = new ArrayList<>(); speakerDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_SPEAKER, speakerDeviceInfoTypes); List<Integer> dockDeviceInfoTypes = new ArrayList<>(); dockDeviceInfoTypes.add(AudioDeviceInfo.TYPE_DOCK); dockDeviceInfoTypes.add(AudioDeviceInfo.TYPE_DOCK_ANALOG); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_DOCK, dockDeviceInfoTypes); List<Integer> bluetoothScoDeviceInfoTypes = new ArrayList<>(); bluetoothScoDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP); bluetoothScoDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLUETOOTH_SCO); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_SCO, bluetoothScoDeviceInfoTypes); List<Integer> bluetoothHearingAidDeviceInfoTypes = new ArrayList<>(); bluetoothHearingAidDeviceInfoTypes.add(AudioDeviceInfo.TYPE_HEARING_AID); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_HA, bluetoothHearingAidDeviceInfoTypes); List<Integer> bluetoothLeDeviceInfoTypes = new ArrayList<>(); bluetoothLeDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLE_HEADSET); bluetoothLeDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLE_SPEAKER); bluetoothLeDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLE_BROADCAST); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_LE, bluetoothLeDeviceInfoTypes); } int getType() { return mAudioRouteType; } String getBluetoothAddress() { return mBluetoothAddress; } // Invoked when entered pending route whose dest route is this route void onDestRouteAsPendingRoute(boolean active, PendingAudioRoute pendingAudioRoute, AudioManager audioManager) { if (pendingAudioRoute.isActive() && !active) { audioManager.clearCommunicationDevice(); } else if (active) { if (mAudioRouteType == TYPE_BLUETOOTH_SCO) { pendingAudioRoute.addMessage(BT_AUDIO_CONNECTED); } else if (mAudioRouteType == TYPE_SPEAKER) { pendingAudioRoute.addMessage(SPEAKER_ON); } if (!audioManager.setCommunicationDevice(mInfo)) { pendingAudioRoute.onMessageReceived(PENDING_ROUTE_FAILED); } } } void onOrigRouteAsPendingRoute(boolean active, PendingAudioRoute pendingAudioRoute, AudioManager audioManager) { if (active) { if (mAudioRouteType == TYPE_BLUETOOTH_SCO) { pendingAudioRoute.addMessage(BT_AUDIO_DISCONNECTED); } else if (mAudioRouteType == TYPE_SPEAKER) { pendingAudioRoute.addMessage(SPEAKER_OFF); } } } @VisibleForTesting public AudioRoute(@AudioRouteType int type, String bluetoothAddress, AudioDeviceInfo info) { mAudioRouteType = type; mBluetoothAddress = bluetoothAddress; mInfo = info; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof AudioRoute otherRoute)) { return false; } if (mAudioRouteType != otherRoute.getType()) { return false; } return !BT_AUDIO_ROUTE_TYPES.contains(mAudioRouteType) || mBluetoothAddress.equals( otherRoute.getBluetoothAddress()); } @Override public int hashCode() { return Objects.hash(mAudioRouteType, mBluetoothAddress); } @Override public String toString() { return getClass().getSimpleName() + "[Type=" + DEVICE_TYPE_STRINGS.get(mAudioRouteType) + ", Address=" + ((mBluetoothAddress != null) ? mBluetoothAddress : "invalid") + "]"; } } src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java +4 −9 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.telecom; import static com.android.server.telecom.AudioRoute.BT_AUDIO_DEVICE_INFO_TYPES; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.media.AudioDeviceInfo; Loading @@ -28,7 +30,6 @@ import com.android.server.telecom.flags.Flags; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.concurrent.Semaphore; /** Loading @@ -41,12 +42,6 @@ public class CallAudioCommunicationDeviceTracker { // Use -1 indicates device is not set for any communication use case private static final int sAUDIO_DEVICE_TYPE_INVALID = -1; // Possible bluetooth audio device types private static final Set<Integer> sBT_AUDIO_DEVICE_TYPES = Set.of( AudioDeviceInfo.TYPE_BLE_HEADSET, AudioDeviceInfo.TYPE_HEARING_AID, AudioDeviceInfo.TYPE_BLUETOOTH_SCO ); private AudioManager mAudioManager; private BluetoothRouteManager mBluetoothRouteManager; private int mAudioDeviceType = sAUDIO_DEVICE_TYPE_INVALID; Loading Loading @@ -101,7 +96,7 @@ public class CallAudioCommunicationDeviceTracker { mLock.tryAcquire(); } // There is only one audio device type associated with each type of BT device. boolean isBtDevice = sBT_AUDIO_DEVICE_TYPES.contains(audioDeviceType); boolean isBtDevice = BT_AUDIO_DEVICE_INFO_TYPES.contains(audioDeviceType); Log.i(this, "setCommunicationDevice: type = %s, isBtDevice = %s, btDevice = %s", audioDeviceType, isBtDevice, btDevice); Loading Loading @@ -182,7 +177,7 @@ public class CallAudioCommunicationDeviceTracker { mLock.tryAcquire(); } // There is only one audio device type associated with each type of BT device. boolean isBtDevice = sBT_AUDIO_DEVICE_TYPES.contains(audioDeviceType); boolean isBtDevice = BT_AUDIO_DEVICE_INFO_TYPES.contains(audioDeviceType); Log.i(this, "clearCommunicationDevice: type = %s, isBtDevice = %s", audioDeviceType, isBtDevice); Loading src/com/android/server/telecom/CallAudioRouteAdapter.java +116 −0 Original line number Diff line number Diff line package com.android.server.telecom; import android.bluetooth.BluetoothDevice; import android.os.Handler; import android.telecom.CallAudioState; import android.util.SparseArray; import com.android.internal.util.IndentingPrintWriter; public interface CallAudioRouteAdapter { /** Valid values for msg.what */ int CONNECT_WIRED_HEADSET = 1; int DISCONNECT_WIRED_HEADSET = 2; int CONNECT_DOCK = 5; int DISCONNECT_DOCK = 6; int BLUETOOTH_DEVICE_LIST_CHANGED = 7; int BT_ACTIVE_DEVICE_PRESENT = 8; int BT_ACTIVE_DEVICE_GONE = 9; int BT_DEVICE_ADDED = 10; int BT_DEVICE_REMOVED = 11; int SWITCH_EARPIECE = 1001; int SWITCH_BLUETOOTH = 1002; int SWITCH_HEADSET = 1003; int SWITCH_SPEAKER = 1004; // Wired headset, earpiece, or speakerphone, in that order of precedence. int SWITCH_BASELINE_ROUTE = 1005; // Messages denoting that the speakerphone was turned on/off. Used to update state when we // weren't the ones who turned it on/off int SPEAKER_ON = 1006; int SPEAKER_OFF = 1007; // Messages denoting that the streaming route switch request was sent. int STREAMING_FORCE_ENABLED = 1008; int STREAMING_FORCE_DISABLED = 1009; int USER_SWITCH_EARPIECE = 1101; int USER_SWITCH_BLUETOOTH = 1102; int USER_SWITCH_HEADSET = 1103; int USER_SWITCH_SPEAKER = 1104; int USER_SWITCH_BASELINE_ROUTE = 1105; int UPDATE_SYSTEM_AUDIO_ROUTE = 1201; // These three messages indicate state changes that come from BluetoothRouteManager. // They may be triggered by the BT stack doing something on its own or they may be sent after // we request that the BT stack do something. Any logic for these messages should take into // account the possibility that the event indicated has already been processed (i.e. handling // should be idempotent). int BT_AUDIO_DISCONNECTED = 1301; int BT_AUDIO_CONNECTED = 1302; int BT_AUDIO_PENDING = 1303; int MUTE_ON = 3001; int MUTE_OFF = 3002; int TOGGLE_MUTE = 3003; int MUTE_EXTERNALLY_CHANGED = 3004; int SWITCH_FOCUS = 4001; // Used in testing to execute verifications. Not compatible with subsessions. int RUN_RUNNABLE = 9001; // Used for PendingAudioRoute to notify audio switch success int EXIT_PENDING_ROUTE = 10001; // Used for PendingAudioRoute to notify audio switch timeout int PENDING_ROUTE_TIMEOUT = 10002; // Used for PendingAudioRoute to notify audio switch failed int PENDING_ROUTE_FAILED = 10003; /** Valid values for mAudioFocusType */ int NO_FOCUS = 1; int ACTIVE_FOCUS = 2; int RINGING_FOCUS = 3; /** Valid arg for BLUETOOTH_DEVICE_LIST_CHANGED */ int DEVICE_CONNECTED = 1; int DEVICE_DISCONNECTED = 2; SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{ put(CONNECT_WIRED_HEADSET, "CONNECT_WIRED_HEADSET"); put(DISCONNECT_WIRED_HEADSET, "DISCONNECT_WIRED_HEADSET"); put(CONNECT_DOCK, "CONNECT_DOCK"); put(DISCONNECT_DOCK, "DISCONNECT_DOCK"); put(BLUETOOTH_DEVICE_LIST_CHANGED, "BLUETOOTH_DEVICE_LIST_CHANGED"); put(BT_ACTIVE_DEVICE_PRESENT, "BT_ACTIVE_DEVICE_PRESENT"); put(BT_ACTIVE_DEVICE_GONE, "BT_ACTIVE_DEVICE_GONE"); put(BT_DEVICE_ADDED, "BT_DEVICE_ADDED"); put(BT_DEVICE_REMOVED, "BT_DEVICE_REMOVED"); put(SWITCH_EARPIECE, "SWITCH_EARPIECE"); put(SWITCH_BLUETOOTH, "SWITCH_BLUETOOTH"); put(SWITCH_HEADSET, "SWITCH_HEADSET"); put(SWITCH_SPEAKER, "SWITCH_SPEAKER"); put(SWITCH_BASELINE_ROUTE, "SWITCH_BASELINE_ROUTE"); put(SPEAKER_ON, "SPEAKER_ON"); put(SPEAKER_OFF, "SPEAKER_OFF"); put(USER_SWITCH_EARPIECE, "USER_SWITCH_EARPIECE"); put(USER_SWITCH_BLUETOOTH, "USER_SWITCH_BLUETOOTH"); put(USER_SWITCH_HEADSET, "USER_SWITCH_HEADSET"); put(USER_SWITCH_SPEAKER, "USER_SWITCH_SPEAKER"); put(USER_SWITCH_BASELINE_ROUTE, "USER_SWITCH_BASELINE_ROUTE"); put(UPDATE_SYSTEM_AUDIO_ROUTE, "UPDATE_SYSTEM_AUDIO_ROUTE"); put(BT_AUDIO_DISCONNECTED, "BT_AUDIO_DISCONNECTED"); put(BT_AUDIO_CONNECTED, "BT_AUDIO_CONNECTED"); put(BT_AUDIO_PENDING, "BT_AUDIO_PENDING"); put(MUTE_ON, "MUTE_ON"); put(MUTE_OFF, "MUTE_OFF"); put(TOGGLE_MUTE, "TOGGLE_MUTE"); put(MUTE_EXTERNALLY_CHANGED, "MUTE_EXTERNALLY_CHANGED"); put(SWITCH_FOCUS, "SWITCH_FOCUS"); put(RUN_RUNNABLE, "RUN_RUNNABLE"); put(EXIT_PENDING_ROUTE, "EXIT_PENDING_ROUTE"); }}; void initialize(); void sendMessageWithSessionInfo(int message); void sendMessageWithSessionInfo(int message, int arg); void sendMessageWithSessionInfo(int message, int arg, String data); void sendMessageWithSessionInfo(int message, int arg, BluetoothDevice bluetoothDevice); void sendMessage(int message, Runnable r); void setCallAudioManager(CallAudioManager callAudioManager); CallAudioState getCurrentCallAudioState(); Loading src/com/android/server/telecom/CallAudioRouteController.java +472 −6 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/> <uses-permission android:name="android.permission.ACCESS_LAST_KNOWN_CELL_ID"/> <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" /> <permission android:name="android.permission.BROADCAST_CALLLOG_INFO" android:label="Broadcast the call type/duration information" Loading
src/com/android/server/telecom/AudioRoute.java 0 → 100644 +285 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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 com.android.server.telecom; import static com.android.server.telecom.CallAudioRouteAdapter.BT_AUDIO_CONNECTED; import static com.android.server.telecom.CallAudioRouteAdapter.BT_AUDIO_DISCONNECTED; import static com.android.server.telecom.CallAudioRouteAdapter.PENDING_ROUTE_FAILED; import static com.android.server.telecom.CallAudioRouteAdapter.SPEAKER_OFF; import static com.android.server.telecom.CallAudioRouteAdapter.SPEAKER_ON; import android.annotation.IntDef; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.telecom.Log; import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class AudioRoute { public static class Factory { private final ScheduledExecutorService mScheduledExecutorService = new ScheduledThreadPoolExecutor(1); private final CompletableFuture<AudioRoute> mAudioRouteFuture = new CompletableFuture<>(); public AudioRoute create(@AudioRouteType int type, String bluetoothAddress, AudioManager audioManager) throws RuntimeException { createRetry(type, bluetoothAddress, audioManager, MAX_CONNECTION_RETRIES); try { return mAudioRouteFuture.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException("Error when creating requested audio route"); } } private void createRetry(@AudioRouteType int type, String bluetoothAddress, AudioManager audioManager, int retryCount) { if (retryCount == 0) { mAudioRouteFuture.complete(null); } Log.i(this, "creating AudioRoute with type %s and address %s, retry count %d", DEVICE_TYPE_STRINGS.get(type), bluetoothAddress, retryCount); AudioDeviceInfo routeInfo = null; List<AudioDeviceInfo> infos = audioManager.getAvailableCommunicationDevices(); List<Integer> possibleInfoTypes = AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.get(type); for (AudioDeviceInfo info : infos) { Log.i(this, "type: " + info.getType()); if (possibleInfoTypes != null && possibleInfoTypes.contains(info.getType())) { if (BT_AUDIO_ROUTE_TYPES.contains(type)) { if (bluetoothAddress.equals(info.getAddress())) { routeInfo = info; break; } } else { routeInfo = info; break; } } } if (routeInfo == null) { CompletableFuture<Boolean> future = new CompletableFuture<>(); mScheduledExecutorService.schedule(new Runnable() { @Override public void run() { createRetry(type, bluetoothAddress, audioManager, retryCount - 1); } }, RETRY_TIME_DELAY, TimeUnit.MILLISECONDS); } else { mAudioRouteFuture.complete(new AudioRoute(type, bluetoothAddress, routeInfo)); } } } private static final long RETRY_TIME_DELAY = 500L; private static final int MAX_CONNECTION_RETRIES = 2; public static final int TYPE_INVALID = 0; public static final int TYPE_EARPIECE = 1; public static final int TYPE_WIRED = 2; public static final int TYPE_SPEAKER = 3; public static final int TYPE_DOCK = 4; public static final int TYPE_BLUETOOTH_SCO = 5; public static final int TYPE_BLUETOOTH_HA = 6; public static final int TYPE_BLUETOOTH_LE = 7; @IntDef(prefix = "TYPE", value = { TYPE_INVALID, TYPE_EARPIECE, TYPE_WIRED, TYPE_SPEAKER, TYPE_DOCK, TYPE_BLUETOOTH_SCO, TYPE_BLUETOOTH_HA, TYPE_BLUETOOTH_LE }) @Retention(RetentionPolicy.SOURCE) public @interface AudioRouteType {} private @AudioRouteType int mAudioRouteType; private String mBluetoothAddress; private AudioDeviceInfo mInfo; public static final Set<Integer> BT_AUDIO_DEVICE_INFO_TYPES = Set.of( AudioDeviceInfo.TYPE_BLE_HEADSET, AudioDeviceInfo.TYPE_BLE_SPEAKER, AudioDeviceInfo.TYPE_BLE_BROADCAST, AudioDeviceInfo.TYPE_HEARING_AID, AudioDeviceInfo.TYPE_BLUETOOTH_SCO ); public static final Set<Integer> BT_AUDIO_ROUTE_TYPES = Set.of( AudioRoute.TYPE_BLUETOOTH_SCO, AudioRoute.TYPE_BLUETOOTH_HA, AudioRoute.TYPE_BLUETOOTH_LE ); public static final HashMap<Integer, String> DEVICE_TYPE_STRINGS; static { DEVICE_TYPE_STRINGS = new HashMap<>(); DEVICE_TYPE_STRINGS.put(TYPE_EARPIECE, "TYPE_EARPIECE"); DEVICE_TYPE_STRINGS.put(TYPE_WIRED, "TYPE_WIRED_HEADSET"); DEVICE_TYPE_STRINGS.put(TYPE_SPEAKER, "TYPE_SPEAKER"); DEVICE_TYPE_STRINGS.put(TYPE_DOCK, "TYPE_DOCK"); DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_SCO, "TYPE_BLUETOOTH_SCO"); DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_HA, "TYPE_BLUETOOTH_HA"); DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_LE, "TYPE_BLUETOOTH_LE"); } public static final HashMap<Integer, Integer> DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE; static { DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE = new HashMap<>(); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, TYPE_EARPIECE); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, TYPE_SPEAKER); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_WIRED_HEADSET, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLUETOOTH_SCO, TYPE_BLUETOOTH_SCO); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_DEVICE, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_ACCESSORY, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_DOCK, TYPE_DOCK); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_HEADSET, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_HEARING_AID, TYPE_BLUETOOTH_HA); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_HEADSET, TYPE_BLUETOOTH_LE); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_SPEAKER, TYPE_BLUETOOTH_LE); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_BROADCAST, TYPE_BLUETOOTH_LE); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_DOCK_ANALOG, TYPE_DOCK); } private static final HashMap<Integer, List<Integer>> AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE; static { AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE = new HashMap<>(); List<Integer> earpieceDeviceInfoTypes = new ArrayList<>(); earpieceDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_EARPIECE, earpieceDeviceInfoTypes); List<Integer> wiredDeviceInfoTypes = new ArrayList<>(); wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_WIRED_HEADSET); wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_WIRED_HEADPHONES); wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_USB_DEVICE); wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_USB_ACCESSORY); wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_USB_HEADSET); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_WIRED, wiredDeviceInfoTypes); List<Integer> speakerDeviceInfoTypes = new ArrayList<>(); speakerDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_SPEAKER, speakerDeviceInfoTypes); List<Integer> dockDeviceInfoTypes = new ArrayList<>(); dockDeviceInfoTypes.add(AudioDeviceInfo.TYPE_DOCK); dockDeviceInfoTypes.add(AudioDeviceInfo.TYPE_DOCK_ANALOG); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_DOCK, dockDeviceInfoTypes); List<Integer> bluetoothScoDeviceInfoTypes = new ArrayList<>(); bluetoothScoDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP); bluetoothScoDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLUETOOTH_SCO); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_SCO, bluetoothScoDeviceInfoTypes); List<Integer> bluetoothHearingAidDeviceInfoTypes = new ArrayList<>(); bluetoothHearingAidDeviceInfoTypes.add(AudioDeviceInfo.TYPE_HEARING_AID); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_HA, bluetoothHearingAidDeviceInfoTypes); List<Integer> bluetoothLeDeviceInfoTypes = new ArrayList<>(); bluetoothLeDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLE_HEADSET); bluetoothLeDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLE_SPEAKER); bluetoothLeDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLE_BROADCAST); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_LE, bluetoothLeDeviceInfoTypes); } int getType() { return mAudioRouteType; } String getBluetoothAddress() { return mBluetoothAddress; } // Invoked when entered pending route whose dest route is this route void onDestRouteAsPendingRoute(boolean active, PendingAudioRoute pendingAudioRoute, AudioManager audioManager) { if (pendingAudioRoute.isActive() && !active) { audioManager.clearCommunicationDevice(); } else if (active) { if (mAudioRouteType == TYPE_BLUETOOTH_SCO) { pendingAudioRoute.addMessage(BT_AUDIO_CONNECTED); } else if (mAudioRouteType == TYPE_SPEAKER) { pendingAudioRoute.addMessage(SPEAKER_ON); } if (!audioManager.setCommunicationDevice(mInfo)) { pendingAudioRoute.onMessageReceived(PENDING_ROUTE_FAILED); } } } void onOrigRouteAsPendingRoute(boolean active, PendingAudioRoute pendingAudioRoute, AudioManager audioManager) { if (active) { if (mAudioRouteType == TYPE_BLUETOOTH_SCO) { pendingAudioRoute.addMessage(BT_AUDIO_DISCONNECTED); } else if (mAudioRouteType == TYPE_SPEAKER) { pendingAudioRoute.addMessage(SPEAKER_OFF); } } } @VisibleForTesting public AudioRoute(@AudioRouteType int type, String bluetoothAddress, AudioDeviceInfo info) { mAudioRouteType = type; mBluetoothAddress = bluetoothAddress; mInfo = info; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof AudioRoute otherRoute)) { return false; } if (mAudioRouteType != otherRoute.getType()) { return false; } return !BT_AUDIO_ROUTE_TYPES.contains(mAudioRouteType) || mBluetoothAddress.equals( otherRoute.getBluetoothAddress()); } @Override public int hashCode() { return Objects.hash(mAudioRouteType, mBluetoothAddress); } @Override public String toString() { return getClass().getSimpleName() + "[Type=" + DEVICE_TYPE_STRINGS.get(mAudioRouteType) + ", Address=" + ((mBluetoothAddress != null) ? mBluetoothAddress : "invalid") + "]"; } }
src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java +4 −9 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.telecom; import static com.android.server.telecom.AudioRoute.BT_AUDIO_DEVICE_INFO_TYPES; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.media.AudioDeviceInfo; Loading @@ -28,7 +30,6 @@ import com.android.server.telecom.flags.Flags; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.concurrent.Semaphore; /** Loading @@ -41,12 +42,6 @@ public class CallAudioCommunicationDeviceTracker { // Use -1 indicates device is not set for any communication use case private static final int sAUDIO_DEVICE_TYPE_INVALID = -1; // Possible bluetooth audio device types private static final Set<Integer> sBT_AUDIO_DEVICE_TYPES = Set.of( AudioDeviceInfo.TYPE_BLE_HEADSET, AudioDeviceInfo.TYPE_HEARING_AID, AudioDeviceInfo.TYPE_BLUETOOTH_SCO ); private AudioManager mAudioManager; private BluetoothRouteManager mBluetoothRouteManager; private int mAudioDeviceType = sAUDIO_DEVICE_TYPE_INVALID; Loading Loading @@ -101,7 +96,7 @@ public class CallAudioCommunicationDeviceTracker { mLock.tryAcquire(); } // There is only one audio device type associated with each type of BT device. boolean isBtDevice = sBT_AUDIO_DEVICE_TYPES.contains(audioDeviceType); boolean isBtDevice = BT_AUDIO_DEVICE_INFO_TYPES.contains(audioDeviceType); Log.i(this, "setCommunicationDevice: type = %s, isBtDevice = %s, btDevice = %s", audioDeviceType, isBtDevice, btDevice); Loading Loading @@ -182,7 +177,7 @@ public class CallAudioCommunicationDeviceTracker { mLock.tryAcquire(); } // There is only one audio device type associated with each type of BT device. boolean isBtDevice = sBT_AUDIO_DEVICE_TYPES.contains(audioDeviceType); boolean isBtDevice = BT_AUDIO_DEVICE_INFO_TYPES.contains(audioDeviceType); Log.i(this, "clearCommunicationDevice: type = %s, isBtDevice = %s", audioDeviceType, isBtDevice); Loading
src/com/android/server/telecom/CallAudioRouteAdapter.java +116 −0 Original line number Diff line number Diff line package com.android.server.telecom; import android.bluetooth.BluetoothDevice; import android.os.Handler; import android.telecom.CallAudioState; import android.util.SparseArray; import com.android.internal.util.IndentingPrintWriter; public interface CallAudioRouteAdapter { /** Valid values for msg.what */ int CONNECT_WIRED_HEADSET = 1; int DISCONNECT_WIRED_HEADSET = 2; int CONNECT_DOCK = 5; int DISCONNECT_DOCK = 6; int BLUETOOTH_DEVICE_LIST_CHANGED = 7; int BT_ACTIVE_DEVICE_PRESENT = 8; int BT_ACTIVE_DEVICE_GONE = 9; int BT_DEVICE_ADDED = 10; int BT_DEVICE_REMOVED = 11; int SWITCH_EARPIECE = 1001; int SWITCH_BLUETOOTH = 1002; int SWITCH_HEADSET = 1003; int SWITCH_SPEAKER = 1004; // Wired headset, earpiece, or speakerphone, in that order of precedence. int SWITCH_BASELINE_ROUTE = 1005; // Messages denoting that the speakerphone was turned on/off. Used to update state when we // weren't the ones who turned it on/off int SPEAKER_ON = 1006; int SPEAKER_OFF = 1007; // Messages denoting that the streaming route switch request was sent. int STREAMING_FORCE_ENABLED = 1008; int STREAMING_FORCE_DISABLED = 1009; int USER_SWITCH_EARPIECE = 1101; int USER_SWITCH_BLUETOOTH = 1102; int USER_SWITCH_HEADSET = 1103; int USER_SWITCH_SPEAKER = 1104; int USER_SWITCH_BASELINE_ROUTE = 1105; int UPDATE_SYSTEM_AUDIO_ROUTE = 1201; // These three messages indicate state changes that come from BluetoothRouteManager. // They may be triggered by the BT stack doing something on its own or they may be sent after // we request that the BT stack do something. Any logic for these messages should take into // account the possibility that the event indicated has already been processed (i.e. handling // should be idempotent). int BT_AUDIO_DISCONNECTED = 1301; int BT_AUDIO_CONNECTED = 1302; int BT_AUDIO_PENDING = 1303; int MUTE_ON = 3001; int MUTE_OFF = 3002; int TOGGLE_MUTE = 3003; int MUTE_EXTERNALLY_CHANGED = 3004; int SWITCH_FOCUS = 4001; // Used in testing to execute verifications. Not compatible with subsessions. int RUN_RUNNABLE = 9001; // Used for PendingAudioRoute to notify audio switch success int EXIT_PENDING_ROUTE = 10001; // Used for PendingAudioRoute to notify audio switch timeout int PENDING_ROUTE_TIMEOUT = 10002; // Used for PendingAudioRoute to notify audio switch failed int PENDING_ROUTE_FAILED = 10003; /** Valid values for mAudioFocusType */ int NO_FOCUS = 1; int ACTIVE_FOCUS = 2; int RINGING_FOCUS = 3; /** Valid arg for BLUETOOTH_DEVICE_LIST_CHANGED */ int DEVICE_CONNECTED = 1; int DEVICE_DISCONNECTED = 2; SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{ put(CONNECT_WIRED_HEADSET, "CONNECT_WIRED_HEADSET"); put(DISCONNECT_WIRED_HEADSET, "DISCONNECT_WIRED_HEADSET"); put(CONNECT_DOCK, "CONNECT_DOCK"); put(DISCONNECT_DOCK, "DISCONNECT_DOCK"); put(BLUETOOTH_DEVICE_LIST_CHANGED, "BLUETOOTH_DEVICE_LIST_CHANGED"); put(BT_ACTIVE_DEVICE_PRESENT, "BT_ACTIVE_DEVICE_PRESENT"); put(BT_ACTIVE_DEVICE_GONE, "BT_ACTIVE_DEVICE_GONE"); put(BT_DEVICE_ADDED, "BT_DEVICE_ADDED"); put(BT_DEVICE_REMOVED, "BT_DEVICE_REMOVED"); put(SWITCH_EARPIECE, "SWITCH_EARPIECE"); put(SWITCH_BLUETOOTH, "SWITCH_BLUETOOTH"); put(SWITCH_HEADSET, "SWITCH_HEADSET"); put(SWITCH_SPEAKER, "SWITCH_SPEAKER"); put(SWITCH_BASELINE_ROUTE, "SWITCH_BASELINE_ROUTE"); put(SPEAKER_ON, "SPEAKER_ON"); put(SPEAKER_OFF, "SPEAKER_OFF"); put(USER_SWITCH_EARPIECE, "USER_SWITCH_EARPIECE"); put(USER_SWITCH_BLUETOOTH, "USER_SWITCH_BLUETOOTH"); put(USER_SWITCH_HEADSET, "USER_SWITCH_HEADSET"); put(USER_SWITCH_SPEAKER, "USER_SWITCH_SPEAKER"); put(USER_SWITCH_BASELINE_ROUTE, "USER_SWITCH_BASELINE_ROUTE"); put(UPDATE_SYSTEM_AUDIO_ROUTE, "UPDATE_SYSTEM_AUDIO_ROUTE"); put(BT_AUDIO_DISCONNECTED, "BT_AUDIO_DISCONNECTED"); put(BT_AUDIO_CONNECTED, "BT_AUDIO_CONNECTED"); put(BT_AUDIO_PENDING, "BT_AUDIO_PENDING"); put(MUTE_ON, "MUTE_ON"); put(MUTE_OFF, "MUTE_OFF"); put(TOGGLE_MUTE, "TOGGLE_MUTE"); put(MUTE_EXTERNALLY_CHANGED, "MUTE_EXTERNALLY_CHANGED"); put(SWITCH_FOCUS, "SWITCH_FOCUS"); put(RUN_RUNNABLE, "RUN_RUNNABLE"); put(EXIT_PENDING_ROUTE, "EXIT_PENDING_ROUTE"); }}; void initialize(); void sendMessageWithSessionInfo(int message); void sendMessageWithSessionInfo(int message, int arg); void sendMessageWithSessionInfo(int message, int arg, String data); void sendMessageWithSessionInfo(int message, int arg, BluetoothDevice bluetoothDevice); void sendMessage(int message, Runnable r); void setCallAudioManager(CallAudioManager callAudioManager); CallAudioState getCurrentCallAudioState(); Loading
src/com/android/server/telecom/CallAudioRouteController.java +472 −6 File changed.Preview size limit exceeded, changes collapsed. Show changes