diff options
35 files changed, 1001 insertions, 1012 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index de4694a086f0..f21518c7f83d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -47544,6 +47544,10 @@ package android.view { field public static final int KEYCODE_CUT = 277; // 0x115 field public static final int KEYCODE_D = 32; // 0x20 field public static final int KEYCODE_DEL = 67; // 0x43 + field public static final int KEYCODE_DEMO_APP_1 = 301; // 0x12d + field public static final int KEYCODE_DEMO_APP_2 = 302; // 0x12e + field public static final int KEYCODE_DEMO_APP_3 = 303; // 0x12f + field public static final int KEYCODE_DEMO_APP_4 = 304; // 0x130 field public static final int KEYCODE_DPAD_CENTER = 23; // 0x17 field public static final int KEYCODE_DPAD_DOWN = 20; // 0x14 field public static final int KEYCODE_DPAD_DOWN_LEFT = 269; // 0x10d @@ -47575,6 +47579,10 @@ package android.view { field public static final int KEYCODE_F7 = 137; // 0x89 field public static final int KEYCODE_F8 = 138; // 0x8a field public static final int KEYCODE_F9 = 139; // 0x8b + field public static final int KEYCODE_FEATURED_APP_1 = 297; // 0x129 + field public static final int KEYCODE_FEATURED_APP_2 = 298; // 0x12a + field public static final int KEYCODE_FEATURED_APP_3 = 299; // 0x12b + field public static final int KEYCODE_FEATURED_APP_4 = 300; // 0x12c field public static final int KEYCODE_FOCUS = 80; // 0x50 field public static final int KEYCODE_FORWARD = 125; // 0x7d field public static final int KEYCODE_FORWARD_DEL = 112; // 0x70 @@ -47740,6 +47748,14 @@ package android.view { field public static final int KEYCODE_U = 49; // 0x31 field public static final int KEYCODE_UNKNOWN = 0; // 0x0 field public static final int KEYCODE_V = 50; // 0x32 + field public static final int KEYCODE_VIDEO_APP_1 = 289; // 0x121 + field public static final int KEYCODE_VIDEO_APP_2 = 290; // 0x122 + field public static final int KEYCODE_VIDEO_APP_3 = 291; // 0x123 + field public static final int KEYCODE_VIDEO_APP_4 = 292; // 0x124 + field public static final int KEYCODE_VIDEO_APP_5 = 293; // 0x125 + field public static final int KEYCODE_VIDEO_APP_6 = 294; // 0x126 + field public static final int KEYCODE_VIDEO_APP_7 = 295; // 0x127 + field public static final int KEYCODE_VIDEO_APP_8 = 296; // 0x128 field public static final int KEYCODE_VOICE_ASSIST = 231; // 0xe7 field public static final int KEYCODE_VOLUME_DOWN = 25; // 0x19 field public static final int KEYCODE_VOLUME_MUTE = 164; // 0xa4 diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 9af6c1b340b6..3a35f249d177 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -113,6 +113,7 @@ package android.media { public class AudioManager { method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int); method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int); + method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BtProfileConnectionInfo); method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setA2dpSuspended(boolean); method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean); method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpEnabled(boolean); @@ -122,6 +123,19 @@ package android.media { field public static final int FLAG_FROM_KEY = 4096; // 0x1000 } + public final class BtProfileConnectionInfo implements android.os.Parcelable { + method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int); + method public int describeContents(); + method public boolean getIsLeOutput(); + method public int getProfile(); + method public boolean getSuppressNoisyIntent(); + method public int getVolume(); + method @NonNull public static android.media.BtProfileConnectionInfo hearingAidInfo(boolean); + method @NonNull public static android.media.BtProfileConnectionInfo leAudio(boolean, boolean); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.media.BtProfileConnectionInfo> CREATOR; + } + public class MediaMetadataRetriever implements java.lang.AutoCloseable { field public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40; // 0x28 } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 6ccdf9185517..d0e659bf36e9 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2748,7 +2748,7 @@ package android.view { method public static String actionToString(int); method public final void setDisplayId(int); field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800 - field public static final int LAST_KEYCODE = 288; // 0x120 + field public static final int LAST_KEYCODE = 304; // 0x130 } public final class KeyboardShortcutGroup implements android.os.Parcelable { diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index dac8ffe5e008..cf00cbd584a4 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -2022,6 +2022,7 @@ public final class BluetoothAdapter { * {@link BluetoothProfile#HEADSET}, * {@link BluetoothProfile#A2DP}, * {@link BluetoothProfile#HEARING_AID} + * {@link BluetoothProfile#LE_AUDIO} * @return A list of active bluetooth devices * @throws IllegalArgumentException If profile is not one of {@link ActiveDeviceProfile} * @hide @@ -2034,12 +2035,14 @@ public final class BluetoothAdapter { public @NonNull List<BluetoothDevice> getActiveDevices(@ActiveDeviceProfile int profile) { if (profile != BluetoothProfile.HEADSET && profile != BluetoothProfile.A2DP - && profile != BluetoothProfile.HEARING_AID) { + && profile != BluetoothProfile.HEARING_AID + && profile != BluetoothProfile.LE_AUDIO) { Log.e(TAG, "Invalid profile param value in getActiveDevices"); throw new IllegalArgumentException("Profiles must be one of " + "BluetoothProfile.A2DP, " + "BluetoothProfile.HEARING_AID, or" - + "BluetoothProfile.HEARING_AID"); + + "BluetoothProfile.HEARING_AID" + + "BluetoothProfile.LE_AUDIO"); } try { mServiceLock.readLock().lock(); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index d811040b6bb2..e781c2fce2b2 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1147,6 +1147,10 @@ public class Intent implements Parcelable, Cloneable { * numbers. Applications can <strong>dial</strong> emergency numbers using * {@link #ACTION_DIAL}, however. * + * <p>Note: An app filling the {@link android.app.role.RoleManager#ROLE_DIALER} role should use + * {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls rather than + * relying on this intent. + * * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} * and above and declares as using the {@link android.Manifest.permission#CALL_PHONE} * permission which is not granted, then attempting to use this action will diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index f3a8b5d79ea0..9f3a847e12eb 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -5365,5 +5365,14 @@ public final class Telephony { */ public static final String COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS = "d2d_sharing_contacts"; + + /** + * TelephonyProvider column name for NR Advanced calling + * Determines if the user has enabled VoNR settings for this subscription. + * + * @hide + */ + public static final String COLUMN_NR_ADVANCED_CALLING_ENABLED = + "nr_advanced_calling_enabled"; } } diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index cda9b233576c..ba6f4eb7e598 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -830,13 +830,45 @@ public class KeyEvent extends InputEvent implements Parcelable { * consuming content. May be consumed by system to set account globally. */ public static final int KEYCODE_PROFILE_SWITCH = 288; - - /** + /** Key code constant: Video Application key #1. */ + public static final int KEYCODE_VIDEO_APP_1 = 289; + /** Key code constant: Video Application key #2. */ + public static final int KEYCODE_VIDEO_APP_2 = 290; + /** Key code constant: Video Application key #3. */ + public static final int KEYCODE_VIDEO_APP_3 = 291; + /** Key code constant: Video Application key #4. */ + public static final int KEYCODE_VIDEO_APP_4 = 292; + /** Key code constant: Video Application key #5. */ + public static final int KEYCODE_VIDEO_APP_5 = 293; + /** Key code constant: Video Application key #6. */ + public static final int KEYCODE_VIDEO_APP_6 = 294; + /** Key code constant: Video Application key #7. */ + public static final int KEYCODE_VIDEO_APP_7 = 295; + /** Key code constant: Video Application key #8. */ + public static final int KEYCODE_VIDEO_APP_8 = 296; + /** Key code constant: Featured Application key #1. */ + public static final int KEYCODE_FEATURED_APP_1 = 297; + /** Key code constant: Featured Application key #2. */ + public static final int KEYCODE_FEATURED_APP_2 = 298; + /** Key code constant: Featured Application key #3. */ + public static final int KEYCODE_FEATURED_APP_3 = 299; + /** Key code constant: Featured Application key #4. */ + public static final int KEYCODE_FEATURED_APP_4 = 300; + /** Key code constant: Demo Application key #1. */ + public static final int KEYCODE_DEMO_APP_1 = 301; + /** Key code constant: Demo Application key #2. */ + public static final int KEYCODE_DEMO_APP_2 = 302; + /** Key code constant: Demo Application key #3. */ + public static final int KEYCODE_DEMO_APP_3 = 303; + /** Key code constant: Demo Application key #4. */ + public static final int KEYCODE_DEMO_APP_4 = 304; + + /** * Integer value of the last KEYCODE. Increases as new keycodes are added to KeyEvent. * @hide */ @TestApi - public static final int LAST_KEYCODE = KEYCODE_PROFILE_SWITCH; + public static final int LAST_KEYCODE = KEYCODE_DEMO_APP_4; // NOTE: If you add a new keycode here you must also add it to: // isSystem() diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 8143cf953f19..ffce4617eec6 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -157,6 +157,7 @@ public final class SurfaceControl implements Parcelable { private static native boolean nativeGetAnimationFrameStats(WindowAnimationFrameStats outStats); private static native long[] nativeGetPhysicalDisplayIds(); + private static native long nativeGetPrimaryPhysicalDisplayId(); private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId); private static native IBinder nativeCreateDisplay(String name, boolean secure); private static native void nativeDestroyDisplay(IBinder displayToken); @@ -2266,6 +2267,15 @@ public final class SurfaceControl implements Parcelable { } /** + * Exposed to identify the correct display to apply the primary display orientation. Avoid using + * for any other purpose. + * @hide + */ + public static long getPrimaryPhysicalDisplayId() { + return nativeGetPrimaryPhysicalDisplayId(); + } + + /** * @hide */ public static IBinder getPhysicalDisplayToken(long physicalDisplayId) { diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 8d12df226ffe..e47718305b7c 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -889,6 +889,12 @@ static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) { return array; } +static jlong nativeGetPrimaryPhysicalDisplayId(JNIEnv* env, jclass clazz) { + PhysicalDisplayId displayId; + SurfaceComposerClient::getPrimaryPhysicalDisplayId(&displayId); + return static_cast<jlong>(displayId.value); +} + static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong physicalDisplayId) { sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(physicalDisplayId)); @@ -1879,6 +1885,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeReleaseFrameRateFlexibilityToken }, {"nativeGetPhysicalDisplayIds", "()[J", (void*)nativeGetPhysicalDisplayIds }, + {"nativeGetPrimaryPhysicalDisplayId", "()J", + (void*)nativeGetPrimaryPhysicalDisplayId }, {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;", (void*)nativeGetPhysicalDisplayToken }, {"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;", diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index a5f505176d5d..dc92e10abf0d 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1984,6 +1984,22 @@ <enum name="KEYCODE_THUMBS_UP" value="286" /> <enum name="KEYCODE_THUMBS_DOWN" value="287" /> <enum name="KEYCODE_PROFILE_SWITCH" value="288" /> + <enum name="KEYCODE_VIDEO_APP_1" value="289" /> + <enum name="KEYCODE_VIDEO_APP_2" value="290" /> + <enum name="KEYCODE_VIDEO_APP_3" value="291" /> + <enum name="KEYCODE_VIDEO_APP_4" value="292" /> + <enum name="KEYCODE_VIDEO_APP_5" value="293" /> + <enum name="KEYCODE_VIDEO_APP_6" value="294" /> + <enum name="KEYCODE_VIDEO_APP_7" value="295" /> + <enum name="KEYCODE_VIDEO_APP_8" value="296" /> + <enum name="KEYCODE_FEATURED_APP_1" value="297" /> + <enum name="KEYCODE_FEATURED_APP_2" value="298" /> + <enum name="KEYCODE_FEATURED_APP_3" value="299" /> + <enum name="KEYCODE_FEATURED_APP_4" value="300" /> + <enum name="KEYCODE_DEMO_APP_1" value="301" /> + <enum name="KEYCODE_DEMO_APP_2" value="302" /> + <enum name="KEYCODE_DEMO_APP_3" value="303" /> + <enum name="KEYCODE_DEMO_APP_4" value="304" /> </attr> <!-- ***************************************************************** --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index a350d14d92ab..be5063f3609a 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -64,16 +64,74 @@ <!-- Displayed when a carrier does not support call forwarding queries when roaming. --> <string name="mmiErrorWhileRoaming">Can not change call forwarding settings from your phone while you are roaming.</string> - <!-- Displayed when a phone feature such as call barring was activated. --> + <!-- Displayed when a phone feature such as call forwarding, call waiting, or call barring was + activated. + Used to build messages of the form: + <X> + <Y> + + Where <X> is the name of the service which was enabled. Can be one of: + {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding}, + {@link #PwdMmi Password change}, {@link #CwMmi Call waiting}, + {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID}, + {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}. + And <Y> is {@link #serviceEnabled} (this string). + --> <string name="serviceEnabled">Service was enabled.</string> <!-- Displayed in front of the list of a set of service classes - (voice, data, fax, etc.) that were enabled. --> + (voice, data, fax, etc.) that call waiting were enabled for. + Will be used with messages of the form: + <X> + <Y1> + ... + <Yn> + Where <X> is {@link #serviceEnabledFor} (this string) and <Y>..<Yn> can be: + {@link #serviceClassData}, {@link #serviceClassVoice}, {@link #serviceClassFAX}, + {@link #serviceClassSMS}, {@link #serviceClassDataAsync}, {@link #serviceClassDataSync}, + {@link #serviceClassPacket}, {@link #serviceClassPAD}. + --> <string name="serviceEnabledFor">Service was enabled for:</string> - <!-- Displayed when a phone feature such as call forwarding was deactivated. --> + <!-- Displayed when a phone feature such as call forwarding was deactivated. + Used to build messages of the form: + <X> + <Y> + + Where <X> is the name of the service which was disabled. Can be one of: + {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding}, + {@link #PwdMmi Password change}, {@link #CwMmi Call waiting}, + {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID}, + {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}. + And <Y> is {@link #serviceDisabled} (this string). + --> <string name="serviceDisabled">Service has been disabled.</string> - <!-- Displayed when a phone property such as a SIM password was registered. --> + <!-- Displayed when a phone property such as a SIM password was registered. Registration + entails setting up a service for use, where {@link #serviceEnabled} entails enabling a + previously registered service. + Used to build messages of the form: + <X> + <Y> + + Where <X> is the name of the service which was registered. Can be one of: + {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding}, + {@link #PwdMmi Password change}, {@link #CwMmi Call waiting}, + {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID}, + {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}. + And <Y> is {@link #serviceRegistered} (this string). --> <string name="serviceRegistered">Registration was successful.</string> - <!-- Displayed when a phone property such as a SIM password was erased. --> + <!-- Displayed when a phone property such as a SIM password was erased. + Erasure is the opposite of {@link #serviceRegistered} and entails removal of a service. + + Used to build messages of the form: + <X> + <Y> + + Where <X> is the name of the service which was registered. Can be one of: + {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding}, + {@link #PwdMmi Password change}, {@link #CwMmi Call waiting}, + {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID}, + {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}. + And <Y> is {@link #serviceErased} (this string). + --> <string name="serviceErased">Erasure was successful.</string> <!-- Displayed when a SIM password was entered incorrectly. --> <string name="passwordIncorrect">Incorrect password.</string> @@ -107,23 +165,32 @@ [CHAR LIMIT=10] --> <string name="meid">MEID</string> - <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. --> + <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="ClipMmi">Incoming Caller ID</string> - <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. --> + <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="ClirMmi">Hide Outgoing Caller ID</string> - <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID. --> + <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="ColpMmi">Connected Line ID</string> - <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction. --> + <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="ColrMmi">Connected Line ID Restriction</string> - <!-- Displayed as the title for a success/failure report enabling/disabling call forwarding. --> + <!-- Displayed as the title for a success/failure report enabling/disabling call forwarding. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="CfMmi">Call forwarding</string> - <!-- Displayed as the title for a success/failure report enabling/disabling call waiting. --> + <!-- Displayed as the title for a success/failure report enabling/disabling call waiting. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="CwMmi">Call waiting</string> - <!-- Displayed as the title for a success/failure report enabling/disabling call barring. --> + <!-- Displayed as the title for a success/failure report enabling/disabling call barring. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="BaMmi">Call barring</string> - <!-- Displayed as the title for a success/failure report changing the SIM password. --> + <!-- Displayed as the title for a success/failure report changing the SIM password. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="PwdMmi">Password change</string> - <!-- Displayed as the title for a success/failure report changing the SIM PIN. --> + <!-- Displayed as the title for a success/failure report changing the SIM PIN. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="PinMmi">PIN change</string> <string name="CnipMmi">Calling number present</string> <string name="CnirMmi">Calling number restricted</string> @@ -198,21 +265,29 @@ <string name="peerTtyModeOff">Peer requested TTY Mode OFF</string> <!-- Mappings between TS 27.007 +CFCC/+CLCK "service classes" and human-readable strings--> <skip /> - <!-- Example: Service was enabled for: Voice, Data --> + <!-- Example: Service was enabled for: Voice, Data + See {@link #serviceEnabledFor}.--> <string name="serviceClassVoice">Voice</string> - <!-- Example: Service was enabled for: Voice, Data --> + <!-- Example: Service was enabled for: Voice, Data. + See {@link #serviceEnabledFor}. --> <string name="serviceClassData">Data</string> - <!-- Example: Service was enabled for: Voice, FAX --> + <!-- Example: Service was enabled for: Voice, FAX + See {@link #serviceEnabledFor}. --> <string name="serviceClassFAX">FAX</string> - <!-- Example: Service was enabled for: Voice, SMS --> + <!-- Example: Service was enabled for: Voice, SMS + See {@link #serviceEnabledFor}. --> <string name="serviceClassSMS">SMS</string> - <!-- Meaning: asynchronous data. Example: Service was enabled for: Voice, Async --> + <!-- Meaning: asynchronous data. Example: Service was enabled for: Voice, Async + See {@link #serviceEnabledFor}. --> <string name="serviceClassDataAsync">Async</string> - <!-- Meaning: synchronous data. Example: Service was enabled for: Voice, Async --> + <!-- Meaning: synchronous data. Example: Service was enabled for: Voice, Async + See {@link #serviceEnabledFor}. --> <string name="serviceClassDataSync">Sync</string> - <!-- Meaning: packet data. Example: Service was enabled for: Voice, Packet --> + <!-- Meaning: packet data. Example: Service was enabled for: Voice, Packet + See {@link #serviceEnabledFor}. --> <string name="serviceClassPacket">Packet</string> - <!-- Meaning: unknown. Example: Service was enabled for: Voice, PAD --> + <!-- Meaning: unknown. Example: Service was enabled for: Voice, PAD + See {@link #serviceEnabledFor}. --> <string name="serviceClassPAD">PAD</string> <!-- CDMA Roaming Indicator Strings (non ERI)--> <skip /> diff --git a/core/tests/coretests/OWNERS b/core/tests/coretests/OWNERS new file mode 100644 index 000000000000..0fb0c3021486 --- /dev/null +++ b/core/tests/coretests/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index b67988ee9646..c94b3d5ce6b2 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -499,6 +499,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-1556507536": { + "message": "Passing transform hint %d for window %s%s", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "-1554521902": { "message": "showInsets(ime) was requested by different window: %s ", "level": "WARN", diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index cf7039b9ed5c..c7f56961e498 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -32,7 +32,6 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothProfile; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -5796,112 +5795,25 @@ public class AudioManager { } } - /** - * Indicate Hearing Aid connection state change and eventually suppress - * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. - * This operation is asynchronous but its execution will still be sequentially scheduled - * relative to calls to {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - * * BluetoothDevice, int, int, boolean, int)} and - * and {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}. - * @param device Bluetooth device connected/disconnected - * @param state new connection state (BluetoothProfile.STATE_xxx) - * @param musicDevice Default get system volume for the connecting device. - * (either {@link android.bluetooth.BluetoothProfile.hearingaid} or - * {@link android.bluetooth.BluetoothProfile.HEARING_AID}) - * @param suppressNoisyIntent if true the - * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. - * {@hide} - */ - public void setBluetoothHearingAidDeviceConnectionState( - BluetoothDevice device, int state, boolean suppressNoisyIntent, - int musicDevice) { - final IAudioService service = getService(); - try { - service.setBluetoothHearingAidDeviceConnectionState(device, - state, suppressNoisyIntent, musicDevice); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** - * Indicate Le Audio output device connection state change and eventually suppress - * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. - * @param device Bluetooth device connected/disconnected - * @param state new connection state (BluetoothProfile.STATE_xxx) - * @param suppressNoisyIntent if true the - * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. - * {@hide} - */ - public void setBluetoothLeAudioOutDeviceConnectionState(BluetoothDevice device, int state, - boolean suppressNoisyIntent) { - final IAudioService service = getService(); - try { - service.setBluetoothLeAudioOutDeviceConnectionState(device, state, suppressNoisyIntent); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Indicate Le Audio input connection state change. - * @param device Bluetooth device connected/disconnected - * @param state new connection state (BluetoothProfile.STATE_xxx) - * {@hide} - */ - public void setBluetoothLeAudioInDeviceConnectionState(BluetoothDevice device, int state) { - final IAudioService service = getService(); - try { - service.setBluetoothLeAudioInDeviceConnectionState(device, state); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Indicate A2DP source or sink connection state change and eventually suppress - * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. - * This operation is asynchronous but its execution will still be sequentially scheduled - * relative to calls to {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice, - * int, boolean, int)} and - * {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}. - * @param device Bluetooth device connected/disconnected - * @param state new connection state, {@link BluetoothProfile#STATE_CONNECTED} - * or {@link BluetoothProfile#STATE_DISCONNECTED} - * @param profile profile for the A2DP device - * @param a2dpVolume New volume for the connecting device. Does nothing if disconnecting. - * (either {@link android.bluetooth.BluetoothProfile.A2DP} or - * {@link android.bluetooth.BluetoothProfile.A2DP_SINK}) - * @param suppressNoisyIntent if true the - * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. - * {@hide} - */ - public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - BluetoothDevice device, int state, - int profile, boolean suppressNoisyIntent, int a2dpVolume) { - final IAudioService service = getService(); - try { - service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device, - state, profile, suppressNoisyIntent, a2dpVolume); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Indicate A2DP device configuration has changed. - * This operation is asynchronous but its execution will still be sequentially scheduled - * relative to calls to - * {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice, int, int, - * boolean, int)} and - * {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice, int, boolean, int)} - * @param device Bluetooth device whose configuration has changed. + * Indicate Bluetooth profile connection state change. + * Configuration changes for A2DP are indicated by having the same <code>newDevice</code> and + * <code>previousDevice</code> + * This operation is asynchronous. + * + * @param newDevice Bluetooth device connected or null if there is no new devices + * @param previousDevice Bluetooth device disconnected or null if there is no disconnected + * devices + * @param info contain all info related to the device. {@link BtProfileConnectionInfo} * {@hide} */ - public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) { + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) + public void handleBluetoothActiveDeviceChanged(@Nullable BluetoothDevice newDevice, + @Nullable BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) { final IAudioService service = getService(); try { - service.handleBluetoothA2dpDeviceConfigChange(device); + service.handleBluetoothActiveDeviceChanged(newDevice, previousDevice, info); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/media/java/android/media/BtProfileConnectionInfo.aidl b/media/java/android/media/BtProfileConnectionInfo.aidl new file mode 100644 index 000000000000..047f06be0964 --- /dev/null +++ b/media/java/android/media/BtProfileConnectionInfo.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2021 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.media; + +parcelable BtProfileConnectionInfo; + diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java new file mode 100644 index 000000000000..19ea2de6a434 --- /dev/null +++ b/media/java/android/media/BtProfileConnectionInfo.java @@ -0,0 +1,163 @@ +/* + * Copyright 2021 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.media; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.bluetooth.BluetoothProfile; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Contains information about Bluetooth profile connection state changed + * {@hide} + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class BtProfileConnectionInfo implements Parcelable { + /** @hide */ + @IntDef({ + BluetoothProfile.A2DP, + BluetoothProfile.A2DP_SINK, // Can only be set by BtHelper + BluetoothProfile.HEADSET, // Can only be set by BtHelper + BluetoothProfile.HEARING_AID, + BluetoothProfile.LE_AUDIO, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BtProfile {} + + private final @BtProfile int mProfile; + private final boolean mSupprNoisy; + private final int mVolume; + private final boolean mIsLeOutput; + + private BtProfileConnectionInfo(@BtProfile int profile, boolean suppressNoisyIntent, int volume, + boolean isLeOutput) { + mProfile = profile; + mSupprNoisy = suppressNoisyIntent; + mVolume = volume; + mIsLeOutput = isLeOutput; + } + + /** + * Constructor used by BtHelper when a profile is connected + * {@hide} + */ + public BtProfileConnectionInfo(@BtProfile int profile) { + this(profile, false, -1, false); + } + + public static final @NonNull Parcelable.Creator<BtProfileConnectionInfo> CREATOR = + new Parcelable.Creator<BtProfileConnectionInfo>() { + @Override + public BtProfileConnectionInfo createFromParcel(Parcel source) { + return new BtProfileConnectionInfo(source.readInt(), source.readBoolean(), + source.readInt(), source.readBoolean()); + } + + @Override + public BtProfileConnectionInfo[] newArray(int size) { + return new BtProfileConnectionInfo[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) { + dest.writeInt(mProfile); + dest.writeBoolean(mSupprNoisy); + dest.writeInt(mVolume); + dest.writeBoolean(mIsLeOutput); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Constructor for A2dp info + * + * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} + * intent will not be sent. + * + * @param volume of device -1 to ignore value + */ + public static @NonNull BtProfileConnectionInfo a2dpInfo(boolean suppressNoisyIntent, + int volume) { + return new BtProfileConnectionInfo(BluetoothProfile.A2DP, suppressNoisyIntent, volume, + false); + } + + /** + * Constructor for hearing aid info + * + * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} + * intent will not be sent. + */ + public static @NonNull BtProfileConnectionInfo hearingAidInfo(boolean suppressNoisyIntent) { + return new BtProfileConnectionInfo(BluetoothProfile.HEARING_AID, suppressNoisyIntent, -1, + false); + } + + /** + * constructor for le audio info + * + * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} + * intent will not be sent. + * + * @param isLeOutput if true mean the device is an output device, if false it's an input device + */ + public static @NonNull BtProfileConnectionInfo leAudio(boolean suppressNoisyIntent, + boolean isLeOutput) { + return new BtProfileConnectionInfo(BluetoothProfile.LE_AUDIO, suppressNoisyIntent, -1, + isLeOutput); + } + + /** + * @return The profile connection + */ + public @BtProfile int getProfile() { + return mProfile; + } + + /** + * @return {@code true} if {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be + * sent + */ + public boolean getSuppressNoisyIntent() { + return mSupprNoisy; + } + + /** + * Only for {@link BluetoothProfile.A2DP} profile + * @return the volume of the connection or -1 if the value is ignored + */ + public int getVolume() { + return mVolume; + } + + /** + * Only for {@link BluetoothProfile.LE_AUDIO} profile + * @return {@code true} is the LE device is an output device, {@code false} if it's an input + * device + */ + public boolean getIsLeOutput() { + return mIsLeOutput; + } +} diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index dd44fdf1e8c4..5ff56f9c680d 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -24,6 +24,7 @@ import android.media.AudioFocusInfo; import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; +import android.media.BtProfileConnectionInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; @@ -207,8 +208,6 @@ interface IAudioService { void setWiredDeviceConnectionState(int type, int state, String address, String name, String caller); - void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device); - @UnsupportedAppUsage AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); @@ -268,16 +267,8 @@ interface IAudioService { oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio); - void setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device, - int state, boolean suppressNoisyIntent, int musicDevice); - - void setBluetoothLeAudioOutDeviceConnectionState(in BluetoothDevice device, int state, - boolean suppressNoisyIntent); - - void setBluetoothLeAudioInDeviceConnectionState(in BluetoothDevice device, int state); - - void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device, - int state, int profile, boolean suppressNoisyIntent, int a2dpVolume); + void handleBluetoothActiveDeviceChanged(in BluetoothDevice newDevice, + in BluetoothDevice previousDevice, in BtProfileConnectionInfo info); oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult, in IAudioPolicyCallback pcb); diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS index ce23a8bc09ca..e1da74466b55 100644 --- a/packages/SystemUI/OWNERS +++ b/packages/SystemUI/OWNERS @@ -11,6 +11,7 @@ asc@google.com awickham@google.com beverlyt@google.com brockman@google.com +brycelee@google.com ccassidy@google.com cinek@google.com cwren@google.com @@ -71,4 +72,4 @@ zakcohen@google.com hseog@google.com #Android TV -rgl@google.com
\ No newline at end of file +rgl@google.com diff --git a/services/core/Android.bp b/services/core/Android.bp index ad3c8442995c..2103bcc01b6a 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -151,7 +151,7 @@ java_library_static { "android.hardware.biometrics.fingerprint-V2.3-java", "android.hardware.biometrics.fingerprint-V1-java", "android.hardware.oemlock-V1.0-java", - "android.hardware.configstore-V1.0-java", + "android.hardware.configstore-V1.1-java", "android.hardware.contexthub-V1.0-java", "android.hardware.rebootescrow-V1-java", "android.hardware.soundtrigger-V2.3-java", diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index c383f5120407..0b2311bc563a 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -17,12 +17,9 @@ package com.android.server.audio; import android.annotation.NonNull; import android.annotation.Nullable; -import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; -import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; -import android.bluetooth.BluetoothLeAudio; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -32,6 +29,7 @@ import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.AudioRoutesInfo; import android.media.AudioSystem; +import android.media.BtProfileConnectionInfo; import android.media.IAudioRoutesObserver; import android.media.ICapturePresetDevicesRoleDispatcher; import android.media.ICommunicationDeviceDispatcher; @@ -516,29 +514,82 @@ import java.util.concurrent.atomic.AtomicBoolean; } }; - /*package*/ static final class BtDeviceConnectionInfo { + /*package*/ static final class BtDeviceChangedData { + final @Nullable BluetoothDevice mNewDevice; + final @Nullable BluetoothDevice mPreviousDevice; + final @NonNull BtProfileConnectionInfo mInfo; + final @NonNull String mEventSource; + + BtDeviceChangedData(@Nullable BluetoothDevice newDevice, + @Nullable BluetoothDevice previousDevice, + @NonNull BtProfileConnectionInfo info, @NonNull String eventSource) { + mNewDevice = newDevice; + mPreviousDevice = previousDevice; + mInfo = info; + mEventSource = eventSource; + } + + @Override + public String toString() { + return "BtDeviceChangedData profile=" + BluetoothProfile.getProfileName( + mInfo.getProfile()) + + ", switch device: [" + mPreviousDevice + "] -> [" + mNewDevice + "]"; + } + } + + /*package*/ static final class BtDeviceInfo { final @NonNull BluetoothDevice mDevice; final @AudioService.BtProfileConnectionState int mState; - final int mProfile; + final @AudioService.BtProfile int mProfile; final boolean mSupprNoisy; final int mVolume; + final boolean mIsLeOutput; + final @NonNull String mEventSource; + final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec; + final int mAudioSystemDevice; + final int mMusicDevice; - BtDeviceConnectionInfo(@NonNull BluetoothDevice device, - @AudioService.BtProfileConnectionState int state, - int profile, boolean suppressNoisyIntent, int vol) { + BtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device, int state, + int audioDevice, @AudioSystem.AudioFormatNativeEnumForBtCodec int codec) { mDevice = device; mState = state; + mProfile = d.mInfo.getProfile(); + mSupprNoisy = d.mInfo.getSuppressNoisyIntent(); + mVolume = d.mInfo.getVolume(); + mIsLeOutput = d.mInfo.getIsLeOutput(); + mEventSource = d.mEventSource; + mAudioSystemDevice = audioDevice; + mMusicDevice = AudioSystem.DEVICE_NONE; + mCodec = codec; + } + + // constructor used by AudioDeviceBroker to search similar message + BtDeviceInfo(@NonNull BluetoothDevice device, int profile) { + mDevice = device; mProfile = profile; - mSupprNoisy = suppressNoisyIntent; - mVolume = vol; - } - - BtDeviceConnectionInfo(@NonNull BtDeviceConnectionInfo info) { - mDevice = info.mDevice; - mState = info.mState; - mProfile = info.mProfile; - mSupprNoisy = info.mSupprNoisy; - mVolume = info.mVolume; + mEventSource = ""; + mMusicDevice = AudioSystem.DEVICE_NONE; + mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT; + mAudioSystemDevice = 0; + mState = 0; + mSupprNoisy = false; + mVolume = -1; + mIsLeOutput = false; + } + + // constructor used by AudioDeviceInventory when config change failed + BtDeviceInfo(@NonNull BluetoothDevice device, int profile, int state, int musicDevice, + int audioSystemDevice) { + mDevice = device; + mProfile = profile; + mEventSource = ""; + mMusicDevice = musicDevice; + mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT; + mAudioSystemDevice = audioSystemDevice; + mState = state; + mSupprNoisy = false; + mVolume = -1; + mIsLeOutput = false; } // redefine equality op so we can match messages intended for this device @@ -550,16 +601,52 @@ import java.util.concurrent.atomic.AtomicBoolean; if (this == o) { return true; } - if (o instanceof BtDeviceConnectionInfo) { - return mDevice.equals(((BtDeviceConnectionInfo) o).mDevice); + if (o instanceof BtDeviceInfo) { + return mProfile == ((BtDeviceInfo) o).mProfile + && mDevice.equals(((BtDeviceInfo) o).mDevice); } return false; } + } - @Override - public String toString() { - return "BtDeviceConnectionInfo dev=" + mDevice.toString(); + BtDeviceInfo createBtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device, + int state) { + int audioDevice; + int codec = AudioSystem.AUDIO_FORMAT_DEFAULT; + switch (d.mInfo.getProfile()) { + case BluetoothProfile.A2DP_SINK: + audioDevice = AudioSystem.DEVICE_IN_BLUETOOTH_A2DP; + break; + case BluetoothProfile.A2DP: + audioDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP; + synchronized (mDeviceStateLock) { + codec = mBtHelper.getA2dpCodec(device); + } + break; + case BluetoothProfile.HEARING_AID: + audioDevice = AudioSystem.DEVICE_OUT_HEARING_AID; + break; + case BluetoothProfile.LE_AUDIO: + if (d.mInfo.getIsLeOutput()) { + audioDevice = AudioSystem.DEVICE_OUT_BLE_HEADSET; + } else { + audioDevice = AudioSystem.DEVICE_IN_BLE_HEADSET; + } + break; + default: throw new IllegalArgumentException("Invalid profile " + d.mInfo.getProfile()); } + return new BtDeviceInfo(d, device, state, audioDevice, codec); + } + + private void btMediaMetricRecord(@NonNull BluetoothDevice device, String state, + @NonNull BtDeviceChangedData data) { + final String name = TextUtils.emptyIfNull(device.getName()); + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR + + "queueOnBluetoothActiveDeviceChanged") + .set(MediaMetrics.Property.STATE, state) + .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile()) + .set(MediaMetrics.Property.NAME, name) + .record(); } /** @@ -567,116 +654,37 @@ import java.util.concurrent.atomic.AtomicBoolean; * not just a simple message post * @param info struct with the (dis)connection information */ - /*package*/ void queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - @NonNull BtDeviceConnectionInfo info) { - final String name = TextUtils.emptyIfNull(info.mDevice.getName()); - new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR - + "postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent") - .set(MediaMetrics.Property.STATE, info.mState == BluetoothProfile.STATE_CONNECTED - ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) - .set(MediaMetrics.Property.INDEX, info.mVolume) - .set(MediaMetrics.Property.NAME, name) - .record(); - - // operations of removing and posting messages related to A2DP device state change must be - // mutually exclusive - synchronized (mDeviceStateLock) { - // when receiving a request to change the connection state of a device, this last - // request is the source of truth, so cancel all previous requests that are already in - // the handler - removeScheduledA2dpEvents(info.mDevice); - - sendLMsgNoDelay( - info.mState == BluetoothProfile.STATE_CONNECTED - ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION - : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, - SENDMSG_QUEUE, info); - } - } - - /** remove all previously scheduled connection and state change events for the given device */ - @GuardedBy("mDeviceStateLock") - private void removeScheduledA2dpEvents(@NonNull BluetoothDevice device) { - mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, device); - - final BtDeviceConnectionInfo connectionInfoToRemove = new BtDeviceConnectionInfo(device, - // the next parameters of the constructor will be ignored when finding the message - // to remove as the equality of the message's object is tested on the device itself - // (see BtDeviceConnectionInfo.equals() method override) - BluetoothProfile.STATE_CONNECTED, 0, false, -1); - mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, - connectionInfoToRemove); - mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION, - connectionInfoToRemove); - - final BtHelper.BluetoothA2dpDeviceInfo devInfoToRemove = - new BtHelper.BluetoothA2dpDeviceInfo(device); - mBrokerHandler.removeEqualMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, - devInfoToRemove); - mBrokerHandler.removeEqualMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, - devInfoToRemove); - mBrokerHandler.removeEqualMessages(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, - devInfoToRemove); - } - - private static final class HearingAidDeviceConnectionInfo { - final @NonNull BluetoothDevice mDevice; - final @AudioService.BtProfileConnectionState int mState; - final boolean mSupprNoisy; - final int mMusicDevice; - final @NonNull String mEventSource; - - HearingAidDeviceConnectionInfo(@NonNull BluetoothDevice device, - @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) { - mDevice = device; - mState = state; - mSupprNoisy = suppressNoisyIntent; - mMusicDevice = musicDevice; - mEventSource = eventSource; - } - } - - /*package*/ void postBluetoothHearingAidDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) { - final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo( - device, state, suppressNoisyIntent, musicDevice, eventSource); - sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); - } - - private static final class LeAudioDeviceConnectionInfo { - final @NonNull BluetoothDevice mDevice; - final @AudioService.BtProfileConnectionState int mState; - final boolean mSupprNoisy; - final @NonNull String mEventSource; - - LeAudioDeviceConnectionInfo(@NonNull BluetoothDevice device, - @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, @NonNull String eventSource) { - mDevice = device; - mState = state; - mSupprNoisy = suppressNoisyIntent; - mEventSource = eventSource; + /*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) { + if (data.mInfo.getProfile() == BluetoothProfile.A2DP && data.mPreviousDevice != null + && data.mPreviousDevice.equals(data.mNewDevice)) { + final String name = TextUtils.emptyIfNull(data.mNewDevice.getName()); + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR + + "queueOnBluetoothActiveDeviceChanged_update") + .set(MediaMetrics.Property.NAME, name) + .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile()) + .record(); + synchronized (mDeviceStateLock) { + postBluetoothA2dpDeviceConfigChange(data.mNewDevice); + } + } else { + synchronized (mDeviceStateLock) { + if (data.mPreviousDevice != null) { + btMediaMetricRecord(data.mPreviousDevice, MediaMetrics.Value.DISCONNECTED, + data); + sendLMsgNoDelay(MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE, + createBtDeviceInfo(data, data.mPreviousDevice, + BluetoothProfile.STATE_DISCONNECTED)); + } + if (data.mNewDevice != null) { + btMediaMetricRecord(data.mNewDevice, MediaMetrics.Value.CONNECTED, data); + sendLMsgNoDelay(MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE, + createBtDeviceInfo(data, data.mNewDevice, + BluetoothProfile.STATE_CONNECTED)); + } + } } } - /*package*/ void postBluetoothLeAudioOutDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, @NonNull String eventSource) { - final LeAudioDeviceConnectionInfo info = new LeAudioDeviceConnectionInfo( - device, state, suppressNoisyIntent, eventSource); - sendLMsgNoDelay(MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); - } - - /*package*/ void postBluetoothLeAudioInDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - @NonNull String eventSource) { - final LeAudioDeviceConnectionInfo info = new LeAudioDeviceConnectionInfo( - device, state, false, eventSource); - sendLMsgNoDelay(MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); - } - /** * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn(). */ @@ -926,19 +934,8 @@ import java.util.concurrent.atomic.AtomicBoolean; } @GuardedBy("mDeviceStateLock") - /*package*/ void postA2dpSinkConnection(@AudioService.BtProfileConnectionState int state, - @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) { - sendILMsg(state == BluetoothA2dp.STATE_CONNECTED - ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED - : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, - SENDMSG_QUEUE, - state, btDeviceInfo, delay); - } - - /*package*/ void postA2dpSourceConnection(@AudioService.BtProfileConnectionState int state, - @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) { - sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, - state, btDeviceInfo, delay); + /*package*/ void postBluetoothActiveDevice(BtDeviceInfo info, int delay) { + sendLMsg(MSG_L_SET_BT_ACTIVE_DEVICE, SENDMSG_QUEUE, info, delay); } /*package*/ void postSetWiredDeviceConnectionState( @@ -946,72 +943,12 @@ import java.util.concurrent.atomic.AtomicBoolean; sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, connectionState, delay); } - /*package*/ void postSetHearingAidConnectionState( - @AudioService.BtProfileConnectionState int state, - @NonNull BluetoothDevice device, int delay) { - sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE, - state, - device, - delay); - } - - /*package*/ void postSetLeAudioOutConnectionState( - @AudioService.BtProfileConnectionState int state, - @NonNull BluetoothDevice device, int delay) { - sendILMsg(MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE, SENDMSG_QUEUE, - state, - device, - delay); - } - - /*package*/ void postSetLeAudioInConnectionState( - @AudioService.BtProfileConnectionState int state, - @NonNull BluetoothDevice device) { - sendILMsgNoDelay(MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE, SENDMSG_QUEUE, - state, - device); - } - - /*package*/ void postDisconnectA2dp() { - sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE); - } - - /*package*/ void postDisconnectA2dpSink() { - sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE); - } - - /*package*/ void postDisconnectHearingAid() { - sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE); - } - - /*package*/ void postDisconnectLeAudio() { - sendMsgNoDelay(MSG_DISCONNECT_BT_LE_AUDIO, SENDMSG_QUEUE); - } - - /*package*/ void postDisconnectHeadset() { - sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE); - } - - /*package*/ void postBtA2dpProfileConnected(BluetoothA2dp a2dpProfile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile); - } - - /*package*/ void postBtA2dpSinkProfileConnected(BluetoothProfile profile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile); - } - - /*package*/ void postBtHeasetProfileConnected(BluetoothHeadset headsetProfile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, headsetProfile); - } - - /*package*/ void postBtHearingAidProfileConnected(BluetoothHearingAid hearingAidProfile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE, - hearingAidProfile); + /*package*/ void postBtProfileDisconnected(int profile) { + sendIMsgNoDelay(MSG_I_BT_SERVICE_DISCONNECTED_PROFILE, SENDMSG_QUEUE, profile); } - /*package*/ void postBtLeAudioProfileConnected(BluetoothLeAudio leAudioProfile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO, SENDMSG_QUEUE, - leAudioProfile); + /*package*/ void postBtProfileConnected(int profile, BluetoothProfile proxy) { + sendILMsgNoDelay(MSG_IL_BT_SERVICE_CONNECTED_PROFILE, SENDMSG_QUEUE, profile, proxy); } /*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) { @@ -1069,13 +1006,6 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ void postSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state, - @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { - final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0; - sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state, - btDeviceInfo); - } - /*package*/ void handleFailureToConnectToBtHeadsetService(int delay) { sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay); } @@ -1088,19 +1018,10 @@ import java.util.concurrent.atomic.AtomicBoolean; sendMsgNoDelay(fromA2dp ? MSG_REPORT_NEW_ROUTES_A2DP : MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP); } - /*package*/ void postA2dpActiveDeviceChange( - @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { - sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo); - } - // must be called synchronized on mConnectedDevices - /*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) { - final BtHelper.BluetoothA2dpDeviceInfo devInfoToCheck = - new BtHelper.BluetoothA2dpDeviceInfo(btDevice); - return (mBrokerHandler.hasEqualMessages( - MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, devInfoToCheck) - || mBrokerHandler.hasEqualMessages( - MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, devInfoToCheck)); + /*package*/ boolean hasScheduledA2dpConnection(BluetoothDevice btDevice) { + final BtDeviceInfo devInfoToCheck = new BtDeviceInfo(btDevice, BluetoothProfile.A2DP); + return mBrokerHandler.hasEqualMessages(MSG_L_SET_BT_ACTIVE_DEVICE, devInfoToCheck); } /*package*/ void setA2dpTimeout(String address, int a2dpCodec, int delayMs) { @@ -1124,12 +1045,6 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) { - synchronized (mDeviceStateLock) { - return mBtHelper.getA2dpCodec(device); - } - } - /*package*/ void broadcastStickyIntentToCurrentProfileGroup(Intent intent) { mSystemServer.broadcastStickyIntentToCurrentProfileGroup(intent); } @@ -1285,39 +1200,11 @@ import java.util.concurrent.atomic.AtomicBoolean; mDeviceInventory.onReportNewRoutes(); } break; - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: - synchronized (mDeviceStateLock) { - mDeviceInventory.onSetA2dpSinkConnectionState( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); - } - break; - case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: - synchronized (mDeviceStateLock) { - mDeviceInventory.onSetA2dpSourceConnectionState( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); - } - break; - case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: + case MSG_L_SET_BT_ACTIVE_DEVICE: synchronized (mDeviceStateLock) { - mDeviceInventory.onSetHearingAidConnectionState( - (BluetoothDevice) msg.obj, msg.arg1, + mDeviceInventory.onSetBtActiveDevice((BtDeviceInfo) msg.obj, mAudioService.getBluetoothContextualVolumeStream()); } - break; - case MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE: - synchronized (mDeviceStateLock) { - mDeviceInventory.onSetLeAudioOutConnectionState( - (BluetoothDevice) msg.obj, msg.arg1, - mAudioService.getBluetoothContextualVolumeStream()); - } - break; - case MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE: - synchronized (mDeviceStateLock) { - mDeviceInventory.onSetLeAudioInConnectionState( - (BluetoothDevice) msg.obj, msg.arg1); - } - break; case MSG_BT_HEADSET_CNCT_FAILED: synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { @@ -1332,14 +1219,10 @@ import java.util.concurrent.atomic.AtomicBoolean; } break; case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: - final int a2dpCodec; final BluetoothDevice btDevice = (BluetoothDevice) msg.obj; synchronized (mDeviceStateLock) { - a2dpCodec = mBtHelper.getA2dpCodec(btDevice); - // TODO: name of method being called on AudioDeviceInventory is currently - // misleading (config change vs active device change), to be - // reconciliated once the BT side has been updated. - mDeviceInventory.onBluetoothA2dpActiveDeviceChange( + final int a2dpCodec = mBtHelper.getA2dpCodec(btDevice); + mDeviceInventory.onBluetoothA2dpDeviceConfigChange( new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec), BtHelper.EVENT_DEVICE_CONFIG_CHANGE); } @@ -1392,96 +1275,47 @@ import java.util.concurrent.atomic.AtomicBoolean; mDeviceInventory.onToggleHdmi(); } break; - case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: - synchronized (mDeviceStateLock) { - mDeviceInventory.onBluetoothA2dpActiveDeviceChange( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, - BtHelper.EVENT_ACTIVE_DEVICE_CHANGE); - } - break; - case MSG_DISCONNECT_A2DP: - synchronized (mDeviceStateLock) { - mDeviceInventory.disconnectA2dp(); - } - break; - case MSG_DISCONNECT_A2DP_SINK: - synchronized (mDeviceStateLock) { - mDeviceInventory.disconnectA2dpSink(); - } - break; - case MSG_DISCONNECT_BT_HEARING_AID: - synchronized (mDeviceStateLock) { - mDeviceInventory.disconnectHearingAid(); - } - break; - case MSG_DISCONNECT_BT_HEADSET: - synchronized (mSetModeLock) { + case MSG_I_BT_SERVICE_DISCONNECTED_PROFILE: + if (msg.arg1 != BluetoothProfile.HEADSET) { synchronized (mDeviceStateLock) { - mBtHelper.disconnectHeadset(); + mDeviceInventory.onBtProfileDisconnected(msg.arg1); + } + } else { + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.disconnectHeadset(); + } } } break; - case MSG_DISCONNECT_BT_LE_AUDIO: - synchronized(mDeviceStateLock) { - mDeviceInventory.disconnectLeAudio(); - } - break; - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP: - synchronized (mDeviceStateLock) { - mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj); - } - break; - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK: - synchronized (mDeviceStateLock) { - mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj); - } - break; - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID: - synchronized (mDeviceStateLock) { - mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj); - } - break; - - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO: - synchronized(mDeviceStateLock) { - mBtHelper.onLeAudioProfileConnected((BluetoothLeAudio) msg.obj); - } - break; - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET: - synchronized (mSetModeLock) { + case MSG_IL_BT_SERVICE_CONNECTED_PROFILE: + if (msg.arg1 != BluetoothProfile.HEADSET) { synchronized (mDeviceStateLock) { - mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + mBtHelper.onBtProfileConnected(msg.arg1, (BluetoothProfile) msg.obj); + } + } else { + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + } } } break; - case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION: - case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: { - final BtDeviceConnectionInfo info = (BtDeviceConnectionInfo) msg.obj; + case MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT: { + final BtDeviceInfo info = (BtDeviceInfo) msg.obj; + if (info.mDevice == null) break; AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "msg: setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent " + "msg: onBluetoothActiveDeviceChange " + " state=" + info.mState // only querying address as this is the only readily available // field on the device + " addr=" + info.mDevice.getAddress() - + " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy - + " vol=" + info.mVolume)).printLog(TAG)); - synchronized (mDeviceStateLock) { - mDeviceInventory.setBluetoothA2dpDeviceConnectionState( - info.mDevice, info.mState, info.mProfile, info.mSupprNoisy, - AudioSystem.DEVICE_NONE, info.mVolume); - } - } break; - case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: { - final HearingAidDeviceConnectionInfo info = - (HearingAidDeviceConnectionInfo) msg.obj; - AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "msg: setHearingAidDeviceConnectionState state=" + info.mState - + " addr=" + info.mDevice.getAddress() + + " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy - + " src=" + info.mEventSource)).printLog(TAG)); + + " src=" + info.mEventSource + )).printLog(TAG)); synchronized (mDeviceStateLock) { - mDeviceInventory.setBluetoothHearingAidDeviceConnectionState( - info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice); + mDeviceInventory.setBluetoothActiveDevice(info); } } break; case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: { @@ -1524,31 +1358,6 @@ import java.util.concurrent.atomic.AtomicBoolean; final int capturePreset = msg.arg1; mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset); } break; - case MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT: { - final LeAudioDeviceConnectionInfo info = - (LeAudioDeviceConnectionInfo) msg.obj; - AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "setLeAudioDeviceOutConnectionState state=" + info.mState - + " addr=" + info.mDevice.getAddress() - + " supprNoisy=" + info.mSupprNoisy - + " src=" + info.mEventSource)).printLog(TAG)); - synchronized (mDeviceStateLock) { - mDeviceInventory.setBluetoothLeAudioOutDeviceConnectionState( - info.mDevice, info.mState, info.mSupprNoisy); - } - } break; - case MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT: { - final LeAudioDeviceConnectionInfo info = - (LeAudioDeviceConnectionInfo) msg.obj; - AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "setLeAudioDeviceInConnectionState state=" + info.mState - + " addr=" + info.mDevice.getAddress() - + " src=" + info.mEventSource)).printLog(TAG)); - synchronized (mDeviceStateLock) { - mDeviceInventory.setBluetoothLeAudioInDeviceConnectionState(info.mDevice, - info.mState); - } - } break; default: Log.wtf(TAG, "Invalid message " + msg.what); } @@ -1579,8 +1388,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_IIL_SET_FORCE_USE = 4; private static final int MSG_IIL_SET_FORCE_BT_A2DP_USE = 5; private static final int MSG_TOGGLE_HDMI = 6; - private static final int MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE = 7; - private static final int MSG_IL_SET_HEARING_AID_CONNECTION_STATE = 8; + private static final int MSG_L_SET_BT_ACTIVE_DEVICE = 7; private static final int MSG_BT_HEADSET_CNCT_FAILED = 9; private static final int MSG_IL_BTA2DP_TIMEOUT = 10; @@ -1593,25 +1401,11 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15; private static final int MSG_I_SET_MODE_OWNER_PID = 16; - // process active A2DP device change, obj is BtHelper.BluetoothA2dpDeviceInfo - private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18; - - private static final int MSG_DISCONNECT_A2DP = 19; - private static final int MSG_DISCONNECT_A2DP_SINK = 20; - private static final int MSG_DISCONNECT_BT_HEARING_AID = 21; - private static final int MSG_DISCONNECT_BT_HEADSET = 22; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP = 23; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK = 24; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID = 25; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET = 26; - - // process change of state, obj is BtHelper.BluetoothA2dpDeviceInfo - private static final int MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED = 27; - private static final int MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED = 28; + private static final int MSG_I_BT_SERVICE_DISCONNECTED_PROFILE = 22; + private static final int MSG_IL_BT_SERVICE_CONNECTED_PROFILE = 23; // process external command to (dis)connect an A2DP device, obj is BtDeviceConnectionInfo - private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION = 29; - private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION = 30; + private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT = 29; // process external command to (dis)connect a hearing aid device private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31; @@ -1630,33 +1424,21 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40; private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41; - private static final int MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE = 42; - private static final int MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE = 43; - private static final int MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT = 44; - private static final int MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT = 45; + private static final int MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT = 45; + // // process set volume for Le Audio, obj is BleVolumeInfo private static final int MSG_II_SET_LE_AUDIO_OUT_VOLUME = 46; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO = 47; - private static final int MSG_DISCONNECT_BT_LE_AUDIO = 48; - private static boolean isMessageHandledUnderWakelock(int msgId) { switch(msgId) { case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: - case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: - case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: + case MSG_L_SET_BT_ACTIVE_DEVICE: case MSG_IL_BTA2DP_TIMEOUT: case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: case MSG_TOGGLE_HDMI: - case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: - case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION: - case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: + case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: case MSG_CHECK_MUTE_MUSIC: - case MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT: - case MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT: return true; default: return false; @@ -1739,14 +1521,10 @@ import java.util.concurrent.atomic.AtomicBoolean; long time = SystemClock.uptimeMillis() + delay; switch (msg) { - case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: - case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: + case MSG_L_SET_BT_ACTIVE_DEVICE: case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: case MSG_IL_BTA2DP_TIMEOUT: case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: - case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: if (sLastDeviceConnectMsgTime >= time) { // add a little delay to make sure messages are ordered as expected time = sLastDeviceConnectMsgTime + 30; @@ -1765,14 +1543,9 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final Set<Integer> MESSAGES_MUTE_MUSIC; static { MESSAGES_MUTE_MUSIC = new HashSet<>(); - MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED); - MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED); - MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE); + MESSAGES_MUTE_MUSIC.add(MSG_L_SET_BT_ACTIVE_DEVICE); MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONFIG_CHANGE); - MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE); - MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION); - MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION); - MESSAGES_MUTE_MUSIC.add(MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT); + MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT); MESSAGES_MUTE_MUSIC.add(MSG_IIL_SET_FORCE_BT_A2DP_USE); MESSAGES_MUTE_MUSIC.add(MSG_REPORT_NEW_ROUTES_A2DP); } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 6c3c736aeb93..0a114b924063 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -16,11 +16,8 @@ package com.android.server.audio; import android.annotation.NonNull; -import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHearingAid; -import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.content.Intent; import android.media.AudioDeviceAttributes; @@ -286,186 +283,102 @@ public class AudioDeviceInventory { } } - // only public for mocking/spying @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - @VisibleForTesting - public void onSetA2dpSinkConnectionState(@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, - @AudioService.BtProfileConnectionState int state) { - final BluetoothDevice btDevice = btInfo.getBtDevice(); - int a2dpVolume = btInfo.getVolume(); - if (AudioService.DEBUG_DEVICES) { - Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice + " state=" - + state + " vol=" + a2dpVolume); - } - String address = btDevice.getAddress(); - if (address == null) { - address = ""; - } - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - address = ""; - } - - final @AudioSystem.AudioFormatNativeEnumForBtCodec int a2dpCodec = btInfo.getCodec(); - - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "A2DP sink connected: device addr=" + address + " state=" + state - + " codec=" + AudioSystem.audioFormatToString(a2dpCodec) - + " vol=" + a2dpVolume)); - - new MediaMetrics.Item(mMetricsId + "a2dp") - .set(MediaMetrics.Property.ADDRESS, address) - .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(a2dpCodec)) - .set(MediaMetrics.Property.EVENT, "onSetA2dpSinkConnectionState") - .set(MediaMetrics.Property.INDEX, a2dpVolume) - .set(MediaMetrics.Property.STATE, - state == BluetoothProfile.STATE_CONNECTED - ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) - .record(); - - synchronized (mDevicesLock) { - final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - btDevice.getAddress()); - final DeviceInfo di = mConnectedDevices.get(key); - boolean isConnected = di != null; - - if (isConnected) { - if (state == BluetoothProfile.STATE_CONNECTED) { - // device is already connected, but we are receiving a connection again, - // it could be for a codec change - if (a2dpCodec != di.mDeviceCodecFormat) { - mDeviceBroker.postBluetoothA2dpDeviceConfigChange(btDevice); - } - } else { - makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat); - } - } else if (state == BluetoothProfile.STATE_CONNECTED) { - // device is not already connected - if (a2dpVolume != -1) { - mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC, - // convert index to internal representation in VolumeStreamState - a2dpVolume * 10, - AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState"); - } - makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice), - "onSetA2dpSinkConnectionState", a2dpCodec); - } - } - } - - /*package*/ void onSetA2dpSourceConnectionState( - @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int state) { - final BluetoothDevice btDevice = btInfo.getBtDevice(); + void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int streamType) { if (AudioService.DEBUG_DEVICES) { - Log.d(TAG, "onSetA2dpSourceConnectionState btDevice=" + btDevice + " state=" - + state); + Log.d(TAG, "onSetBtActiveDevice" + + " btDevice=" + btInfo.mDevice + + " profile=" + BluetoothProfile.getProfileName(btInfo.mProfile) + + " state=" + BluetoothProfile.getConnectionStateName(btInfo.mState)); } - String address = btDevice.getAddress(); + String address = btInfo.mDevice.getAddress(); if (!BluetoothAdapter.checkBluetoothAddress(address)) { address = ""; } - synchronized (mDevicesLock) { - final String key = DeviceInfo.makeDeviceListKey( - AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address); - final DeviceInfo di = mConnectedDevices.get(key); - boolean isConnected = di != null; - - new MediaMetrics.Item(mMetricsId + "onSetA2dpSourceConnectionState") - .set(MediaMetrics.Property.ADDRESS, address) - .set(MediaMetrics.Property.DEVICE, - AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) - .set(MediaMetrics.Property.STATE, - state == BluetoothProfile.STATE_CONNECTED - ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) - .record(); - - if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { - makeA2dpSrcUnavailable(address); - } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { - makeA2dpSrcAvailable(address); - } - } - } - - /*package*/ void onSetHearingAidConnectionState(BluetoothDevice btDevice, - @AudioService.BtProfileConnectionState int state, int streamType) { - String address = btDevice.getAddress(); - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - address = ""; - } - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "onSetHearingAidConnectionState addr=" + address)); + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent("BT connected:" + + " addr=" + address + + " profile=" + btInfo.mProfile + + " state=" + btInfo.mState + + " codec=" + AudioSystem.audioFormatToString(btInfo.mCodec))); - new MediaMetrics.Item(mMetricsId + "onSetHearingAidConnectionState") - .set(MediaMetrics.Property.ADDRESS, address) + new MediaMetrics.Item(mMetricsId + "onSetBtActiveDevice") + .set(MediaMetrics.Property.STATUS, btInfo.mProfile) .set(MediaMetrics.Property.DEVICE, - AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) - .set(MediaMetrics.Property.STATE, - state == BluetoothProfile.STATE_CONNECTED - ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) + AudioSystem.getDeviceName(btInfo.mAudioSystemDevice)) + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.ENCODING, + AudioSystem.audioFormatToString(btInfo.mCodec)) + .set(MediaMetrics.Property.EVENT, "onSetBtActiveDevice") .set(MediaMetrics.Property.STREAM_TYPE, AudioSystem.streamToString(streamType)) + .set(MediaMetrics.Property.STATE, + btInfo.mState == BluetoothProfile.STATE_CONNECTED + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) .record(); synchronized (mDevicesLock) { - final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, - btDevice.getAddress()); + final String key = DeviceInfo.makeDeviceListKey(btInfo.mAudioSystemDevice, address); final DeviceInfo di = mConnectedDevices.get(key); - boolean isConnected = di != null; - if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { - makeHearingAidDeviceUnavailable(address); - } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { - makeHearingAidDeviceAvailable(address, BtHelper.getName(btDevice), streamType, - "onSetHearingAidConnectionState"); - } - } - } + final boolean isConnected = di != null; - /*package*/ void onSetLeAudioConnectionState(BluetoothDevice btDevice, - @AudioService.BtProfileConnectionState int state, int streamType, int device) { - String address = btDevice.getAddress(); - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - address = ""; - } - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "onSetLeAudioConnectionState addr=" + address)); + final boolean switchToUnavailable = isConnected + && btInfo.mState != BluetoothProfile.STATE_CONNECTED; + final boolean switchToAvailable = !isConnected + && btInfo.mState == BluetoothProfile.STATE_CONNECTED; - synchronized (mDevicesLock) { - DeviceInfo di = null; - boolean isConnected = false; - - String key = DeviceInfo.makeDeviceListKey(device, btDevice.getAddress()); - di = mConnectedDevices.get(key); - isConnected = di != null; - - if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { - makeLeAudioDeviceUnavailable(address, device); - } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { - makeLeAudioDeviceAvailable(address, BtHelper.getName(btDevice), streamType, - device, "onSetLeAudioConnectionState"); + switch (btInfo.mProfile) { + case BluetoothProfile.A2DP_SINK: + if (switchToUnavailable) { + makeA2dpSrcUnavailable(address); + } else if (switchToAvailable) { + makeA2dpSrcAvailable(address); + } + break; + case BluetoothProfile.A2DP: + if (switchToUnavailable) { + makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat); + } else if (switchToAvailable) { + // device is not already connected + if (btInfo.mVolume != -1) { + mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC, + // convert index to internal representation in VolumeStreamState + btInfo.mVolume * 10, btInfo.mAudioSystemDevice, + "onSetBtActiveDevice"); + } + makeA2dpDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), + "onSetBtActiveDevice", btInfo.mCodec); + } + break; + case BluetoothProfile.HEARING_AID: + if (switchToUnavailable) { + makeHearingAidDeviceUnavailable(address); + } else if (switchToAvailable) { + makeHearingAidDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), + streamType, "onSetBtActiveDevice"); + } + break; + case BluetoothProfile.LE_AUDIO: + if (switchToUnavailable) { + makeLeAudioDeviceUnavailable(address, btInfo.mAudioSystemDevice); + } else if (switchToAvailable) { + makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), + streamType, btInfo.mAudioSystemDevice, "onSetBtActiveDevice"); + } + break; + default: throw new IllegalArgumentException("Invalid profile " + + BluetoothProfile.getProfileName(btInfo.mProfile)); } } } - /*package*/ void onSetLeAudioOutConnectionState(BluetoothDevice btDevice, - @AudioService.BtProfileConnectionState int state, int streamType) { - // TODO: b/198610537 clarify DEVICE_OUT_BLE_HEADSET vs DEVICE_OUT_BLE_SPEAKER criteria - onSetLeAudioConnectionState(btDevice, state, streamType, - AudioSystem.DEVICE_OUT_BLE_HEADSET); - } - - /*package*/ void onSetLeAudioInConnectionState(BluetoothDevice btDevice, - @AudioService.BtProfileConnectionState int state) { - onSetLeAudioConnectionState(btDevice, state, AudioSystem.STREAM_DEFAULT, - AudioSystem.DEVICE_IN_BLE_HEADSET); - } @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - /*package*/ void onBluetoothA2dpActiveDeviceChange( + /*package*/ void onBluetoothA2dpDeviceConfigChange( @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) { MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId - + "onBluetoothA2dpActiveDeviceChange") + + "onBluetoothA2dpDeviceConfigChange") .set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event)); final BluetoothDevice btDevice = btInfo.getBtDevice(); @@ -474,7 +387,7 @@ public class AudioDeviceInventory { return; } if (AudioService.DEBUG_DEVICES) { - Log.d(TAG, "onBluetoothA2dpActiveDeviceChange btDevice=" + btDevice); + Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice); } int a2dpVolume = btInfo.getVolume(); @AudioSystem.AudioFormatNativeEnumForBtCodec final int a2dpCodec = btInfo.getCodec(); @@ -484,11 +397,11 @@ public class AudioDeviceInventory { address = ""; } AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "onBluetoothA2dpActiveDeviceChange addr=" + address + "onBluetoothA2dpDeviceConfigChange addr=" + address + " event=" + BtHelper.a2dpDeviceEventToString(event))); synchronized (mDevicesLock) { - if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) { + if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "A2dp config change ignored (scheduled connection change)") .printLog(TAG)); @@ -500,7 +413,7 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); final DeviceInfo di = mConnectedDevices.get(key); if (di == null) { - Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpActiveDeviceChange"); + Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpDeviceConfigChange"); mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record(); return; } @@ -518,7 +431,7 @@ public class AudioDeviceInventory { // convert index to internal representation in VolumeStreamState a2dpVolume * 10, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - "onBluetoothA2dpActiveDeviceChange"); + "onBluetoothA2dpDeviceConfigChange"); } } else if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) { if (di.mDeviceCodecFormat != a2dpCodec) { @@ -539,10 +452,9 @@ public class AudioDeviceInventory { int musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC); // force A2DP device disconnection in case of error so that AudioService state is // consistent with audio policy manager state - setBluetoothA2dpDeviceConnectionState( - btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP, - false /* suppressNoisyIntent */, musicDevice, - -1 /* a2dpVolume */); + setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btDevice, + BluetoothProfile.A2DP, BluetoothProfile.STATE_DISCONNECTED, + musicDevice, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); } else { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "APM handleDeviceConfigChange success for A2DP device addr=" + address @@ -828,7 +740,7 @@ public class AudioDeviceInventory { } - /*package*/ void disconnectA2dp() { + private void disconnectA2dp() { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices @@ -850,7 +762,7 @@ public class AudioDeviceInventory { } } - /*package*/ void disconnectA2dpSink() { + private void disconnectA2dpSink() { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices @@ -865,7 +777,7 @@ public class AudioDeviceInventory { } } - /*package*/ void disconnectHearingAid() { + private void disconnectHearingAid() { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_OUT_HEARING_AID devices @@ -887,6 +799,28 @@ public class AudioDeviceInventory { } } + /*package*/ synchronized void onBtProfileDisconnected(int profile) { + switch (profile) { + case BluetoothProfile.A2DP: + disconnectA2dp(); + break; + case BluetoothProfile.A2DP_SINK: + disconnectA2dpSink(); + break; + case BluetoothProfile.HEARING_AID: + disconnectHearingAid(); + break; + case BluetoothProfile.LE_AUDIO: + disconnectLeAudio(); + break; + default: + // Not a valid profile to disconnect + Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect " + + BluetoothProfile.getProfileName(profile)); + break; + } + } + /*package*/ void disconnectLeAudio() { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); @@ -934,46 +868,39 @@ public class AudioDeviceInventory { // only public for mocking/spying @GuardedBy("AudioDeviceBroker.mDeviceStateLock") @VisibleForTesting - public void setBluetoothA2dpDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - int profile, boolean suppressNoisyIntent, int musicDevice, int a2dpVolume) { + public int setBluetoothActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo info) { int delay; - if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) { - throw new IllegalArgumentException("invalid profile " + profile); - } synchronized (mDevicesLock) { - if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) { + if (!info.mSupprNoisy + && ((info.mProfile == BluetoothProfile.LE_AUDIO && info.mIsLeOutput) + || info.mProfile == BluetoothProfile.HEARING_AID + || info.mProfile == BluetoothProfile.A2DP)) { @AudioService.ConnectionState int asState = - (state == BluetoothA2dp.STATE_CONNECTED) + (info.mState == BluetoothProfile.STATE_CONNECTED) ? AudioService.CONNECTION_STATE_CONNECTED : AudioService.CONNECTION_STATE_DISCONNECTED; - delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - asState, musicDevice); + delay = checkSendBecomingNoisyIntentInt(info.mAudioSystemDevice, asState, + info.mMusicDevice); } else { delay = 0; } - final int a2dpCodec = mDeviceBroker.getA2dpCodec(device); - if (AudioService.DEBUG_DEVICES) { - Log.i(TAG, "setBluetoothA2dpDeviceConnectionState device: " + device - + " state: " + state + " delay(ms): " + delay - + " codec:" + Integer.toHexString(a2dpCodec) - + " suppressNoisyIntent: " + suppressNoisyIntent); + Log.i(TAG, "setBluetoothActiveDevice device: " + info.mDevice + + " profile: " + BluetoothProfile.getProfileName(info.mProfile) + + " state: " + BluetoothProfile.getConnectionStateName(info.mState) + + " delay(ms): " + delay + + " codec:" + Integer.toHexString(info.mCodec) + + " suppressNoisyIntent: " + info.mSupprNoisy); } - - final BtHelper.BluetoothA2dpDeviceInfo a2dpDeviceInfo = - new BtHelper.BluetoothA2dpDeviceInfo(device, a2dpVolume, a2dpCodec); - if (profile == BluetoothProfile.A2DP) { - mDeviceBroker.postA2dpSinkConnection(state, - a2dpDeviceInfo, - delay); - } else { //profile == BluetoothProfile.A2DP_SINK - mDeviceBroker.postA2dpSourceConnection(state, - a2dpDeviceInfo, - delay); + mDeviceBroker.postBluetoothActiveDevice(info, delay); + if (info.mProfile == BluetoothProfile.HEARING_AID + && info.mState == BluetoothProfile.STATE_CONNECTED) { + mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE, + "HEARING_AID set to CONNECTED"); } } + return delay; } /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, @@ -987,50 +914,6 @@ public class AudioDeviceInventory { } } - /*package*/ int setBluetoothHearingAidDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, int musicDevice) { - int delay; - synchronized (mDevicesLock) { - if (!suppressNoisyIntent) { - int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0; - delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_HEARING_AID, - intState, musicDevice); - } else { - delay = 0; - } - mDeviceBroker.postSetHearingAidConnectionState(state, device, delay); - if (state == BluetoothHearingAid.STATE_CONNECTED) { - mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE, - "HEARING_AID set to CONNECTED"); - } - return delay; - } - } - - /*package*/ int setBluetoothLeAudioOutDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent) { - synchronized (mDevicesLock) { - /* Active device become null and it's previous device is not connected anymore */ - int delay = 0; - if (!suppressNoisyIntent) { - int intState = (state == BluetoothLeAudio.STATE_CONNECTED) ? 1 : 0; - delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_BLE_HEADSET, - intState, AudioSystem.DEVICE_NONE); - } - mDeviceBroker.postSetLeAudioOutConnectionState(state, device, delay); - return delay; - } - } - - /*package*/ void setBluetoothLeAudioInDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state) { - synchronized (mDevicesLock) { - mDeviceBroker.postSetLeAudioInConnectionState(state, device); - } - } - //------------------------------------------------------------------- // Internal utilities diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d75f21c4ef85..51784bbce2e0 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -83,6 +83,7 @@ import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; import android.media.AudioSystem; +import android.media.BtProfileConnectionInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; @@ -309,8 +310,8 @@ public class AudioService extends IAudioService.Stub private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35; private static final int MSG_UPDATE_AUDIO_MODE = 36; private static final int MSG_RECORDING_CONFIG_CHANGE = 37; - private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38; - private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39; + private static final int MSG_BT_DEV_CHANGED = 38; + private static final int MSG_DISPATCH_AUDIO_MODE = 40; // start of messages handled under wakelock @@ -6266,77 +6267,44 @@ public class AudioService extends IAudioService.Stub public @interface BtProfileConnectionState {} /** - * See AudioManager.setBluetoothHearingAidDeviceConnectionState() - */ - public void setBluetoothHearingAidDeviceConnectionState( - @NonNull BluetoothDevice device, @BtProfileConnectionState int state, - boolean suppressNoisyIntent, int musicDevice) - { - if (device == null) { - throw new IllegalArgumentException("Illegal null device"); - } - if (state != BluetoothProfile.STATE_CONNECTED - && state != BluetoothProfile.STATE_DISCONNECTED) { - throw new IllegalArgumentException("Illegal BluetoothProfile state for device " - + " (dis)connection, got " + state); - } - mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( - device, state, suppressNoisyIntent, musicDevice, "AudioService"); - } - - private void setBluetoothLeAudioDeviceConnectionState(@NonNull BluetoothDevice device, - @BtProfileConnectionState int state) { - if (device == null) { - throw new IllegalArgumentException("Illegal null device"); - } - if (state != BluetoothProfile.STATE_CONNECTED - && state != BluetoothProfile.STATE_DISCONNECTED) { - throw new IllegalArgumentException("Illegal BluetoothProfile state for device " - + " (dis)connection, got " + state); - } - } - - /** - * See AudioManager.setBluetoothLeAudioOutDeviceConnectionState() + * @hide + * The profiles that can be used with AudioService.handleBluetoothActiveDeviceChanged() */ - public void setBluetoothLeAudioOutDeviceConnectionState( - @NonNull BluetoothDevice device, @BtProfileConnectionState int state, - boolean suppressNoisyIntent) { - setBluetoothLeAudioDeviceConnectionState(device, state); - mDeviceBroker.postBluetoothLeAudioOutDeviceConnectionState(device, state, - suppressNoisyIntent, "AudioService"); - } + @IntDef({ + BluetoothProfile.HEARING_AID, + BluetoothProfile.A2DP, + BluetoothProfile.A2DP_SINK, + BluetoothProfile.LE_AUDIO, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BtProfile {} - /** - * See AudioManager.setBluetoothLeAudioInDeviceConnectionState() - */ - public void setBluetoothLeAudioInDeviceConnectionState( - @NonNull BluetoothDevice device, @BtProfileConnectionState int state) { - setBluetoothLeAudioDeviceConnectionState(device, state); - mDeviceBroker.postBluetoothLeAudioInDeviceConnectionState(device, state, "AudioService"); - } /** - * See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() + * See AudioManager.handleBluetoothActiveDeviceChanged(...) */ - public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - @NonNull BluetoothDevice device, @BtProfileConnectionState int state, - int profile, boolean suppressNoisyIntent, int a2dpVolume) { - if (device == null) { - throw new IllegalArgumentException("Illegal null device"); + public void handleBluetoothActiveDeviceChanged(BluetoothDevice newDevice, + BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_STACK) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Bluetooth is the only caller allowed"); } - if (state != BluetoothProfile.STATE_CONNECTED - && state != BluetoothProfile.STATE_DISCONNECTED) { - throw new IllegalArgumentException("Illegal BluetoothProfile state for device " - + " (dis)connection, got " + state); + if (info == null) { + throw new IllegalArgumentException("Illegal null BtProfileConnectionInfo for device " + + previousDevice + " -> " + newDevice); } - - AudioDeviceBroker.BtDeviceConnectionInfo info = - new AudioDeviceBroker.BtDeviceConnectionInfo(device, state, - profile, suppressNoisyIntent, a2dpVolume); - sendMsg(mAudioHandler, MSG_SET_A2DP_DEV_CONNECTION_STATE, SENDMSG_QUEUE, - 0 /*arg1*/, 0 /*arg2*/, - /*obj*/ info, 0 /*delay*/); + final int profile = info.getProfile(); + if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK + && profile != BluetoothProfile.LE_AUDIO + && profile != BluetoothProfile.HEARING_AID) { + throw new IllegalArgumentException("Illegal BluetoothProfile profile for device " + + previousDevice + " -> " + newDevice + ". Got: " + profile); + } + AudioDeviceBroker.BtDeviceChangedData data = + new AudioDeviceBroker.BtDeviceChangedData(newDevice, previousDevice, info, + "AudioService"); + sendMsg(mAudioHandler, MSG_BT_DEV_CHANGED, SENDMSG_QUEUE, 0, 0, + /*obj*/ data, /*delay*/ 0); } /** only public for mocking/spying, do not call outside of AudioService */ @@ -6345,19 +6313,6 @@ public class AudioService extends IAudioService.Stub mStreamStates[AudioSystem.STREAM_MUSIC].muteInternally(mute); } - /** - * See AudioManager.handleBluetoothA2dpDeviceConfigChange() - * @param device - */ - public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) - { - if (device == null) { - throw new IllegalArgumentException("Illegal null device"); - } - sendMsg(mAudioHandler, MSG_A2DP_DEV_CONFIG_CHANGE, SENDMSG_QUEUE, 0, 0, - /*obj*/ device, /*delay*/ 0); - } - private static final Set<Integer> DEVICE_MEDIA_UNMUTED_ON_PLUG_SET; static { DEVICE_MEDIA_UNMUTED_ON_PLUG_SET = new HashSet<>(); @@ -7709,13 +7664,9 @@ public class AudioService extends IAudioService.Stub } break; - case MSG_SET_A2DP_DEV_CONNECTION_STATE: - mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - (AudioDeviceBroker.BtDeviceConnectionInfo) msg.obj); - break; - - case MSG_A2DP_DEV_CONFIG_CHANGE: - mDeviceBroker.postBluetoothA2dpDeviceConfigChange((BluetoothDevice) msg.obj); + case MSG_BT_DEV_CHANGED: + mDeviceBroker.queueOnBluetoothActiveDeviceChanged( + (AudioDeviceBroker.BtDeviceChangedData) msg.obj); break; case MSG_DISPATCH_AUDIO_MODE: diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index c924fde23f9d..9273a5d5cf9c 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -31,6 +31,7 @@ import android.content.Intent; import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.AudioSystem; +import android.media.BtProfileConnectionInfo; import android.os.Binder; import android.os.UserHandle; import android.provider.Settings; @@ -451,11 +452,11 @@ public class BtHelper { } /*package*/ synchronized void disconnectAllBluetoothProfiles() { - mDeviceBroker.postDisconnectA2dp(); - mDeviceBroker.postDisconnectA2dpSink(); - mDeviceBroker.postDisconnectHeadset(); - mDeviceBroker.postDisconnectHearingAid(); - mDeviceBroker.postDisconnectLeAudio(); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.A2DP); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.A2DP_SINK); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.HEADSET); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.HEARING_AID); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.LE_AUDIO); } // @GuardedBy("AudioDeviceBroker.mSetModeLock") @@ -474,63 +475,32 @@ public class BtHelper { mBluetoothHeadset = null; } - /*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) { - mA2dp = a2dp; - final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices(); - if (deviceList.isEmpty()) { + /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) { + if (profile == BluetoothProfile.HEADSET) { + onHeadsetProfileConnected((BluetoothHeadset) proxy); return; } - final BluetoothDevice btDevice = deviceList.get(0); - // the device is guaranteed CONNECTED - mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(btDevice, - BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK, - true, -1)); - } - - /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) { - final List<BluetoothDevice> deviceList = profile.getConnectedDevices(); - if (deviceList.isEmpty()) { - return; + if (profile == BluetoothProfile.A2DP) { + mA2dp = (BluetoothA2dp) proxy; + } else if (profile == BluetoothProfile.LE_AUDIO) { + mLeAudio = (BluetoothLeAudio) proxy; } - final BluetoothDevice btDevice = deviceList.get(0); - final @BluetoothProfile.BtProfileState int state = - profile.getConnectionState(btDevice); - mDeviceBroker.postSetA2dpSourceConnectionState( - state, new BluetoothA2dpDeviceInfo(btDevice)); - } - - /*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) { - mHearingAid = hearingAid; - final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices(); + final List<BluetoothDevice> deviceList = proxy.getConnectedDevices(); if (deviceList.isEmpty()) { return; } final BluetoothDevice btDevice = deviceList.get(0); final @BluetoothProfile.BtProfileState int state = - mHearingAid.getConnectionState(btDevice); - mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( - btDevice, state, - /*suppressNoisyIntent*/ false, - /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE, - /*eventSource*/ "mBluetoothProfileServiceListener"); - } - - /*package*/ synchronized void onLeAudioProfileConnected(BluetoothLeAudio leAudio) { - mLeAudio = leAudio; - final List<BluetoothDevice> deviceList = mLeAudio.getConnectedDevices(); - if (deviceList.isEmpty()) { - return; + proxy.getConnectionState(btDevice); + if (state == BluetoothProfile.STATE_CONNECTED) { + mDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(btDevice, null, + new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener")); + } else { + mDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(null, btDevice, + new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener")); } - - final BluetoothDevice btDevice = deviceList.get(0); - final @BluetoothProfile.BtProfileState int state = - mLeAudio.getConnectionState(btDevice); - mDeviceBroker.postBluetoothLeAudioOutDeviceConnectionState( - btDevice, state, - /*suppressNoisyIntent*/ false, - /*musicDevice android.media.AudioSystem.DEVICE_NONE,*/ - /*eventSource*/ "mBluetoothProfileServiceListener"); } // @GuardedBy("AudioDeviceBroker.mSetModeLock") @@ -677,36 +647,16 @@ public class BtHelper { public void onServiceConnected(int profile, BluetoothProfile proxy) { switch(profile) { case BluetoothProfile.A2DP: - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting A2DP profile")); - mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy); - break; - case BluetoothProfile.A2DP_SINK: - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting A2DP_SINK profile")); - mDeviceBroker.postBtA2dpSinkProfileConnected(proxy); - break; - case BluetoothProfile.HEADSET: - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting HEADSET profile")); - mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy); - break; - case BluetoothProfile.HEARING_AID: - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting HEARING_AID profile")); - mDeviceBroker.postBtHearingAidProfileConnected( - (BluetoothHearingAid) proxy); - break; - case BluetoothProfile.LE_AUDIO: AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting LE_AUDIO profile")); - mDeviceBroker.postBtLeAudioProfileConnected( - (BluetoothLeAudio) proxy); + "BT profile service: connecting " + + BluetoothProfile.getProfileName(profile) + " profile")); + mDeviceBroker.postBtProfileConnected(profile, proxy); break; + default: break; } @@ -715,22 +665,11 @@ public class BtHelper { switch (profile) { case BluetoothProfile.A2DP: - mDeviceBroker.postDisconnectA2dp(); - break; - case BluetoothProfile.A2DP_SINK: - mDeviceBroker.postDisconnectA2dpSink(); - break; - case BluetoothProfile.HEADSET: - mDeviceBroker.postDisconnectHeadset(); - break; - case BluetoothProfile.HEARING_AID: - mDeviceBroker.postDisconnectHearingAid(); - break; case BluetoothProfile.LE_AUDIO: - mDeviceBroker.postDisconnectLeAudio(); + mDeviceBroker.postBtProfileDisconnected(profile); break; default: diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index 0cd2e3d0ff59..c97ad55ceeec 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -60,6 +60,7 @@ import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; @@ -70,6 +71,7 @@ import com.android.internal.util.ArrayUtils; import com.android.server.SystemService; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -81,6 +83,8 @@ public class AuthService extends SystemService { private static final String SETTING_HIDL_DISABLED = "com.android.server.biometrics.AuthService.hidlDisabled"; private static final int DEFAULT_HIDL_DISABLED = 0; + private static final String SYSPROP_FIRST_API_LEVEL = "ro.board.first_api_level"; + private static final String SYSPROP_API_LEVEL = "ro.board.api_level"; private final Injector mInjector; @@ -623,7 +627,16 @@ public class AuthService extends SystemService { final SensorConfig[] hidlConfigs; if (!mInjector.isHidlDisabled(getContext())) { - final String[] configStrings = mInjector.getConfiguration(getContext()); + final int firstApiLevel = SystemProperties.getInt(SYSPROP_FIRST_API_LEVEL, 0); + final int apiLevel = SystemProperties.getInt(SYSPROP_API_LEVEL, firstApiLevel); + String[] configStrings = mInjector.getConfiguration(getContext()); + if (configStrings.length == 0 && apiLevel == Build.VERSION_CODES.R) { + // For backwards compatibility with R where biometrics could work without being + // configured in config_biometric_sensors. In the absence of a vendor provided + // configuration, we assume the weakest biometric strength (i.e. convenience). + Slog.w(TAG, "Found R vendor partition without config_biometric_sensors"); + configStrings = generateRSdkCompatibleConfiguration(); + } hidlConfigs = new SensorConfig[configStrings.length]; for (int i = 0; i < configStrings.length; ++i) { hidlConfigs[i] = new SensorConfig(configStrings[i]); @@ -639,6 +652,31 @@ public class AuthService extends SystemService { } /** + * Generates an array of string configs with entries that correspond to the biometric features + * declared on the device. Returns an empty array if no biometric features are declared. + * Biometrics are assumed to be of the weakest strength class, i.e. convenience. + */ + private @NonNull String[] generateRSdkCompatibleConfiguration() { + final PackageManager pm = getContext().getPackageManager(); + final ArrayList<String> modalities = new ArrayList<>(); + if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { + modalities.add(String.valueOf(BiometricAuthenticator.TYPE_FINGERPRINT)); + } + if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) { + modalities.add(String.valueOf(BiometricAuthenticator.TYPE_FACE)); + } + final String strength = String.valueOf(Authenticators.BIOMETRIC_CONVENIENCE); + final String[] configStrings = new String[modalities.size()]; + for (int i = 0; i < modalities.size(); ++i) { + final String id = String.valueOf(i); + final String modality = modalities.get(i); + configStrings[i] = String.join(":" /* delimiter */, id, modality, strength); + } + Slog.d(TAG, "Generated config_biometric_sensors: " + Arrays.toString(configStrings)); + return configStrings; + } + + /** * Registers HIDL and AIDL authenticators for all of the available modalities. * * @param hidlSensors Array of {@link SensorConfig} configuration for all of the HIDL sensors diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index 73de0f814325..ffc1aed4c672 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -51,6 +51,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; class BluetoothRouteProvider { private static final String TAG = "BTRouteProvider"; @@ -174,8 +175,9 @@ class BluetoothRouteProvider { private void buildBluetoothRoutes() { mBluetoothRoutes.clear(); - if (mBluetoothAdapter.getBondedDevices() != null) { - for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) { + Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices(); + if (bondedDevices != null) { + for (BluetoothDevice device : bondedDevices) { if (device.isConnected()) { BluetoothRouteInfo newBtRoute = createBluetoothRoute(device); if (newBtRoute.connectedProfiles.size() > 0) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 827dfc0caa16..1a0a885d88b4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -21918,7 +21918,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) { ScanPartition sp = SYSTEM_PARTITIONS.get(i); if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith( - sp.getFolder().getAbsolutePath())) { + sp.getFolder().getAbsolutePath() + File.separator)) { return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX); } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index e40882268e67..4a9772093bb7 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2666,6 +2666,24 @@ public class PhoneWindowManager implements WindowManagerPolicy { Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in" + " interceptKeyBeforeQueueing"); return key_consumed; + case KeyEvent.KEYCODE_VIDEO_APP_1: + case KeyEvent.KEYCODE_VIDEO_APP_2: + case KeyEvent.KEYCODE_VIDEO_APP_3: + case KeyEvent.KEYCODE_VIDEO_APP_4: + case KeyEvent.KEYCODE_VIDEO_APP_5: + case KeyEvent.KEYCODE_VIDEO_APP_6: + case KeyEvent.KEYCODE_VIDEO_APP_7: + case KeyEvent.KEYCODE_VIDEO_APP_8: + case KeyEvent.KEYCODE_FEATURED_APP_1: + case KeyEvent.KEYCODE_FEATURED_APP_2: + case KeyEvent.KEYCODE_FEATURED_APP_3: + case KeyEvent.KEYCODE_FEATURED_APP_4: + case KeyEvent.KEYCODE_DEMO_APP_1: + case KeyEvent.KEYCODE_DEMO_APP_2: + case KeyEvent.KEYCODE_DEMO_APP_3: + case KeyEvent.KEYCODE_DEMO_APP_4: + Slog.wtf(TAG, "KEYCODE_APP_X should be handled in interceptKeyBeforeQueueing"); + return key_consumed; case KeyEvent.KEYCODE_SYSRQ: if (down && repeatCount == 0) { mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); @@ -3773,6 +3791,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { } break; } + case KeyEvent.KEYCODE_VIDEO_APP_1: + case KeyEvent.KEYCODE_VIDEO_APP_2: + case KeyEvent.KEYCODE_VIDEO_APP_3: + case KeyEvent.KEYCODE_VIDEO_APP_4: + case KeyEvent.KEYCODE_VIDEO_APP_5: + case KeyEvent.KEYCODE_VIDEO_APP_6: + case KeyEvent.KEYCODE_VIDEO_APP_7: + case KeyEvent.KEYCODE_VIDEO_APP_8: + case KeyEvent.KEYCODE_FEATURED_APP_1: + case KeyEvent.KEYCODE_FEATURED_APP_2: + case KeyEvent.KEYCODE_FEATURED_APP_3: + case KeyEvent.KEYCODE_FEATURED_APP_4: + case KeyEvent.KEYCODE_DEMO_APP_1: + case KeyEvent.KEYCODE_DEMO_APP_2: + case KeyEvent.KEYCODE_DEMO_APP_3: + case KeyEvent.KEYCODE_DEMO_APP_4: { + // Just drop if keys are not intercepted for direct key. + result &= ~ACTION_PASS_TO_USER; + break; + } } // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users. diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 28947083854b..b9ceec15c9ab 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -315,6 +315,7 @@ public final class TvInputManagerService extends SystemService { PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, userId); List<TvInputInfo> inputList = new ArrayList<>(); + List<ComponentName> hardwareComponents = new ArrayList<>(); for (ResolveInfo ri : services) { ServiceInfo si = ri.serviceInfo; if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) { @@ -325,6 +326,7 @@ public final class TvInputManagerService extends SystemService { ComponentName component = new ComponentName(si.packageName, si.name); if (hasHardwarePermission(pm, component)) { + hardwareComponents.add(component); ServiceState serviceState = userState.serviceStateMap.get(component); if (serviceState == null) { // New hardware input found. Create a new ServiceState and connect to the @@ -397,6 +399,15 @@ public final class TvInputManagerService extends SystemService { } } + // Clean up ServiceState corresponding to the removed hardware inputs + Iterator<ServiceState> it = userState.serviceStateMap.values().iterator(); + while (it.hasNext()) { + ServiceState serviceState = it.next(); + if (serviceState.isHardware && !hardwareComponents.contains(serviceState.component)) { + it.remove(); + } + } + userState.inputMap.clear(); userState.inputMap = inputMap; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 9caef70f6b51..3421b28454f0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -167,8 +167,10 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; -import android.hardware.configstore.V1_0.ISurfaceFlingerConfigs; import android.hardware.configstore.V1_0.OptionalBool; +import android.hardware.configstore.V1_1.DisplayOrientation; +import android.hardware.configstore.V1_1.ISurfaceFlingerConfigs; +import android.hardware.configstore.V1_1.OptionalDisplayOrientation; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.hardware.input.InputManager; @@ -220,6 +222,7 @@ import android.util.TypedValue; import android.util.proto.ProtoOutputStream; import android.view.Choreographer; import android.view.Display; +import android.view.DisplayAddress; import android.view.DisplayInfo; import android.view.Gravity; import android.view.IAppTransitionAnimationSpecsFuture; @@ -465,6 +468,8 @@ public class WindowManagerService extends IWindowManager.Stub */ static final boolean ENABLE_FIXED_ROTATION_TRANSFORM = SystemProperties.getBoolean("persist.wm.fixed_rotation_transform", true); + private @Surface.Rotation int mPrimaryDisplayOrientation = Surface.ROTATION_0; + private DisplayAddress mPrimaryDisplayPhysicalAddress; // Enums for animation scale update types. @Retention(RetentionPolicy.SOURCE) @@ -2461,16 +2466,21 @@ public class WindowManagerService extends IWindowManager.Stub configChanged = displayContent.updateOrientation(); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - final DisplayInfo rotatedDisplayInfo = - win.mToken.getFixedRotationTransformDisplayInfo(); - if (rotatedDisplayInfo != null) { - outSurfaceControl.setTransformHint(rotatedDisplayInfo.rotation); - } else { - // We have to update the transform hint of display here, but we need to get if from - // SurfaceFlinger, so set it as rotation of display for most cases, then - // SurfaceFlinger would still update the transform hint of display in next frame. - outSurfaceControl.setTransformHint(displayContent.getDisplayInfo().rotation); - } + final DisplayInfo displayInfo = win.getDisplayInfo(); + int transformHint = displayInfo.rotation; + // If the window is on the primary display, use the panel orientation to adjust the + // transform hint + final boolean isPrimaryDisplay = displayInfo.address != null && + displayInfo.address.equals(mPrimaryDisplayPhysicalAddress); + if (isPrimaryDisplay) { + transformHint = (transformHint + mPrimaryDisplayOrientation) % 4; + } + outSurfaceControl.setTransformHint(transformHint); + ProtoLog.v(WM_DEBUG_ORIENTATION, + "Passing transform hint %d for window %s%s", + transformHint, win, + isPrimaryDisplay ? " on primary display with orientation " + + mPrimaryDisplayOrientation : ""); if (toBeDisplayed && win.mIsWallpaper) { displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */); @@ -4868,6 +4878,9 @@ public class WindowManagerService extends IWindowManager.Stub mTaskSnapshotController.systemReady(); mHasWideColorGamutSupport = queryWideColorGamutSupport(); mHasHdrSupport = queryHdrSupport(); + mPrimaryDisplayOrientation = queryPrimaryDisplayOrientation(); + mPrimaryDisplayPhysicalAddress = + DisplayAddress.fromPhysicalDisplayId(SurfaceControl.getPrimaryPhysicalDisplayId()); UiThread.getHandler().post(mSettingsObserver::loadSettings); IVrManager vrManager = IVrManager.Stub.asInterface( ServiceManager.getService(Context.VR_SERVICE)); @@ -4887,6 +4900,9 @@ public class WindowManagerService extends IWindowManager.Stub } } + + // Keep logic in sync with SurfaceFlingerProperties.cpp + // Consider exposing properties via ISurfaceComposer instead. private static boolean queryWideColorGamutSupport() { boolean defaultValue = false; Optional<Boolean> hasWideColorProp = SurfaceFlingerProperties.has_wide_color_display(); @@ -4927,6 +4943,39 @@ public class WindowManagerService extends IWindowManager.Stub return false; } + private static @Surface.Rotation int queryPrimaryDisplayOrientation() { + Optional<SurfaceFlingerProperties.primary_display_orientation_values> prop = + SurfaceFlingerProperties.primary_display_orientation(); + if (prop.isPresent()) { + switch (prop.get()) { + case ORIENTATION_90: return Surface.ROTATION_90; + case ORIENTATION_180: return Surface.ROTATION_180; + case ORIENTATION_270: return Surface.ROTATION_270; + case ORIENTATION_0: + default: + return Surface.ROTATION_0; + } + } + try { + ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService(); + OptionalDisplayOrientation primaryDisplayOrientation = + surfaceFlinger.primaryDisplayOrientation(); + if (primaryDisplayOrientation != null && primaryDisplayOrientation.specified) { + switch (primaryDisplayOrientation.value) { + case DisplayOrientation.ORIENTATION_90: return Surface.ROTATION_90; + case DisplayOrientation.ORIENTATION_180: return Surface.ROTATION_180; + case DisplayOrientation.ORIENTATION_270: return Surface.ROTATION_270; + case DisplayOrientation.ORIENTATION_0: + default: + return Surface.ROTATION_0; + } + } + } catch (Exception e) { + // Use default value if we can't talk to config store. + } + return Surface.ROTATION_0; + } + void reportFocusChanged(IBinder oldToken, IBinder newToken) { WindowState lastFocus; WindowState newFocus; diff --git a/services/tests/mockingservicestests/OWNERS b/services/tests/mockingservicestests/OWNERS new file mode 100644 index 000000000000..0fb0c3021486 --- /dev/null +++ b/services/tests/mockingservicestests/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS diff --git a/services/tests/servicestests/OWNERS b/services/tests/servicestests/OWNERS new file mode 100644 index 000000000000..0fb0c3021486 --- /dev/null +++ b/services/tests/servicestests/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java index 5c53d43fa1df..9e1445cf589d 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java @@ -26,11 +26,11 @@ import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.media.AudioManager; import android.media.AudioSystem; +import android.media.BtProfileConnectionInfo; import android.util.Log; import androidx.test.InstrumentationRegistry; @@ -98,16 +98,12 @@ public class AudioDeviceBrokerTest { Log.i(TAG, "starting testPostA2dpDeviceConnectionChange"); Assert.assertNotNull("invalid null BT device", mFakeBtDevice); - mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1)); + mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, + BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource")); Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS); - verify(mSpyDevInventory, times(1)).setBluetoothA2dpDeviceConnectionState( - any(BluetoothDevice.class), - ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED) /*state*/, - ArgumentMatchers.eq(BluetoothProfile.A2DP) /*profile*/, - ArgumentMatchers.eq(true) /*suppressNoisyIntent*/, anyInt() /*musicDevice*/, - ArgumentMatchers.eq(1) /*a2dpVolume*/ + verify(mSpyDevInventory, times(1)).setBluetoothActiveDevice( + any(AudioDeviceBroker.BtDeviceInfo.class) ); // verify the connection was reported to AudioSystem @@ -210,30 +206,29 @@ public class AudioDeviceBrokerTest { ((NoOpAudioSystemAdapter) mSpyAudioSystem).configureIsStreamActive(mockMediaPlayback); // first connection: ensure the device is connected as a starting condition for the test - mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1)); + mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, + BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource")); Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // disconnection - mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, - BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, false, -1)); + mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(null, mFakeBtDevice, + BtProfileConnectionInfo.a2dpInfo(false, -1), "testSource")); if (delayAfterDisconnection > 0) { Thread.sleep(delayAfterDisconnection); } // reconnection - mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 2)); + mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, + BtProfileConnectionInfo.a2dpInfo(true, 2), "testSource")); Thread.sleep(AudioService.BECOMING_NOISY_DELAY_MS + MAX_MESSAGE_HANDLING_DELAY_MS); // Verify disconnection has been cancelled and we're seeing two connections attempts, // with the device connected at the end of the test - verify(mSpyDevInventory, times(2)).onSetA2dpSinkConnectionState( - any(BtHelper.BluetoothA2dpDeviceInfo.class), - ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED)); + verify(mSpyDevInventory, times(2)).onSetBtActiveDevice( + any(AudioDeviceBroker.BtDeviceInfo.class), anyInt()); Assert.assertTrue("Mock device not connected", mSpyDevInventory.isA2dpDeviceConnected(mFakeBtDevice)); diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index cac716efe78f..0ddd52dfc76d 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -69,7 +69,14 @@ import java.util.List; * them know that the app has crashed and that their call was continued using the pre-loaded dialer * app. * <p> - * Further, the pre-loaded dialer will ALWAYS be used when the user places an emergency call. + * The pre-loaded dialer will ALWAYS be used when the user places an emergency call, even if your + * app fills the {@link android.app.role.RoleManager#ROLE_DIALER} role. To ensure an optimal + * experience when placing an emergency call, the default dialer should ALWAYS use + * {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls (including + * emergency calls). This ensures that the platform is able to verify that the request came from + * the default dialer. If a non-preloaded dialer app uses {@link Intent#ACTION_CALL} to place an + * emergency call, it will be raised to the preloaded dialer app using {@link Intent#ACTION_DIAL} + * for confirmation; this is a suboptimal user experience. * <p> * Below is an example manifest registration for an {@code InCallService}. The meta-data * {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index df1f13294961..894bb8e15333 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -5269,6 +5269,16 @@ public class CarrierConfigManager { public static final String KEY_VONR_SETTING_VISIBILITY_BOOL = "vonr_setting_visibility_bool"; /** + * Flag specifying whether VoNR should be enabled for carrier. + * If true, VoNr will be enabled. If false, hard disabled. + * + * Disabled by default. + * + * @hide + */ + public static final String KEY_VONR_ENABLED_BOOL = "vonr_enabled_bool"; + + /** * Determine whether unthrottle data retry when tracking area code (TAC/LAC) from cell changes * * @hide @@ -5893,6 +5903,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL, false); sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false); sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, false); + sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false); } /** diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 3b44a34048b8..d5315acf60d5 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -949,6 +949,15 @@ public class SubscriptionManager { public static final String VOIMS_OPT_IN_STATUS = SimInfo.COLUMN_VOIMS_OPT_IN_STATUS; /** + * TelephonyProvider column name for NR Advanced calling + * Determines if the user has enabled VoNR settings for this subscription. + * + * @hide + */ + public static final String NR_ADVANCED_CALLING_ENABLED = + SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED; + + /** * Profile class of the subscription * @hide */ |