Merge "Fix EINTR handling"
diff --git a/Android.bp b/Android.bp
index fc713ff..3d087c0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -563,6 +563,7 @@
"android.hardware.vibrator-V1.3-java",
"framework-protos",
"art.module.public.api",
+ "sdk_module-lib_current_framework-tethering",
// There are a few classes from modules used by the core that
// need to be resolved by metalava. We use a prebuilt stub of the
// full sdk to ensure we can resolve them. If a new class gets added,
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 44c55c2..d090296 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -318,6 +318,7 @@
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":module-lib-api-stubs-docs-non-updatable"],
libs: [
+ "sdk_module-lib_current_framework-tethering",
"sdk_system_current_android",
// NOTE: The below can be removed once the prebuilt stub contains IKE.
"sdk_system_current_android.net.ipsec.ike",
diff --git a/core/api/Android.bp b/core/api/Android.bp
index 170febb..114a957 100644
--- a/core/api/Android.bp
+++ b/core/api/Android.bp
@@ -51,11 +51,17 @@
filegroup {
name: "non-updatable-module-lib-current.txt",
srcs: ["module-lib-current.txt"],
- visibility: ["//frameworks/base/api"],
+ visibility: [
+ "//frameworks/base/api",
+ "//cts/tests/signature/api",
+ ],
}
filegroup {
name: "non-updatable-module-lib-removed.txt",
srcs: ["module-lib-removed.txt"],
- visibility: ["//frameworks/base/api"],
+ visibility: [
+ "//frameworks/base/api",
+ "//cts/tests/signature/api",
+ ],
}
diff --git a/core/api/current.txt b/core/api/current.txt
index f21518c..f15d457 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -43474,8 +43474,10 @@
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVtSettingEnabled();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback);
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback);
field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1
field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0
@@ -43492,7 +43494,9 @@
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE"}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback);
field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
}
@@ -43691,6 +43695,19 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsRegistrationAttributes> CREATOR;
}
+ public abstract class ImsStateCallback {
+ ctor public ImsStateCallback();
+ method public abstract void onAvailable();
+ method public abstract void onError();
+ method public abstract void onUnavailable(int);
+ field public static final int REASON_IMS_SERVICE_DISCONNECTED = 3; // 0x3
+ field public static final int REASON_IMS_SERVICE_NOT_READY = 6; // 0x6
+ field public static final int REASON_NO_IMS_SERVICE_CONFIGURED = 4; // 0x4
+ field public static final int REASON_SUBSCRIPTION_INACTIVE = 5; // 0x5
+ field public static final int REASON_UNKNOWN_PERMANENT_ERROR = 2; // 0x2
+ field public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1; // 0x1
+ }
+
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
}
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 9af6c1b..3a35f24 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -113,6 +113,7 @@
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 @@
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/system-current.txt b/core/api/system-current.txt
index 78e6a07..84a4a44 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -13741,7 +13741,9 @@
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isSupported() throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerFullNetworkRegistration(@NonNull android.telephony.ims.SipDelegateConnection, @IntRange(from=100, to=699) int, @Nullable String);
+ method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback);
field public static final int DENIED_REASON_INVALID = 4; // 0x4
field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1
field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2
diff --git a/core/java/Android.bp b/core/java/Android.bp
index e08a493..ca9a468 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -343,7 +343,6 @@
filegroup {
name: "framework-telephony-common-shared-srcs",
srcs: [
- ":modules-utils-preconditions-srcs",
"android/os/RegistrantList.java",
"android/os/Registrant.java",
"android/util/IndentingPrintWriter.java",
@@ -355,10 +354,7 @@
"com/android/internal/util/BitwiseInputStream.java",
"com/android/internal/util/FastXmlSerializer.java",
"com/android/internal/util/HexDump.java",
- "com/android/internal/util/IState.java",
"com/android/internal/util/IndentingPrintWriter.java",
- "com/android/internal/util/State.java",
- "com/android/internal/util/StateMachine.java",
"com/android/internal/util/UserIcons.java",
],
}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 8aa2785..5143579 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -552,6 +552,11 @@
inflateAsync(rvToApply);
return;
}
+
+ // Prepare a local reference to the remote Context so we're ready to
+ // inflate any requested LayoutParams
+ mRemoteContext = getRemoteContext();
+
int layoutId = rvToApply.getLayoutId();
if (rvToApply.canRecycleView(mView)) {
try {
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 12bcd8b..b5fe4f5 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -62,6 +62,10 @@
* another buffer allocation and copy, and even more pressure on the gc.
* That means that if your log message is filtered out, you might be doing
* significant work and incurring significant overhead.
+ *
+ * <p>When calling the log methods that take a Throwable parameter,
+ * if any of the throwables in the cause chain is an <code>UnknownHostException</code>,
+ * then the stack trace is not logged.
*/
public final class Log {
/** @hide */
@@ -341,6 +345,9 @@
/**
* Handy function to get a loggable stack trace from a Throwable
+
+ * <p>If any of the throwables in the cause chain is an <code>UnknownHostException</code>,
+ * this returns an empty string.
* @param tr An exception to log
*/
@NonNull
diff --git a/core/java/com/android/internal/view/OWNERS b/core/java/com/android/internal/view/OWNERS
index eb2478f..7a590d0 100644
--- a/core/java/com/android/internal/view/OWNERS
+++ b/core/java/com/android/internal/view/OWNERS
@@ -15,7 +15,7 @@
# WindowManager
per-file AppearanceRegion = file:/services/core/java/com/android/server/wm/OWNERS
-per-file BaseIWIndow.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file BaseIWindow.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file RotationPolicy.java = file:/services/core/java/com/android/server/wm/OWNERS
per-file WindowManagerPolicyThread.java = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java
index 2959667..26ff192 100644
--- a/core/java/com/android/server/NetworkManagementSocketTagger.java
+++ b/core/java/com/android/server/NetworkManagementSocketTagger.java
@@ -17,7 +17,6 @@
package com.android.server;
import android.os.StrictMode;
-import android.os.SystemProperties;
import android.util.Log;
import android.util.Slog;
@@ -33,13 +32,6 @@
private static final String TAG = "NetworkManagementSocketTagger";
private static final boolean LOGD = false;
- /**
- * {@link SystemProperties} key that indicates if {@code qtaguid} bandwidth
- * controls have been enabled.
- */
- // TODO: remove when always enabled, or once socket tagging silently fails.
- public static final String PROP_QTAGUID_ENABLED = "net.qtaguid_enabled";
-
private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() {
@Override
protected SocketTags initialValue() {
@@ -88,13 +80,11 @@
private void tagSocketFd(FileDescriptor fd, int tag, int uid) {
if (tag == -1 && uid == -1) return;
- if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
- final int errno = native_tagSocketFd(fd, tag, uid);
- if (errno < 0) {
- Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", "
- + tag + ", " +
- + uid + ") failed with errno" + errno);
- }
+ final int errno = native_tagSocketFd(fd, tag, uid);
+ if (errno < 0) {
+ Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", "
+ + tag + ", "
+ + uid + ") failed with errno" + errno);
}
}
@@ -110,11 +100,9 @@
final SocketTags options = threadSocketTags.get();
if (options.statsTag == -1 && options.statsUid == -1) return;
- if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
- final int errno = native_untagSocketFd(fd);
- if (errno < 0) {
- Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno);
- }
+ final int errno = native_untagSocketFd(fd);
+ if (errno < 0) {
+ Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno);
}
}
@@ -124,21 +112,17 @@
}
public static void setKernelCounterSet(int uid, int counterSet) {
- if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
- final int errno = native_setCounterSet(counterSet, uid);
- if (errno < 0) {
- Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno "
- + errno);
- }
+ final int errno = native_setCounterSet(counterSet, uid);
+ if (errno < 0) {
+ Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno "
+ + errno);
}
}
public static void resetKernelUidStats(int uid) {
- if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) {
- int errno = native_deleteTagData(0, uid);
- if (errno < 0) {
- Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno);
- }
+ int errno = native_deleteTagData(0, uid);
+ if (errno < 0) {
+ Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno);
}
}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 97cac29..a131111 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -746,8 +746,9 @@
}
const bool checkJni = GetBoolProperty("dalvik.vm.checkjni", false);
- ALOGV("CheckJNI is %s\n", checkJni ? "ON" : "OFF");
if (checkJni) {
+ ALOGD("CheckJNI is ON");
+
/* extended JNI checking */
addOption("-Xcheck:jni");
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index cf7039b..c7f5696 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -32,7 +32,6 @@
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 @@
}
}
- /**
- * 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.
+ * 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 setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- BluetoothDevice device, int state,
- int profile, boolean suppressNoisyIntent, int a2dpVolume) {
+ @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.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.
- * {@hide}
- */
- public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) {
- 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 0000000..047f06b
--- /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 0000000..19ea2de
--- /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 dd44fdf..5ff56f9 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -24,6 +24,7 @@
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 @@
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 @@
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/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index ecd9cc1..39b560b 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -65,6 +65,7 @@
case HAL_PIXEL_FORMAT_Y8:
case HAL_PIXEL_FORMAT_Y16:
case HAL_PIXEL_FORMAT_RAW16:
+ case HAL_PIXEL_FORMAT_RAW12:
case HAL_PIXEL_FORMAT_RAW10:
case HAL_PIXEL_FORMAT_RAW_OPAQUE:
case HAL_PIXEL_FORMAT_BLOB:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 15f77ff..8a213ec 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -239,18 +239,20 @@
if (mGhbmView != null && surface == null) {
Log.e(TAG, "doIlluminate | surface must be non-null for GHBM");
}
- mHbmProvider.enableHbm(mHbmType, surface, () -> {
- if (mGhbmView != null) {
- mGhbmView.drawIlluminationDot(mSensorRect);
- }
- if (onIlluminatedRunnable != null) {
- // No framework API can reliably tell when a frame reaches the panel. A timeout
- // is the safest solution.
- postDelayed(onIlluminatedRunnable, mOnIlluminatedDelayMs);
- } else {
- Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null");
- }
- });
+ if (mHbmProvider != null) {
+ mHbmProvider.enableHbm(mHbmType, surface, () -> {
+ if (mGhbmView != null) {
+ mGhbmView.drawIlluminationDot(mSensorRect);
+ }
+ if (onIlluminatedRunnable != null) {
+ // No framework API can reliably tell when a frame reaches the panel. A timeout
+ // is the safest solution.
+ postDelayed(onIlluminatedRunnable, mOnIlluminatedDelayMs);
+ } else {
+ Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null");
+ }
+ });
+ }
}
@Override
@@ -263,6 +265,8 @@
mGhbmView.setGhbmIlluminationListener(null);
mGhbmView.setVisibility(View.INVISIBLE);
}
- mHbmProvider.disableHbm(null /* onHbmDisabled */);
+ if (mHbmProvider != null) {
+ mHbmProvider.disableHbm(null /* onHbmDisabled */);
+ }
}
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index c7e9068..a2c2dbd 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -39,8 +39,6 @@
import static android.net.NetworkStats.TAG_NONE;
import static android.net.TrafficStats.UID_TETHERING;
-import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
-
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
@@ -72,7 +70,6 @@
import android.os.ServiceSpecificException;
import android.os.StrictMode;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.Trace;
import android.text.TextUtils;
import android.util.Log;
@@ -446,9 +443,6 @@
// push any existing quota or UID rules
synchronized (mQuotaLock) {
- // Netd unconditionally enable bandwidth control
- SystemProperties.set(PROP_QTAGUID_ENABLED, "1");
-
mStrictEnabled = true;
setDataSaverModeEnabled(mDataSaverMode);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index c383f51..0b2311b 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 @@
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.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 @@
}
};
- /*package*/ static final class BtDeviceConnectionInfo {
- final @NonNull BluetoothDevice mDevice;
- final @AudioService.BtProfileConnectionState int mState;
- final int mProfile;
- final boolean mSupprNoisy;
- final int mVolume;
+ /*package*/ static final class BtDeviceChangedData {
+ final @Nullable BluetoothDevice mNewDevice;
+ final @Nullable BluetoothDevice mPreviousDevice;
+ final @NonNull BtProfileConnectionInfo mInfo;
+ final @NonNull String mEventSource;
- BtDeviceConnectionInfo(@NonNull BluetoothDevice device,
- @AudioService.BtProfileConnectionState int state,
- int profile, boolean suppressNoisyIntent, int vol) {
- mDevice = device;
- mState = state;
- mProfile = profile;
- mSupprNoisy = suppressNoisyIntent;
- mVolume = vol;
+ BtDeviceChangedData(@Nullable BluetoothDevice newDevice,
+ @Nullable BluetoothDevice previousDevice,
+ @NonNull BtProfileConnectionInfo info, @NonNull String eventSource) {
+ mNewDevice = newDevice;
+ mPreviousDevice = previousDevice;
+ mInfo = info;
+ mEventSource = eventSource;
}
- BtDeviceConnectionInfo(@NonNull BtDeviceConnectionInfo info) {
- mDevice = info.mDevice;
- mState = info.mState;
- mProfile = info.mProfile;
- mSupprNoisy = info.mSupprNoisy;
- mVolume = info.mVolume;
+ @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 @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;
+
+ 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;
+ 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 @@
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 @@
* 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);
+ /*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));
+ }
+ }
}
}
- /** 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 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 @@
}
@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 @@
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 postBtProfileDisconnected(int profile) {
+ sendIMsgNoDelay(MSG_I_BT_SERVICE_DISCONNECTED_PROFILE, SENDMSG_QUEUE, profile);
}
- /*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 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 @@
}
}
- /*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 @@
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 @@
}
}
- /*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) {
- synchronized (mDeviceStateLock) {
- return mBtHelper.getA2dpCodec(device);
- }
- }
-
/*package*/ void broadcastStickyIntentToCurrentProfileGroup(Intent intent) {
mSystemServer.broadcastStickyIntentToCurrentProfileGroup(intent);
}
@@ -1285,39 +1200,11 @@
mDeviceInventory.onReportNewRoutes();
}
break;
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
+ case MSG_L_SET_BT_ACTIVE_DEVICE:
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:
- 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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 6c3c736..0a114b9 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 @@
}
}
- // 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();
+ void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int streamType) {
if (AudioService.DEBUG_DEVICES) {
- Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice + " state="
- + state + " vol=" + a2dpVolume);
+ Log.d(TAG, "onSetBtActiveDevice"
+ + " btDevice=" + btInfo.mDevice
+ + " profile=" + BluetoothProfile.getProfileName(btInfo.mProfile)
+ + " state=" + BluetoothProfile.getConnectionStateName(btInfo.mState));
}
- String address = btDevice.getAddress();
- if (address == null) {
- address = "";
- }
+ String address = btInfo.mDevice.getAddress();
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
address = "";
}
- final @AudioSystem.AudioFormatNativeEnumForBtCodec int a2dpCodec = btInfo.getCodec();
+ AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent("BT connected:"
+ + " addr=" + address
+ + " profile=" + btInfo.mProfile
+ + " state=" + btInfo.mState
+ + " codec=" + AudioSystem.audioFormatToString(btInfo.mCodec)));
- 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")
+ new MediaMetrics.Item(mMetricsId + "onSetBtActiveDevice")
+ .set(MediaMetrics.Property.STATUS, btInfo.mProfile)
+ .set(MediaMetrics.Property.DEVICE,
+ AudioSystem.getDeviceName(btInfo.mAudioSystemDevice))
.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.ENCODING,
+ AudioSystem.audioFormatToString(btInfo.mCodec))
+ .set(MediaMetrics.Property.EVENT, "onSetBtActiveDevice")
+ .set(MediaMetrics.Property.STREAM_TYPE,
+ AudioSystem.streamToString(streamType))
.set(MediaMetrics.Property.STATE,
- state == BluetoothProfile.STATE_CONNECTED
+ btInfo.mState == 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 String key = DeviceInfo.makeDeviceListKey(btInfo.mAudioSystemDevice, address);
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);
+ final boolean isConnected = di != null;
+
+ final boolean switchToUnavailable = isConnected
+ && btInfo.mState != BluetoothProfile.STATE_CONNECTED;
+ final boolean switchToAvailable = !isConnected
+ && btInfo.mState == BluetoothProfile.STATE_CONNECTED;
+
+ switch (btInfo.mProfile) {
+ case BluetoothProfile.A2DP_SINK:
+ if (switchToUnavailable) {
+ makeA2dpSrcUnavailable(address);
+ } else if (switchToAvailable) {
+ makeA2dpSrcAvailable(address);
}
- } 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);
+ 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 onSetA2dpSourceConnectionState(
- @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int state) {
- final BluetoothDevice btDevice = btInfo.getBtDevice();
- if (AudioService.DEBUG_DEVICES) {
- Log.d(TAG, "onSetA2dpSourceConnectionState btDevice=" + btDevice + " state="
- + state);
- }
- String address = btDevice.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));
-
- new MediaMetrics.Item(mMetricsId + "onSetHearingAidConnectionState")
- .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)
- .set(MediaMetrics.Property.STREAM_TYPE,
- AudioSystem.streamToString(streamType))
- .record();
-
- synchronized (mDevicesLock) {
- final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID,
- btDevice.getAddress());
- 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");
- }
- }
- }
-
- /*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));
-
- 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");
- }
- }
- }
-
- /*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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
}
- /*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 @@
}
}
- /*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 @@
}
}
- /*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 @@
}
}
+ /*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 @@
// 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 @@
}
}
- /*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 d75f21c..51784bb 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.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 @@
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 @interface BtProfileConnectionState {}
/**
- * See AudioManager.setBluetoothHearingAidDeviceConnectionState()
+ * @hide
+ * The profiles that can be used with AudioService.handleBluetoothActiveDeviceChanged()
*/
- 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");
- }
+ @IntDef({
+ BluetoothProfile.HEARING_AID,
+ BluetoothProfile.A2DP,
+ BluetoothProfile.A2DP_SINK,
+ BluetoothProfile.LE_AUDIO,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BtProfile {}
- 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()
+ * See AudioManager.handleBluetoothActiveDeviceChanged(...)
*/
- public void setBluetoothLeAudioOutDeviceConnectionState(
- @NonNull BluetoothDevice device, @BtProfileConnectionState int state,
- boolean suppressNoisyIntent) {
- setBluetoothLeAudioDeviceConnectionState(device, state);
- mDeviceBroker.postBluetoothLeAudioOutDeviceConnectionState(device, state,
- suppressNoisyIntent, "AudioService");
- }
-
- /**
- * See AudioManager.setBluetoothLeAudioInDeviceConnectionState()
- */
- public void setBluetoothLeAudioInDeviceConnectionState(
- @NonNull BluetoothDevice device, @BtProfileConnectionState int state) {
- setBluetoothLeAudioDeviceConnectionState(device, state);
- mDeviceBroker.postBluetoothLeAudioInDeviceConnectionState(device, state, "AudioService");
- }
-
- /**
- * See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent()
- */
- 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 @@
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 @@
}
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 c924fde..9273a5d 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.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 @@
}
/*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 @@
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 (profile == BluetoothProfile.A2DP) {
+ mA2dp = (BluetoothA2dp) proxy;
+ } else if (profile == BluetoothProfile.LE_AUDIO) {
+ mLeAudio = (BluetoothLeAudio) proxy;
+ }
+ final List<BluetoothDevice> deviceList = proxy.getConnectedDevices();
if (deviceList.isEmpty()) {
return;
}
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();
- 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 =
- 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;
- }
-
- 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 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 @@
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/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1a0a885..20bcc5e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -21917,8 +21917,10 @@
ApexManager.ActiveApexInfo apexInfo) {
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() + File.separator)) {
+ if (apexInfo.preInstalledApexPath.getAbsolutePath().equals(
+ sp.getFolder().getAbsolutePath())
+ || apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
+ 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/timezonedetector/ReferenceWithHistory.java b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
index 4eb1b99..60068cb 100644
--- a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
+++ b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java
@@ -25,6 +25,7 @@
import java.time.Duration;
import java.util.ArrayDeque;
+import java.util.Iterator;
/**
* A class that behaves like the following definition, except it stores the history of values set
@@ -112,9 +113,11 @@
if (mValues == null) {
ipw.println("{Empty}");
} else {
- int i = mSetCount;
- for (TimestampedValue<V> valueHolder : mValues) {
- ipw.print(--i);
+ int i = mSetCount - mValues.size();
+ Iterator<TimestampedValue<V>> reverseIterator = mValues.descendingIterator();
+ while (reverseIterator.hasNext()) {
+ TimestampedValue<V> valueHolder = reverseIterator.next();
+ ipw.print(i++);
ipw.print("@");
ipw.print(Duration.ofMillis(valueHolder.getReferenceTimeMillis()).toString());
ipw.print(": ");
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 4fb801e..8369319 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -35,6 +35,7 @@
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.os.BackgroundThread;
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -146,7 +147,7 @@
connectNativeService();
break;
default:
- throw new AssertionError("Unknown message: " + message.toString());
+ throw new AssertionError("Unknown message: " + message);
}
}
}
@@ -190,11 +191,14 @@
Log.d(LOG_TAG, "Starting background process job");
}
- try {
- sSelfService.mIProfcollect.process(false);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, e.getMessage());
- }
+ BackgroundThread.get().getThreadHandler().post(
+ () -> {
+ try {
+ sSelfService.mIProfcollect.process();
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, e.getMessage());
+ }
+ });
return true;
}
@@ -301,7 +305,7 @@
}
Context context = getContext();
- new Thread(() -> {
+ BackgroundThread.get().getThreadHandler().post(() -> {
try {
// Prepare profile report
String reportName = mIProfcollect.report() + ".zip";
@@ -321,6 +325,6 @@
} catch (RemoteException e) {
Log.e(LOG_TAG, e.getMessage());
}
- }).start();
+ });
}
}
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 5c53d43..9e1445c 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 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 @@
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 @@
((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/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java b/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
index ce72499..d5d2cbd 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
@@ -17,17 +17,19 @@
package com.android.server.timedetector;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.util.IndentingPrintWriter;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.IndentingPrintWriter;
import com.android.server.timezonedetector.ReferenceWithHistory;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.StringWriter;
+import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
public class ReferenceWithHistoryTest {
@@ -41,31 +43,34 @@
// Check unset behavior.
compareGet(referenceWithHistory, reference, null);
- assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+ assertDumpContent(referenceWithHistory);
compareToString(referenceWithHistory, reference, "null");
// Try setting null.
setAndCompareReturnValue(referenceWithHistory, reference, null);
compareGet(referenceWithHistory, reference, null);
- assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+ assertDumpContent(referenceWithHistory, new DumpLine(0, "null"));
compareToString(referenceWithHistory, reference, "null");
// Try setting a non-null value.
setAndCompareReturnValue(referenceWithHistory, reference, "Foo");
compareGet(referenceWithHistory, reference, "Foo");
- assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+ assertDumpContent(referenceWithHistory,
+ new DumpLine(0, "null"), new DumpLine(1, "Foo"));
compareToString(referenceWithHistory, reference, "Foo");
// Try setting null again.
- setAndCompareReturnValue(referenceWithHistory, reference, "Foo");
- compareGet(referenceWithHistory, reference, "Foo");
- assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
- compareToString(referenceWithHistory, reference, "Foo");
+ setAndCompareReturnValue(referenceWithHistory, reference, null);
+ compareGet(referenceWithHistory, reference, null);
+ assertDumpContent(referenceWithHistory,
+ new DumpLine(1, "Foo"), new DumpLine(2, "null"));
+ compareToString(referenceWithHistory, reference, "null");
// Try a non-null value again.
setAndCompareReturnValue(referenceWithHistory, reference, "Bar");
compareGet(referenceWithHistory, reference, "Bar");
- assertNotNull(dumpReferenceWithHistory(referenceWithHistory));
+ assertDumpContent(referenceWithHistory,
+ new DumpLine(2, "null"), new DumpLine(3, "Bar"));
compareToString(referenceWithHistory, reference, "Bar");
}
@@ -132,11 +137,54 @@
assertEquals(expected, referenceWithHistory.toString());
}
- private static String dumpReferenceWithHistory(ReferenceWithHistory<?> referenceWithHistory) {
+ private static void assertDumpContent(
+ ReferenceWithHistory<?> referenceWithHistory, DumpLine... expectedLines) {
+ String[] actualLines = dumpReferenceWithHistory(referenceWithHistory);
+
+ if (expectedLines.length == 0) {
+ String expectedEmptyOutput = "{Empty}";
+ assertEquals(expectedEmptyOutput, 1, actualLines.length);
+ assertEquals(expectedEmptyOutput, actualLines[0]);
+ } else {
+ assertEquals("Expected=" + Arrays.toString(expectedLines)
+ + ", actual=" + Arrays.toString(actualLines),
+ expectedLines.length, actualLines.length);
+ for (int i = 0; i < expectedLines.length; i++) {
+ DumpLine expectedLine = expectedLines[i];
+ String actualLine = actualLines[i];
+ assertTrue("i=" + i + ", expected=" + expectedLine + ", actual=" + actualLine,
+ actualLine.startsWith(Integer.toString(expectedLine.mIndex)));
+ assertTrue("i=" + i + ", expected=" + expectedLine + ", actual=" + actualLine,
+ actualLine.endsWith(expectedLine.mLine));
+ }
+ }
+ }
+
+ private static String[] dumpReferenceWithHistory(ReferenceWithHistory<?> referenceWithHistory) {
StringWriter stringWriter = new StringWriter();
try (IndentingPrintWriter ipw = new IndentingPrintWriter(stringWriter, " ")) {
referenceWithHistory.dump(ipw);
- return stringWriter.toString();
+ return stringWriter.toString().split("\n");
+ }
+ }
+
+ /** An expected line of {@link ReferenceWithHistory#dump} output. */
+ private static class DumpLine {
+
+ final int mIndex;
+ final String mLine;
+
+ DumpLine(int index, String line) {
+ mIndex = index;
+ mLine = line;
+ }
+
+ @Override
+ public String toString() {
+ return "DumpLine{"
+ + "mIndex=" + mIndex
+ + ", mLine='" + mLine + '\''
+ + '}';
}
}
}
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index 42d7707..fc76f99 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -119,7 +119,7 @@
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
}
- return new ImsRcsManager(mContext, subscriptionId, sRcsCache);
+ return new ImsRcsManager(mContext, subscriptionId, sRcsCache, sTelephonyCache);
}
/**
@@ -135,7 +135,7 @@
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
}
- return new ImsMmTelManager(subscriptionId, sTelephonyCache);
+ return new ImsMmTelManager(mContext, subscriptionId, sTelephonyCache);
}
/**
@@ -157,7 +157,7 @@
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
}
- return new SipDelegateManager(mContext, subscriptionId, sRcsCache);
+ return new SipDelegateManager(mContext, subscriptionId, sRcsCache, sTelephonyCache);
}
private static IImsRcsController getIImsRcsControllerInterface() {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 6ffdc6b..a1d68b2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -315,6 +315,12 @@
*/
public static final int UNINITIALIZED_CARD_ID = -2;
+ /**
+ * Default port index for the UICC Card
+ * @hide
+ */
+ public static final int DEFAULT_PORT_INDEX = 0;
+
private final Context mContext;
private final int mSubId;
@UnsupportedAppUsage
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 36082dc..683bb92 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -25,6 +25,7 @@
import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.content.Context;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -45,6 +46,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -214,6 +216,7 @@
}
}
+ private final Context mContext;
private final int mSubId;
private final BinderCacheManager<ITelephony> mBinderCache;
@@ -255,6 +258,16 @@
*/
@VisibleForTesting
public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) {
+ this(null, subId, binderCache);
+ }
+
+ /**
+ * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead.
+ * @hide
+ */
+ @VisibleForTesting
+ public ImsMmTelManager(Context context, int subId, BinderCacheManager<ITelephony> binderCache) {
+ mContext = context;
mSubId = subId;
mBinderCache = binderCache;
}
@@ -1482,6 +1495,74 @@
}
}
+ /**
+ * Register a new callback, which is used to notify the registrant of changes to
+ * the state of the underlying IMS service that is attached to telephony to
+ * implement IMS functionality. If the manager is created for
+ * the {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
+ * this throws an {@link ImsException}.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
+ * or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
+ * @param callback The callback instance being registered.
+ * @throws ImsException in the case that the callback can not be registered.
+ * See {@link ImsException#getCode} for more information on when this is called.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
+ public void registerImsStateCallback(@NonNull Executor executor,
+ @NonNull ImsStateCallback callback) throws ImsException {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+ Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+ callback.init(executor);
+ ITelephony telephony = mBinderCache.listenOnBinder(callback, callback::binderDied);
+ if (telephony == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ telephony.registerImsStateCallback(
+ mSubId, ImsFeature.FEATURE_MMTEL,
+ callback.getCallbackBinder(), getOpPackageName());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Unregisters a previously registered callback.
+ *
+ * @param callback The callback instance to be unregistered.
+ */
+ public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+
+ ITelephony telephony = mBinderCache.removeRunnable(callback);
+ try {
+ if (telephony != null) {
+ telephony.unregisterImsStateCallback(callback.getCallbackBinder());
+ }
+ } catch (RemoteException ignore) {
+ // ignore it
+ }
+ }
+
+ private String getOpPackageName() {
+ if (mContext != null) {
+ return mContext.getOpPackageName();
+ } else {
+ return null;
+ }
+ }
+
private ITelephony getITelephony() {
return mBinderCache.getBinder();
}
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 8d6fa41..1b047c7 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -39,9 +39,11 @@
import android.util.Log;
import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.ITelephony;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -159,6 +161,7 @@
private final int mSubId;
private final Context mContext;
private final BinderCacheManager<IImsRcsController> mBinderCache;
+ private final BinderCacheManager<ITelephony> mTelephonyBinderCache;
private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter>
mAvailabilityChangedCallbacks;
@@ -167,11 +170,13 @@
* @hide
*/
public ImsRcsManager(Context context, int subId,
- BinderCacheManager<IImsRcsController> binderCache) {
+ BinderCacheManager<IImsRcsController> binderCache,
+ BinderCacheManager<ITelephony> telephonyBinderCache) {
mSubId = subId;
mContext = context;
mBinderCache = binderCache;
mAvailabilityChangedCallbacks = new HashMap<>();
+ mTelephonyBinderCache = telephonyBinderCache;
}
/**
@@ -534,6 +539,67 @@
}
/**
+ * Register a new callback, which is used to notify the registrant of changes to
+ * the state of the underlying IMS service that is attached to telephony to
+ * implement IMS functionality. If the manager is created for
+ * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
+ * this throws an {@link ImsException}.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
+ * or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
+ * @param callback The callback instance being registered.
+ * @throws ImsException in the case that the callback can not be registered.
+ * See {@link ImsException#getCode} for more information on when this is called.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE,
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE})
+ public void registerImsStateCallback(@NonNull Executor executor,
+ @NonNull ImsStateCallback callback) throws ImsException {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+ Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+ callback.init(executor);
+ ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied);
+ if (telephony == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ telephony.registerImsStateCallback(
+ mSubId, ImsFeature.FEATURE_RCS,
+ callback.getCallbackBinder(), mContext.getOpPackageName());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Unregisters a previously registered callback.
+ *
+ * @param callback The callback instance to be unregistered.
+ */
+ public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+
+ ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback);
+ try {
+ if (telephony != null) {
+ telephony.unregisterImsStateCallback(callback.getCallbackBinder());
+ }
+ } catch (RemoteException ignore) {
+ // ignore it
+ }
+ }
+
+ /**
* Add the {@link OnAvailabilityChangedListener} to collection for tracking.
* @param executor The executor that will be used when the publish state is changed and the
* {@link OnAvailabilityChangedListener} is called.
diff --git a/telephony/java/android/telephony/ims/ImsStateCallback.java b/telephony/java/android/telephony/ims/ImsStateCallback.java
new file mode 100644
index 0000000..b9ba93f
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsStateCallback.java
@@ -0,0 +1,184 @@
+/*
+ * 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.telephony.ims;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Binder;
+
+import com.android.internal.telephony.IImsStateCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Executor;
+
+/**
+ * A callback class used for monitoring changes in IMS service connection states
+ * for a specific subscription.
+ * <p>
+ * @see ImsMmTelManager#registerImsStateCallback(Executor, ImsStateCallback)
+ * @see ImsRcsManager#registerImsStateCallback(Executor, ImsStateCallback)
+ */
+public abstract class ImsStateCallback {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "REASON_", value = {
+ REASON_UNKNOWN_TEMPORARY_ERROR,
+ REASON_UNKNOWN_PERMANENT_ERROR,
+ REASON_IMS_SERVICE_DISCONNECTED,
+ REASON_NO_IMS_SERVICE_CONFIGURED,
+ REASON_SUBSCRIPTION_INACTIVE,
+ REASON_IMS_SERVICE_NOT_READY
+ })
+ public @interface DisconnectedReason {}
+
+ /**
+ * The underlying IMS service is temporarily unavailable for the
+ * associated subscription.
+ * {@link #onAvailable} will be called when the IMS service becomes
+ * available again.
+ */
+ public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1;
+
+ /**
+ * The underlying IMS service is permanently unavailable for the
+ * associated subscription and there will be no Manager available for
+ * this subscription.
+ */
+ public static final int REASON_UNKNOWN_PERMANENT_ERROR = 2;
+
+ /**
+ * The underlying IMS service has died, is reconfiguring, or has never
+ * come up yet and as a result is currently unavailable.
+ * {@link #onAvailable} will be called when the IMS service becomes
+ * available. All callbacks should be unregistered now and registered again
+ * if the IMS service moves back to available.
+ */
+ public static final int REASON_IMS_SERVICE_DISCONNECTED = 3;
+
+ /**
+ * There is no IMS service configured for the subscription ID specified.
+ * This is a permanent error and there will be no Manager available for
+ * this subscription.
+ */
+ public static final int REASON_NO_IMS_SERVICE_CONFIGURED = 4;
+
+ /**
+ * The subscription associated with this Manager has moved to an inactive
+ * state (e.g. SIM removed) and the IMS service has torn down the resources
+ * related to this subscription. This has caused this callback
+ * to be deregistered. The callback must be re-registered when this subscription
+ * becomes active in order to continue listening to the IMS service state.
+ */
+ public static final int REASON_SUBSCRIPTION_INACTIVE = 5;
+
+ /**
+ * The IMS service is connected, but in a NOT_READY state. Once the
+ * service moves to ready, {@link #onAvailable} will be called.
+ */
+ public static final int REASON_IMS_SERVICE_NOT_READY = 6;
+
+ private IImsStateCallbackStub mCallback;
+
+ /**
+ * @hide
+ */
+ public void init(@NonNull @CallbackExecutor Executor executor) {
+ if (executor == null) {
+ throw new IllegalArgumentException("ImsStateCallback Executor must be non-null");
+ }
+ mCallback = new IImsStateCallbackStub(this, executor);
+ }
+
+ /**
+ * Using a static class and weak reference here to avoid memory leak caused by the
+ * IImsStateCallback.Stub callback retaining references to the outside ImsStateCallback.
+ */
+ private static class IImsStateCallbackStub extends IImsStateCallback.Stub {
+ private WeakReference<ImsStateCallback> mImsStateCallbackWeakRef;
+ private Executor mExecutor;
+
+ IImsStateCallbackStub(ImsStateCallback imsStateCallback, Executor executor) {
+ mImsStateCallbackWeakRef = new WeakReference<ImsStateCallback>(imsStateCallback);
+ mExecutor = executor;
+ }
+
+ Executor getExecutor() {
+ return mExecutor;
+ }
+
+ public void onAvailable() {
+ ImsStateCallback callback = mImsStateCallbackWeakRef.get();
+ if (callback == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> callback.onAvailable()));
+ }
+
+ public void onUnavailable(int reason) {
+ ImsStateCallback callback = mImsStateCallbackWeakRef.get();
+ if (callback == null) return;
+
+ Binder.withCleanCallingIdentity(
+ () -> mExecutor.execute(() -> callback.onUnavailable(reason)));
+ }
+ }
+
+ /**
+ * The IMS service has disconnected or is reporting NOT_READY and is no longer
+ * available to users. The user should clean up all related state and
+ * unregister callbacks. If it is a temporary error, {@link #onAvailable} will
+ * be called when the IMS service becomes available again.
+ *
+ * @param reason the specified reason
+ */
+ public abstract void onUnavailable(@DisconnectedReason int reason);
+
+ /**
+ * The IMS service is connected and is ready for communication over the
+ * provided Manager.
+ */
+ public abstract void onAvailable();
+
+ /**
+ * An unexpected error has occurred and the Telephony process has crashed. This
+ * has caused this callback to be deregistered. The callback must be
+ * re-registered in order to continue listening to the IMS service state.
+ */
+ public abstract void onError();
+
+ /**
+ * The callback to notify the death of telephony process
+ * @hide
+ */
+ public final void binderDied() {
+ if (mCallback != null) {
+ mCallback.getExecutor().execute(() -> onError());
+ }
+ }
+
+ /**
+ * Return the callback binder
+ * @hide
+ */
+ public IImsStateCallbackStub getCallbackBinder() {
+ return mCallback;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 5a80663..f913df5 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -28,15 +28,16 @@
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.telephony.BinderCacheManager;
-import android.telephony.CarrierConfigManager;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.aidl.SipDelegateConnectionAidlWrapper;
+import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.stub.DelegateConnectionMessageCallback;
import android.telephony.ims.stub.DelegateConnectionStateCallback;
import android.telephony.ims.stub.SipDelegate;
import android.util.ArrayMap;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.ITelephony;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -282,6 +283,7 @@
private final Context mContext;
private final int mSubId;
private final BinderCacheManager<IImsRcsController> mBinderCache;
+ private final BinderCacheManager<ITelephony> mTelephonyBinderCache;
/**
* Only visible for testing. To instantiate an instance of this class, please use
@@ -290,10 +292,12 @@
*/
@VisibleForTesting
public SipDelegateManager(Context context, int subId,
- BinderCacheManager<IImsRcsController> binderCache) {
+ BinderCacheManager<IImsRcsController> binderCache,
+ BinderCacheManager<ITelephony> telephonyBinderCache) {
mContext = context;
mSubId = subId;
mBinderCache = binderCache;
+ mTelephonyBinderCache = telephonyBinderCache;
}
/**
@@ -446,4 +450,65 @@
+ " into this method");
}
}
+
+ /**
+ * Register a new callback, which is used to notify the registrant of changes to
+ * the state of the underlying IMS service that is attached to telephony to
+ * implement IMS functionality. If the manager is created for
+ * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
+ * this throws an {@link ImsException}.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
+ * or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
+ * @param callback The callback instance being registered.
+ * @throws ImsException in the case that the callback can not be registered.
+ * See {@link ImsException#getCode} for more information on when this is called.
+ */
+ @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION})
+ public void registerImsStateCallback(@NonNull Executor executor,
+ @NonNull ImsStateCallback callback) throws ImsException {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+ Objects.requireNonNull(executor, "Must include a non-null Executor.");
+
+ callback.init(executor);
+ ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied);
+ if (telephony == null) {
+ throw new ImsException("Telephony server is down",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+
+ try {
+ telephony.registerImsStateCallback(
+ mSubId, ImsFeature.FEATURE_RCS,
+ callback.getCallbackBinder(), mContext.getOpPackageName());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
+ } catch (RemoteException | IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Unregisters a previously registered callback.
+ *
+ * @param callback The callback instance to be unregistered.
+ */
+ public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
+ Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
+
+ ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback);
+
+ try {
+ if (telephony != null) {
+ telephony.unregisterImsStateCallback(callback.getCallbackBinder());
+ }
+ } catch (RemoteException ignore) {
+ // ignore it
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl b/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl
new file mode 100644
index 0000000..e04b01d
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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 com.android.internal.telephony;
+
+oneway interface IImsStateCallback {
+ void onUnavailable(int reason);
+ void onAvailable();
+}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d586a4a..6b33a68 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -67,6 +67,7 @@
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IBooleanConsumer;
import com.android.internal.telephony.ICallForwardingInfoCallback;
+import com.android.internal.telephony.IImsStateCallback;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.OperatorInfo;
@@ -2497,4 +2498,15 @@
* NSSAIs (configured, allowed and rejected).
*/
void getSlicingConfig(in ResultReceiver callback);
+
+ /**
+ * Register an IMS connection state callback
+ */
+ void registerImsStateCallback(int subId, int feature, in IImsStateCallback cb,
+ in String callingPackage);
+
+ /**
+ * Unregister an IMS connection state callback
+ */
+ void unregisterImsStateCallback(in IImsStateCallback cb);
}