From 110c76ac4d4189a61a0b8cf0a42a9854d4b69133 Mon Sep 17 00:00:00 2001 From: Simao Gomes Viana Date: Thu, 18 Nov 2021 10:20:03 +0000 Subject: [PATCH 01/92] core: jni: Switch to -O3 Change-Id: I5db0598b3bf9b353fffa6493a42da43d02aa287a --- core/jni/Android.bp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 4f27d218f05c..a159337f12f3 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -19,6 +19,7 @@ cc_library_shared { name: "libandroid_runtime", host_supported: true, cflags: [ + "-O3", "-Wno-unused-parameter", "-Wno-non-virtual-dtor", "-Wno-maybe-uninitialized", @@ -36,7 +37,7 @@ cc_library_shared { "-Wunreachable-code", ], - cppflags: ["-Wno-conversion-null"], + cppflags: ["-Wno-conversion-null", "-O3"], srcs: [ "android_animation_PropertyValuesHolder.cpp", From e628373396346bae74c51858cb8c9302f83ccacf Mon Sep 17 00:00:00 2001 From: leo zhang Date: Wed, 4 Mar 2020 13:56:23 +0800 Subject: [PATCH 02/92] os: Process: Fix wrong code in isThreadInProcess rename the "tid" and "pid" variables that had the names inverted. Change-Id: I920136bb9636fbf5ebb4adfd01f46eda4e39a7da --- core/java/android/os/Process.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 9f37c4877199..6356390dc137 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -1331,10 +1331,10 @@ public static final class ProcessStartResult { * @return true if this thread belongs to a process * @hide */ - public static final boolean isThreadInProcess(int tid, int pid) { + public static final boolean isThreadInProcess(int pid, int tid) { StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); try { - if (Os.access("/proc/" + tid + "/task/" + pid, OsConstants.F_OK)) { + if (Os.access("/proc/" + pid + "/task/" + tid, OsConstants.F_OK)) { return true; } else { return false; From 1c43054748b62c614e1b3abee104b8d83e6165ed Mon Sep 17 00:00:00 2001 From: maxwen Date: Sat, 14 Sep 2019 17:40:26 +0200 Subject: [PATCH 03/92] SettingLib: Add deep sleep info to uptime preference Add deep sleep ratio to "About Phone" -> "Uptime" original from Stefan Change-Id: I6949a66bae077b5132304448a7c8d7130665437a --- .../SettingsLib/res/values/styx_strings.xml | 17 +++++++++++++++++ .../AbstractUptimePreferenceController.java | 14 +++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 packages/SettingsLib/res/values/styx_strings.xml diff --git a/packages/SettingsLib/res/values/styx_strings.xml b/packages/SettingsLib/res/values/styx_strings.xml new file mode 100644 index 000000000000..4093ca68a88d --- /dev/null +++ b/packages/SettingsLib/res/values/styx_strings.xml @@ -0,0 +1,17 @@ + + + + + (Deep sleep: %1$d%2$s) + diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java index 5f7226969699..51cff414f2da 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java @@ -26,6 +26,7 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import com.android.settingslib.R; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -89,7 +90,18 @@ private Handler getHandler() { } private void updateTimes() { - mUptime.setSummary(DateUtils.formatElapsedTime(SystemClock.elapsedRealtime() / 1000)); + long ut = Math.max((SystemClock.elapsedRealtime() / 1000), 1); + + float deepSleepRatio = Math.max((float) (SystemClock.elapsedRealtime() - SystemClock.uptimeMillis()), 0f) + / SystemClock.elapsedRealtime(); + int deepSleepPercent = Math.round(deepSleepRatio * 100); + + final StringBuilder summary = new StringBuilder(); + summary.append(DateUtils.formatElapsedTime(SystemClock.elapsedRealtime() / 1000)); + summary.append(" "); + summary.append(mContext.getString(R.string.status_deep_sleep, deepSleepPercent, "%")); + + mUptime.setSummary(summary.toString()); } private static class MyHandler extends Handler { From d39d6a30515546611f528d6c0131d7152a908538 Mon Sep 17 00:00:00 2001 From: Brett Chabot Date: Fri, 21 May 2021 08:41:58 -0700 Subject: [PATCH 04/92] Backport 'Add ability to disable GL draws'. This feature is intended for use in headless test environments, where disabling drawing has minimal compatibility impact and can save significant resource usage when running UI tests. This feature is controlled via a new system property 'debug.hwui.drawing_enabled', and a HardwareRenderer API that can override the property value on a per-process basis. Bug: 201691826 Test: boot emulator Merged-In: I93c21e680382e03342f235dbf58bd7a5c8a6f767 Change-Id: I391c841e3a0cdcfcc6c7910a316a4dd5e970547d --- .../android/graphics/HardwareRenderer.java | 52 +++++++++++++++++++ libs/hwui/Properties.cpp | 21 ++++++++ libs/hwui/Properties.h | 14 +++++ .../jni/android_graphics_HardwareRenderer.cpp | 11 ++++ libs/hwui/renderthread/CanvasContext.cpp | 5 +- libs/hwui/renderthread/CanvasContext.h | 10 +++- 6 files changed, 110 insertions(+), 3 deletions(-) diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index 14ad74e12618..bbe9c790f96b 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -1095,6 +1096,53 @@ public static void setContextForInit(Context context) { ProcessInitializer.sInstance.setContext(context); } + /** + * Returns true if HardwareRender will produce output. + * + * This value is global to the process and affects all uses of HardwareRenderer, + * including + * those created by the system such as those used by the View tree when using hardware + * accelerated rendering. + * + * Default is true in all production environments, but may be false in testing-focused + * emulators or if {@link #setDrawingEnabled(boolean)} is used. + * + * Backported from android T. + * + * @hide + */ + @UnsupportedAppUsage + public static boolean isDrawingEnabled() { + return nIsDrawingEnabled(); + } + + /** + * Toggles whether or not HardwareRenderer will produce drawing output globally in the current + * process. + * + * This applies to all HardwareRenderer instances, including those created by the platform such + * as those used by the system for hardware accelerated View rendering. + * + * The capability to disable drawing output is intended for test environments, primarily + * headless ones. By setting this to false, tests that launch activities or interact with Views + * can be quicker with less RAM usage by skipping the final step of View drawing. All View + * lifecycle events will occur as normal, only the final step of rendering on the GPU to the + * display will be skipped. + * + * This can be toggled on and off at will, so screenshot tests can also run in this same + * environment by toggling drawing back on and forcing a frame to be drawn such as by calling + * view#invalidate(). Once drawn and the screenshot captured, this can then be turned back off. + * + * Backported from android T. + * + * @hide + */ + // TODO: Add link to androidx's Screenshot library for help with this + @UnsupportedAppUsage + public static void setDrawingEnabled(boolean drawingEnabled) { + nSetDrawingEnabled(drawingEnabled); + } + private static final class DestroyContextRunnable implements Runnable { private final long mNativeInstance; @@ -1416,4 +1464,8 @@ private static native int nCopySurfaceInto(Surface surface, private static native void nInitDisplayInfo(int width, int height, float refreshRate, int wideColorDataspace, long appVsyncOffsetNanos, long presentationDeadlineNanos); + + private static native void nSetDrawingEnabled(boolean drawingEnabled); + + private static native boolean nIsDrawingEnabled(); } diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 475fd700ccc9..401748c2a5cc 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -90,6 +90,9 @@ bool Properties::enableWebViewOverlays = true; StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI; +bool Properties::drawingEnabled = true; +OverrideDrawingEnabled Properties::overrideDrawingEnabled = OverrideDrawingEnabled::Default; + bool Properties::load() { bool prevDebugLayersUpdates = debugLayersUpdates; bool prevDebugOverdraw = debugOverdraw; @@ -143,6 +146,11 @@ bool Properties::load() { enableWebViewOverlays = base::GetBoolProperty(PROPERTY_WEBVIEW_OVERLAYS_ENABLED, true); + drawingEnabled = base::GetBoolProperty(PROPERTY_DRAWING_ENABLED, true); + if (!drawingEnabled) { + enableRTAnimations = false; + } + return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } @@ -212,5 +220,18 @@ void Properties::overrideRenderPipelineType(RenderPipelineType type, bool inUnit sRenderPipelineType = type; } +void Properties::setDrawingEnabled(bool newDrawingEnabled) { + overrideDrawingEnabled = + newDrawingEnabled ? OverrideDrawingEnabled::On : OverrideDrawingEnabled::Off; + enableRTAnimations = newDrawingEnabled; +} + +bool Properties::isDrawingEnabled() { + if (overrideDrawingEnabled == OverrideDrawingEnabled::Default) { + return drawingEnabled; + } + return overrideDrawingEnabled == OverrideDrawingEnabled::On; +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index d224a547ab4d..8ff9aa5f796c 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -187,6 +187,12 @@ enum DebugLevel { */ #define PROPERTY_WEBVIEW_OVERLAYS_ENABLED "debug.hwui.webview_overlays_enabled" +/** + * Property for globally GL drawing state. Can be overridden per process with + * setDrawingEnabled. + */ +#define PROPERTY_DRAWING_ENABLED "debug.hwui.drawing_enabled" + /////////////////////////////////////////////////////////////////////////////// // Misc /////////////////////////////////////////////////////////////////////////////// @@ -208,6 +214,8 @@ enum class StretchEffectBehavior { UniformScale // Uniform scale stretch everywhere }; +enum class OverrideDrawingEnabled { Default, On, Off }; + /** * Renderthread-only singleton which manages several static rendering properties. Most of these * are driven by system properties which are queried once at initialization, and again if init() @@ -302,6 +310,12 @@ class Properties { stretchEffectBehavior = behavior; } + // Represents if GL drawing is enabled. Should only be false in headless testing environments + static bool drawingEnabled; + static OverrideDrawingEnabled overrideDrawingEnabled; + static bool isDrawingEnabled(); + static void setDrawingEnabled(bool enable); + private: static StretchEffectBehavior stretchEffectBehavior; static ProfileType sProfileType; diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index b5536ad4830d..dff38612ce58 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -838,6 +838,14 @@ static void android_view_ThreadedRenderer_initDisplayInfo(JNIEnv*, jclass, jint DeviceInfo::setPresentationDeadlineNanos(presentationDeadlineNanos); } +static void android_view_ThreadedRenderer_setDrawingEnabled(JNIEnv*, jclass, jboolean enabled) { + Properties::setDrawingEnabled(enabled); +} + +static jboolean android_view_ThreadedRenderer_isDrawingEnabled(JNIEnv*, jclass) { + return Properties::isDrawingEnabled(); +} + // ---------------------------------------------------------------------------- // HardwareRendererObserver // ---------------------------------------------------------------------------- @@ -976,6 +984,9 @@ static const JNINativeMethod gMethods[] = { {"preload", "()V", (void*)android_view_ThreadedRenderer_preload}, {"isWebViewOverlaysEnabled", "()Z", (void*)android_view_ThreadedRenderer_isWebViewOverlaysEnabled}, + {"nSetDrawingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDrawingEnabled}, + {"nIsDrawingEnabled", "()Z", (void*)android_view_ThreadedRenderer_isDrawingEnabled}, + }; static JavaVM* mJvm = nullptr; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index a066e6f7c693..0cd6ffba323c 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -256,7 +256,7 @@ void CanvasContext::setStopped(bool stopped) { } void CanvasContext::allocateBuffers() { - if (mNativeSurface) { + if (mNativeSurface && Properties::isDrawingEnabled()) { ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow()); } } @@ -480,7 +480,8 @@ nsecs_t CanvasContext::draw() { SkRect dirty; mDamageAccumulator.finish(&dirty); - if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) { + if (!Properties::isDrawingEnabled() || + (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); if (auto grContext = getGrContext()) { // Submit to ensure that any texture uploads complete and Skia can diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 389546ea7973..56d01e1988a2 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -90,9 +90,17 @@ class CanvasContext : public IFrameCallback { * and false otherwise (e.g. cache limits have been exceeded). */ bool pinImages(std::vector& mutableImages) { + if (!Properties::isDrawingEnabled()) { + return true; + } return mRenderPipeline->pinImages(mutableImages); } - bool pinImages(LsaVector>& images) { return mRenderPipeline->pinImages(images); } + bool pinImages(LsaVector>& images) { + if (!Properties::isDrawingEnabled()) { + return true; + } + return mRenderPipeline->pinImages(images); + } /** * Unpin any image that had be previously pinned to the GPU cache From 669750613a0efb4136e068ef96b9d97d952bac9c Mon Sep 17 00:00:00 2001 From: Brett Chabot Date: Fri, 16 Jul 2021 14:38:11 -0700 Subject: [PATCH 05/92] Lazy load Properties::isDrawingEnabled. Properties::isDrawingEnabled can unexpectedly return true even if the system property debug.hwui.drawing_enabled is false, if its called before RenderThread is created. This commit changes the underlying logic to lazy load the debug.hwui.drawing_enabled prop value. Bug: 201691826 Test: m Change-Id: I599426c8734d418f073fd0456c2b1149fb69d80c --- libs/hwui/Properties.cpp | 20 +++++++++----------- libs/hwui/Properties.h | 7 +++---- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 401748c2a5cc..bcfe9c3ecab4 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -90,8 +90,7 @@ bool Properties::enableWebViewOverlays = true; StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI; -bool Properties::drawingEnabled = true; -OverrideDrawingEnabled Properties::overrideDrawingEnabled = OverrideDrawingEnabled::Default; +DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized; bool Properties::load() { bool prevDebugLayersUpdates = debugLayersUpdates; @@ -146,10 +145,8 @@ bool Properties::load() { enableWebViewOverlays = base::GetBoolProperty(PROPERTY_WEBVIEW_OVERLAYS_ENABLED, true); - drawingEnabled = base::GetBoolProperty(PROPERTY_DRAWING_ENABLED, true); - if (!drawingEnabled) { - enableRTAnimations = false; - } + // call isDrawingEnabled to force loading of the property + isDrawingEnabled(); return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } @@ -221,16 +218,17 @@ void Properties::overrideRenderPipelineType(RenderPipelineType type, bool inUnit } void Properties::setDrawingEnabled(bool newDrawingEnabled) { - overrideDrawingEnabled = - newDrawingEnabled ? OverrideDrawingEnabled::On : OverrideDrawingEnabled::Off; + drawingEnabled = newDrawingEnabled ? DrawingEnabled::On : DrawingEnabled::Off; enableRTAnimations = newDrawingEnabled; } bool Properties::isDrawingEnabled() { - if (overrideDrawingEnabled == OverrideDrawingEnabled::Default) { - return drawingEnabled; + if (drawingEnabled == DrawingEnabled::NotInitialized) { + bool drawingEnabledProp = base::GetBoolProperty(PROPERTY_DRAWING_ENABLED, true); + drawingEnabled = drawingEnabledProp ? DrawingEnabled::On : DrawingEnabled::Off; + enableRTAnimations = drawingEnabledProp; } - return overrideDrawingEnabled == OverrideDrawingEnabled::On; + return drawingEnabled == DrawingEnabled::On; } } // namespace uirenderer diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 8ff9aa5f796c..2f8c67903a8b 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -214,7 +214,7 @@ enum class StretchEffectBehavior { UniformScale // Uniform scale stretch everywhere }; -enum class OverrideDrawingEnabled { Default, On, Off }; +enum class DrawingEnabled { NotInitialized, On, Off }; /** * Renderthread-only singleton which manages several static rendering properties. Most of these @@ -310,9 +310,8 @@ class Properties { stretchEffectBehavior = behavior; } - // Represents if GL drawing is enabled. Should only be false in headless testing environments - static bool drawingEnabled; - static OverrideDrawingEnabled overrideDrawingEnabled; + // Represents if drawing is enabled. Should only be Off in headless testing environments + static DrawingEnabled drawingEnabled; static bool isDrawingEnabled(); static void setDrawingEnabled(bool enable); From 0d219092800a411a143b0bd36baac6e20aa213f7 Mon Sep 17 00:00:00 2001 From: Adithya R Date: Wed, 14 Apr 2021 23:14:16 +0530 Subject: [PATCH 06/92] display: Don't spam log when display state changes * dynamic refresh rate devices keep switching display configuration for changing refresh rate and ends up spamming log way too much [ghostrider-reborn] - DisplayManagerService -> DisplayDeviceRepository in android 12 Test: adb logcat; confirm logs not visible Change-Id: I57c19a0042083a86e86f2c210288904f6dc87e2c Signed-off-by: Adithya R --- .../server/display/DisplayDeviceRepository.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java index 2b52350f0634..5e3be4a08b79 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java +++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java @@ -39,6 +39,8 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener { private static final String TAG = "DisplayDeviceRepository"; + private static final boolean DEBUG = false; + public static final int DISPLAY_DEVICE_EVENT_ADDED = 1; public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2; public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3; @@ -158,11 +160,14 @@ private void handleDisplayDeviceChanged(DisplayDevice device) { } int diff = device.mDebugLastLoggedDeviceInfo.diff(info); - if (diff == DisplayDeviceInfo.DIFF_STATE) { - Slog.i(TAG, "Display device changed state: \"" + info.name + + if (DEBUG) { + if (diff == DisplayDeviceInfo.DIFF_STATE) { + Slog.i(TAG, "Display device changed state: \"" + info.name + "\", " + Display.stateToString(info.state)); - } else if (diff != 0) { - Slog.i(TAG, "Display device changed: " + info); + } else if (diff != 0) { + Slog.i(TAG, "Display device changed: " + info); + } } if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) { From d84d9eafecdd5387ce91b4ccf428e42fb576ab0e Mon Sep 17 00:00:00 2001 From: Dan Sandler Date: Fri, 13 Sep 2019 17:37:22 -0400 Subject: [PATCH 07/92] Updates to various icons: - updated android head in launcher icons - USB notifications now use a USB icon instead of the platform logo (ADB still uses the platform logo however) Fixes: 138661761 Test: attach to adb; confirm that one notification uses the Q and one the USB logo Test: view system apps in Settings>Apps that have no proper icon Change-Id: Ia11736c010b4862fea8d0e73c424cfc5c51badfa --- .../res/drawable-hdpi/stat_sys_data_usb.png | Bin 968 -> 0 bytes .../res/drawable-ldpi/stat_sys_data_usb.png | Bin 429 -> 0 bytes .../res/drawable-mdpi/stat_sys_data_usb.png | Bin 707 -> 0 bytes .../res/drawable-xhdpi/stat_sys_data_usb.png | Bin 1244 -> 0 bytes .../res/drawable-xxhdpi/stat_sys_data_usb.png | Bin 1027 -> 0 bytes core/res/res/drawable/stat_sys_data_usb.xml | 24 +++++++++ core/res/res/drawable/sym_def_app_icon.xml | 18 +++++-- .../drawable/sym_def_app_icon_foreground.xml | 46 ++++++++++++++++++ .../sym_def_app_icon_foreground.png | Bin 5117 -> 0 bytes .../sym_def_app_icon_foreground.png | Bin 2652 -> 0 bytes .../sym_def_app_icon_foreground.png | Bin 7011 -> 0 bytes .../sym_def_app_icon_foreground.png | Bin 14578 -> 0 bytes .../sym_def_app_icon_foreground.png | Bin 21908 -> 0 bytes packages/SystemUI/res/drawable/android.xml | 28 +++-------- packages/SystemUI/res/drawable/ic_android.xml | 17 ++++--- .../android/server/usb/UsbDeviceManager.java | 2 + 16 files changed, 104 insertions(+), 31 deletions(-) delete mode 100644 core/res/res/drawable-hdpi/stat_sys_data_usb.png delete mode 100644 core/res/res/drawable-ldpi/stat_sys_data_usb.png delete mode 100644 core/res/res/drawable-mdpi/stat_sys_data_usb.png delete mode 100644 core/res/res/drawable-xhdpi/stat_sys_data_usb.png delete mode 100644 core/res/res/drawable-xxhdpi/stat_sys_data_usb.png create mode 100644 core/res/res/drawable/stat_sys_data_usb.xml create mode 100644 core/res/res/drawable/sym_def_app_icon_foreground.xml delete mode 100644 core/res/res/mipmap-hdpi/sym_def_app_icon_foreground.png delete mode 100644 core/res/res/mipmap-mdpi/sym_def_app_icon_foreground.png delete mode 100644 core/res/res/mipmap-xhdpi/sym_def_app_icon_foreground.png delete mode 100644 core/res/res/mipmap-xxhdpi/sym_def_app_icon_foreground.png delete mode 100644 core/res/res/mipmap-xxxhdpi/sym_def_app_icon_foreground.png diff --git a/core/res/res/drawable-hdpi/stat_sys_data_usb.png b/core/res/res/drawable-hdpi/stat_sys_data_usb.png deleted file mode 100644 index f14f9080c1213d5b1b6f14ed18f51fc98d76341d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 968 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTQ^&* zE`McJ>2eexH~o zo-o^&)TwlYt>0*wGYePNf|LotOO_~HdTdg=WYOv5X`6I?D@9fauHrWKz*{L2^d$IidE@TfY0?csI@ab|jSpvDZgktj$XeYf`EB#T zvRmsy1j>IuUh%iyVoJ5~0`FG}=~MP;mMwd%a%N4x+l0`sO%Z0*SJnkGZ0j#Q$nfR% z)jW32KZn*YDVtxHz3+a@153&KhN~_{?aflmW13Q8^~m)9SO4=rgZ5os2boFyt=akR{0Bs1DrvLx| diff --git a/core/res/res/drawable-ldpi/stat_sys_data_usb.png b/core/res/res/drawable-ldpi/stat_sys_data_usb.png deleted file mode 100644 index ffaccbde6952acb30ff6373e10c8f5246ef5a93e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 429 zcmeAS@N?(olHy`uVBq!ia0vp^96&70!2%@N#akkP6jPG7y9>kr_Wm>bfjrIvkH}&m z?E%JaC$sH9f@KAc=|CE+mtjU;-8Ud-tEY=&h{WZ&6AbwdCP=i&*VlI*WbEmCz#wM+ z;sFzzQlhX`l9>r_@05#54;k+2@Flzvu!L>)5z_WSgm$aP}3)^vH(dHPk+-hce6UDxl1AHOH? zJ1y;)rpx6YEB=Mqcis>Zi}HUfs3EDqUwJ(FUU6SF&x_B!vQ7e<#CEbQSn`0wVwu8o z-p>cbtiNc?isU*GJFmEV(Y~J{UN@(dR5>qsBqbm5fV2FD@)fJ66Dl7ROyYNL3yDcs z`0*{f`N|(>v?ea{T+vbURCFS5nPMf+ob;(D3_(+)DQ;q_^@;vv|b4!1wV{ zpO`@Rd+Wp5U-`#Zc=Dv@7-t)?sf6W#5q%{45a~Y0L z=sMe|f0aFHKli`c*Cs@$PMjmW?X}O%74z5em;MYFIUxGr(|)DdQl;!`RxjN(@7rb$ z$C#2eVy|u-&8sYrHeo2{nJ6S_#BMBo^-AIFSDm|Ne%s9HcqX>^wCU%~a_xEx#iC4~ ztG(c7cKKI!Li^=%VEl=exJHyX7o{eaWaj57gkPeW+0V+@hDM(2y zNwrD_sY+!qGB7mMH89aNGz>AYv@$fZGPTq-Ft;)=sC@s^5=BF9eoAIqC2kEIn;ct# z8e~8=1m~xflqVLYGB~E>C#5R5WfrBD=NDxcD>w(67H)lP22{u3>FVdQ&MBb@0PXP- Av;Y7A diff --git a/core/res/res/drawable-xhdpi/stat_sys_data_usb.png b/core/res/res/drawable-xhdpi/stat_sys_data_usb.png deleted file mode 100644 index 57c1099d0d55aca7fd42bafb96f3eece617d3365..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1244 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;M!Q z+(IDCcKfnz$52J2-w_pT?Lfr0ser;B4q#Nn+oyfb25C63n{e}CqkdtQSK|FSlxNG#zc-!o8APys6(;Y*#^QdF-wdVV3Zy?qv~y8RbF((YGC9RVKIw ztl&NgZgO!`&S-a%9!$Xx7lx* z02OAXpAEVjLgs&#Gl+Hi;^?5yuu*ZIk@4q{DybE}GQJ3kpE(=*A+Wj7K_IX1zjx_j z-qK@kj0OxHUVo?TTKMwM#RC~ai+?ofnr@%+dtXqErG72_(?aI{a5pscv$SV%8b|UAzSaE z%e$F84mdQNN@wO(VG_7J*`%`A`}pZQH(u4Ay*q_j;-p~4lGOrNilSJUP8qdNVAvv= zl)HP|UJb4%F7r~R+H?C>PrANjCc~1MI&UWI$X?!Ib6&^Z==5@%DI#x#Qdmq1FwJr4T&EnaGTjd*8XL(SmTKlzLuV z_w4?*tEYo}-ZrMPdrbV2-c=grp7i~^YXRpZMrD)TPg?&niYRz~(pq$E(Z7|>fxHd6 zM}J!}yjH!U(0yNHUP<6pSN~H9K&Ic)0}F2zt!j~AaS%B_W9ICY%XS+#Yt?l>E_-@< zY509P5m!f>#A|`ORzFLdCs3_&(rUE^gRphi`Uam1#qtsC4hfxMs{&7#Tk1}Gu`}p3 zBg?y@PLJJddNPG~9=W?NG&@#s@xjISd>-!VSY>V-^~QvqF~H}Gp?;PB)#|t}5mmPr zm-jlYR1P>`?8D@(6Ov!GV#Qbe1DkSJe6!wI(iq2;^*?9BjPsim&N*H8J-qA>`vw=4 zVs6#-t-y>eTH+c};#`!PSdy8arx22vo62CQZ)~Vzopr0O;l%pa1{> diff --git a/core/res/res/drawable-xxhdpi/stat_sys_data_usb.png b/core/res/res/drawable-xxhdpi/stat_sys_data_usb.png deleted file mode 100644 index 7fcf5cd999abaa6ce7b1bae90bf84a0dc099eb7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1027 zcmV+e1pNDnP)dbVG7wVRUJ4ZXi@?ZDjy4FEcSOF)=O}HJktd16)Z& zK~#90?VIay+b|G>7n%IG+<{VolnNqu;0~k?ln!K9kaQ5Gg1CdY1FHkG1JM<1et?=V zia@+hJTSpG*!v$fo+NI z=DTtml8vi;PHf@}W;u^ZY*~^k01p5@4YVyQ#!0RRg$N_Xqy$P3=2_yVVjk4hm*gWu zWl65+pUU+4MsnG!H$Ap71fu=VCkaGyA1TaSG6}{Kh_o~PO@ctl!c0w|6OBtKX<2Ea!2wtFxd7al;^r5$uBX&6e-YI!`38U0)-k9X2*<^9XYX$ zvh_^ci*1p%9(0?#rkxYxsC)C>*`Wp$g=o2!Tk>N$wqg+cRN; zDn>Y(&d1odesW^e^q`j!9-v7vvq{4uIZ`{aS;nNp~aSI{=O6(gs$Xr1$2t(432%wPQ{4 x2Y>JFAG^`W;TI4(j(WN!*<=6!002ovPDHLkV1o3XzA^v+ diff --git a/core/res/res/drawable/stat_sys_data_usb.xml b/core/res/res/drawable/stat_sys_data_usb.xml new file mode 100644 index 000000000000..fb1ad2a47706 --- /dev/null +++ b/core/res/res/drawable/stat_sys_data_usb.xml @@ -0,0 +1,24 @@ + + + + diff --git a/core/res/res/drawable/sym_def_app_icon.xml b/core/res/res/drawable/sym_def_app_icon.xml index 129d38a74750..38d91477fbce 100644 --- a/core/res/res/drawable/sym_def_app_icon.xml +++ b/core/res/res/drawable/sym_def_app_icon.xml @@ -1,7 +1,19 @@ + - - - + diff --git a/core/res/res/drawable/sym_def_app_icon_foreground.xml b/core/res/res/drawable/sym_def_app_icon_foreground.xml new file mode 100644 index 000000000000..0a5a334d10f7 --- /dev/null +++ b/core/res/res/drawable/sym_def_app_icon_foreground.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + diff --git a/core/res/res/mipmap-hdpi/sym_def_app_icon_foreground.png b/core/res/res/mipmap-hdpi/sym_def_app_icon_foreground.png deleted file mode 100644 index 4e526c95b4d61d7c6a8adbe49ea7e67c8970cdd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5117 zcmd^D_g4}O)V9~sGDm9Vz>%*jLo-8^9PpNk`kI=SISL#&DnKPPw^W+qo~7YR%~kFk zi91n9OASjLp$WNi;>M9*@AupHH+<*Zd!JwLx%auxJ?DwJVPz^LAT7Yd!y{yFW^DTh z3;xUFe1GbPwX=Xfa0G8_dX1;DUuK?%N4(M8`0A~|BTG5saqbNNo{|taDc&H1mWo05 z#K14ZMG`R)9&XXcPl~x2i$%siZ@hcF2NFaxI_DE~>ZgJxobY&w-_-2f(Z2wuH64v+ zl7L8e<(gE8Sidn1r7Hf}j&aZ1(b?|cP5VUaK$(TTf6gx_z@w^q#_sUgUMPCA3_Y+o znx-ga#+UlU{oMaUI3eY5GSmmz8oW7?35yBRnaM!Oeb9C7H|c1KLgALHs4b_~UuMcX z7oZ)E`kb7^k37C`>-1t`rUYtt$5mbUj>4Itdaag3$byEJHD!a-UK1D~6HHtfJ666M z)N<(orG4wy%ij@$2mK>Oo-#vI#?~Gjvp#^OEyF>(1Nfj%h8 zK9cfcitm~$zHpzE-EQ!UC?McER62W#6Mxk22qEe1!p26LX z=&MgtwGRI=_oZ+$#xfC%**K`4eAm#h6~(P0A?*r{yalLOEy9(Vlup5#+0ak;2<{Lq z#HmL#0<;puPSEvkTP82G0d~gGS~V+4ZlSyV`YT`M^i;)nXzzHhUkTy6Wmdc_M!vHc z7i9VA0Oz%T~ppkoZNnq{IH|MtJgAms$b#GgITNIl4OArKK-5V_<=BWfnAtox9}dUZEFm@a)Rrn zwK>6Vp|00-%C~Z7vQ|X6?BdTCo!j<5C?**^jH`6@)gTuiXl?er2%wzH2coj!U&q1G zc|kKTlT1Os!rm9-nm%fohOl$LL>_Jp8i_Eay%=2Zrp5CFNhLpAdO~Tl@e0?~Y?;^! zkKau3$_v|K6iRt)m-7A8S=kK|k_o`@WP3yP7U_u#pTzor}q3LCWDMiME& z0c-fSUxO4|Vj9Mp+KJFxaCsxTIT(uggxHg@=^nR?XuPCUMM-zxssv4_1gXXzIQe|( zlqYvjgsy*npeNLi{C9ivF&D)V^fgUaUx_70l$MeUWuQ0~rS(hX?95uAUe{|}4@k3Z zhqcoEax>YWpLv6ki|P~%R15~~jzd=T@H3zCOsmqJBQst1J{f%LhOEy!1@x z3~$mt%zZg7EWwtBzD374wgr@;nP*UpyJoivTVB+k|KTHN5V(40YUu5phulib!Pa=Y z|BsF*@)VP~XK_gnm)eTH^szKPcLMpDei~mQb>ZSr`S-<|hPQ0}7^k<#(>S;vrXupt zP%>n?@*ah`wPjfa7cp4vaMGSKsu?+BSsEAXvn~_oGiy+QUO>42o>StzAdEMJzTD|+ zGDO|+W$lFSRMpN7=!f-|XA{ ze(!U|b-(*|nO3#`-W@RcD@^9fR^UQKqg+DtJS^sMfciaq1N*uK#|z(9q82#SMFT0S zVePA|A#2#d{QC!Bqz`oGM}&N={y>@Ir0w&YO{1o^AyjBu+m~=bPzp9R3 z)aFI<7nr7+&P`4X!fdSk7ZUV7TTLUhQyV4C-7pV|GepMw&L z8^h^%(Y|Rw^Nbzd;4Aa2NJ%^F{)vmomYsLRFJ00iSVP0*%Gv=x!H3?XXy2%qt+95( zp`kUD`;&8=j=)VCcgh~sL@WLh?wDf=v6e{f$Ga#D@qv0MZf;Nk9NkvY)gnD04(sH5 zml~0swMC!M>pC4$KIzXuI1?d=_O|c&)(GwFtTtsrf5H>wx{#SOVX=lPOfeegn#)gg z_Kls!d6+dgyE-qqsc*-O*n6loN!B$vvu56ki-C^5pS6SxVi|-g&pw=)5-AMVw zR&OcA*P?4{eVT-s9S(a}GEIM}dr(tkI01*is~_Ow&iP>{+rqZJGaSIi+UpG@x9=bd zGT=|d$N6+I{e~>t0l$5yUxx&Q!VWAg%RKLrv^e-T!U)F|@4#(lz{+TIKvkPDIaRUk zIBwv_hmg?7N|modQM2*90gc_4b={IiqvgYQ8g4xL?A4prV9|2ZccZK%S5FT2;2+B# zTQIv8TmXZeo>`MJQD>l}Q}Z94^IE(6ww6lI zWG%@xUUy9J(pz%7?!n5?tw+Vm-6wUYL|GC|VCqk(cpkBmt;(8w5;3<1;@O`$_*m?tNc`)1UP$L5m{~i zW+JCNNL566t4Ff6C*D2*I7#FczV|+rENAU%{Ale8`?7Aqafzl;A{zZ&v^yq>WZPA= zkkbrWyF-UPY9u_oZB9)8%4pb-($evD{Us#u>e#N5SUGiDO!p^{Gd;Hc1Lz?XVd>C1 zm)xQ~sJv+|;5^nLUpxhlljuG-&8r!fA7Ov~yS#g)+w2-DRrASHW# zW#k)QJSO_&w3S|@#nY7KM^x}UB}9FMKHLD?yKpHANl;l(t}M6E=Zpm0 zy12eaL@Ldfp#J?O@3`$5>9RT{r<3a!wdb)887>mi7lSR3{IV9(7OmG6&HxcNyQ1CJukXDipiNZXfJ=12_>J-olB~S5l$e`Y0`&7Y8PDX7O z^RfKV)`1Bmrq&$$-)X<{g`%qa6_P4KW%v)gS7I`l{#=W!{ge0M!Uep3VLOgRAQKrq zNpnME`PlhmG1JAxVu^0O)N{ofTqiPByijH?k-7{W)qe2a=F#@sBjArqLTcF}t`J@N z0sWaxqAWmHn<7(g*4jj;q`w7P(-q0y1kL8cW~o-W7Mdu}{6E4h*E zy!%`PutUFnhk+TQ3>lmC7wk)0qF2Li;XxPLD7#l;@|x}zZ;SeK{=QG*5JZ)(aRbEE zGW$rCt?t_~73p%Cc~R7Sl%|Kqgce=ugPsJ1Wh%gijMi698LHwu%2~6QUsQ-HxJp0$ z8ZW)e{#!_N<(-#0X1U~X?fv&6ckXZdr&-By!4A!fA-gb-S#@vCO)=eA&^B;5dha_F z6y^xPIsJ7Hez0&{&`h&ve?$hDCBb(+P3i23%DX6276Vz!&83G4-4;_1e;qn!wndk2 zjlySbyiBq~8;jD~Uv=k*+NvcEBTxI8Gh*JF`>y3DiHi*5duI}CehY&U5;w2^yr5G+1ZLS#jG(rVM)XqQ35NT&!#taHK2*&x@8OgwV{E&_S_V?l8FW2H z!m5FUXu~GhVU1IYJx`ZS=h#36K>zC9)huu}W=I<3b*XFf87B)vyY1u2f3278xfNs#K(rPje z|Fyf~CEAZ-MqeZ>eex(D7Ei)ZgnC<~JT%N1(twAG zXx_puYM$H_D*;Im*WGOu}7MLYhdB{JP zZ?CotO25eqb1-Lx@a!q*2ZHRe%T>QdiFQuGa38pov4M%2ho;MFVN_b2q@U*HWB%jk zm@4@)F3DLu5hgJJehWF6s!sRb>V7TTXPnAzoEs(HIX~+DN{Y)auZ#wCy}Vbl=oU;LRcFJONcf+pUJHZDQ8E z={1&3eSs_)Ybzy5_n2wSPNQ;`4)Zo0Azi+5c@1~Y#CM*2^Kon4m2Gf5yC}j&hKzDw z&)(_j9`^m2ys_^xnpDqLrLQMKpom?IE20Bkb+i&5vs$i`-m$d2dK3}w$TEm#4>DmF zbVtVIc4#O0^wImElu=^Z7g`>c9v@pgSETe#z~QX5j=)Lb6K7$z;eJXX4-$}FABS~l z_Fc*2TJ$hPTo&y`0z?#HSPcbwUx?J@eR*F!G}d2MrglR-X|gEKh#W=T)&RghK2wT% zFZ45t@3w8%dPwM`{`3HbGEZd3ojiTbq~L}J{8EvhO!o^RMBsqA0qzGeWg}B8Nq)Y` z0}bdkfw}|2-JEq;lyo6pI8(~*63OG%7uf}}&f7D&Eo;q47LzJv0BcH#Wt{8xm;{yN zO&^)5`&?=DcA#iHupG?OkSe*@`{#T@HR4wgaX7(Xq1hk=f8WdG#LTdK*~ZKX z9&BALf8aopHK%Ceh4`el7R5^W&&1DQznzOMiCQf#ebU(*eX;CnD50J+bWrJYPDgty zSJy9r@Ogx5m)3s*vy%z+X9UJ#E$|94SIMoP zhU#AA%yWLGt1nHD)!-R_8iU2!Igp+ps+%aY^n;(t6*?WPw^Qj|{8}y?3pf1!_e~6i zYkR#@t?m)N^?tx5v$d}*+5e^Mo{OeO%y^Bz4r{+`OT|rB`Y~9n{I$r`pGTOes!C_0 zN1WO&ds2rjqbF*3%Ln1X(2H$gwBO+`U}?v0wYO~J^ z^k4-^Ck-~;E5bUeRngQRcQQXk&*T*4s{CLIkn&qB=r?FKC77<1|A=OI;qd#xC=p*0 z*vB^!I&n%nN-6VlCLAuNn7ts!{~|lLU9E>5U_1c-E=p>|T*D6@XX~5@^eNW(2;`eG zZ1~HuTn&K3?GMH49kKi)@M>B06-`8Z(|e#S%1hNnF!0(;$K&?L`J7?+xiUOO>~EFipq{ zq_xBm=EEQqZkAoAeL->`n_z)aRhAh1*fpIWEExaBW;EUW&gDzGeD`{6`>|cuvjyw4 zUcp~WJp(eZHS-pHo;^I4#B4{_^>o4OUH?p+C~?(5&Gh^;AvFufs#yIWd(1(b_fRDH Xu!Sc9jjsLU=kb`ESQ%Gdb9wqdLNX-? diff --git a/core/res/res/mipmap-mdpi/sym_def_app_icon_foreground.png b/core/res/res/mipmap-mdpi/sym_def_app_icon_foreground.png deleted file mode 100644 index 2c38c719088d8d97ecb7092fc07fd26c2ff96bf9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2652 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 diff --git a/core/res/res/mipmap-xhdpi/sym_def_app_icon_foreground.png b/core/res/res/mipmap-xhdpi/sym_def_app_icon_foreground.png deleted file mode 100644 index 072467eaaafb06293a727a85b97ad57f6667b318..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7011 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 + + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> - - - + android:fillColor="#000000" + android:pathData="M17.6 11.48l1.84-3.18a0.63 0.63 0 0 0-1.09-0.63l-1.88 3.24a11.43 11.43 0 0 0-8.94 0L5.65 7.67A0.63 0.63 0 0 0 4.56 8.3l1.84 3.18A10.81 10.81 0 0 0 1 20h22a10.81 10.81 0 0 0-5.4-8.52zM7 17.25A1.25 1.25 0 1 1 8.25 16 1.25 1.25 0 0 1 7 17.25zm10 0A1.25 1.25 0 1 1 18.25 16 1.25 1.25 0 0 1 17 17.25z"/> diff --git a/packages/SystemUI/res/drawable/ic_android.xml b/packages/SystemUI/res/drawable/ic_android.xml index 19ee9a7a396b..ce938ebd7d53 100644 --- a/packages/SystemUI/res/drawable/ic_android.xml +++ b/packages/SystemUI/res/drawable/ic_android.xml @@ -1,7 +1,7 @@ + + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#000000" + android:pathData="M17.6 11.48l1.84-3.18a0.63 0.63 0 0 0-1.09-0.63l-1.88 3.24a11.43 11.43 0 0 0-8.94 0L5.65 7.67A0.63 0.63 0 0 0 4.56 8.3l1.84 3.18A10.81 10.81 0 0 0 1 20h22a10.81 10.81 0 0 0-5.4-8.52zM7 17.25A1.25 1.25 0 1 1 8.25 16 1.25 1.25 0 0 1 7 17.25zm10 0A1.25 1.25 0 1 1 18.25 16 1.25 1.25 0 0 1 17 17.25z"/> diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index bb3da5c0f1b0..93ac2d9b49cd 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -61,6 +61,7 @@ import android.hidl.manager.V1_0.IServiceManager; import android.hidl.manager.V1_0.IServiceNotification; import android.os.BatteryManager; +import android.os.Build; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; @@ -1282,6 +1283,7 @@ protected void updateUsbNotification(boolean force) { .system_notification_accent_color)) .setContentTitle(title) .setContentText(message) + .setSubText(Build.ID) .setContentIntent(pi) .setVisibility(Notification.VISIBILITY_PUBLIC); From 38e9ce68e7e516916e2969e1d0c3f15f649327bb Mon Sep 17 00:00:00 2001 From: jianzhou Date: Tue, 9 Oct 2018 10:11:18 +0800 Subject: [PATCH 08/92] view: add null check for dispatch touch view during monkey test, sometimes in low memory status, the view will be null when dispatching the touch event. Add null check to avoid this crash. Change-Id: I5b04cfbc6b1c5c46a1da7d1133d8a7e1b9af556f --- core/java/android/view/ViewGroup.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 679da313edcc..5dcdbcf8472e 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2891,7 +2891,7 @@ private void resetTouchState() { * Returns true if the flag was previously set. */ private static boolean resetCancelNextUpFlag(@NonNull View view) { - if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) { + if (view != null && (view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) { view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT; return true; } From d598cdebeef3bd0cc4bb552342437a33bb8b4b7b Mon Sep 17 00:00:00 2001 From: mqi Date: Sun, 4 Jun 2017 20:03:01 +0800 Subject: [PATCH 09/92] frameworks: Fix null pointer Issue Add a null pointer check to avoid crash while customizing quick settings in systemui. CRs-Fixed: 1094610 Change-Id: I03eaaaeeb8bbf7fbc91ca09eb16dd1ab559d2d6f --- core/java/android/widget/ArrayAdapter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java index de9f76d6eea1..30c8d7c167aa 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -448,7 +448,9 @@ public long getItemId(int position) { } final T item = getItem(position); - if (item instanceof CharSequence) { + if (item == null) { + text.setText(""); + } else if (item instanceof CharSequence) { text.setText((CharSequence) item); } else { text.setText(item.toString()); From 3722150d26f5f6360815fe0b1b94ad83d5678ad3 Mon Sep 17 00:00:00 2001 From: Niraj kumar Mishra Date: Fri, 6 Oct 2017 17:42:30 +0530 Subject: [PATCH 10/92] Crash occured due to null pointer exception. Reason: Trying to access remote device object (mobjectdevice)which have been set to NULL before. Fix:Avoid handling state changes for invalid devices(NULL) CRs-Fixed: 2121814 PS2: Kill log spam Change-Id: Ib6429bac9b72e933850e6a0f7f670f1f5b2313f8 --- .../settingslib/bluetooth/LocalBluetoothProfileManager.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 63cb38153d8d..c7953568353c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -269,6 +269,11 @@ private class StateChangedHandler implements BluetoothEventManager.Handler { } public void onReceive(Context context, Intent intent, BluetoothDevice device) { + if (device == null) { + if(DEBUG) Log.d(TAG, "StateChangedHandler receives state-change for invalid device"); + return; + } + CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); if (cachedDevice == null) { Log.w(TAG, "StateChangedHandler found new device: " + device); From b84d6c33e0cd30dfc7722c5c625690dd78bf304c Mon Sep 17 00:00:00 2001 From: Simao Gomes Viana Date: Tue, 22 Nov 2016 16:13:50 +0100 Subject: [PATCH 11/92] ParcelFileDescriptor: Stop the panic Change-Id: Ifa58336abc264571ee307e3c2d69d3c294e2d8c3 --- core/java/android/os/ParcelFileDescriptor.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 810bd636de07..9cf909087286 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -203,7 +203,11 @@ public ParcelFileDescriptor(FileDescriptor fd, FileDescriptor commChannel) { IoUtils.setFdOwner(mCommFd, this); } - mGuard.open("close"); + try { + mGuard.open("close"); + } catch(Throwable e) { + Log.w("ParcelFileDescriptor", "Explicit termination method 'close' not called"); + } } /** From 1331de6a9ef4f89394b441cedf34c94723c6e824 Mon Sep 17 00:00:00 2001 From: AndroidRul3z Date: Sun, 9 Nov 2014 18:34:05 +0100 Subject: [PATCH 12/92] Turn off some debugs Change-Id: I36bd0034f6a0d7e674e97152ac35ec1961718527 --- media/java/android/media/Ringtone.java | 2 +- .../com/android/systemui/navigationbar/buttons/DeadZone.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index 86a94a9e0662..8b40e74a4099 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -48,7 +48,7 @@ */ public class Ringtone { private static final String TAG = "Ringtone"; - private static final boolean LOGD = true; + private static final boolean LOGD = false; private static final String[] MEDIA_COLUMNS = new String[] { MediaStore.Audio.Media._ID, diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java index 7e5b5548237b..b5756242200b 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java @@ -43,7 +43,7 @@ public class DeadZone { public static final int HORIZONTAL = 0; // Consume taps along the top edge. public static final int VERTICAL = 1; // Consume taps along the left edge. - private static final boolean CHATTY = true; // print to logcat when we eat a click + private static final boolean CHATTY = false; // print to logcat when we eat a click private final NavigationBarController mNavBarController; private final NavigationBarView mNavigationBarView; From bb135cbfe624d2f326483d143c9854e9e5235111 Mon Sep 17 00:00:00 2001 From: Adithya R Date: Thu, 4 Mar 2021 18:12:20 +0530 Subject: [PATCH 13/92] SystemUI: Redraw display cutout on overlay changes Fixes notch hide overlay on some devices commit 5481d59996b34cda1cb6b680af7510fee7b53b42 Author: daniml3 Date: Tue Mar 9 08:11:13 2021 +0100 SystemUI: check if the cutout views array is null before using it Signed-off-by: daniml3 Change-Id: I1316c61280dadc30a86f2ae72559437a61dd4616 Co-authored-by: daniml3 Change-Id: I5a049099ab375833f1e5ebbda49dc36c3c0b0a68 --- .../src/com/android/systemui/ScreenDecorations.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index e566ccb49e3b..bca3c86a722c 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -667,6 +667,13 @@ protected void onConfigurationChanged(Configuration newConfig) { updateRoundedCornerRadii(); if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation); setupDecorations(); + if (mCutoutViews != null) { + for (DisplayCutoutView dcv : mCutoutViews) { + if (dcv != null) { + dcv.update(); + } + } + } if (mOverlays != null) { // Updating the layout params ensures that ViewRootImpl will call relayoutWindow(), // which ensures that the forced seamless rotation will end, even if we updated From 01f9f4f571804ea144e37b8bfd9bf7cc630928cf Mon Sep 17 00:00:00 2001 From: spezi77 Date: Tue, 24 Aug 2021 17:39:13 +0200 Subject: [PATCH 14/92] PowerUI: Mute logcat spam. https://katb.in/kat9848 I'm not 100% sure, but I believe that this was the reason for a SystemUI hang, which was responsible for the fact that the screen could not be switched on for several seconds and just remained black. --- .../src/com/android/systemui/power/PowerUI.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index 625265485f6d..c6341d5eee14 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -65,7 +65,7 @@ public class PowerUI extends SystemUI implements CommandQueue.Callbacks { static final String TAG = "PowerUI"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + static final boolean DEBUG = false; private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS; private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS; private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer @@ -437,9 +437,11 @@ void maybeShowHybridWarning(BatteryStateSnapshot currentSnapshot, boolean shouldShowHybridWarning(BatteryStateSnapshot snapshot) { if (snapshot.getPlugged() || snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN) { - Slog.d(TAG, "can't show warning due to - plugged: " + snapshot.getPlugged() - + " status unknown: " - + (snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN)); + if (DEBUG) { + Slog.d(TAG, "can't show warning due to - plugged: " + snapshot.getPlugged() + + " status unknown: " + + (snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN)); + } return false; } From 882c7aa8b0250325cd1d920ccbc4f22c21ee7021 Mon Sep 17 00:00:00 2001 From: maxwen Date: Sun, 10 Dec 2017 09:30:50 +0100 Subject: [PATCH 15/92] Use new gradient dialog also for recovery/factory reset action @jhenrique09 edits: Adapt to PE original commit: https://gerrit.omnirom.org/#/c/27327/ Change-Id: I3a05ca554927dacff7e10d901de5947569a08d07 Signed-off-by: SagarMakhar Signed-off-by: Joey Huab --- .../core/java/com/android/server/power/ShutdownThread.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index e94575c43363..4caf7dc6e8ec 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -325,6 +325,9 @@ private static ProgressDialog showShutdownDialog(Context context) { pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); pd.setIndeterminate(true); } else { + if (showSysuiReboot()) { + return null; + } // Factory reset path. Set the dialog message accordingly. pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title)); pd.setMessage(context.getText( From 808ba143ec2a50bdaac4ebcc0e0be8f2623aa87a Mon Sep 17 00:00:00 2001 From: mqi Date: Fri, 7 Jul 2017 15:20:06 +0800 Subject: [PATCH 16/92] SystemUI: Fix SystemUI Crash SystemUI may crash sometimes in monkey test due to exception. Catch the exception to avoid the crash. Change-Id: I548be5c6465f3c7a81c33d1cfa9b30e7269fd86d --- .../android/systemui/statusbar/policy/UserInfoControllerImpl.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoControllerImpl.java old mode 100644 new mode 100755 From a2934f8382fb85e816b4fefb55e69d15ca63e369 Mon Sep 17 00:00:00 2001 From: Aryan Sinha Date: Thu, 17 Mar 2022 04:06:55 +0000 Subject: [PATCH 17/92] base: Disable Remaining Debug Signed-off-by: Aryan Sinha --- .../com/android/backupconfirm/BackupRestoreConfirmation.java | 2 +- .../src/com/android/printspooler/model/MutexFileProvider.java | 2 +- .../src/com/android/sharedstoragebackup/ObbBackupService.java | 2 +- .../src/com/android/sharedstoragebackup/SharedStorageAgent.java | 2 +- .../src/com/android/systemui/statusbar/GestureRecorder.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java index d6b6bf8d1e56..be8c26cb9cbb 100644 --- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java +++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java @@ -50,7 +50,7 @@ */ public class BackupRestoreConfirmation extends Activity { static final String TAG = "BackupRestoreConfirmation"; - static final boolean DEBUG = true; + static final boolean DEBUG = false; static final String KEY_DID_ACKNOWLEDGE = "did_acknowledge"; static final String KEY_TOKEN = "token"; diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/MutexFileProvider.java b/packages/PrintSpooler/src/com/android/printspooler/model/MutexFileProvider.java index 0df5e3cf18d7..60c9b586cb1a 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/MutexFileProvider.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/MutexFileProvider.java @@ -35,7 +35,7 @@ public final class MutexFileProvider { private static final String LOG_TAG = "MutexFileProvider"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private final Object mLock = new Object(); diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java index 0f8ccd7bc403..ce8c377153a6 100644 --- a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java +++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java @@ -43,7 +43,7 @@ */ public class ObbBackupService extends Service { static final String TAG = "ObbBackupService"; - static final boolean DEBUG = true; + static final boolean DEBUG = false; /** * IObbBackupService interface implementation diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java index e453cf53a98b..1b9c0cff7ce8 100644 --- a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java +++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java @@ -16,7 +16,7 @@ public class SharedStorageAgent extends FullBackupAgent { static final String TAG = "SharedStorageAgent"; - static final boolean DEBUG = true; + static final boolean DEBUG = false; StorageVolume[] mVolumes; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java index f2adaf042b2f..5580d6c5647d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java @@ -34,7 +34,7 @@ * Convenience class for capturing gestures for later analysis. */ public class GestureRecorder { - public static final boolean DEBUG = true; // for now + public static final boolean DEBUG = false; // for now public static final String TAG = GestureRecorder.class.getSimpleName(); public class Gesture { From 852f901f4229ce74722ffbacc5f69e31fddb4a45 Mon Sep 17 00:00:00 2001 From: Jyotiraditya Panda Date: Thu, 17 Dec 2020 19:40:32 +0000 Subject: [PATCH 18/92] SystemUI: Show Bolt icon while charging. [alibei] Adapted to 12L [SparXFusion] Adapted to S Signed-off-by: Jyotiraditya Panda Signed-off-by: SparXFusion Signed-off-by: ad1tyas1ngh Signed-off-by: alibei --- .../res/drawable/bolt_charging_state_0.xml | 13 +++ .../res/drawable/bolt_charging_state_10.xml | 13 +++ .../res/drawable/bolt_charging_state_100.xml | 13 +++ .../res/drawable/bolt_charging_state_20.xml | 13 +++ .../res/drawable/bolt_charging_state_30.xml | 13 +++ .../res/drawable/bolt_charging_state_40.xml | 13 +++ .../res/drawable/bolt_charging_state_50.xml | 13 +++ .../res/drawable/bolt_charging_state_60.xml | 13 +++ .../res/drawable/bolt_charging_state_70.xml | 13 +++ .../res/drawable/bolt_charging_state_80.xml | 13 +++ .../res/drawable/bolt_charging_state_90.xml | 13 +++ packages/SystemUI/res/values/styx_dimens.xml | 20 +++++ .../systemui/BatteryBoltChargeView.java | 84 +++++++++++++++++++ .../systemui/battery/BatteryMeterView.java | 34 ++++++++ .../battery/BatteryMeterViewController.java | 1 + 15 files changed, 282 insertions(+) create mode 100644 packages/SystemUI/res/drawable/bolt_charging_state_0.xml create mode 100644 packages/SystemUI/res/drawable/bolt_charging_state_10.xml create mode 100644 packages/SystemUI/res/drawable/bolt_charging_state_100.xml create mode 100644 packages/SystemUI/res/drawable/bolt_charging_state_20.xml create mode 100644 packages/SystemUI/res/drawable/bolt_charging_state_30.xml create mode 100644 packages/SystemUI/res/drawable/bolt_charging_state_40.xml create mode 100644 packages/SystemUI/res/drawable/bolt_charging_state_50.xml create mode 100644 packages/SystemUI/res/drawable/bolt_charging_state_60.xml create mode 100644 packages/SystemUI/res/drawable/bolt_charging_state_70.xml create mode 100644 packages/SystemUI/res/drawable/bolt_charging_state_80.xml create mode 100644 packages/SystemUI/res/drawable/bolt_charging_state_90.xml create mode 100644 packages/SystemUI/res/values/styx_dimens.xml create mode 100644 packages/SystemUI/src/com/android/systemui/BatteryBoltChargeView.java diff --git a/packages/SystemUI/res/drawable/bolt_charging_state_0.xml b/packages/SystemUI/res/drawable/bolt_charging_state_0.xml new file mode 100644 index 000000000000..81845562254d --- /dev/null +++ b/packages/SystemUI/res/drawable/bolt_charging_state_0.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/bolt_charging_state_10.xml b/packages/SystemUI/res/drawable/bolt_charging_state_10.xml new file mode 100644 index 000000000000..420eef794ccd --- /dev/null +++ b/packages/SystemUI/res/drawable/bolt_charging_state_10.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/bolt_charging_state_100.xml b/packages/SystemUI/res/drawable/bolt_charging_state_100.xml new file mode 100644 index 000000000000..c376aa963b85 --- /dev/null +++ b/packages/SystemUI/res/drawable/bolt_charging_state_100.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/bolt_charging_state_20.xml b/packages/SystemUI/res/drawable/bolt_charging_state_20.xml new file mode 100644 index 000000000000..1cf189c38f2b --- /dev/null +++ b/packages/SystemUI/res/drawable/bolt_charging_state_20.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/bolt_charging_state_30.xml b/packages/SystemUI/res/drawable/bolt_charging_state_30.xml new file mode 100644 index 000000000000..ca644e3cbe11 --- /dev/null +++ b/packages/SystemUI/res/drawable/bolt_charging_state_30.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/bolt_charging_state_40.xml b/packages/SystemUI/res/drawable/bolt_charging_state_40.xml new file mode 100644 index 000000000000..6411dafcba19 --- /dev/null +++ b/packages/SystemUI/res/drawable/bolt_charging_state_40.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/bolt_charging_state_50.xml b/packages/SystemUI/res/drawable/bolt_charging_state_50.xml new file mode 100644 index 000000000000..4109c4ddf2df --- /dev/null +++ b/packages/SystemUI/res/drawable/bolt_charging_state_50.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/bolt_charging_state_60.xml b/packages/SystemUI/res/drawable/bolt_charging_state_60.xml new file mode 100644 index 000000000000..7f0101e764ad --- /dev/null +++ b/packages/SystemUI/res/drawable/bolt_charging_state_60.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/bolt_charging_state_70.xml b/packages/SystemUI/res/drawable/bolt_charging_state_70.xml new file mode 100644 index 000000000000..851f7fcdefa9 --- /dev/null +++ b/packages/SystemUI/res/drawable/bolt_charging_state_70.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/bolt_charging_state_80.xml b/packages/SystemUI/res/drawable/bolt_charging_state_80.xml new file mode 100644 index 000000000000..b3ea9811ca6f --- /dev/null +++ b/packages/SystemUI/res/drawable/bolt_charging_state_80.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/SystemUI/res/drawable/bolt_charging_state_90.xml b/packages/SystemUI/res/drawable/bolt_charging_state_90.xml new file mode 100644 index 000000000000..30bd781b2994 --- /dev/null +++ b/packages/SystemUI/res/drawable/bolt_charging_state_90.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/packages/SystemUI/res/values/styx_dimens.xml b/packages/SystemUI/res/values/styx_dimens.xml new file mode 100644 index 000000000000..da6415060312 --- /dev/null +++ b/packages/SystemUI/res/values/styx_dimens.xml @@ -0,0 +1,20 @@ + + + + + + 13.5dp + 9.7dp + + diff --git a/packages/SystemUI/src/com/android/systemui/BatteryBoltChargeView.java b/packages/SystemUI/src/com/android/systemui/BatteryBoltChargeView.java new file mode 100644 index 000000000000..183cdc2ce027 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/BatteryBoltChargeView.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2020 Wave-OS + * + * 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.systemui; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.util.AttributeSet; +import android.widget.ImageView; + +public class BatteryBoltChargeView extends ImageView { + + private int mLevel; + + public BatteryBoltChargeView(Context context) { + this(context, null /* attrs */); + } + + public BatteryBoltChargeView(Context context, AttributeSet attrs) { + this(context, attrs, 0 /* defStyleAttr */); + } + + public BatteryBoltChargeView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0 /* defStyleRes */); + } + + public BatteryBoltChargeView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + mLevel = -1; + } + + public void setIconTint(int intensity) { + setImageTintList(ColorStateList.valueOf(intensity)); + } + + public void updateViews() { + setImageResource(getImageResId(mLevel)); + } + + public void setLevel(int level) { + mLevel = level; + setImageResource(getImageResId(level)); + } + + private int getImageResId(int level) { + if (level == 0) { + return R.drawable.bolt_charging_state_0; + } else if (level > 0 && level <= 10) { + return R.drawable.bolt_charging_state_10; + } else if (level > 10 && level <= 20) { + return R.drawable.bolt_charging_state_20; + } else if (level > 20 && level <= 30) { + return R.drawable.bolt_charging_state_30; + } else if (level > 30 && level <= 40) { + return R.drawable.bolt_charging_state_40; + } else if (level > 40 && level <= 50) { + return R.drawable.bolt_charging_state_50; + } else if (level > 50 && level <= 60) { + return R.drawable.bolt_charging_state_60; + } else if (level > 60 && level <= 70) { + return R.drawable.bolt_charging_state_70; + } else if (level > 70 && level <= 80) { + return R.drawable.bolt_charging_state_80; + } else if (level > 80 && level <= 90) { + return R.drawable.bolt_charging_state_90; + } else if (level <= 90 || level > 100) { + return 0; + } + return R.drawable.bolt_charging_state_100; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index 39088c367a27..50bdd7bfb041 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -36,6 +36,7 @@ import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; +import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; @@ -45,6 +46,7 @@ import androidx.annotation.VisibleForTesting; import com.android.settingslib.graph.ThemedBatteryDrawable; +import com.android.systemui.BatteryBoltChargeView; import com.android.systemui.DualToneHandler; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; @@ -84,6 +86,8 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { private DualToneHandler mDualToneHandler; + protected BatteryBoltChargeView mBatteryBoltChargeView; + private int mNonAdaptedSingleToneColor; private int mNonAdaptedForegroundColor; private int mNonAdaptedBackgroundColor; @@ -122,6 +126,13 @@ public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) { getResources().getDimensionPixelOffset(R.dimen.battery_margin_bottom)); addView(mBatteryIconView, mlp); + mBatteryBoltChargeView = new BatteryBoltChargeView(context, null); + final MarginLayoutParams mlp2 = new MarginLayoutParams( + getResources().getDimensionPixelSize(R.dimen.status_bar_battery_bolt_icon_width), + getResources().getDimensionPixelSize(R.dimen.status_bar_battery_bolt_icon_height)); + mlp2.setMargins(0, 0, 0, 0); + addView(mBatteryBoltChargeView, mlp2); + updateShowPercent(); mDualToneHandler = new DualToneHandler(context); // Init to not dark at all. @@ -190,6 +201,8 @@ void onBatteryLevelChanged(int level, boolean pluggedIn) { mCharging = pluggedIn; mLevel = level; updatePercentText(); + updateBoltChargeView(); + updateBatteryMeterVisibility(); } void onPowerSaveChanged(boolean isPowerSave) { @@ -324,6 +337,22 @@ void onBatteryUnknownStateChanged(boolean isUnknown) { updateShowPercent(); } + public void updateBoltChargeView() { + if (mCharging) { + mBatteryBoltChargeView.setLevel(mLevel); + mBatteryBoltChargeView.setVisibility(View.VISIBLE); + return; + } + mBatteryBoltChargeView.setVisibility(View.GONE); + } + + public void updateBatteryMeterVisibility() { + if (mCharging) { + mBatteryIconView.setVisibility(View.GONE); + } else { + mBatteryIconView.setVisibility(View.VISIBLE); + } + } /** * Looks up the scale factor for status bar icons and scales the battery view by that amount. @@ -344,6 +373,9 @@ void scaleBatteryMeterViews() { scaledLayoutParams.setMargins(0, 0, 0, marginBottom); mBatteryIconView.setLayoutParams(scaledLayoutParams); + + mBatteryBoltChargeView.updateViews(); + mBatteryBoltChargeView.setLayoutParams(mBatteryBoltChargeView.getLayoutParams()); } @Override @@ -355,6 +387,7 @@ public void onDarkChanged(Rect area, float darkIntensity, int tint) { updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor, mNonAdaptedSingleToneColor); + mBatteryBoltChargeView.setIconTint(tint); } /** @@ -375,6 +408,7 @@ public void updateColors(int foregroundColor, int backgroundColor, int singleTon if (mUnknownStateDrawable != null) { mUnknownStateDrawable.setTint(singleToneColor); } + mBatteryBoltChargeView.setIconTint(foregroundColor); } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java index ae9a32309d45..f49740534b43 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java @@ -130,6 +130,7 @@ protected void onViewAttached() { mCurrentUserTracker.startTracking(); mView.updateShowPercent(); + mView.updateBatteryMeterVisibility(); } @Override From c11eee702ee7ccda7ec7a62f6809e8afff1feef7 Mon Sep 17 00:00:00 2001 From: maxwen Date: Sat, 16 Oct 2021 21:03:16 +0200 Subject: [PATCH 19/92] base: PSSSSST! disable some DEBUG logspill Change-Id: I719cd895a5225cadd6120980665754d63cb98687 Signed-off-by: Joey Huab --- .../android/security/net/config/ManifestConfigSource.java | 2 +- .../interruption/NotificationInterruptStateProviderImpl.java | 2 +- .../core/java/com/android/server/DropBoxManagerService.java | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/java/android/security/net/config/ManifestConfigSource.java b/core/java/android/security/net/config/ManifestConfigSource.java index b885e726918d..0e20997d307f 100644 --- a/core/java/android/security/net/config/ManifestConfigSource.java +++ b/core/java/android/security/net/config/ManifestConfigSource.java @@ -25,7 +25,7 @@ /** @hide */ public class ManifestConfigSource implements ConfigSource { - private static final boolean DBG = true; + private static final boolean DBG = false; private static final String LOG_TAG = "NetworkSecurityConfig"; private final Object mLock = new Object(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index cbc849aa8408..ab7d4799affd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -54,7 +54,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInterruptStateProvider { private static final String TAG = "InterruptionStateProvider"; private static final boolean DEBUG = false; - private static final boolean DEBUG_HEADS_UP = Build.IS_DEBUGGABLE; + private static final boolean DEBUG_HEADS_UP = false; private static final boolean ENABLE_HEADS_UP = true; private static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up"; diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index a2a232d6f3ec..b90404867d0f 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -85,6 +85,7 @@ */ public final class DropBoxManagerService extends SystemService { private static final String TAG = "DropBoxManagerService"; + private static final boolean DBG = false; private static final int DEFAULT_AGE_SECONDS = 3 * 86400; private static final int DEFAULT_MAX_FILES = 1000; private static final int DEFAULT_MAX_FILES_LOWRAM = 300; @@ -463,7 +464,7 @@ public void close() throws IOException { public void addEntry(String tag, EntrySource entry, int flags) { File temp = null; try { - Slog.i(TAG, "add tag=" + tag + " isTagEnabled=" + isTagEnabled(tag) + if (DBG) Slog.i(TAG, "add tag=" + tag + " isTagEnabled=" + isTagEnabled(tag) + " flags=0x" + Integer.toHexString(flags)); if ((flags & DropBoxManager.IS_EMPTY) != 0) throw new IllegalArgumentException(); @@ -1028,7 +1029,7 @@ private synchronized void init() throws IOException { // Scan pre-existing files. for (File file : files) { if (file.getName().endsWith(".tmp")) { - Slog.i(TAG, "Cleaning temp file: " + file); + if (DBG) Slog.i(TAG, "Cleaning temp file: " + file); file.delete(); continue; } From 6d10e73f9497806a04f230b07148a2df938427a6 Mon Sep 17 00:00:00 2001 From: Chet Kener Date: Wed, 19 Nov 2014 16:05:35 -0500 Subject: [PATCH 20/92] GpsNetInitiatedHandler: Disable more debugging Change-Id: I1ced2943df0986bcfc0f44d2847e50e393101e82 Signed-off-by: Chet Kener Signed-off-by: spezi77 Signed-off-by: Joey Huab --- .../com/android/internal/location/GpsNetInitiatedHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java index 4ce9a320a159..fa49746c8400 100644 --- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java +++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java @@ -48,7 +48,7 @@ public class GpsNetInitiatedHandler { private static final String TAG = "GpsNetInitiatedHandler"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean DEBUG = false; // string constants for defining data fields in NI Intent public static final String NI_INTENT_KEY_NOTIF_ID = "notif_id"; From 1bf34924e84f49413c5eb151bacae8a0821ae46a Mon Sep 17 00:00:00 2001 From: jhonboy121 Date: Sat, 22 Jan 2022 13:14:27 +0530 Subject: [PATCH 21/92] SystemUI: PeopleSpaceWidgetManager: don't spam logcat * These unguarded logs spam like hell when non-conversation notifications are posted (like download notifications for example) Signed-off-by: jhonboy121 --- .../systemui/people/widget/PeopleSpaceWidgetManager.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 985903435b9a..923e09cb779e 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -392,7 +392,7 @@ public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWi } if (mIPeopleManager == null || mLauncherApps == null) { - Log.d(TAG, "System services are null"); + if (DEBUG) Log.d(TAG, "System services are null"); return null; } try { @@ -450,14 +450,14 @@ private void updateWidgetsWithNotificationChangedInBackground(StatusBarNotificat PeopleTileKey key = new PeopleTileKey( sbn.getShortcutId(), sbn.getUser().getIdentifier(), sbn.getPackageName()); if (!PeopleTileKey.isValid(key)) { - Log.d(TAG, "Sbn doesn't contain valid PeopleTileKey: " + key.toString()); + if (DEBUG) Log.d(TAG, "Sbn doesn't contain valid PeopleTileKey: " + key.toString()); return; } int[] widgetIds = mAppWidgetManager.getAppWidgetIds( new ComponentName(mContext, PeopleSpaceWidgetProvider.class) ); if (widgetIds.length == 0) { - Log.d(TAG, "No app widget ids returned"); + if (DEBUG) Log.d(TAG, "No app widget ids returned"); return; } synchronized (mLock) { @@ -959,7 +959,7 @@ private void uncacheConversationShortcut(PeopleTileKey key) { UserHandle.of(key.getUserId()), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); } catch (Exception e) { - Log.d(TAG, "Exception uncaching shortcut:" + e); + Log.e(TAG, "Exception uncaching shortcut:" + e); } } From f837d56483d5878fa9f2ba3ddc3c357f1d243937 Mon Sep 17 00:00:00 2001 From: HeroBuxx Date: Thu, 4 Mar 2021 22:30:27 +0700 Subject: [PATCH 22/92] core: Give fresh look for Action Bar back arrow Signed-off-by: HeroBuxx Change-Id: I8c83e0afe3ea46c0a800b5edbc29a1e1c87dc31a --- core/res/res/drawable/ic_ab_back_material.xml | 8 ++++---- core/res/res/drawable/ic_ab_back_material_dark.xml | 4 ++-- core/res/res/drawable/ic_ab_back_material_light.xml | 4 ++-- core/res/res/drawable/ic_ab_back_material_settings.xml | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/res/res/drawable/ic_ab_back_material.xml b/core/res/res/drawable/ic_ab_back_material.xml index e4bc6ccfbf07..208f127a7019 100644 --- a/core/res/res/drawable/ic_ab_back_material.xml +++ b/core/res/res/drawable/ic_ab_back_material.xml @@ -1,7 +1,7 @@ @@ -174,6 +175,7 @@ Qualcomm® aptX™ audio Qualcomm® aptX™ HD audio LDAC + Qualcomm® aptX™ Adaptive audio diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index 3c7856048860..67ca523e51dc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -312,6 +312,9 @@ public String getHighQualityAudioOptionLabel(BluetoothDevice device) { case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC: index = 5; break; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE: + index = 6; + break; } if (index < 0) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java index 9afdd43ce73c..ef4fe7b38cb6 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java @@ -130,7 +130,7 @@ public void isHighQualityAudioEnabled() { private static String KNOWN_CODEC_LABEL = "Use high quality audio: %1$s"; private static String UNKNOWN_CODEC_LABEL = "Use high quality audio"; private static String[] CODEC_NAMES = - new String[]{"Default", "SBC", "AAC", "aptX", "aptX HD", "LDAC"}; + new String[]{"Default", "SBC", "AAC", "aptX", "aptX HD", "LDAC", "aptX Adaptive"}; /** * Helper for setting up several tests of getHighQualityAudioOptionLabel From a578431da12f1775135a41b529f668ff414d0b46 Mon Sep 17 00:00:00 2001 From: pramod kotreshappa Date: Thu, 4 Oct 2018 03:12:23 -0700 Subject: [PATCH 33/92] TWS-A2DP: Add TWS+ codec to DevUI support Conflicts: core/java/android/bluetooth/BluetoothCodecConfig.java packages/SettingsLib/res/values-en-rGB/arrays.xml packages/SettingsLib/res/values/arrays.xml Change-Id: I26ac7cc34875d01a987f00b34e43cc7fca98ef3a CRs-Fixed: 2318635 Signed-off-by: Volodymyr Zhdanov --- .../android/bluetooth/BluetoothCodecConfig.java | 13 ++++++++++++- packages/SettingsLib/res/values-en-rGB/arrays.xml | 2 ++ packages/SettingsLib/res/values/arrays.xml | 2 ++ .../android/settingslib/bluetooth/A2dpProfile.java | 3 +++ .../settingslib/bluetooth/A2dpProfileTest.java | 2 +- 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index fac1e13fb677..16c03f153472 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -71,7 +71,16 @@ public final class BluetoothCodecConfig implements Parcelable { public static final int SOURCE_CODEC_TYPE_LDAC = 5; @UnsupportedAppUsage - public static final int SOURCE_CODEC_TYPE_MAX = 6; + public static final int SOURCE_CODEC_TYPE_APTX_TWSP = 6; + + @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_MAX = 7; + + /* CELT is not an A2DP Codec and only used to fetch encoder + ** format for BA usecase, moving out of a2dp codec value list + */ + @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_CELT = 8; @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; @@ -408,6 +417,8 @@ public void writeToParcel(Parcel out, int flags) { return "LDAC"; case SOURCE_CODEC_TYPE_APTX_ADAPTIVE: return "aptX Adaptive"; + case SOURCE_CODEC_TYPE_APTX_TWSP: + return "aptX TWS+"; case SOURCE_CODEC_TYPE_INVALID: return "INVALID CODEC"; default: diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml index 0d5b07a50f8d..6d3e556c642c 100644 --- a/packages/SettingsLib/res/values-en-rGB/arrays.xml +++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml @@ -93,6 +93,7 @@ "Qualcomm® aptX™ HD audio" "LDAC" "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Use system selection (default)" @@ -102,6 +103,7 @@ "Qualcomm® aptX™ HD audio" "LDAC" "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Use system selection (default)" diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index 0a326e1384ca..a385cb5b0316 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -155,6 +155,7 @@ Qualcomm® aptX™ HD audio LDAC Qualcomm® aptX™ Adaptive audio + Qualcomm® aptX™ TWS+ audio @@ -176,6 +177,7 @@ Qualcomm® aptX™ HD audio LDAC Qualcomm® aptX™ Adaptive audio + Qualcomm® aptX™ TWS+ audio diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index 67ca523e51dc..8bb9af79c6a2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -315,6 +315,9 @@ public String getHighQualityAudioOptionLabel(BluetoothDevice device) { case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE: index = 6; break; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_TWSP: + index = 7; + break; } if (index < 0) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java index ef4fe7b38cb6..16f37aecd6e2 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java @@ -130,7 +130,7 @@ public void isHighQualityAudioEnabled() { private static String KNOWN_CODEC_LABEL = "Use high quality audio: %1$s"; private static String UNKNOWN_CODEC_LABEL = "Use high quality audio"; private static String[] CODEC_NAMES = - new String[]{"Default", "SBC", "AAC", "aptX", "aptX HD", "LDAC", "aptX Adaptive"}; + new String[]{"Default", "SBC", "AAC", "aptX", "aptX HD", "LDAC", "aptX Adaptive", "aptX TWS+"}; /** * Helper for setting up several tests of getHighQualityAudioOptionLabel From 3ebd9036f8f959b81fa868001ad5d5400ab32424 Mon Sep 17 00:00:00 2001 From: Samyak Jain Date: Wed, 19 Jun 2019 17:34:20 +0530 Subject: [PATCH 34/92] Revert the change: AudioService: remove dead BT code. Conflicts: services/core/java/com/android/server/audio/AudioDeviceBroker.java services/core/java/com/android/server/audio/AudioService.java Change-Id: If9902d3d458b6c5ccd474ffe69133d81beb49ca9 CRs-Fixed: 2475616 --- media/java/android/media/AudioManager.java | 32 ++++++++++++ media/java/android/media/IAudioService.aidl | 3 ++ .../server/audio/AudioDeviceBroker.java | 49 +++++++++++++++---- .../server/audio/AudioDeviceInventory.java | 45 +++++++++++++++++ .../android/server/audio/AudioService.java | 21 ++++++++ 5 files changed, 140 insertions(+), 10 deletions(-) diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 38f9607c9529..1f32094fc8c1 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -5853,6 +5853,38 @@ public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) { } } + /** + * Indicate A2DP source or sink active device 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 (BluetoothProfile.STATE_xxx) + * @param profile profile for the A2DP device + * (either {@link android.bluetooth.BluetoothProfile.A2DP} or + * {@link android.bluetooth.BluetoothProfile.A2DP_SINK}) + * @param a2dpVolume New volume for the connecting device. Does nothing if + * disconnecting. Pass value -1 in case you want this field to be ignored + * @param suppressNoisyIntent if true the + * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. + * @return a delay in ms that the caller should wait before broadcasting + * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent. + * {@hide} + */ + public void handleBluetoothA2dpActiveDeviceChange( + BluetoothDevice device, int state, int profile, + boolean suppressNoisyIntent, int a2dpVolume) { + final IAudioService service = getService(); + try { + service.handleBluetoothA2dpActiveDeviceChange(device, + state, profile, suppressNoisyIntent, a2dpVolume); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** {@hide} */ public IRingtonePlayer getRingtonePlayer() { try { diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index abd067cfe2f3..5ecfe90db163 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -216,6 +216,9 @@ interface IAudioService { void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device); + void handleBluetoothA2dpActiveDeviceChange(in BluetoothDevice device, + int state, int profile, boolean suppressNoisyIntent, int a2dpVolume); + @UnsupportedAppUsage AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index b1e62483c472..c59bcb996df8 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -615,6 +615,15 @@ private void removeScheduledA2dpEvents(@NonNull BluetoothDevice device) { devInfoToRemove); } + /*package*/ void postBluetoothA2dpDeviceConfigChangeExt( + @NonNull BluetoothDevice device, + @AudioService.BtProfileConnectionState int state, int profile, + boolean suppressNoisyIntent, int a2dpVolume) { + final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile, + suppressNoisyIntent, a2dpVolume); + sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE, info); + } + private static final class HearingAidDeviceConnectionInfo { final @NonNull BluetoothDevice mDevice; final @AudioService.BtProfileConnectionState int mState; @@ -1510,6 +1519,22 @@ public void handleMessage(Message msg) { final int capturePreset = msg.arg1; mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset); } break; + case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE_EXT: { + final BtDeviceConnectionInfo info = (BtDeviceConnectionInfo) msg.obj; + AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( + "handleBluetoothA2dpActiveDeviceChangeExt " + + " 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.handleBluetoothA2dpActiveDeviceChangeExt( + info.mDevice, info.mState, info.mProfile, + info.mSupprNoisy, info.mVolume); + } + } break; default: Log.wtf(TAG, "Invalid message " + msg.what); } @@ -1577,19 +1602,22 @@ public void handleMessage(Message msg) { // process external command to (dis)connect a hearing aid device private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31; - private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 32; - private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 33; + // process external command to (dis)connect or change active A2DP device + private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE_EXT = 32; + + private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 33; + private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 34; - private static final int MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED = 34; - private static final int MSG_CHECK_MUTE_MUSIC = 35; - private static final int MSG_REPORT_NEW_ROUTES_A2DP = 36; + private static final int MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED = 35; + private static final int MSG_CHECK_MUTE_MUSIC = 36; + private static final int MSG_REPORT_NEW_ROUTES_A2DP = 37; - private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 37; - private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 38; + private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 38; + private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 39; - private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE = 39; - 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_L_UPDATE_COMMUNICATION_ROUTE = 40; + private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 41; + private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 42; private static final int MSG_L_SET_COMMUNICATION_ROUTE_FOR_CLIENT = 42; private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43; private static final int MSG_I_SCO_AUDIO_STATE_CHANGED = 44; @@ -1609,6 +1637,7 @@ private static boolean isMessageHandledUnderWakelock(int msgId) { case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: case MSG_CHECK_MUTE_MUSIC: + case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE_EXT: return true; default: return false; diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 5288a04ce86e..7366f54fc218 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -50,6 +50,7 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -917,6 +918,50 @@ public void setBluetoothA2dpDeviceConnectionState( } } + /*package*/ void handleBluetoothA2dpActiveDeviceChangeExt( + @NonNull BluetoothDevice device, + @AudioService.BtProfileConnectionState int state, int profile, + boolean suppressNoisyIntent, int a2dpVolume) { + if (state == BluetoothProfile.STATE_DISCONNECTED) { + mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( + new AudioDeviceBroker.BtDeviceConnectionInfo(device, state, profile, + suppressNoisyIntent, a2dpVolume)); + return; + } + // state == BluetoothProfile.STATE_CONNECTED + synchronized (mConnectedDevices) { + final String address = device.getAddress(); + final int a2dpCodec = mDeviceBroker.getA2dpCodec(device); + final String deviceKey = DeviceInfo.makeDeviceListKey( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); + DeviceInfo deviceInfo = mConnectedDevices.get(deviceKey); + if (deviceInfo != null) { + // Device config change for matching A2DP device + mDeviceBroker.postBluetoothA2dpDeviceConfigChange(device); + return; + } + for (Map.Entry existingDevice : mConnectedDevices.entrySet()) { + if (existingDevice.getValue().mDeviceType + != AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) { + continue; + } + // A2DP device exists, handle active device change + mConnectedDevices.remove(existingDevice.getKey()); + mConnectedDevices.put(deviceKey, new DeviceInfo( + AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, BtHelper.getName(device), + address, a2dpCodec)); + mDeviceBroker.postA2dpActiveDeviceChange( + new BtHelper.BluetoothA2dpDeviceInfo( + device, a2dpVolume, a2dpCodec)); + return; + } + } + // New A2DP device connection + mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( + new AudioDeviceBroker.BtDeviceConnectionInfo(device, state, profile, + suppressNoisyIntent, a2dpVolume)); + } + /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, String address, String name, String caller) { synchronized (mDevicesLock) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index e599cb935e92..f37eacf0369b 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -6345,6 +6345,27 @@ public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) /*obj*/ device, /*delay*/ 0); } + /** + * @see AudioManager#handleBluetoothA2dpActiveDeviceChange(BluetoothDevice, int, int, + * boolean, int) + */ + public void handleBluetoothA2dpActiveDeviceChange( + BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent, + int a2dpVolume) { + if (device == null) { + throw new IllegalArgumentException("Illegal null device"); + } + if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) { + throw new IllegalArgumentException("invalid profile " + profile); + } + if (state != BluetoothProfile.STATE_CONNECTED + && state != BluetoothProfile.STATE_DISCONNECTED) { + throw new IllegalArgumentException("Invalid state " + state); + } + mDeviceBroker.postBluetoothA2dpDeviceConfigChangeExt(device, state, profile, + suppressNoisyIntent, a2dpVolume); + } + private static final Set DEVICE_MEDIA_UNMUTED_ON_PLUG_SET; static { DEVICE_MEDIA_UNMUTED_ON_PLUG_SET = new HashSet<>(); From 03297d9033ec924216fcc080bca6dfcc660ba3b0 Mon Sep 17 00:00:00 2001 From: Bandari Ramesh Date: Sat, 31 Mar 2018 22:57:44 +0530 Subject: [PATCH 35/92] Bluetooth: Add SDK API support for TWS feature (2) - Add new SDK APIs to know the device is a TWS device and to get the peer TWS device address Conflicts: core/java/android/bluetooth/BluetoothDevice.java CRs-Fixed: 2225904 Change-Id: Ia7e3103d603659d524fcb3f42e0b026e316914c5 Signed-off-by: Volodymyr Zhdanov --- .../android/bluetooth/BluetoothDevice.java | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 65ebcc09c4c6..8754f1307452 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1,3 +1,38 @@ +/* + * Copyright (C) 2017, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted (subject to the limitations in the + * disclaimer below) provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE + * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT + * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + /* * Copyright (C) 2009 The Android Open Source Project * @@ -258,6 +293,17 @@ public final class BluetoothDevice implements Parcelable, Attributable { */ public static final int BATTERY_LEVEL_BLUETOOTH_OFF = -100; + /** + * Broadcast Action: Indicates the remote devices are TWS plus earbuds pair. + *

Always contains the extra fields {@link #EXTRA_TWS_PLUS_DEVICE1}, + * {@link #EXTRA_TWS_PLUS_DEVICE2}. + *

Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_TWS_PLUS_DEVICE_PAIR = + "android.bluetooth.device.action.TWS_PLUS_DEVICE_PAIR"; + /** * Used as a Parcelable {@link BluetoothDevice} extra field in every intent * broadcast by this class. It contains the {@link BluetoothDevice} that @@ -310,6 +356,23 @@ public final class BluetoothDevice implements Parcelable, Attributable { */ public static final String EXTRA_PREVIOUS_BOND_STATE = "android.bluetooth.device.extra.PREVIOUS_BOND_STATE"; + + /** + * Used as a String extra field in {@link #ACTION_TWS+_DEVICE_PAIR} + * intents. It contains the first TWS+ earbud address of pair. + * @hide + */ + public static final String EXTRA_TWS_PLUS_DEVICE1 = + "android.bluetooth.device.extra.EXTRA_TWS_PLUS_DEVICE1"; + + /** + * Used as a String extra field in {@link #ACTION_TWS+_DEVICE_PAIR} + * intents. It contains the second TWS+ earbud address of pair. + * @hide + */ + public static final String EXTRA_TWS_PLUS_DEVICE2 = + "android.bluetooth.device.extra.EXTRA_TWS_PLUS_DEVICE2"; + /** * Indicates the remote device is not bonded (paired). *

There is no shared link key with the remote device, so communication @@ -1845,6 +1908,41 @@ public boolean sdpSearch(ParcelUuid uuid) { return false; } + /** + * Returns whether if the device is TWS+ device. + * + * @return True if the devcie is TWS+ device. + * @hide + */ + public boolean isTwsPlusDevice() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot query remote device sdp records"); + return false; + } + try { + return sService.isTwsPlusDevice(this); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return false; + } + + /** + * Get the TWS+ peer address of the remote device. + * + * @return the TWS+ peer address of the remote device if available, otherwise + * null. + * @hide + */ + public String getTwsPlusPeerAddress() { + if (sService == null) { + Log.e(TAG, "BT not enabled. Cannot get Remote Device name"); + return null; + } + try { + return sService.getTwsPlusPeerAddress(this); + } catch (RemoteException e) {Log.e(TAG, "", e);} + return null; + } + /** * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN} * From cea05bf626d398beea1d215ec3dfe3fdbbd59e51 Mon Sep 17 00:00:00 2001 From: Kiran Kelageri Date: Thu, 5 Apr 2018 00:18:10 -0700 Subject: [PATCH 36/92] Bluetooth: Unpair both earbuds on unpair. 1> On unpair initiation to primary, Initiate Unpair for peer secondary TWS device as well. 2> On pairing to primary earbud, Initiate connection to second earbud. [wight554: updated code from LA.QSSI.11.0.r1-05600-qssi.0] Conflicts: packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java CRs-fixed: 2230362 Change-Id: I0d0e7c3bc66d4ff24a2b767996bc1367022901d6 Signed-off-by: Volodymyr Zhdanov --- .../bluetooth/CachedBluetoothDevice.java | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 7beb82457e18..be86ee38e5c3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -33,6 +33,7 @@ import android.os.Message; import android.os.ParcelUuid; import android.os.SystemClock; +import android.os.SystemProperties; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; @@ -157,6 +158,22 @@ protected int sizeOf(String key, BitmapDrawable bitmap) { }; } + /* Gets Device for seondary TWS device + * @param mDevice Primary TWS device to get secondary + * @return Description of the device + */ + + private BluetoothDevice getTwsPeerDevice() { + BluetoothAdapter bluetoothAdapter; + BluetoothDevice peerDevice = null; + if (mDevice.isTwsPlusDevice()) { + bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + String peerAddress = mDevice.getTwsPlusPeerAddress(); + peerDevice = bluetoothAdapter.getRemoteDevice(peerAddress); + } + return peerDevice; + } + /** * Describes the current device and profile for logging. * @@ -402,6 +419,17 @@ public void unpair() { if (state != BluetoothDevice.BOND_NONE) { final BluetoothDevice dev = mDevice; + if (mDevice.isTwsPlusDevice()) { + BluetoothDevice peerDevice = getTwsPeerDevice(); + if (peerDevice != null) { + final boolean peersuccessful = peerDevice.removeBond(); + if (peersuccessful) { + if (Utils.D) { + Log.d(TAG, "Command sent successfully:REMOVE_BOND " + peerDevice.getName()); + } + } + } + } if (dev != null) { mUnpairing = true; final boolean successful = dev.removeBond(); @@ -745,8 +773,15 @@ void onBondingStateChanged(int bondState) { refresh(); - if (bondState == BluetoothDevice.BOND_BONDED && mDevice.isBondingInitiatedLocally()) { - connect(); + if (bondState == BluetoothDevice.BOND_BONDED) { + if (SystemProperties.getBoolean("persist.vendor.bt.connect.peer_earbud", true)) { + Log.d(TAG, "Initiating connection to" + mDevice); + if (mDevice.isBondingInitiatedLocally() || mDevice.isTwsPlusDevice()) { + connect(); + } + } else if (mDevice.isBondingInitiatedLocally()) { + connect(); + } } } From 504378fdbd60e283f17f56c20c6007d12dc99b10 Mon Sep 17 00:00:00 2001 From: Safoorah Banu Shaik Date: Thu, 5 Jul 2018 18:56:42 +0530 Subject: [PATCH 37/92] Bluetooth: Rename bluetooth property to support new P Treble rules - Renamed the property persist.vendor.bt.connect.peer_earbud to persist.vendor.btstack.connect.peer_earbud to support new P Treble rules. Conflicts: packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java CRs-Fixed: 2277397 Change-Id: I45dc740a45195154e2fe2a88a0384181fd0c7907 Signed-off-by: Volodymyr Zhdanov --- .../android/settingslib/bluetooth/CachedBluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index be86ee38e5c3..00e206c5e312 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -774,7 +774,7 @@ void onBondingStateChanged(int bondState) { refresh(); if (bondState == BluetoothDevice.BOND_BONDED) { - if (SystemProperties.getBoolean("persist.vendor.bt.connect.peer_earbud", true)) { + if (SystemProperties.getBoolean("persist.vendor.btstack.connect.peer_earbud", true)) { Log.d(TAG, "Initiating connection to" + mDevice); if (mDevice.isBondingInitiatedLocally() || mDevice.isTwsPlusDevice()) { connect(); From c516ffcd5e042e3950578765407b74b5058e3ee3 Mon Sep 17 00:00:00 2001 From: Kiran Kelageri Date: Wed, 22 Aug 2018 12:34:22 -0700 Subject: [PATCH 38/92] Bluetooth-TWS: Disable outgoing connection for 2nd earbud. Change ensures outgoing connection for second earbud on pairing to disabled by default. if needed can be enabled by setting property. Conflicts: packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java CRs-fixed: 2300140 Change-Id: Id00fdb30825aeee1e6adff55c767e94df96d52b5 Signed-off-by: Volodymyr Zhdanov --- .../android/settingslib/bluetooth/CachedBluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 00e206c5e312..ccd6a544eb32 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -774,7 +774,7 @@ void onBondingStateChanged(int bondState) { refresh(); if (bondState == BluetoothDevice.BOND_BONDED) { - if (SystemProperties.getBoolean("persist.vendor.btstack.connect.peer_earbud", true)) { + if (SystemProperties.getBoolean("persist.vendor.btstack.connect.peer_earbud", false)) { Log.d(TAG, "Initiating connection to" + mDevice); if (mDevice.isBondingInitiatedLocally() || mDevice.isTwsPlusDevice()) { connect(); From 79aa5467b0d1784f873492791e0309aeca5fabf2 Mon Sep 17 00:00:00 2001 From: Kiran Kelageri Date: Thu, 4 Oct 2018 17:52:31 -0700 Subject: [PATCH 39/92] Bluetooth-TWS: Disable outgoing connection to 2nd earbud Disbaling outgoing connection to 2nd earbud and removed vendor specific property. Conflicts: packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java Change-Id: I912c3acd84f449b6f967d6ef2c7beaebca4ef05c CRs-fixed: 2326015 Signed-off-by: Volodymyr Zhdanov --- .../android/settingslib/bluetooth/CachedBluetoothDevice.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index ccd6a544eb32..08faa551e985 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -33,7 +33,6 @@ import android.os.Message; import android.os.ParcelUuid; import android.os.SystemClock; -import android.os.SystemProperties; import android.text.TextUtils; import android.util.EventLog; import android.util.Log; @@ -68,6 +67,7 @@ public class CachedBluetoothDevice implements Comparable private static final long MAX_HEARING_AIDS_DELAY_FOR_AUTO_CONNECT = 15000; private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000; private static final long MAX_MEDIA_PROFILE_CONNECT_DELAY = 60000; + private static final boolean mIsTwsConnectEnabled = false; private final Context mContext; private final BluetoothAdapter mLocalAdapter; @@ -774,7 +774,7 @@ void onBondingStateChanged(int bondState) { refresh(); if (bondState == BluetoothDevice.BOND_BONDED) { - if (SystemProperties.getBoolean("persist.vendor.btstack.connect.peer_earbud", false)) { + if (mIsTwsConnectEnabled) { Log.d(TAG, "Initiating connection to" + mDevice); if (mDevice.isBondingInitiatedLocally() || mDevice.isTwsPlusDevice()) { connect(); From 883848e7677a6f99acb134cb46289d14645da258 Mon Sep 17 00:00:00 2001 From: Venkata Jagadeesh Garaga Date: Thu, 27 Jun 2019 10:44:00 +0530 Subject: [PATCH 40/92] GAP: Reset bondingInitiatedLocally flag(1/3) When Pairing initiated from DUT, bondingIinitiatedLocally flag sets from adapterservice. If user disable profile options from UI and tries to connect from remote,DUT rejects the incoming connection, when dut rejects connection some remotes may initiates pairing again. Profile options should be in disable state after incoming pairing successful as user disabled profile options, but profile options getting enabled as bondingInitiatedLocally not reset. Hence Reset bondingInitiatedLocally flag after connection Conflicts: packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java Change-Id: I612b0718fcc4f6aaeded112fee4cb51ad7f25772 CRs-Fixed: 2457374 Signed-off-by: Volodymyr Zhdanov --- core/java/android/bluetooth/BluetoothDevice.java | 16 ++++++++++++++++ .../bluetooth/CachedBluetoothDevice.java | 9 +++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 8754f1307452..80f5ec7c01e3 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1600,6 +1600,22 @@ public boolean isBondingInitiatedLocally() { return false; } + /** @hide */ + @UnsupportedAppUsage + public void setBondingInitiatedLocally(boolean localInitiated) { + final IBluetooth service = sService; + if (service == null) { + Log.w(TAG, "BT not enabled, setBondingInitiatedLocally failed"); + return; + } + try { + service.setBondingInitiatedLocally(this, localInitiated); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + return; + } + /** * Cancel an in-progress bonding request started with {@link #createBond}. * diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 08faa551e985..2e2cd18e84df 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -774,12 +774,17 @@ void onBondingStateChanged(int bondState) { refresh(); if (bondState == BluetoothDevice.BOND_BONDED) { + boolean mIsBondingInitiatedLocally = mDevice.isBondingInitiatedLocally(); + Log.w(TAG, "mIsBondingInitiatedLocally" + mIsBondingInitiatedLocally); + if (mIsBondingInitiatedLocally) { + mDevice.setBondingInitiatedLocally(false); + } if (mIsTwsConnectEnabled) { Log.d(TAG, "Initiating connection to" + mDevice); - if (mDevice.isBondingInitiatedLocally() || mDevice.isTwsPlusDevice()) { + if (mIsBondingInitiatedLocally || mDevice.isTwsPlusDevice()) { connect(); } - } else if (mDevice.isBondingInitiatedLocally()) { + } else if (mIsBondingInitiatedLocally) { connect(); } } From a3e54a92e8057cd3a76317711ed2ad7b8572f9f1 Mon Sep 17 00:00:00 2001 From: Sumit Bajpai Date: Mon, 22 Feb 2016 19:41:33 +0530 Subject: [PATCH 41/92] BT: Send info if call is CS type from telecomm service to BT apps. The change adds broadcast intent for updating call info from telecomm service to bluetooth application. CRs-Fixed: 2260445 Change-Id: I18d3a22d25099906d86efb0412f309696c02e321 --- .../java/android/telecom/TelecomManager.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 4cdeb0b07187..0e7a2695ebf6 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -189,6 +189,17 @@ public class TelecomManager { public static final String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED"; + /** + *@hide Broadcast intent action indicating the call type(CS call or Non-CS call). + * The string extra {@link #EXTRA_CALL_TYPE_CS} will contain the + * boolean value true if call is CS call else false. + * + * @see #EXTRA_CALL_TYPE_CS + */ + public static final String ACTION_CALL_TYPE = + "codeaurora.telecom.action.CALL_TYPE"; + + /** * Extra value used to provide the package name for {@link #ACTION_CHANGE_DEFAULT_DIALER}. */ @@ -479,6 +490,12 @@ public class TelecomManager { public static final String EXTRA_CALL_NETWORK_TYPE = "android.telecom.extra.CALL_NETWORK_TYPE"; + /** + *@hide Extra value used to provide the call type for {@link #ACTION_CALL_TYPE}. + */ + public static final String EXTRA_CALL_TYPE_CS = + "codeaurora.telecom.extra.CALL_TYPE_CS"; + /** * An optional {@link android.content.Intent#ACTION_CALL} intent extra denoting the * package name of the app specifying an alternative gateway for the call. From 6c4ace78e0cee548a9a6b0cca2c795b23682a16a Mon Sep 17 00:00:00 2001 From: Bhakthavatsala Raghavendra Date: Fri, 3 May 2019 15:50:18 -0700 Subject: [PATCH 42/92] Bluetooth: TWSP: Support Battery Status information display Reference changes to use the Battery status Intent and display in Settings UI Conflicts: packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java CRs-fixed: 2467643 Change-Id: Ibc81611529df1d1c8ff0453768eb494a8957b9c2 Signed-off-by: Volodymyr Zhdanov --- .../android/bluetooth/BluetoothHeadset.java | 35 +++++++++++++++++++ core/res/AndroidManifest.xml | 2 ++ .../bluetooth/BluetoothEventManager.java | 20 +++++++++++ .../bluetooth/CachedBluetoothDevice.java | 33 ++++++++++++++--- .../CachedBluetoothDeviceManager.java | 3 ++ 5 files changed, 89 insertions(+), 4 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index b594ae34436e..59ebd21f7eaa 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -293,6 +293,41 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public static final int STATE_AUDIO_CONNECTED = 12; + /** + * Intent used to broadcast the Battery status of TWS+ devices + * + *

This intent will have 2 extras: + *

    + *
  • {@link #EXTRA_HF_TWSP_BATTERY_STATE} - Current Battey state of TWS+ + * device. 0 for Discharging, 1 for Charging + * <\li> + *
  • {@link #EXTRA_HF_TWSP_BATTERY_LEVEL} - Current Battey charging level + * in percentage of TWS+ device. + * <\li> + * + * @hide + */ + public static final String ACTION_HF_TWSP_BATTERY_STATE_CHANGED = + "android.bluetooth.headset.action.HF_TWSP_BATTERY_STATE_CHANGED"; + + /** + * A int extra field in {@link #EXTRA_HF_TWSP_BATTERY_STATE} + * intents that contains the battery state of TWS+ device + * + * @hide + */ + public static final String EXTRA_HF_TWSP_BATTERY_STATE = + "android.bluetooth.headset.extra.HF_TWSP_BATTERY_STATE"; + + /** + * A int extra field in {@link #EXTRA_HF_TWSP_BATTERY_LEVEL} + * intents that contains the value of battery level in percentage for TWS+ device + * @hide + */ + public static final String EXTRA_HF_TWSP_BATTERY_LEVEL = + "android.bluetooth.headset.extra.HF_TWSP_BATTERY_LEVEL"; + + /** * Intent used to broadcast the headset's indicator status * diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1bb9f2bb6c89..66743cc7d20b 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -185,6 +185,8 @@ android:name="android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED" /> + private final Collection mCallbacks = new CopyOnWriteArrayList<>(); + public int mTwspBatteryState; + public int mTwspBatteryLevel; /** * Last time a bt profile auto-connect was attempted. * If an ACTION_UUID intent comes in within @@ -144,6 +146,8 @@ public void handleMessage(Message msg) { fillData(); mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID; initDrawableCache(); + mTwspBatteryState = -1; + mTwspBatteryLevel = -1; } private void initDrawableCache() { @@ -256,6 +260,10 @@ void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) { mProfiles.remove(profile); mRemovedProfiles.add(profile); mLocalNapRoleConnected = false; + } else if (profile instanceof HeadsetProfile + && newProfileState == BluetoothProfile.STATE_DISCONNECTED) { + mTwspBatteryState = -1; + mTwspBatteryLevel = -1; } } @@ -1021,11 +1029,28 @@ public String getConnectionSummary(boolean shortSummary) { // BluetoothDevice.BATTERY_LEVEL_BLUETOOTH_OFF, or BluetoothDevice.BATTERY_LEVEL_UNKNOWN, // any other value should be a framework bug. Thus assume here that if value is greater // than BluetoothDevice.BATTERY_LEVEL_UNKNOWN, it must be valid - final int batteryLevel = getBatteryLevel(); - if (batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { - // TODO: name com.android.settingslib.bluetooth.Utils something different - batteryLevelPercentageString = + + if (mDevice.isTwsPlusDevice() && mTwspBatteryState != -1 && + mTwspBatteryLevel != -1) { + String s = "TWSP: "; + String chargingState; + if (mTwspBatteryState == 1) { + chargingState = "Charging, "; + } else { + chargingState = "Discharging, "; + } + s = s.concat (chargingState); + s = s.concat( + com.android.settingslib.Utils.formatPercentage(mTwspBatteryLevel)); + batteryLevelPercentageString = s; + Log.i(TAG, "UI string" + batteryLevelPercentageString); + } else { + final int batteryLevel = getBatteryLevel(); + if (batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { + // TODO: name com.android.settingslib.bluetooth.Utils something different + batteryLevelPercentageString = com.android.settingslib.Utils.formatPercentage(batteryLevel); + } } int stringRes = R.string.bluetooth_pairing; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java index cca9cfac2d22..7c8cc8cb52df 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java @@ -224,6 +224,9 @@ public synchronized void onBluetoothStateChanged(int bluetoothState) { cachedDevice.setJustDiscovered(false); mCachedDevices.remove(i); } + //Clear if there any Tws battery info on BT turning OFF + cachedDevice.mTwspBatteryState = -1; + cachedDevice.mTwspBatteryLevel = -1; } } } From 3583f2677d3e4a9afdf61f578fed4402117b77b9 Mon Sep 17 00:00:00 2001 From: Subramanian Srinivasan Date: Sun, 24 Jun 2018 00:15:35 -0700 Subject: [PATCH 43/92] Add support for Transport discovery AD type (1/2) Add support for Transport discovery AD type in BLE advertisement data. CRs-Fixed: 2299975 Change-Id: I76ef89856746ea5d88af77142a38398b33393449 --- .../android/bluetooth/le/AdvertiseData.java | 45 ++++++++++++++++--- .../bluetooth/le/BluetoothLeUtils.java | 41 +++++++++++++++++ 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java index cec658049ca5..b5a752f39520 100644 --- a/core/java/android/bluetooth/le/AdvertiseData.java +++ b/core/java/android/bluetooth/le/AdvertiseData.java @@ -51,19 +51,22 @@ public final class AdvertiseData implements Parcelable { private final Map mServiceData; private final boolean mIncludeTxPowerLevel; private final boolean mIncludeDeviceName; + private final byte[] mTransportDiscoveryData; private AdvertiseData(List serviceUuids, List serviceSolicitationUuids, SparseArray manufacturerData, Map serviceData, boolean includeTxPowerLevel, - boolean includeDeviceName) { + boolean includeDeviceName, + byte[] transportDiscoveryData) { mServiceUuids = serviceUuids; mServiceSolicitationUuids = serviceSolicitationUuids; mManufacturerSpecificData = manufacturerData; mServiceData = serviceData; mIncludeTxPowerLevel = includeTxPowerLevel; mIncludeDeviceName = includeDeviceName; + mTransportDiscoveryData = transportDiscoveryData; } /** @@ -111,13 +114,21 @@ public boolean getIncludeDeviceName() { return mIncludeDeviceName; } + /** + * Returns an array of Transport Discovery data. + * @hide + */ + public byte[] getTransportDiscoveryData() { + return mTransportDiscoveryData; + } + /** * @hide */ @Override public int hashCode() { return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mManufacturerSpecificData, - mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); + mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel, mTransportDiscoveryData); } /** @@ -138,7 +149,8 @@ public boolean equals(@Nullable Object obj) { other.mManufacturerSpecificData) && BluetoothLeUtils.equals(mServiceData, other.mServiceData) && mIncludeDeviceName == other.mIncludeDeviceName - && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel; + && mIncludeTxPowerLevel == other.mIncludeTxPowerLevel + && BluetoothLeUtils.equals(mTransportDiscoveryData, other.mTransportDiscoveryData); } @Override @@ -148,7 +160,8 @@ public String toString() { + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData) + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" - + mIncludeDeviceName + "]"; + + mIncludeDeviceName + ", mTransportDiscoveryData=" + + BluetoothLeUtils.toString(mTransportDiscoveryData)+ "]"; } @Override @@ -175,6 +188,10 @@ public void writeToParcel(Parcel dest, int flags) { } dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); + if(mTransportDiscoveryData != null) { + dest.writeInt(mTransportDiscoveryData.length); + dest.writeByteArray(mTransportDiscoveryData); + } } public static final @android.annotation.NonNull Parcelable.Creator CREATOR = @@ -211,6 +228,11 @@ public AdvertiseData createFromParcel(Parcel in) { } builder.setIncludeTxPowerLevel(in.readByte() == 1); builder.setIncludeDeviceName(in.readByte() == 1); + int transportDiscoveryDataSize = in.readInt(); + if (transportDiscoveryDataSize > 0) { + byte[] transportDiscoveryData = in.createByteArray(); + builder.addTransportDiscoveryData(transportDiscoveryData); + } return builder.build(); } }; @@ -227,6 +249,7 @@ public static final class Builder { private Map mServiceData = new ArrayMap(); private boolean mIncludeTxPowerLevel; private boolean mIncludeDeviceName; + private byte[] mTransportDiscoveryData; /** * Add a service UUID to advertise data. @@ -314,13 +337,25 @@ public Builder setIncludeDeviceName(boolean includeDeviceName) { return this; } + /** + * Add Transport Discovery data + * @hide + */ + public Builder addTransportDiscoveryData(byte[] transportDiscoveryData) { + if ((transportDiscoveryData == null) || (transportDiscoveryData.length == 0)) { + throw new IllegalArgumentException("transportDiscoveryData is null"); + } + mTransportDiscoveryData = transportDiscoveryData; + return this; + } + /** * Build the {@link AdvertiseData}. */ public AdvertiseData build() { return new AdvertiseData(mServiceUuids, mServiceSolicitationUuids, mManufacturerSpecificData, mServiceData, mIncludeTxPowerLevel, - mIncludeDeviceName); + mIncludeDeviceName, mTransportDiscoveryData); } } } diff --git a/core/java/android/bluetooth/le/BluetoothLeUtils.java b/core/java/android/bluetooth/le/BluetoothLeUtils.java index 6381f557c1b2..f8d1c517540d 100644 --- a/core/java/android/bluetooth/le/BluetoothLeUtils.java +++ b/core/java/android/bluetooth/le/BluetoothLeUtils.java @@ -76,6 +76,28 @@ static String toString(Map map) { return buffer.toString(); } + /** + * Returns a string composed from a byte array. + */ + static String toString(byte[] data) { + if (data == null) { + return "null"; + } + if (data.length == 0) { + return "{}"; + } + StringBuilder buffer = new StringBuilder(); + buffer.append('{'); + for(int i=0; i < data.length; i++) { + buffer.append(data[i]); + if ((i+1) < data.length) { + buffer.append(", "); + } + } + buffer.append('}'); + return buffer.toString(); + } + /** * Check whether two {@link SparseArray} equal. */ @@ -125,6 +147,25 @@ static boolean equals(Map map, Map otherMap) { return true; } + /** + * Check whether two byte arrays are equal. + */ + static boolean equals(byte[] data, byte[] otherData) { + if (data == otherData) { + return true; + } + if (data == null || otherData == null) { + return false; + } + if (data.length != otherData.length) { + return false; + } + if (!Objects.deepEquals(data, otherData)) { + return false; + } + return true; + } + /** * Ensure Bluetooth is turned on. * From 201cd9e861b0a2b325c55d376680afe236193ef8 Mon Sep 17 00:00:00 2001 From: Mingbo Zhang Date: Thu, 10 May 2018 17:51:38 +0800 Subject: [PATCH 44/92] Support CALLBACK_TYPE_SENSOR_ROUTING Add support to CALLBACK_TYPE_SENSOR_ROUTING when start scanning Change-Id: I958938b2e428b2ba68297d22eb829b4f94deab30 CRs-Fixed: 2095995 --- .../bluetooth/le/BluetoothLeScanner.java | 22 +++++++++++++++++++ .../android/bluetooth/le/ScanSettings.java | 9 +++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java index 34aac8bfdb25..2afe24f1439a 100644 --- a/core/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java @@ -38,6 +38,7 @@ import android.os.WorkSource; import android.util.Log; +import java.util.Arrays; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -260,6 +261,13 @@ private int startScan(List filters, ScanSettings settings, if (gatt == null) { return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR); } + + if ((settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_SENSOR_ROUTING) + && (filters == null || filters.isEmpty())) { + ScanFilter filter = (new ScanFilter.Builder()).build(); + filters = Arrays.asList(filter); + } + if (!isSettingsConfigAllowedForScan(settings)) { return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); @@ -272,6 +280,10 @@ private int startScan(List filters, ScanSettings settings, return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); } + if (!isRoutingAllowedForScan(settings)) { + return postCallbackErrorOrReturn(callback, + ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED); + } if (callback != null) { BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, settings, workSource, callback, resultStorages); @@ -648,4 +660,14 @@ private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) { } return true; } + + private boolean isRoutingAllowedForScan(ScanSettings settings) { + final int callbackType = settings.getCallbackType(); + + if (callbackType == ScanSettings.CALLBACK_TYPE_SENSOR_ROUTING + && settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC) { + return false; + } + return true; + } } diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java index 1aa7cb5111ce..f946cadc7311 100644 --- a/core/java/android/bluetooth/le/ScanSettings.java +++ b/core/java/android/bluetooth/le/ScanSettings.java @@ -79,6 +79,12 @@ public final class ScanSettings implements Parcelable { */ public static final int CALLBACK_TYPE_MATCH_LOST = 4; + /** + * Provide results to sensor router instead of the apps processor + * @hide + */ + public static final int CALLBACK_TYPE_SENSOR_ROUTING = 8; + /** * Determines how many advertisements to match per filter, as this is scarce hw resource @@ -319,7 +325,8 @@ public Builder setCallbackType(int callbackType) { private boolean isValidCallbackType(int callbackType) { if (callbackType == CALLBACK_TYPE_ALL_MATCHES || callbackType == CALLBACK_TYPE_FIRST_MATCH - || callbackType == CALLBACK_TYPE_MATCH_LOST) { + || callbackType == CALLBACK_TYPE_MATCH_LOST + || callbackType == CALLBACK_TYPE_SENSOR_ROUTING) { return true; } return callbackType == (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST); From 687058d761045c89ba6c29681c5a38912a5e202a Mon Sep 17 00:00:00 2001 From: Subramanian Srinivasan Date: Fri, 1 Mar 2019 11:34:44 -0800 Subject: [PATCH 45/92] Add scan filter for Transport Discovery data Add scan filter support for Transport Discovery data. CRs-Fixed: 2418407 Change-Id: I84b791d7424620bc7db5b19e8f6a14938b1526af --- .../java/android/bluetooth/le/ScanFilter.java | 128 +++++++++++++++++- .../java/android/bluetooth/le/ScanRecord.java | 27 +++- 2 files changed, 146 insertions(+), 9 deletions(-) diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java index 8ff018121ab0..9459c4b3ec83 100644 --- a/core/java/android/bluetooth/le/ScanFilter.java +++ b/core/java/android/bluetooth/le/ScanFilter.java @@ -51,6 +51,12 @@ */ public final class ScanFilter implements Parcelable { + /** + * Provide TDS data scan results for WiFi Alliance Org id + * @hide + */ + public static final int WIFI_ALLIANCE_ORG_ID = 2; + @Nullable private final String mDeviceName; @@ -85,6 +91,11 @@ public final class ScanFilter implements Parcelable { @Nullable private final byte[] mManufacturerDataMask; + private final int mOrgId; + private final int mTDSFlags; + private final int mTDSFlagsMask; + private final byte[] mWifiNANHash; + /** @hide */ public static final ScanFilter EMPTY = new ScanFilter.Builder().build(); @@ -93,7 +104,8 @@ private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, ParcelUuid solicitationUuidMask, ParcelUuid serviceDataUuid, byte[] serviceData, byte[] serviceDataMask, int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask, - @AddressType int addressType, @Nullable byte[] irk) { + @AddressType int addressType, @Nullable byte[] irk, + int orgId, int TDSFlags, int TDSFlagsMask, byte[] wifiNANHash) { mDeviceName = name; mServiceUuid = uuid; mServiceUuidMask = uuidMask; @@ -108,6 +120,10 @@ private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, mManufacturerDataMask = manufacturerDataMask; mAddressType = addressType; mIrk = irk; + mOrgId = orgId; + mTDSFlags = TDSFlags; + mTDSFlagsMask = TDSFlagsMask; + mWifiNANHash = wifiNANHash; } @Override @@ -177,6 +193,15 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeByteArray(mIrk); } } + + dest.writeInt(mOrgId); + dest.writeInt(mTDSFlags); + dest.writeInt(mTDSFlagsMask); + dest.writeInt(mWifiNANHash == null ? 0 : 1); + if (mWifiNANHash != null) { + dest.writeInt(mWifiNANHash.length); + dest.writeByteArray(mWifiNANHash); + } } /** @@ -267,6 +292,21 @@ public ScanFilter createFromParcel(Parcel in) { builder.setDeviceAddress(address, addressType); } } + + int orgId = in.readInt(); + int TDSFlags = in.readInt(); + int TDSFlagsMask = in.readInt(); + if (in.readInt() == 1) { + int wifiNANHashLength = in.readInt(); + byte[] wifiNanHash = new byte[wifiNANHashLength]; + in.readByteArray(wifiNanHash); + builder.setTransportDiscoveryData(orgId, TDSFlags, TDSFlagsMask, + wifiNanHash); + } + else { + builder.setTransportDiscoveryData(orgId, TDSFlags, TDSFlagsMask, null); + } + return builder.build(); } }; @@ -362,6 +402,37 @@ public byte[] getManufacturerDataMask() { return mManufacturerDataMask; } + /** + * @hide + * Returns the organization id. -1 if the organization id is not set. + */ + public int getOrgId() { + return mOrgId; + } + + /** + * @hide + * Returns the TDS flags. -1 if TDS flags is not set. + */ + public int getTDSFlags() { + return mTDSFlags; + } + + /** + * @hide + * Returns the TDS flags mask. -1 if TDS flags mask is not set. + */ + public int getTDSFlagsMask() { + return mTDSFlagsMask; + } + + /** + * @hide + */ + public byte[] getWifiNANHash() { + return mWifiNANHash; + } + /** * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match * if it matches all the field filters. @@ -419,6 +490,18 @@ public boolean matches(ScanResult scanResult) { return false; } } + + //Transport Discovery data match + if(mOrgId >= 0) { + byte[] tdsData = scanRecord.getTDSData(); + if ((tdsData != null) && (tdsData.length > 0)) { + if ((mOrgId != tdsData[0]) || + ((mTDSFlags & mTDSFlagsMask) != (tdsData[1] & mTDSFlagsMask))) { + return false; + } + } + } + // All filters match. return true; } @@ -513,7 +596,10 @@ public String toString() { + Arrays.toString(mServiceData) + ", mServiceDataMask=" + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId + ", mManufacturerData=" + Arrays.toString(mManufacturerData) - + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + "]"; + + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + + ", mOrganizationId=" + mOrgId + ", mTDSFlags=" + mTDSFlags + + ", mTDSFlagsMask=" + mTDSFlagsMask + + ", mWifiNANHash=" + Arrays.toString(mWifiNANHash) +"]"; } @Override @@ -525,7 +611,8 @@ public int hashCode() { Arrays.hashCode(mServiceData), Arrays.hashCode(mServiceDataMask), mServiceUuid, mServiceUuidMask, - mServiceSolicitationUuid, mServiceSolicitationUuidMask); + mServiceSolicitationUuid, mServiceSolicitationUuidMask, + mOrgId, mTDSFlags, mTDSFlagsMask, Arrays.hashCode(mWifiNANHash)); } @Override @@ -549,7 +636,11 @@ public boolean equals(@Nullable Object obj) { && Objects.equals(mServiceUuidMask, other.mServiceUuidMask) && Objects.equals(mServiceSolicitationUuid, other.mServiceSolicitationUuid) && Objects.equals(mServiceSolicitationUuidMask, - other.mServiceSolicitationUuidMask); + other.mServiceSolicitationUuidMask) + && mOrgId == other.mOrgId + && mTDSFlags == other.mTDSFlags + && mTDSFlagsMask == other.mTDSFlagsMask + && Objects.deepEquals(mWifiNANHash, other.mWifiNANHash); } /** @@ -591,6 +682,11 @@ public static final class Builder { private byte[] mManufacturerData; private byte[] mManufacturerDataMask; + private int mOrgId = -1; + private int mTDSFlags = -1; + private int mTDSFlagsMask = -1; + private byte[] mWifiNANHash; + /** * Set filter on device name. */ @@ -895,6 +991,27 @@ public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData, return this; } + + /** + * @hide + * Set filter on transport discovery data. + * @throws IllegalArgumentException If the {@code orgId} is invalid or {@code + * wifiNANhash} is not null while {@code orgId} is non-Wifi. + */ + public Builder setTransportDiscoveryData(int orgId, int TDSFlags, int TDSFlagsMask, + byte[] wifiNANHash) { + if (orgId < 0) { + throw new IllegalArgumentException("invalid organization id"); + } + if ((orgId != WIFI_ALLIANCE_ORG_ID) && (wifiNANHash != null)) { + throw new IllegalArgumentException("Wifi NAN Hash is not null for non-Wifi Org Id"); + } + mOrgId = orgId; + mTDSFlags = TDSFlags; + mTDSFlagsMask = TDSFlagsMask; + mWifiNANHash = wifiNANHash; + return this; + } /** * Build {@link ScanFilter}. * @@ -906,7 +1023,8 @@ public ScanFilter build() { mServiceSolicitationUuidMask, mServiceDataUuid, mServiceData, mServiceDataMask, mManufacturerId, mManufacturerData, mManufacturerDataMask, - mAddressType, mIrk); + mAddressType, mIrk, + mOrgId, mTDSFlags, mTDSFlagsMask, mWifiNANHash); } } } diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java index 9b8c2eaf4d19..da96b142ecd1 100644 --- a/core/java/android/bluetooth/le/ScanRecord.java +++ b/core/java/android/bluetooth/le/ScanRecord.java @@ -59,6 +59,7 @@ public final class ScanRecord { private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_32_BIT = 0x1F; private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT = 0x15; private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; + private static final int DATA_TYPE_TRANSPORT_DISCOVERY_DATA = 0x26; // Flags of the advertising data. private final int mAdvertiseFlags; @@ -81,6 +82,9 @@ public final class ScanRecord { // Raw bytes of scan record. private final byte[] mBytes; + // Transport Discovery data. + private final byte[] mTDSData; + /** * Returns the advertising flags indicating the discoverable mode and capability of the device. * Returns -1 if the flag field is not set. @@ -164,6 +168,14 @@ public String getDeviceName() { return mDeviceName; } + /** + * @hide + * Returns Transport Discovery data + */ + public byte[] getTDSData() { + return mTDSData; + } + /** * Returns raw bytes of scan record. */ @@ -197,7 +209,7 @@ private ScanRecord(List serviceUuids, SparseArray manufacturerData, Map serviceData, int advertiseFlags, int txPowerLevel, - String localName, byte[] bytes) { + String localName, byte[] tdsData, byte[] bytes) { mServiceSolicitationUuids = serviceSolicitationUuids; mServiceUuids = serviceUuids; mManufacturerSpecificData = manufacturerData; @@ -205,6 +217,7 @@ private ScanRecord(List serviceUuids, mDeviceName = localName; mAdvertiseFlags = advertiseFlags; mTxPowerLevel = txPowerLevel; + mTDSData = tdsData; mBytes = bytes; } @@ -235,6 +248,8 @@ public static ScanRecord parseFromBytes(byte[] scanRecord) { SparseArray manufacturerData = new SparseArray(); Map serviceData = new ArrayMap(); + byte[] tdsData = null; + try { while (currentPos < scanRecord.length) { // length is unsigned int. @@ -312,6 +327,9 @@ public static ScanRecord parseFromBytes(byte[] scanRecord) { dataLength - 2); manufacturerData.put(manufacturerId, manufacturerDataBytes); break; + case DATA_TYPE_TRANSPORT_DISCOVERY_DATA: + tdsData = extractBytes(scanRecord, currentPos, dataLength); + break; default: // Just ignore, we don't handle such data type. break; @@ -323,12 +341,12 @@ public static ScanRecord parseFromBytes(byte[] scanRecord) { serviceUuids = null; } return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData, - serviceData, advertiseFlag, txPowerLevel, localName, scanRecord); + serviceData, advertiseFlag, txPowerLevel, localName, tdsData, scanRecord); } catch (Exception e) { Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord)); // As the record is invalid, ignore all the parsed results for this packet // and return an empty record with raw scanRecord bytes in results - return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null, scanRecord); + return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null, null, scanRecord); } } @@ -339,7 +357,8 @@ public String toString() { + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString( mManufacturerSpecificData) + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData) - + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]"; + + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + + ", mTDSData=" + BluetoothLeUtils.toString(mTDSData) +"]"; } // Parse service UUIDs. From da221d9ce80b48fca1e308f226205a55bd5594af Mon Sep 17 00:00:00 2001 From: Sravan Kumar V Date: Thu, 1 Feb 2018 15:51:00 +0530 Subject: [PATCH 46/92] Obex: Squashed commit of the following Obex : Reduce MTU Size. Use case: 1. Start Low Latency and High Tx Power adv from the DUT 2. From a 2nd DUT scan, connect and pair with the LEP DUT 3. Connect the DUT to a SCO HS 4. Connect the DUT to 2 BR/EDR HID devices 5. Start MO call from the DUT going over SCO 6. Start sending a file from Phone over OPP to the DUT Result: After a while the OPP link disconnects reason: "Connection Timeout" Fix: Reduce Obex MTU to 8k if reduce MTU is enable Change-Id: I21cb27ff98e07cd923d6e28a67b3b52835b68956 OBEX: Dynamic VERBOSE level logging for OBEX. - Support runtime VERBOSE level logging control for OBEX lib. - Added extra informative logs useful to debug issues from Obex layer. Change-Id: If94c88b438b3c283aca0ba4cc46f42bb1d8c37eb OBEX : Handle Negative index Exception Use case: 1. Send file to remote device. 2. Wait for accepting the file transfer on remote device. Use Specific remote device(that sends some optional headers). Failure: No file acceptance popup seen on remote device. Root cause: Failure in com.android.bluetooth. FATAL EXCEPTION: BtOpp ClientThread Process: com.android.bluetooth, PID: 22527 java.lang.NegativeArraySizeException: -3 at javax.obex.ObexHelper.updateHeaderSet(ObexHelper.java:216) at javax.obex.ClientSession.sendRequest(ClientSession.java:568) at javax.obex.ClientSession.connect(ClientSession.java:148) at com.android.bluetooth.opp.BluetoothOppObexClientSession$ClientThread. connect(BluetoothOppObexClientSession.java:317) at com.android.bluetooth.opp.BluetoothOppObexClientSession$ClientThread. run(BluetoothOppObexClientSession.java:231) am_craash( 1402): [22527,0,com.android.bluetooth,818462277,java.lang. NegativeArraySizeException,-3,ObexHelper.java,216] Fix: Add length check before allocate memory and break loop if length is less than expected header length as per OBEX Specification. CRs-Fixed: 2197150 Change-Id: I805e6b1d51f69645d5132c3c18db2e752d04b096 --- obex/javax/obex/ClientOperation.java | 2 +- obex/javax/obex/ClientSession.java | 2 +- obex/javax/obex/ObexHelper.java | 32 ++++++++++++++++---- obex/javax/obex/ServerOperation.java | 9 +++++- obex/javax/obex/ServerSession.java | 44 +++++++++++++++++++++++++++- 5 files changed, 80 insertions(+), 9 deletions(-) diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java index c627dfb8abac..d090bf5cb479 100644 --- a/obex/javax/obex/ClientOperation.java +++ b/obex/javax/obex/ClientOperation.java @@ -50,7 +50,7 @@ */ public final class ClientOperation implements Operation, BaseStream { - private static final String TAG = "ClientOperation"; + private static final String TAG = "ObexClientOperation"; private static final boolean V = ObexHelper.VDBG; diff --git a/obex/javax/obex/ClientSession.java b/obex/javax/obex/ClientSession.java index 272a920754f5..9aaf7bcda034 100644 --- a/obex/javax/obex/ClientSession.java +++ b/obex/javax/obex/ClientSession.java @@ -47,7 +47,7 @@ */ public final class ClientSession extends ObexSession { - private static final String TAG = "ClientSession"; + private static final String TAG = "ObexClientSession"; private boolean mOpen; diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java index 843793ad98f8..0922be283912 100644 --- a/obex/javax/obex/ObexHelper.java +++ b/obex/javax/obex/ObexHelper.java @@ -53,7 +53,8 @@ public final class ObexHelper { private static final String TAG = "ObexHelper"; - public static final boolean VDBG = false; + public static final String LOG_TAG = "BluetoothObex"; + public static final boolean VDBG = Log.isLoggable(LOG_TAG, Log.VERBOSE); /** * Defines the basic packet length used by OBEX. Every OBEX packet has the * same basic format:
    @@ -90,6 +91,8 @@ private ObexHelper() { */ public static final int MAX_CLIENT_PACKET_SIZE = 0xFC00; + public static final int A2DP_SCO_OBEX_MAX_CLIENT_PACKET_SIZE = 0x2000; + public static final int OBEX_OPCODE_FINAL_BIT_MASK = 0x80; public static final int OBEX_OPCODE_CONNECT = 0x80; @@ -194,6 +197,7 @@ public static byte[] updateHeaderSet(HeaderSet header, byte[] headerArray) throw try { while (index < headerArray.length) { headerID = 0xFF & headerArray[index]; + if (VDBG) Log.v(TAG,"updateHeaderSet headerID = " + headerID); switch (headerID & (0xC0)) { /* @@ -212,9 +216,9 @@ public static byte[] updateHeaderSet(HeaderSet header, byte[] headerArray) throw length = ((0xFF & headerArray[index]) << 8) + (0xFF & headerArray[index + 1]); index += 2; - if (length <= OBEX_BYTE_SEQ_HEADER_LEN) { + if (length < OBEX_BYTE_SEQ_HEADER_LEN) { Log.e(TAG, "Remote sent an OBEX packet with " + - "incorrect header length = " + length); + "incorrect header length : " + length); break; } length -= OBEX_BYTE_SEQ_HEADER_LEN; @@ -382,8 +386,9 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { * Determine if there is a connection ID to send. If there is, * then it should be the first header in the packet. */ + if (VDBG) Log.v(TAG,"createHeader = " + head); if ((headImpl.mConnectionID != null) && (headImpl.getHeader(HeaderSet.TARGET) == null)) { - + if (VDBG) Log.v(TAG," Add Header = " + HeaderSet.CONNECTION_ID); out.write((byte)HeaderSet.CONNECTION_ID); out.write(headImpl.mConnectionID); } @@ -391,6 +396,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { // Count Header intHeader = (Long)headImpl.getHeader(HeaderSet.COUNT); if (intHeader != null) { + if (VDBG) Log.v(TAG," Add Header = " + HeaderSet.COUNT); out.write((byte)HeaderSet.COUNT); value = ObexHelper.convertToByteArray(intHeader.longValue()); out.write(value); @@ -402,6 +408,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { // Name Header stringHeader = (String)headImpl.getHeader(HeaderSet.NAME); if (stringHeader != null) { + if (VDBG) Log.v(TAG," Add Header = " + HeaderSet.NAME); out.write((byte)HeaderSet.NAME); value = ObexHelper.convertToUnicodeByteArray(stringHeader); length = value.length + 3; @@ -422,6 +429,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { // Type Header stringHeader = (String)headImpl.getHeader(HeaderSet.TYPE); if (stringHeader != null) { + if (VDBG) Log.v(TAG," Add Header = " + HeaderSet.TYPE); out.write((byte)HeaderSet.TYPE); try { value = stringHeader.getBytes("ISO8859_1"); @@ -443,6 +451,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { // Length Header intHeader = (Long)headImpl.getHeader(HeaderSet.LENGTH); if (intHeader != null) { + if (VDBG) Log.v(TAG," Add Header = " + HeaderSet.LENGTH); out.write((byte)HeaderSet.LENGTH); value = ObexHelper.convertToByteArray(intHeader.longValue()); out.write(value); @@ -454,7 +463,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { // Time ISO Header dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_ISO_8601); if (dateHeader != null) { - + if (VDBG) Log.v(TAG," Add dateHeader = " + HeaderSet.TIME_ISO_8601); /* * The ISO Header should take the form YYYYMMDDTHHMMSSZ. The * 'Z' will only be included if it is a UTC time. @@ -516,6 +525,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { // Time 4 Byte Header dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_4_BYTE); if (dateHeader != null) { + if (VDBG) Log.v(TAG," Add dateHeader = " + HeaderSet.TIME_4_BYTE); out.write(HeaderSet.TIME_4_BYTE); /* @@ -550,6 +560,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { // Target Header value = (byte[])headImpl.getHeader(HeaderSet.TARGET); if (value != null) { + if (VDBG) Log.v(TAG," Add Header = " + HeaderSet.TARGET); out.write((byte)HeaderSet.TARGET); length = value.length + 3; lengthArray[0] = (byte)(255 & (length >> 8)); @@ -578,6 +589,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { // Who Header value = (byte[])headImpl.getHeader(HeaderSet.WHO); if (value != null) { + if (VDBG) Log.v(TAG," Add Header = " + HeaderSet.WHO); out.write((byte)HeaderSet.WHO); length = value.length + 3; lengthArray[0] = (byte)(255 & (length >> 8)); @@ -592,6 +604,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { // Connection ID Header value = (byte[])headImpl.getHeader(HeaderSet.APPLICATION_PARAMETER); if (value != null) { + if (VDBG) Log.v(TAG," Add APP PARAM Header = " + HeaderSet.APPLICATION_PARAMETER); out.write((byte)HeaderSet.APPLICATION_PARAMETER); length = value.length + 3; lengthArray[0] = (byte)(255 & (length >> 8)); @@ -630,6 +643,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { lengthArray[1] = (byte)(255 & length); out.write(lengthArray); out.write(value); + if (VDBG) Log.v(TAG," Add Unicode String value = " + value); if (nullOut) { headImpl.setHeader(i + 0x30, null); } @@ -644,6 +658,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { lengthArray[1] = (byte)(255 & length); out.write(lengthArray); out.write(value); + if (VDBG) Log.v(TAG," Add ByteSeq value = " + value); if (nullOut) { headImpl.setHeader(i + 0x70, null); } @@ -654,6 +669,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { if (byteHeader != null) { out.write((byte)i + 0xB0); out.write(byteHeader.byteValue()); + if (VDBG) Log.v(TAG," Add ByteHeader value = " + byteHeader.byteValue()); if (nullOut) { headImpl.setHeader(i + 0xB0, null); } @@ -664,6 +680,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { if (intHeader != null) { out.write((byte)i + 0xF0); out.write(ObexHelper.convertToByteArray(intHeader.longValue())); + if (VDBG) Log.v(TAG," Add Int value = " + intHeader.longValue()); if (nullOut) { headImpl.setHeader(i + 0xF0, null); } @@ -678,6 +695,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { lengthArray[1] = (byte)(255 & length); out.write(lengthArray); out.write(headImpl.mAuthChall); + if (VDBG) Log.v(TAG," Add mAuthChall value = " + headImpl.mAuthChall); if (nullOut) { headImpl.mAuthChall = null; } @@ -691,6 +709,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { lengthArray[1] = (byte)(255 & length); out.write(lengthArray); out.write(headImpl.mAuthResp); + if (VDBG) Log.v(TAG," Add mAuthChall value = " + headImpl.mAuthResp); if (nullOut) { headImpl.mAuthResp = null; } @@ -706,8 +725,10 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { // Add the SRM header byteHeader = (Byte)headImpl.getHeader(HeaderSet.SINGLE_RESPONSE_MODE); if (byteHeader != null) { + if (VDBG) Log.v(TAG," Add SRM Header = " + HeaderSet.SINGLE_RESPONSE_MODE); out.write((byte)HeaderSet.SINGLE_RESPONSE_MODE); out.write(byteHeader.byteValue()); + if (VDBG) Log.v(TAG," Add SRM value = " + byteHeader.byteValue()); if (nullOut) { headImpl.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, null); } @@ -716,6 +737,7 @@ public static byte[] createHeader(HeaderSet head, boolean nullOut) { // Add the SRM parameter header byteHeader = (Byte)headImpl.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER); if (byteHeader != null) { + if (VDBG) Log.v(TAG," Add Header = " + HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER); out.write((byte)HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER); out.write(byteHeader.byteValue()); if (nullOut) { diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java index 15ea36789e2c..1cc720b3a914 100644 --- a/obex/javax/obex/ServerOperation.java +++ b/obex/javax/obex/ServerOperation.java @@ -57,7 +57,7 @@ */ public final class ServerOperation implements Operation, BaseStream { - private static final String TAG = "ServerOperation"; + private static final String TAG = "ObexServerOperation"; private static final boolean V = ObexHelper.VDBG; // Verbose debugging @@ -124,6 +124,7 @@ public final class ServerOperation implements Operation, BaseStream { */ public ServerOperation(ServerSession p, InputStream in, int request, int maxSize, ServerRequestHandler listen) throws IOException { + if (V) Log.v(TAG, "ServerOperation"); isAborted = false; mParent = p; @@ -340,14 +341,17 @@ public boolean isValidBody() { */ public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream) throws IOException { + if (V) Log.v(TAG, "continueOperation"); if (!mGetOperation) { if (!finalBitSet) { if (sendEmpty) { sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); + if (V) Log.v(TAG, "continueOperation:ServerSet SRM sendEmpty clause"); return true; } else { if ((mResponseSize > 3) || (mPrivateOutput.size() > 0)) { sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); + if (V) Log.v(TAG, "continueOperation: Server setting SRM"); return true; } else { return false; @@ -357,6 +361,7 @@ public synchronized boolean continueOperation(boolean sendEmpty, boolean inStrea return false; } } else { + if (V) Log.v(TAG, "Get continueOperation "); sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); return true; } @@ -405,6 +410,8 @@ public synchronized boolean sendReply(int type) throws IOException { bodyLength = mPrivateOutput.size(); orginalBodyLength = bodyLength; } + if(V) Log.v(TAG, "mMaxPcKLen :" + mMaxPacketLength + " headerArryLen :" + + headerArray.length); if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketLength) { diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java index dbfeefdfb037..a45687f766fc 100644 --- a/obex/javax/obex/ServerSession.java +++ b/obex/javax/obex/ServerSession.java @@ -63,6 +63,12 @@ public final class ServerSession extends ObexSession implements Runnable { private boolean mClosed; + private boolean setMTU = false; + + private boolean updateMtu = false; + + private int updatedMtuSize = 0; + /** * Creates new ServerSession. * @param trans the connection to the client @@ -85,6 +91,25 @@ public ServerSession(ObexTransport trans, ServerRequestHandler handler, Authenti mProcessThread.start(); } + public void setMaxPacketSize(int size) { + if (V) Log.v(TAG, "setMaxPacketSize" + size); + mMaxPacketLength = size; + } + + public int getMaxPacketSize() { + return mMaxPacketLength; + } + + public void reduceMTU(boolean enable) { + setMTU = enable; + } + + public void updateMTU(int mtuSize) { + updateMtu = true; + updatedMtuSize = mtuSize; + Log.i(TAG,"updateMTU: " + mtuSize); + } + /** * Processes requests made to the server and forwards them to the * appropriate event listener. @@ -124,6 +149,7 @@ public void run() { break; case -1: + Log.d(TAG, "Read request returned -1, exiting from loop"); done = true; break; @@ -194,6 +220,7 @@ private void handleAbortRequest() throws IOException { * @throws IOException if an error occurred at the transport layer */ private void handlePutRequest(int type) throws IOException { + if (V) Log.v(TAG, "handlePutRequest"); ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener); try { int response = -1; @@ -205,10 +232,12 @@ private void handlePutRequest(int type) throws IOException { response = validateResponseCode(mListener.onPut(op)); } if (response != ResponseCodes.OBEX_HTTP_OK && !op.isAborted) { + if (V) Log.v(TAG, "handlePutRequest pre != HTTP_OK sendReply"); op.sendReply(response); } else if (!op.isAborted) { // wait for the final bit while (!op.finalBitSet) { + if (V) Log.v(TAG, "handlePutRequest pre looped sendReply"); op.sendReply(ResponseCodes.OBEX_HTTP_CONTINUE); } op.sendReply(response); @@ -240,6 +269,7 @@ private void handlePutRequest(int type) throws IOException { * @throws IOException if an error occurred at the transport layer */ private void handleGetRequest(int type) throws IOException { + if (V) Log.v(TAG, "handleGetRequest"); ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener); try { int response = validateResponseCode(mListener.onGet(op)); @@ -262,6 +292,7 @@ private void handleGetRequest(int type) throws IOException { public void sendResponse(int code, byte[] header) throws IOException { int totalLength = 3; byte[] data = null; + if (V) Log.v(TAG,"sendResponse code " + code + " header : " + header); OutputStream op = mOutput; if (op == null) { return; @@ -269,6 +300,7 @@ public void sendResponse(int code, byte[] header) throws IOException { if (header != null) { totalLength += header.length; + if (V) Log.v(TAG, "header != null totalLength = " + totalLength); data = new byte[totalLength]; data[0] = (byte)code; data[1] = (byte)(totalLength >> 8); @@ -558,9 +590,19 @@ private void handleConnectRequest() throws IOException { + " MaxLength: " + mMaxPacketLength + " flags: " + flags); // should we check it? - if (mMaxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) { + if (setMTU) { + mMaxPacketLength = ObexHelper.A2DP_SCO_OBEX_MAX_CLIENT_PACKET_SIZE; + setMTU = false; + } else if (updateMtu) { + Log.d(TAG, "mMaxPacketLength: " + mMaxPacketLength + + ", updatedMtuSize: " + updatedMtuSize); + if (mMaxPacketLength > updatedMtuSize) + mMaxPacketLength = updatedMtuSize; + updateMtu = false; + } else if (mMaxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) { mMaxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT; } + Log.d(TAG,"handleConnectRequest() - Updated MaxPacketLengh: " + mMaxPacketLength); if(mMaxPacketLength > ObexHelper.getMaxTxPacketSize(mTransport)) { Log.w(TAG, "Requested MaxObexPacketSize " + mMaxPacketLength From 74ffc2a8859d04c84b5aeb341439e88d5315ff13 Mon Sep 17 00:00:00 2001 From: Venkata Jagadeesh Garaga Date: Mon, 2 Mar 2020 14:42:48 +0530 Subject: [PATCH 47/92] GAP: Handle the race condition cases in auto connect logic Issue: Auto connection failing when onUuidChanged and onBondingStateChanged triggered in race condition. Steps: 1.Turn on BT 2.Scan the remote headset from DUT 3.Initiating pairing to remote device 4.unpair the remote from DUT UI Repeat steps from 2 to 4. Some times auto connection not getting initiated from DUT. Root Cause: if mProfiles accessed simultaneously from onUuidChanged and connectAllEnabledProfiles with two different process context. mProfiles not reflecting latest value in connectAllEnabledProfiles, Settings not able to initiate Auto connection as mProfiles list shown as empty. onBondingStateChanged getting triggered multiple times from settings with different process contexts and BondingInitiatedLocally resets in first instance when no profiles added, its leading auto connection failure FIX: - mProfiles accessed inside onUuidChanged without syncronization,also mProfiles empty check already there in connectWithoutResettingTimer - Hence remove redundant mProfiles empty check in onUuidChanged - Reset BondingInitiatedLocally flag while initiating connection. [wight554: updated code from LA.QSSI.11.0.r1-05600-qssi.0] Conflicts: packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java Change-Id: I88aa6a6cd46d264f4dd32db71e413079bbd40779 CRs-Fixed: 2630956 Signed-off-by: Volodymyr Zhdanov --- .../settingslib/bluetooth/CachedBluetoothDevice.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 44553831a080..989619e12c63 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -331,6 +331,7 @@ public void connect() { } mConnectAttempted = SystemClock.elapsedRealtime(); + Log.d(TAG, "connect: mConnectAttempted = " + mConnectAttempted); connectAllEnabledProfiles(); } @@ -366,6 +367,13 @@ private void connectAllEnabledProfiles() { Log.d(TAG, "No profiles. Maybe we will connect later for device " + mDevice); return; } + // BondingInitiatedLocally flag should be reset in onBondingStateChanged + // But Settings executing onBondingStateChanged twice and its lead to auto connection + // failure. this flag will be moved from here once settings issue fixed. + if (mDevice.isBondingInitiatedLocally()) { + Log.w(TAG, "reset BondingInitiatedLocally flag"); + mDevice.setBondingInitiatedLocally(false); + } mLocalAdapter.connectAllEnabledProfiles(mDevice); } @@ -784,9 +792,6 @@ void onBondingStateChanged(int bondState) { if (bondState == BluetoothDevice.BOND_BONDED) { boolean mIsBondingInitiatedLocally = mDevice.isBondingInitiatedLocally(); Log.w(TAG, "mIsBondingInitiatedLocally" + mIsBondingInitiatedLocally); - if (mIsBondingInitiatedLocally) { - mDevice.setBondingInitiatedLocally(false); - } if (mIsTwsConnectEnabled) { Log.d(TAG, "Initiating connection to" + mDevice); if (mIsBondingInitiatedLocally || mDevice.isTwsPlusDevice()) { From 7e1cc7550a53199c5fb07cdc718a359302b81031 Mon Sep 17 00:00:00 2001 From: Diego Wilson Date: Mon, 13 Aug 2018 11:26:51 -0700 Subject: [PATCH 48/92] Use BluetoothUtils instead of Utils This class got renamed upstream. Change-Id: I7a7a52bfb33851ad2457e5a2b76babbaa0a9835c --- .../android/settingslib/bluetooth/CachedBluetoothDevice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 989619e12c63..0f918e8562fd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -440,7 +440,7 @@ public void unpair() { if (peerDevice != null) { final boolean peersuccessful = peerDevice.removeBond(); if (peersuccessful) { - if (Utils.D) { + if (BluetoothUtils.D) { Log.d(TAG, "Command sent successfully:REMOVE_BOND " + peerDevice.getName()); } } From 572a251fdfbe4295016e04491578a07c5a471cdb Mon Sep 17 00:00:00 2001 From: Naresh Tanniru Date: Wed, 20 Mar 2019 19:35:03 +0530 Subject: [PATCH 49/92] audio: Add support for audio extended codecs - Add support for APTX adaptive and CELT formats [wight554: updated code from LA.QSSI.11.0.r1-05600-qssi.0] Conflicts: media/java/android/media/AudioSystem.java services/core/java/com/android/server/audio/BtHelper.java Change-Id: I735a297acc4d3d4c7f8d2cc9302a13ebcf4e4c3d CRs-Fixed: 2433041 Signed-off-by: Volodymyr Zhdanov --- media/java/android/media/AudioSystem.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 6ff551a68c18..cbf71549e631 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -230,6 +230,10 @@ public static String modeToString(int mode) { public static final int AUDIO_FORMAT_APTX_HD = 0x21000000; /** @hide */ public static final int AUDIO_FORMAT_LDAC = 0x23000000; + /** @hide */ + public static final int AUDIO_FORMAT_CELT = 0x26000000; + /** @hide */ + public static final int AUDIO_FORMAT_APTX_ADAPTIVE = 0x27000000; /** @hide */ @IntDef(flag = false, prefix = "AUDIO_FORMAT_", value = { @@ -256,6 +260,9 @@ public static int audioFormatToBluetoothSourceCodec( case AUDIO_FORMAT_APTX: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX; case AUDIO_FORMAT_APTX_HD: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD; case AUDIO_FORMAT_LDAC: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC; + case AUDIO_FORMAT_CELT: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_CELT; + case AUDIO_FORMAT_APTX_ADAPTIVE: + return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE; default: Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat) + " for conversion to BT codec"); @@ -281,6 +288,10 @@ public static int audioFormatToBluetoothSourceCodec( return AudioSystem.AUDIO_FORMAT_APTX_HD; case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC: return AudioSystem.AUDIO_FORMAT_LDAC; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_CELT: + return AudioSystem.AUDIO_FORMAT_CELT; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE: + return AudioSystem.AUDIO_FORMAT_APTX_ADAPTIVE; default: Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec) + " for conversion to audio format"); From 06b06af4f88c71c4d7178cfbd30441215f28c8c7 Mon Sep 17 00:00:00 2001 From: Dhananjay Kumar Date: Thu, 18 Apr 2019 21:52:36 +0530 Subject: [PATCH 50/92] audio: Add support for APTX TWSP audio codec - Add support for APTX TWSP audio format for BT. [wight554: updated code from LA.QSSI.11.0.r1-05600-qssi.0] Conflicts: media/java/android/media/AudioSystem.java services/core/java/com/android/server/audio/BtHelper.java CRs-Fixed: 2433041 Change-Id: I19e256ae981516e9054b6e6eed8372ae2929b006 Signed-off-by: Volodymyr Zhdanov --- media/java/android/media/AudioSystem.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index cbf71549e631..59179615cf8c 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -234,6 +234,8 @@ public static String modeToString(int mode) { public static final int AUDIO_FORMAT_CELT = 0x26000000; /** @hide */ public static final int AUDIO_FORMAT_APTX_ADAPTIVE = 0x27000000; + /** @hide */ + public static final int AUDIO_FORMAT_APTX_TWSP = 0x2A000000; /** @hide */ @IntDef(flag = false, prefix = "AUDIO_FORMAT_", value = { @@ -263,6 +265,8 @@ public static int audioFormatToBluetoothSourceCodec( case AUDIO_FORMAT_CELT: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_CELT; case AUDIO_FORMAT_APTX_ADAPTIVE: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE; + case AUDIO_FORMAT_APTX_TWSP: + return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_TWSP; default: Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat) + " for conversion to BT codec"); @@ -292,6 +296,8 @@ public static int audioFormatToBluetoothSourceCodec( return AudioSystem.AUDIO_FORMAT_CELT; case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE: return AudioSystem.AUDIO_FORMAT_APTX_ADAPTIVE; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_TWSP: + return AudioSystem.AUDIO_FORMAT_APTX_TWSP; default: Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec) + " for conversion to audio format"); From 096deba7cd66fc83995db15150877586eecc6637 Mon Sep 17 00:00:00 2001 From: pramod kotreshappa Date: Thu, 9 May 2019 15:52:05 -0700 Subject: [PATCH 51/92] TWS_A2DP: Handle active device change between TWS+ earbuds Check for TWS+ device switch in active device change. If TWS+ device switch then remove disconnected device and add peer pair device into connected devices list and return. [wight554: updated code from LA.QSSI.11.0.r1-05600-qssi.0] CRs-fixed: 2446639 Change-Id: I97e66fba56fceb2e9b2e7c253119033bf34175bb Signed-off-by: Volodymyr Zhdanov --- .../server/audio/AudioDeviceInventory.java | 6 ++++++ .../java/com/android/server/audio/BtHelper.java | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 7366f54fc218..e34d90df8f60 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -950,6 +950,12 @@ public void setBluetoothA2dpDeviceConnectionState( mConnectedDevices.put(deviceKey, new DeviceInfo( AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, BtHelper.getName(device), address, a2dpCodec)); + if (BtHelper.isTwsPlusSwitch(device, existingDevice.getValue().mDeviceAddress)) { + if (AudioService.DEBUG_DEVICES) { + Log.d(TAG,"TWS+ device switch"); + } + return; + } mDeviceBroker.postA2dpActiveDeviceChange( new BtHelper.BluetoothA2dpDeviceInfo( device, a2dpVolume, a2dpCodec)); diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index a9a8d1096dd8..ed15229fa1f3 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -212,6 +212,21 @@ public boolean equals(Object o) { return deviceName; } + /*packages*/ @NonNull static boolean isTwsPlusSwitch(@NonNull BluetoothDevice device, + String address) { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (device == null || adapter.getRemoteDevice(address) == null || + device.getTwsPlusPeerAddress() == null) { + return false; + } + if (device.isTwsPlusDevice() && + adapter.getRemoteDevice(address).isTwsPlusDevice() && + device.getTwsPlusPeerAddress().equals(address)) { + Log.i(TAG,"isTwsPlusSwitch true"); + return true; + } + return false; + } //---------------------------------------------------------------------- // Interface for AudioDeviceBroker From f573374f7701dccf045f6cd3cc7b05acd0e81ac7 Mon Sep 17 00:00:00 2001 From: Gurpreet Ghai Date: Thu, 30 Jan 2020 11:32:32 +0530 Subject: [PATCH 52/92] BT_Audio: Updated Check for TWS+ switch - Race condition between disconnection of first earbud and connection of second earbud, the configuration of second earbud is ignored because it is considered a case of TWS switch. - Since MM Audio removes the disconnected device from list after delay of ~600ms, this creates a window where a new connection goes unprocessed when it happens immediately after disconnection. - This change fetches real time connection state from A2DP to overcome the race condition. CRs-Fixed: 2606382 Change-Id: I2df8447acb1d8e1b843a49906f2f3e3993cce5d6 --- .../core/java/com/android/server/audio/BtHelper.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index ed15229fa1f3..36c30b8fa384 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -64,7 +64,7 @@ public class BtHelper { private @Nullable BluetoothHearingAid mHearingAid; // Reference to BluetoothA2dp to query for AbsoluteVolume. - private @Nullable BluetoothA2dp mA2dp; + static private @Nullable BluetoothA2dp mA2dp; // If absolute volume is supported in AVRCP device private boolean mAvrcpAbsVolSupported = false; @@ -215,13 +215,18 @@ public boolean equals(Object o) { /*packages*/ @NonNull static boolean isTwsPlusSwitch(@NonNull BluetoothDevice device, String address) { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (device == null || adapter.getRemoteDevice(address) == null || + BluetoothDevice connDevice = adapter.getRemoteDevice(address); + if (device == null || connDevice == null || device.getTwsPlusPeerAddress() == null) { return false; } if (device.isTwsPlusDevice() && - adapter.getRemoteDevice(address).isTwsPlusDevice() && + connDevice.isTwsPlusDevice() && device.getTwsPlusPeerAddress().equals(address)) { + if(mA2dp.getConnectionState(connDevice) != BluetoothProfile.STATE_CONNECTED) { + Log.w(TAG,"Active earbud is already disconnected"); + return false; + } Log.i(TAG,"isTwsPlusSwitch true"); return true; } From 4512ab1470668af793ea61b246d1c1f39dd8ac04 Mon Sep 17 00:00:00 2001 From: Gurpreet Ghai Date: Wed, 6 Sep 2017 16:25:17 +0530 Subject: [PATCH 53/92] BT: Add new intent to broadcast IOT device details This intent will be used to broadcast IOT device details when IOT related issue is encountered. CRs-Fixed: 2109009 Change-Id: I5eb97e3e2835a5acf3b2edb6c3d29e9da81bdc75 --- .../android/bluetooth/BluetoothDevice.java | 75 +++++++++++++++++++ core/res/AndroidManifest.xml | 1 + 2 files changed, 76 insertions(+) diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 80f5ec7c01e3..629e84f2a30b 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -253,6 +253,18 @@ public final class BluetoothDevice implements Parcelable, Attributable { public static final String ACTION_BOND_STATE_CHANGED = "android.bluetooth.device.action.BOND_STATE_CHANGED"; + /** + * Broadcast Action: Broadcast details of IOT device when an IOT + * related issue is observed. + *

    Always contains the extra fields {@link #EXTRA_NAME}. + *

    Requires {@link android.Manifest.permission#BLUETOOTH} to receive. + * @hide + **/ + + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_REMOTE_ISSUE_OCCURRED = + "org.codeaurora.intent.bluetooth.action.REMOTE_ISSUE_OCCURRED"; + /** * Broadcast Action: Indicates the battery level of a remote device has * been retrieved for the first time, or changed since the last retrieval @@ -324,6 +336,69 @@ public final class BluetoothDevice implements Parcelable, Attributable { */ public static final String EXTRA_BQR = "android.bluetooth.qti.extra.EXTRA_BQR"; + /** + * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} + * intents. It contains the type of IOT issue that occurred. + * @hide + */ + public static final String EXTRA_ISSUE_TYPE = "android.bluetooth.qti.extra.ERROR_TYPE"; + + /** + * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents. + * It contains the details of details of the issue. + * @hide + */ + public static final String EXTRA_ERROR_CODE = "android.bluetooth.qti.extra.ERROR_CODE"; + + /** + * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents. + * It contains the SoC event mask when issue occurred. + * @hide + */ + public static final String EXTRA_ERROR_EVENT_MASK = "android.bluetooth.qti.extra.ERROR_EVENT_MASK"; + + /** + * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents. + * It contains the LMP Version of IOT device. + * @hide + */ + public static final String EXTRA_LMP_VERSION = "android.bluetooth.qti.extra.EXTRA_LMP_VERSION"; + + /** + * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents. + * It contains the LMP Sub Version of IOT device. + * @hide + */ + public static final String EXTRA_LMP_SUBVER = "android.bluetooth.qti.extra.EXTRA_LMP_SUBVER"; + + /** + * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents. + * It contains the Manufacturer ID of IOT device. + * @hide + */ + public static final String EXTRA_MANUFACTURER = "android.bluetooth.qti.extra.EXTRA_MANUFACTURER"; + + /** + * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents. + * It contains the Power level. + * @hide + */ + public static final String EXTRA_POWER_LEVEL = "android.bluetooth.qti.extra.EXTRA_POWER_LEVEL"; + + /** + * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents. + * It contains the Link Quality of the connection. + * @hide + */ + public static final String EXTRA_LINK_QUALITY = "android.bluetooth.qti.extra.EXTRA_LINK_QUALITY"; + + /** + * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents. + * It contains the coutnt of glitches occured since last broadcast. + * @hide + */ + public static final String EXTRA_GLITCH_COUNT = "android.bluetooth.qti.extra.EXTRA_GLITCH_COUNT"; + /** * Used as an optional short extra field in {@link #ACTION_FOUND} intents. * Contains the RSSI value of the remote device as reported by the diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 66743cc7d20b..e6646f97e7d9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -175,6 +175,7 @@ + Date: Wed, 8 Nov 2017 18:51:04 +0530 Subject: [PATCH 54/92] Bluetooth: Fix for Global reference table overflow issue[1/2] - To avoid RemoteCallbackList object leaks in Bluetooth Manager Service, Added unregister API in BluetoothAdapter.java which internally calls unregisterAdapter of Manager Service CRs-Fixed: 2138560 Change-Id: I58ce5f5e1b86d87e0ff602b453abb74d0b5e1982 --- .../java/android/bluetooth/BluetoothAdapter.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 5094498dbee5..7e64f65aa811 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -3497,6 +3497,22 @@ public void onBluetoothStateChange(boolean on) { } } + /** + * @hide + */ + public void unregisterAdapter() { + try { + //mServiceLock.writeLock().lock(); + if (mManagerService != null){ + mManagerService.unregisterAdapter(mManagerCallback); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + //mServiceLock.writeLock().unlock(); + } + } + private Set toDeviceSet(List devices) { Set deviceSet = new HashSet(devices); return Collections.unmodifiableSet(deviceSet); From 7e60e48146e55bbcf61ab1d1ff68c974a84f1ff1 Mon Sep 17 00:00:00 2001 From: Gurpreet Ghai Date: Tue, 25 Sep 2018 19:12:40 +0530 Subject: [PATCH 55/92] BT Dev: Increase array size for codec selection in Dev Op Increase array size to accomodate additional codec in Developer Option codec list Conflicts: packages/SettingsLib/res/values/arrays.xml CRs-Fixed: 2322634 Change-Id: I1de7d70e675bffed25b1016efbd1ea908b24d97d Signed-off-by: Volodymyr Zhdanov --- packages/SettingsLib/res/values/arrays.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index a385cb5b0316..bdf2b78798f3 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -166,6 +166,7 @@ 2 3 4 + 5 From e41146c1055f6248172e84bb705f847abb460c66 Mon Sep 17 00:00:00 2001 From: pramod kotreshappa Date: Wed, 24 Jul 2019 15:03:52 -0700 Subject: [PATCH 56/92] Bluetooth: Fix for out of bound exception Increase array length to accomadate TWS+ codec entry in DevUI settings. Conflicts: packages/SettingsLib/res/values/arrays.xml CRs-fixed: 2495783 Change-Id: I3b334f9420eff0850ead4db9d452c69ce4295c50 Signed-off-by: Volodymyr Zhdanov --- packages/SettingsLib/res/values/arrays.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index bdf2b78798f3..239395e6f213 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -167,6 +167,7 @@ 3 4 5 + 6 From baabb16550afb230ca3a100b6ce8b6a0ee3dc9bd Mon Sep 17 00:00:00 2001 From: Satheesh Kumar Pallemoni Date: Tue, 6 Nov 2018 17:51:11 +0530 Subject: [PATCH 57/92] Keep a null check before accessing the BluetoothA2dpWrapper APIs. When switching from default user mode to guest mode from system settings, Bluetooth gets turn OFF and ON. Then, try connect to peer device which was connected previousely in the default user mode. At this point if BluetoothA2dpWrapper has not init, then its object would be null, and accessing the APIs of BluetoothA2dpWrapper in A2dpProfile, results in NPE. So keep a null check on object of BluetoothA2dpWrapper class before accessing APIs using it. [wight554: updated code from LA.QSSI.11.0.r1-05600-qssi.0] Conflicts: packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java CRs-Fixed: 2454049 Change-Id: Idd949bc27fec10b46a149b5db67abc6f3b5097d1 Signed-off-by: Volodymyr Zhdanov --- .../settingslib/bluetooth/A2dpProfile.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index 8bb9af79c6a2..3d1bdb2b8eb9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -39,6 +39,7 @@ public class A2dpProfile implements LocalBluetoothProfile { private static final String TAG = "A2dpProfile"; + private static boolean V = true; private Context mContext; @@ -218,6 +219,11 @@ boolean isA2dpPlaying() { } public boolean supportsHighQualityAudio(BluetoothDevice device) { + if (V) Log.d(TAG, " execute supportsHighQualityAudio()"); + if (mService == null) { + if (V) Log.d(TAG,"mService is null."); + return false; + } BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); if (bluetoothDevice == null) { return false; @@ -227,6 +233,11 @@ public boolean supportsHighQualityAudio(BluetoothDevice device) { } public boolean isHighQualityAudioEnabled(BluetoothDevice device) { + if (V) Log.d(TAG, " execute isHighQualityAudioEnabled()"); + if (mService == null) { + if (V) Log.d(TAG,"mService is null."); + return false; + } BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); if (bluetoothDevice == null) { return false; @@ -253,13 +264,18 @@ && supportsHighQualityAudio(bluetoothDevice)) { } public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) { + if (V) Log.d(TAG, " execute setHighQualityAudioEnabled()"); + int prefValue = enabled + ? BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED + : BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED; + if (mService == null) { + if (V) Log.d(TAG,"mService is null."); + return; + } BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); if (bluetoothDevice == null) { return; } - int prefValue = enabled - ? BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED - : BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED; mService.setOptionalCodecsEnabled(bluetoothDevice, prefValue); if (getConnectionStatus(bluetoothDevice) != BluetoothProfile.STATE_CONNECTED) { return; @@ -272,6 +288,7 @@ public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) } public String getHighQualityAudioOptionLabel(BluetoothDevice device) { + if (V) Log.d(TAG, " execute getHighQualityAudioOptionLabel()"); BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice(); int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec; if (bluetoothDevice == null || !supportsHighQualityAudio(device) @@ -281,7 +298,7 @@ public String getHighQualityAudioOptionLabel(BluetoothDevice device) { // We want to get the highest priority codec, since that's the one that will be used with // this device, and see if it is high-quality (ie non-mandatory). BluetoothCodecConfig[] selectable = null; - if (mService.getCodecStatus(device) != null) { + if (mService != null && mService.getCodecStatus(device) != null) { selectable = mService.getCodecStatus(device).getCodecsSelectableCapabilities(); // To get the highest priority, we sort in reverse. Arrays.sort(selectable, From 1c70a6abb7dcbfeadf40a067f347c6ee1121096b Mon Sep 17 00:00:00 2001 From: Naval saini Date: Thu, 1 Aug 2019 19:42:31 +0530 Subject: [PATCH 58/92] Enable codec change request when mode changes in aptX-adaptive Enable codec change request when mode changes in aptX-adaptive CRs-Fixed: 2537521 Change-Id: I74c8446a2a163282fb8d6edc5b293410a1b5d7c5 --- core/java/android/bluetooth/BluetoothCodecConfig.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index 16c03f153472..c87785eba643 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -668,6 +668,10 @@ public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) { if (mCodecSpecific1 != other.mCodecSpecific1) { return false; } + case SOURCE_CODEC_TYPE_APTX_ADAPTIVE: + if (other.mCodecSpecific4 > 0) { + return false; + } // fall through default: return true; From 917c0ed08cd0198624e79120f6cf6eb8e37940a3 Mon Sep 17 00:00:00 2001 From: dianlujitao Date: Fri, 5 Jun 2020 22:37:41 +0800 Subject: [PATCH 59/92] BT: Relocate A2DP codec strings for translation Conflicts: packages/SettingsLib/res/values-en-rGB/arrays.xml packages/SettingsLib/res/values/arrays.xml [wight554: adapted arrays for android 11] Change-Id: I2fba6c35331175fc3432cdf1108d75f6495b4f03 Signed-off-by: Volodymyr Zhdanov --- .../SettingsLib/res/values-en-rGB/arrays.xml | 4 -- packages/SettingsLib/res/values/arrays.xml | 4 -- packages/SettingsLib/res/values/cm_arrays.xml | 44 +++++++++++++++++++ .../settingslib/bluetooth/A2dpProfile.java | 3 +- .../bluetooth/A2dpProfileTest.java | 2 +- 5 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 packages/SettingsLib/res/values/cm_arrays.xml diff --git a/packages/SettingsLib/res/values-en-rGB/arrays.xml b/packages/SettingsLib/res/values-en-rGB/arrays.xml index 6d3e556c642c..83cdadb2e101 100644 --- a/packages/SettingsLib/res/values-en-rGB/arrays.xml +++ b/packages/SettingsLib/res/values-en-rGB/arrays.xml @@ -92,8 +92,6 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" - "Qualcomm® aptX™ Adaptive audio" - "Qualcomm® aptX™ TWS+ audio" "Use system selection (default)" @@ -102,8 +100,6 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" - "Qualcomm® aptX™ Adaptive audio" - "Qualcomm® aptX™ TWS+ audio" "Use system selection (default)" diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index 239395e6f213..f410ab3e45d4 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -154,8 +154,6 @@ Qualcomm® aptX™ audio Qualcomm® aptX™ HD audio LDAC - Qualcomm® aptX™ Adaptive audio - Qualcomm® aptX™ TWS+ audio @@ -178,8 +176,6 @@ Qualcomm® aptX™ audio Qualcomm® aptX™ HD audio LDAC - Qualcomm® aptX™ Adaptive audio - Qualcomm® aptX™ TWS+ audio diff --git a/packages/SettingsLib/res/values/cm_arrays.xml b/packages/SettingsLib/res/values/cm_arrays.xml new file mode 100644 index 000000000000..75fe215dcd5e --- /dev/null +++ b/packages/SettingsLib/res/values/cm_arrays.xml @@ -0,0 +1,44 @@ + + + + + + Use System Selection (Default) + SBC + AAC + Qualcomm® aptX™ audio + Qualcomm® aptX™ HD audio + LDAC + Qualcomm® aptX™ Adaptive audio + Qualcomm® aptX™ TWS+ audio + + + + + Use System Selection (Default) + SBC + AAC + Qualcomm® aptX™ audio + Qualcomm® aptX™ HD audio + LDAC + Qualcomm® aptX™ Adaptive audio + Qualcomm® aptX™ TWS+ audio + + diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index 3d1bdb2b8eb9..cce2267890a0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -341,7 +341,8 @@ public String getHighQualityAudioOptionLabel(BluetoothDevice device) { return mContext.getString(unknownCodecId); } return mContext.getString(R.string.bluetooth_profile_a2dp_high_quality, - mContext.getResources().getStringArray(R.array.bluetooth_a2dp_codec_titles)[index]); + mContext.getResources().getStringArray( + R.array.bluetooth_a2dp_codec_titles_cm)[index]); } public String toString() { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java index 16f37aecd6e2..f5715bd3513c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java @@ -147,7 +147,7 @@ private void setupLabelTest() { final Resources res = mock(Resources.class); when(mContext.getResources()).thenReturn(res); - when(res.getStringArray(eq(R.array.bluetooth_a2dp_codec_titles))) + when(res.getStringArray(eq(R.array.bluetooth_a2dp_codec_titles_cm))) .thenReturn(CODEC_NAMES); // Most tests want to simulate optional codecs being supported by the device, so do that From fae30aaa8d7119b65b083cb7dd9131f586bc0466 Mon Sep 17 00:00:00 2001 From: Subramanian Srinivasan Date: Tue, 26 Mar 2019 23:50:18 -0700 Subject: [PATCH 60/92] Fix BLE transport discovery scan filter Add BLE transport discovery scan filter only when transport discovery organization id is valid (greater than or equal to 0). CRs-Fixed: 2424135 Change-Id: I40e6013c9c6be28c82b7265edae70e7d6d577749 --- .../java/android/bluetooth/le/ScanFilter.java | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java index 9459c4b3ec83..b5d56be9a1ce 100644 --- a/core/java/android/bluetooth/le/ScanFilter.java +++ b/core/java/android/bluetooth/le/ScanFilter.java @@ -195,12 +195,15 @@ public void writeToParcel(Parcel dest, int flags) { } dest.writeInt(mOrgId); - dest.writeInt(mTDSFlags); - dest.writeInt(mTDSFlagsMask); - dest.writeInt(mWifiNANHash == null ? 0 : 1); - if (mWifiNANHash != null) { - dest.writeInt(mWifiNANHash.length); - dest.writeByteArray(mWifiNANHash); + dest.writeInt(mOrgId < 0 ? 0 : 1); + if(mOrgId >= 0) { + dest.writeInt(mTDSFlags); + dest.writeInt(mTDSFlagsMask); + dest.writeInt(mWifiNANHash == null ? 0 : 1); + if (mWifiNANHash != null) { + dest.writeInt(mWifiNANHash.length); + dest.writeByteArray(mWifiNANHash); + } } } @@ -294,17 +297,19 @@ public ScanFilter createFromParcel(Parcel in) { } int orgId = in.readInt(); - int TDSFlags = in.readInt(); - int TDSFlagsMask = in.readInt(); - if (in.readInt() == 1) { - int wifiNANHashLength = in.readInt(); - byte[] wifiNanHash = new byte[wifiNANHashLength]; - in.readByteArray(wifiNanHash); - builder.setTransportDiscoveryData(orgId, TDSFlags, TDSFlagsMask, - wifiNanHash); - } - else { - builder.setTransportDiscoveryData(orgId, TDSFlags, TDSFlagsMask, null); + if(in.readInt() == 1) { + int tdsFlags = in.readInt(); + int tdsFlagsMask = in.readInt(); + if (in.readInt() == 1) { + int wifiNANHashLength = in.readInt(); + byte[] wifiNanHash = new byte[wifiNANHashLength]; + in.readByteArray(wifiNanHash); + builder.setTransportDiscoveryData(orgId, tdsFlags, tdsFlagsMask, + wifiNanHash); + } + else { + builder.setTransportDiscoveryData(orgId, tdsFlags, tdsFlagsMask, null); + } } return builder.build(); From 4d8736a074af716719ed1bd3bfdca0429deba627 Mon Sep 17 00:00:00 2001 From: Sravan Voleti Date: Thu, 30 Aug 2018 11:03:25 +0530 Subject: [PATCH 61/92] BLE: Fix NPE during start advertising Use case: Open any app that performs advertising. Expected result: BLE should Start advertisement. Observed result : Observed force close in BA App. Fix: set zero into parcel if transport discovery data is empty. Test: BLE Start advertisement successes after performing above use case. CRs-Fixed: 2304532 Change-Id: Ica730f8d22367864308412621a8d87e625198bec --- core/java/android/bluetooth/le/AdvertiseData.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java index b5a752f39520..f0075263894f 100644 --- a/core/java/android/bluetooth/le/AdvertiseData.java +++ b/core/java/android/bluetooth/le/AdvertiseData.java @@ -188,8 +188,8 @@ public void writeToParcel(Parcel dest, int flags) { } dest.writeByte((byte) (getIncludeTxPowerLevel() ? 1 : 0)); dest.writeByte((byte) (getIncludeDeviceName() ? 1 : 0)); - if(mTransportDiscoveryData != null) { - dest.writeInt(mTransportDiscoveryData.length); + dest.writeInt(mTransportDiscoveryData != null ? mTransportDiscoveryData.length : 0); + if (mTransportDiscoveryData != null) { dest.writeByteArray(mTransportDiscoveryData); } } From 2729cc96a692645b1643665cb00c6fff9d87776a Mon Sep 17 00:00:00 2001 From: Sagar Verma Date: Fri, 3 Jul 2020 22:08:04 +0530 Subject: [PATCH 62/92] BT-Audio: Keep track of Active TWS+ EB device in BTHelper Keep track of active TWS+ EB device in MM Audio module based on device connect/disconnect events as part of Active device update. This device tracking will help us to make decision for Audio updates regarding reconfig should be done OR not based on EBs transition use-case. CRs-Fixed: 2717923 Change-Id: I2df8447acb1d8e1b843a49906f2f3e3993dee5d6 --- .../com/android/server/audio/AudioDeviceInventory.java | 2 ++ .../core/java/com/android/server/audio/BtHelper.java | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index e34d90df8f60..68fa842ed8b7 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -926,6 +926,7 @@ public void setBluetoothA2dpDeviceConnectionState( mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( new AudioDeviceBroker.BtDeviceConnectionInfo(device, state, profile, suppressNoisyIntent, a2dpVolume)); + BtHelper.SetA2dpActiveDevice(null); return; } // state == BluetoothProfile.STATE_CONNECTED @@ -951,6 +952,7 @@ public void setBluetoothA2dpDeviceConnectionState( AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, BtHelper.getName(device), address, a2dpCodec)); if (BtHelper.isTwsPlusSwitch(device, existingDevice.getValue().mDeviceAddress)) { + BtHelper.SetA2dpActiveDevice(device); if (AudioService.DEBUG_DEVICES) { Log.d(TAG,"TWS+ device switch"); } diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 36c30b8fa384..e0d0d370b9a3 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -65,6 +65,7 @@ public class BtHelper { // Reference to BluetoothA2dp to query for AbsoluteVolume. static private @Nullable BluetoothA2dp mA2dp; + static private @Nullable BluetoothDevice mBluetoothA2dpActiveDevice; // If absolute volume is supported in AVRCP device private boolean mAvrcpAbsVolSupported = false; @@ -212,6 +213,11 @@ public boolean equals(Object o) { return deviceName; } + /*packages*/ static void SetA2dpActiveDevice(BluetoothDevice device) { + Log.w(TAG,"SetA2dpActiveDevice for TWS+ pair as " + device); + mBluetoothA2dpActiveDevice = device; + } + /*packages*/ @NonNull static boolean isTwsPlusSwitch(@NonNull BluetoothDevice device, String address) { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -223,8 +229,8 @@ public boolean equals(Object o) { if (device.isTwsPlusDevice() && connDevice.isTwsPlusDevice() && device.getTwsPlusPeerAddress().equals(address)) { - if(mA2dp.getConnectionState(connDevice) != BluetoothProfile.STATE_CONNECTED) { - Log.w(TAG,"Active earbud is already disconnected"); + if (mBluetoothA2dpActiveDevice == null) { + Log.w(TAG,"Not a TwsPlusSwitch as previous active device was null"); return false; } Log.i(TAG,"isTwsPlusSwitch true"); From 508ff0ab8ba63506bf70029ce65030d3454cc5ab Mon Sep 17 00:00:00 2001 From: Manisha Agarwal Date: Thu, 2 Apr 2020 15:22:48 +0530 Subject: [PATCH 63/92] Audio: Update current active bt device of mApmConnectedDevices. Update key of bt-a2dp inside mApmConnectedDevices when the active bt device changes. CRs-Fixed: 2634767 Change-Id: I92fa39581a10fd8900e41139d71ca46dd2445eb6 --- .../core/java/com/android/server/audio/AudioDeviceInventory.java | 1 + 1 file changed, 1 insertion(+) diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 68fa842ed8b7..ebb7373c8847 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -477,6 +477,7 @@ public void onSetA2dpSinkConnectionState(@NonNull BtHelper.BluetoothA2dpDeviceIn if (event == BtHelper.EVENT_ACTIVE_DEVICE_CHANGE) { // Device is connected + mApmConnectedDevices.replace(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, key); if (a2dpVolume != -1) { mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC, // convert index to internal representation in VolumeStreamState From 3dfbd5f19656b0e37038ffadd3c23830dc2937ab Mon Sep 17 00:00:00 2001 From: Zhou Song Date: Thu, 13 Feb 2020 15:46:35 +0800 Subject: [PATCH 64/92] Audio: use cached BT codec info during disconnecting When disconnecting BT device, BT device state machine may be destroyed and cause query of codec failed. This can make device disconnection failed in low layer audio service. CRs-Fixed: 2604724 Change-Id: I970802310eae0b5b43340aaddb312906e1a4c444 --- .../server/audio/AudioDeviceInventory.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index ebb7373c8847..963ab5bf1d46 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -896,7 +896,21 @@ public void setBluetoothA2dpDeviceConnectionState( delay = 0; } - final int a2dpCodec = mDeviceBroker.getA2dpCodec(device); + final int a2dpCodec; + if (state == BluetoothA2dp.STATE_DISCONNECTED) { + final String key = + DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, + device.getAddress()); + final DeviceInfo di = mConnectedDevices.get(key); + if (di != null) { + a2dpCodec = di.mDeviceCodecFormat; + } else { + Log.e(TAG, "invalid null DeviceInfo in setBluetoothA2dpDeviceConnectionState"); + return; + } + } else { + a2dpCodec = mDeviceBroker.getA2dpCodec(device); + } if (AudioService.DEBUG_DEVICES) { Log.i(TAG, "setBluetoothA2dpDeviceConnectionState device: " + device From 28ac1a91feb439dbe9492ca500c24e1c7122cc6b Mon Sep 17 00:00:00 2001 From: Gurpreet Ghai Date: Wed, 28 Aug 2019 11:07:01 +0530 Subject: [PATCH 65/92] Audio: Do not disconnect profiles till Bluetooth Off - Disconnecting and clearing Bluetooth Profiles during Turning Off State doesn't give chance to A2DP/AVRCP profiles to save and retain Audio related data like Volume level. - This change ensures Audio Service only disconnect Bluetooth Profile on receiving Bluetooth Off state. Change-Id: Ie99cb2b35b9ddc7e4f0d0f89407cda9641379fb8 CRs-Fixed: 2512169 --- services/core/java/com/android/server/audio/AudioService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index f37eacf0369b..1de3df161fe5 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -7977,8 +7977,7 @@ public void onReceive(Context context, Intent intent) { UserManager.DISALLOW_RECORD_AUDIO, false, userId); } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); - if (state == BluetoothAdapter.STATE_OFF || - state == BluetoothAdapter.STATE_TURNING_OFF) { + if (state == BluetoothAdapter.STATE_OFF) { mDeviceBroker.disconnectAllBluetoothProfiles(); } } else if (action.equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) || From b959dcfd40ff08d3da61f9f213a9fa0ef37eb67f Mon Sep 17 00:00:00 2001 From: Mingbo Zhang Date: Sun, 9 Aug 2020 21:34:41 +0800 Subject: [PATCH 66/92] Add callback onA2dpCodecConfigChanged Add callback onA2dpCodecConfigChanged for intent BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED. Change-Id: I4d19f0427c2f68dfd01a241eb631c6782d948395 CRs-Fixed: 2735935 --- .../bluetooth/BluetoothCallback.java | 12 +++++++ .../bluetooth/BluetoothEventManager.java | 33 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java index 3152e65d5a36..f0c12a3b5ace 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java @@ -16,6 +16,7 @@ package com.android.settingslib.bluetooth; +import android.bluetooth.BluetoothCodecStatus; /** * BluetoothCallback provides a callback interface for the settings @@ -140,4 +141,15 @@ default void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, */ default void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { } + + /** + * Called when a2dp codec config is changed. It listens to + * {@link android.bluetooth.BluetoothA2dp#ACTION_CODEC_CONFIG_CHANGED}. + * + * @param cachedDevice Bluetooth device that changed + * @param codecStatus the current codec status of the a2dp profile + */ + default void onA2dpCodecConfigChanged(CachedBluetoothDevice cachedDevice, + BluetoothCodecStatus codecStatus) { + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index f82d2ddd8a7b..52874ca09bca 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -18,6 +18,7 @@ import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; @@ -127,6 +128,7 @@ interface Handler { // ACL connection changed broadcasts addHandler(BluetoothDevice.ACTION_ACL_CONNECTED, new AclStateChangedHandler()); addHandler(BluetoothDevice.ACTION_ACL_DISCONNECTED, new AclStateChangedHandler()); + addHandler(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED, new A2dpCodecConfigChangedHandler()); registerAdapterIntentReceiver(); } @@ -241,6 +243,13 @@ private void dispatchAclStateChanged(CachedBluetoothDevice activeDevice, int sta } } + private void dispatchA2dpCodecConfigChanged(CachedBluetoothDevice cachedDevice, + BluetoothCodecStatus codecStatus) { + for (BluetoothCallback callback : mCallbacks) { + callback.onA2dpCodecConfigChanged(cachedDevice, codecStatus); + } + } + @VisibleForTesting void addHandler(String action, Handler handler) { mHandlerMap.put(action, handler); @@ -520,4 +529,28 @@ public void onReceive(Context context, Intent intent, BluetoothDevice device) { dispatchAudioModeChanged(); } } + + private class A2dpCodecConfigChangedHandler implements Handler { + + @Override + public void onReceive(Context context, Intent intent, BluetoothDevice device) { + final String action = intent.getAction(); + if (action == null) { + Log.w(TAG, "A2dpCodecConfigChangedHandler: action is null"); + return; + } + + CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); + if (cachedDevice == null) { + Log.w(TAG, "A2dpCodecConfigChangedHandler: device is null"); + return; + } + + BluetoothCodecStatus codecStatus = intent.getParcelableExtra( + BluetoothCodecStatus.EXTRA_CODEC_STATUS); + Log.d(TAG, "A2dpCodecConfigChangedHandler: device=" + device + + ", codecStatus=" + codecStatus); + dispatchA2dpCodecConfigChanged(cachedDevice, codecStatus); + } + } } From a3a7a2dc8ae1384b9e5c9fa85bd500338124a58b Mon Sep 17 00:00:00 2001 From: Kiran Kelageri Date: Wed, 16 Sep 2020 12:45:12 -0700 Subject: [PATCH 67/92] Bluetooth: Add APTX-Adaptive and APTX-TWS+ entries. Add APTX-Adaptive and APTX-TWS+ entires to bluetooth a2dp codec summaries w.r.t major languages. CRs-Fixed: 2778223 Change-Id: Ia8ce5099a4247cf2e84978e9cfb2ee18864a71d7 --- packages/SettingsLib/res/values-af/arrays.xml | 4 ++++ packages/SettingsLib/res/values-am/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ar/arrays.xml | 4 ++++ packages/SettingsLib/res/values-as/arrays.xml | 4 ++++ packages/SettingsLib/res/values-az/arrays.xml | 4 ++++ packages/SettingsLib/res/values-b+sr+Latn/arrays.xml | 4 ++++ packages/SettingsLib/res/values-be/arrays.xml | 4 ++++ packages/SettingsLib/res/values-bg/arrays.xml | 4 ++++ packages/SettingsLib/res/values-bn/arrays.xml | 4 ++++ packages/SettingsLib/res/values-bs/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ca/arrays.xml | 4 ++++ packages/SettingsLib/res/values-cs/arrays.xml | 4 ++++ packages/SettingsLib/res/values-da/arrays.xml | 4 ++++ packages/SettingsLib/res/values-de/arrays.xml | 4 ++++ packages/SettingsLib/res/values-el/arrays.xml | 4 ++++ packages/SettingsLib/res/values-en-rAU/arrays.xml | 4 ++++ packages/SettingsLib/res/values-en-rCA/arrays.xml | 4 ++++ packages/SettingsLib/res/values-en-rIN/arrays.xml | 4 ++++ packages/SettingsLib/res/values-en-rXC/arrays.xml | 4 ++++ packages/SettingsLib/res/values-es-rUS/arrays.xml | 4 ++++ packages/SettingsLib/res/values-es/arrays.xml | 4 ++++ packages/SettingsLib/res/values-et/arrays.xml | 4 ++++ packages/SettingsLib/res/values-eu/arrays.xml | 4 ++++ packages/SettingsLib/res/values-fa/arrays.xml | 4 ++++ packages/SettingsLib/res/values-fi/arrays.xml | 4 ++++ packages/SettingsLib/res/values-fr-rCA/arrays.xml | 4 ++++ packages/SettingsLib/res/values-fr/arrays.xml | 4 ++++ packages/SettingsLib/res/values-gl/arrays.xml | 4 ++++ packages/SettingsLib/res/values-gu/arrays.xml | 4 ++++ packages/SettingsLib/res/values-hi/arrays.xml | 4 ++++ packages/SettingsLib/res/values-hr/arrays.xml | 4 ++++ packages/SettingsLib/res/values-hu/arrays.xml | 4 ++++ packages/SettingsLib/res/values-hy/arrays.xml | 4 ++++ packages/SettingsLib/res/values-in/arrays.xml | 4 ++++ packages/SettingsLib/res/values-is/arrays.xml | 4 ++++ packages/SettingsLib/res/values-it/arrays.xml | 4 ++++ packages/SettingsLib/res/values-iw/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ja/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ka/arrays.xml | 4 ++++ packages/SettingsLib/res/values-kk/arrays.xml | 4 ++++ packages/SettingsLib/res/values-km/arrays.xml | 4 ++++ packages/SettingsLib/res/values-kn/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ko/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ky/arrays.xml | 4 ++++ packages/SettingsLib/res/values-lo/arrays.xml | 4 ++++ packages/SettingsLib/res/values-lt/arrays.xml | 4 ++++ packages/SettingsLib/res/values-lv/arrays.xml | 4 ++++ packages/SettingsLib/res/values-mk/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ml/arrays.xml | 4 ++++ packages/SettingsLib/res/values-mn/arrays.xml | 4 ++++ packages/SettingsLib/res/values-mr/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ms/arrays.xml | 4 ++++ packages/SettingsLib/res/values-my/arrays.xml | 4 ++++ packages/SettingsLib/res/values-nb/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ne/arrays.xml | 4 ++++ packages/SettingsLib/res/values-nl/arrays.xml | 4 ++++ packages/SettingsLib/res/values-or/arrays.xml | 4 ++++ packages/SettingsLib/res/values-pa/arrays.xml | 4 ++++ packages/SettingsLib/res/values-pl/arrays.xml | 4 ++++ packages/SettingsLib/res/values-pt-rBR/arrays.xml | 4 ++++ packages/SettingsLib/res/values-pt-rPT/arrays.xml | 4 ++++ packages/SettingsLib/res/values-pt/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ro/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ru/arrays.xml | 4 ++++ packages/SettingsLib/res/values-si/arrays.xml | 4 ++++ packages/SettingsLib/res/values-sk/arrays.xml | 4 ++++ packages/SettingsLib/res/values-sl/arrays.xml | 4 ++++ packages/SettingsLib/res/values-sq/arrays.xml | 4 ++++ packages/SettingsLib/res/values-sr/arrays.xml | 4 ++++ packages/SettingsLib/res/values-sv/arrays.xml | 4 ++++ packages/SettingsLib/res/values-sw/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ta/arrays.xml | 4 ++++ packages/SettingsLib/res/values-te/arrays.xml | 4 ++++ packages/SettingsLib/res/values-th/arrays.xml | 4 ++++ packages/SettingsLib/res/values-tl/arrays.xml | 4 ++++ packages/SettingsLib/res/values-tr/arrays.xml | 4 ++++ packages/SettingsLib/res/values-uk/arrays.xml | 4 ++++ packages/SettingsLib/res/values-ur/arrays.xml | 4 ++++ packages/SettingsLib/res/values-uz/arrays.xml | 4 ++++ packages/SettingsLib/res/values-vi/arrays.xml | 4 ++++ packages/SettingsLib/res/values-zh-rCN/arrays.xml | 4 ++++ packages/SettingsLib/res/values-zh-rHK/arrays.xml | 4 ++++ packages/SettingsLib/res/values-zh-rTW/arrays.xml | 4 ++++ packages/SettingsLib/res/values-zu/arrays.xml | 4 ++++ 84 files changed, 336 insertions(+) diff --git a/packages/SettingsLib/res/values-af/arrays.xml b/packages/SettingsLib/res/values-af/arrays.xml index 6e066e5ed121..73b79afaf11e 100644 --- a/packages/SettingsLib/res/values-af/arrays.xml +++ b/packages/SettingsLib/res/values-af/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™-oudio" "Qualcomm® aptX™ HD-oudio" "LDAC" + "Qualcomm® aptX™ Adaptive-oudio" + "Qualcomm® aptX™ TWS+-oudio" "Gebruik stelselkeuse (verstek)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™-oudio" "Qualcomm® aptX™ HD-oudio" "LDAC" + "Qualcomm® aptX™ Adaptive-oudio" + "Qualcomm® aptX™ TWS+-oudio" "Gebruik stelselkeuse (verstek)" diff --git a/packages/SettingsLib/res/values-am/arrays.xml b/packages/SettingsLib/res/values-am/arrays.xml index 1f96752c0f1e..36d3053e99f0 100644 --- a/packages/SettingsLib/res/values-am/arrays.xml +++ b/packages/SettingsLib/res/values-am/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ኦዲዮ" "Qualcomm® aptX™ HD ኦዲዮ" "LDAC" + "Qualcomm® aptX™ Adaptive ኦዲዮ" + "Qualcomm® aptX™ TWS+ ኦዲዮ" "የስርዓቱን ምርጫ (ነባሪ) ተጠቀም" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ኦዲዮ" "Qualcomm® aptX™ HD ኦዲዮ" "LDAC" + "Qualcomm® aptX™ Adaptive ኦዲዮ" + "Qualcomm® aptX™ TWS+ ኦዲዮ" "የስርዓቱን ምርጫ (ነባሪ) ተጠቀም" diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml index f512db09659c..4f3ae3f9ef23 100644 --- a/packages/SettingsLib/res/values-ar/arrays.xml +++ b/packages/SettingsLib/res/values-ar/arrays.xml @@ -92,6 +92,8 @@ "صوت Qualcomm® aptX™" "صوت Qualcomm® aptX™ HD" "LDAC" + "ﺹﻮﺗ Qualcomm® aptX™ Adaptive" + "ﺹﻮﺗ Qualcomm® aptX™ TWS+" "استخدام اختيار النظام (تلقائي)" @@ -100,6 +102,8 @@ "صوت Qualcomm® aptX™" "صوت Qualcomm® aptX™ HD" "LDAC" + "ﺹﻮﺗ Qualcomm® aptX™ Adaptive" + "ﺹﻮﺗ Qualcomm® aptX™ TWS+" "استخدام اختيار النظام (تلقائي)" diff --git a/packages/SettingsLib/res/values-as/arrays.xml b/packages/SettingsLib/res/values-as/arrays.xml index 6a658859a815..691cf066e087 100644 --- a/packages/SettingsLib/res/values-as/arrays.xml +++ b/packages/SettingsLib/res/values-as/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ অডিঅ\'" "Qualcomm® aptX™ HD অডিঅ’" "LDAC" + "Qualcomm® aptX™ Adaptive অডিঅ\'" + "Qualcomm® aptX™ TWS+ অডিঅ\'"" "ছিষ্টেমৰ বাছনি ব্যৱহাৰ কৰক (ডিফ\'ল্ট)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ অডিঅ’" "Qualcomm® aptX™ HD অডিঅ’" "LDAC" + "Qualcomm® aptX™ Adaptive অডিঅ\'" + "Qualcomm® aptX™ TWS+ অডিঅ\'"" "ছিষ্টেমৰ বাছনি ব্যৱহাৰ কৰক (ডিফ\'ল্ট)" diff --git a/packages/SettingsLib/res/values-az/arrays.xml b/packages/SettingsLib/res/values-az/arrays.xml index 4224e5a93d39..c1859aa88ba0 100644 --- a/packages/SettingsLib/res/values-az/arrays.xml +++ b/packages/SettingsLib/res/values-az/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Sistem Seçimini istifadə edin (Defolt)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Sistem Seçimini istifadə edin (Defolt)" diff --git a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml index 9da8745e9473..2e0cbdd83d9c 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Koristi izbor sistema (podrazumevano)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Koristi izbor sistema (podrazumevano)" diff --git a/packages/SettingsLib/res/values-be/arrays.xml b/packages/SettingsLib/res/values-be/arrays.xml index d03f9baaef56..8dfb715a70d1 100644 --- a/packages/SettingsLib/res/values-be/arrays.xml +++ b/packages/SettingsLib/res/values-be/arrays.xml @@ -92,6 +92,8 @@ "Аўдыя Qualcomm® aptX™" "Аўдыя Qualcomm® aptX™ HD" "LDAC" + "Аўдыя Qualcomm® aptX™ Adaptive" + "Аўдыя Qualcomm® aptX™ TWS+" "Выбар сістэмы (стандартны)" @@ -100,6 +102,8 @@ "Аўдыя Qualcomm® aptX™" "Аўдыя Qualcomm® aptX™ HD" "LDAC" + "Аўдыя Qualcomm® aptX™ Adaptive" + "Аўдыя Qualcomm® aptX™ TWS+" "Выбар сістэмы (стандартны)" diff --git a/packages/SettingsLib/res/values-bg/arrays.xml b/packages/SettingsLib/res/values-bg/arrays.xml index b57324d8d957..18265424a6dd 100644 --- a/packages/SettingsLib/res/values-bg/arrays.xml +++ b/packages/SettingsLib/res/values-bg/arrays.xml @@ -92,6 +92,8 @@ "Аудио: aptX™ от Qualcomm®" "Аудио: aptX™ HD от Qualcomm®" "LDAC" + "Аудио: Qualcomm® aptX™ Adaptive" + "Аудио: Qualcomm® aptX™ TWS+" "Използване на сист. избор (стандартно)" @@ -100,6 +102,8 @@ "Аудио: aptX™ от Qualcomm®" "Аудио: aptX™ HD от Qualcomm®" "LDAC" + "Аудио: Qualcomm® aptX™ Adaptive" + "Аудио: Qualcomm® aptX™ TWS+" "Използване на сист. избор (стандартно)" diff --git a/packages/SettingsLib/res/values-bn/arrays.xml b/packages/SettingsLib/res/values-bn/arrays.xml index c476ce91bbed..9faec6025334 100644 --- a/packages/SettingsLib/res/values-bn/arrays.xml +++ b/packages/SettingsLib/res/values-bn/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ অডিও" "Qualcomm® aptX™ HD অডিও" "LDAC" + "Qualcomm® aptX™ Adaptive অডিওa" + "Qualcomm® aptX™ TWS+ অডিও" "সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ অডিও" "Qualcomm® aptX™ HD অডিও" "LDAC" + "Qualcomm® aptX™ Adaptive অডিওa" + "Qualcomm® aptX™ TWS+ অডিও" "সিস্টেমের নির্বাচন ব্যবহার করুন (ডিফল্ট)" diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml index a246f0088ae3..ed0a0ab4363c 100644 --- a/packages/SettingsLib/res/values-bs/arrays.xml +++ b/packages/SettingsLib/res/values-bs/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Korištenje odabira sistema (zadano)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Korištenje odabira sistema (zadano)" diff --git a/packages/SettingsLib/res/values-ca/arrays.xml b/packages/SettingsLib/res/values-ca/arrays.xml index 73f9c1fda319..7614a6ff86bb 100644 --- a/packages/SettingsLib/res/values-ca/arrays.xml +++ b/packages/SettingsLib/res/values-ca/arrays.xml @@ -92,6 +92,8 @@ "Àudio Qualcomm® aptX™" "Àudio Qualcomm® aptX™ HD" "LDAC" + "Àudio Qualcomm® aptX™ Adaptive" + "Àudio Qualcomm® aptX™ TWS+" "Utilitza la selecció del sistema (predeterminada)" @@ -100,6 +102,8 @@ "Àudio Qualcomm® aptX™" "Àudio Qualcomm® aptX™ HD" "LDAC" + "Àudio Qualcomm® aptX™ Adaptive" + "Àudio Qualcomm® aptX™ TWS+" "Utilitza la selecció del sistema (predeterminada)" diff --git a/packages/SettingsLib/res/values-cs/arrays.xml b/packages/SettingsLib/res/values-cs/arrays.xml index 54e809679bc7..29fd0bd43780 100644 --- a/packages/SettingsLib/res/values-cs/arrays.xml +++ b/packages/SettingsLib/res/values-cs/arrays.xml @@ -92,6 +92,8 @@ "Zvuk Qualcomm® aptX™" "Zvuk Qualcomm® aptX™ HD" "LDAC" + "Zvuk Qualcomm® aptX™ Adaptive" + "Zvuk Qualcomm® aptX™ TWS+" "Použít systémový výběr (výchozí)" @@ -100,6 +102,8 @@ "Zvuk Qualcomm® aptX™" "Zvuk Qualcomm® aptX™ HD" "LDAC" + "Zvuk Qualcomm® aptX™ Adaptive" + "Zvuk Qualcomm® aptX™ TWS+" "Použít systémový výběr (výchozí)" diff --git a/packages/SettingsLib/res/values-da/arrays.xml b/packages/SettingsLib/res/values-da/arrays.xml index 07c6ab8505b6..9c925116cd69 100644 --- a/packages/SettingsLib/res/values-da/arrays.xml +++ b/packages/SettingsLib/res/values-da/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™-lyd" "Qualcomm® aptX™ HD-lyd" "LDAC" + "Qualcomm® aptX™ Adaptive-lyd" + "Qualcomm® aptX™ TWS+-lyd" "Brug systemvalg (standard)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™-lyd" "Qualcomm® aptX™ HD-lyd" "LDAC" + "Qualcomm® aptX™ Adaptive-lyd" + "Qualcomm® aptX™ TWS+-lyd" "Brug systemvalg (standard)" diff --git a/packages/SettingsLib/res/values-de/arrays.xml b/packages/SettingsLib/res/values-de/arrays.xml index be226c386015..0ffb6054a549 100644 --- a/packages/SettingsLib/res/values-de/arrays.xml +++ b/packages/SettingsLib/res/values-de/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™-Audio" "Qualcomm® aptX™ HD-Audio" "LDAC" + "Qualcomm® aptX™ Adaptive-Audio" + "Qualcomm® aptX™ TWS+-Audio" "Systemauswahl verwenden (Standard)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™-Audio" "Qualcomm® aptX™ HD-Audio" "LDAC" + "Qualcomm® aptX™ Adaptive-Audio" + "Qualcomm® aptX™ TWS+-Audio" "Systemauswahl verwenden (Standard)" diff --git a/packages/SettingsLib/res/values-el/arrays.xml b/packages/SettingsLib/res/values-el/arrays.xml index 9e7c85ad87a3..f02a933d8ed7 100644 --- a/packages/SettingsLib/res/values-el/arrays.xml +++ b/packages/SettingsLib/res/values-el/arrays.xml @@ -92,6 +92,8 @@ "Ήχος Qualcomm® aptX™" "Ήχος Qualcomm® aptX™ HD" "LDAC" + "Ήχος Qualcomm® aptX™ Adaptive" + "Ήχος Qualcomm® aptX™ TWS+" "Χρήση επιλογής συστήματος (Προεπιλογή)" @@ -100,6 +102,8 @@ "Ήχος Qualcomm® aptX™" "Ήχος Qualcomm® aptX™ HD" "LDAC" + "Ήχος Qualcomm® aptX™ Adaptive" + "Ήχος Qualcomm® aptX™ TWS+" "Χρήση επιλογής συστήματος (Προεπιλογή)" diff --git a/packages/SettingsLib/res/values-en-rAU/arrays.xml b/packages/SettingsLib/res/values-en-rAU/arrays.xml index 83cdadb2e101..6d3e556c642c 100644 --- a/packages/SettingsLib/res/values-en-rAU/arrays.xml +++ b/packages/SettingsLib/res/values-en-rAU/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Use system selection (default)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Use system selection (default)" diff --git a/packages/SettingsLib/res/values-en-rCA/arrays.xml b/packages/SettingsLib/res/values-en-rCA/arrays.xml index 83cdadb2e101..6d3e556c642c 100644 --- a/packages/SettingsLib/res/values-en-rCA/arrays.xml +++ b/packages/SettingsLib/res/values-en-rCA/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Use system selection (default)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Use system selection (default)" diff --git a/packages/SettingsLib/res/values-en-rIN/arrays.xml b/packages/SettingsLib/res/values-en-rIN/arrays.xml index 83cdadb2e101..6d3e556c642c 100644 --- a/packages/SettingsLib/res/values-en-rIN/arrays.xml +++ b/packages/SettingsLib/res/values-en-rIN/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Use system selection (default)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Use system selection (default)" diff --git a/packages/SettingsLib/res/values-en-rXC/arrays.xml b/packages/SettingsLib/res/values-en-rXC/arrays.xml index a35d3ab5ec88..c62c926e70a3 100644 --- a/packages/SettingsLib/res/values-en-rXC/arrays.xml +++ b/packages/SettingsLib/res/values-en-rXC/arrays.xml @@ -92,6 +92,8 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‏‏‎Qualcomm®‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎aptX™‎‏‎‎‏‏‏‎ audio‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎Qualcomm®‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎aptX™ HD‎‏‎‎‏‏‏‎ audio‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎LDAC‎‏‎‎‏‎" + "Qualcomm® aptX™ Adaptive‎ audio‎" + "Qualcomm® aptX™ TWS+‎ audio‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‎‎‎Use System Selection (Default)‎‏‎‎‏‎" @@ -100,6 +102,8 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‎‎‏‎‎‏‏‎Qualcomm®‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎aptX™‎‏‎‎‏‏‏‎ audio‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎Qualcomm®‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎aptX™ HD‎‏‎‎‏‏‏‎ audio‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎LDAC‎‏‎‎‏‎" + "Qualcomm® aptX™ Adaptive‎ audio‎" + "Qualcomm® aptX™ TWS+‎ audio‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‏‏‏‎Use System Selection (Default)‎‏‎‎‏‎" diff --git a/packages/SettingsLib/res/values-es-rUS/arrays.xml b/packages/SettingsLib/res/values-es-rUS/arrays.xml index d187f268aa95..c1a69ea9eb21 100644 --- a/packages/SettingsLib/res/values-es-rUS/arrays.xml +++ b/packages/SettingsLib/res/values-es-rUS/arrays.xml @@ -92,6 +92,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Usar selección del sistema (predeterminado)" @@ -100,6 +102,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Usar selección del sistema (predeterminado)" diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml index eb39d62a79fe..3efb3916d15a 100644 --- a/packages/SettingsLib/res/values-es/arrays.xml +++ b/packages/SettingsLib/res/values-es/arrays.xml @@ -92,6 +92,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Usar preferencia del sistema (predeterminado)" @@ -100,6 +102,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Usar preferencia del sistema (predeterminado)" diff --git a/packages/SettingsLib/res/values-et/arrays.xml b/packages/SettingsLib/res/values-et/arrays.xml index 13f7920faffd..ebfd1d2b2f23 100644 --- a/packages/SettingsLib/res/values-et/arrays.xml +++ b/packages/SettingsLib/res/values-et/arrays.xml @@ -92,6 +92,8 @@ "Heli: Qualcomm® aptX™" "Heli: Qualcomm® aptX™ HD" "LDAC" + "Heli: Qualcomm® aptX™ Adaptive" + "Heli: Qualcomm® aptX™ TWS+" "Süsteemi valiku kasutamine (vaikeseade)" @@ -100,6 +102,8 @@ "Heli: Qualcomm® aptX™" "Heli: Qualcomm® aptX™ HD" "LDAC" + "Heli: Qualcomm® aptX™ Adaptive" + "Heli: Qualcomm® aptX™ TWS+" "Süsteemi valiku kasutamine (vaikeseade)" diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml index 894a05f7966f..d59cc95588c5 100644 --- a/packages/SettingsLib/res/values-eu/arrays.xml +++ b/packages/SettingsLib/res/values-eu/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ audioa" "Qualcomm® aptX™ HD audioa" "LDAC" + "Qualcomm® aptX™ Adaptive audioa" + "Qualcomm® aptX™ TWS+ audioa" "Erabili sistema-hautapena (lehenetsia)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ audioa" "Qualcomm® aptX™ HD audioa" "LDAC" + "Qualcomm® aptX™ Adaptive audioa" + "Qualcomm® aptX™ TWS+ audioa" "Erabili sistema-hautapena (lehenetsia)" diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml index db1dffa423e7..b77461f592c1 100644 --- a/packages/SettingsLib/res/values-fa/arrays.xml +++ b/packages/SettingsLib/res/values-fa/arrays.xml @@ -92,6 +92,8 @@ "صوت Qualcomm® aptX™" "صوت Qualcomm® aptX™ HD" "LDAC" + "ﺹﻮﺗ Qualcomm® aptX™ Adaptive" + "ﺹﻮﺗ Qualcomm® aptX™ TWS+" "استفاده از انتخاب سیستم (پیش‌فرض)" @@ -100,6 +102,8 @@ "صوت Qualcomm® aptX™" "صوت Qualcomm® aptX™ HD" "LDAC" + "ﺹﻮﺗ Qualcomm® aptX™ Adaptive" + "ﺹﻮﺗ Qualcomm® aptX™ TWS+" "استفاده از انتخاب سیستم (پیش‌فرض)" diff --git a/packages/SettingsLib/res/values-fi/arrays.xml b/packages/SettingsLib/res/values-fi/arrays.xml index 90c40cebf956..73e55cc23e5c 100644 --- a/packages/SettingsLib/res/values-fi/arrays.xml +++ b/packages/SettingsLib/res/values-fi/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ‑ääni" "Qualcomm® aptX™ HD ‑ääni" "LDAC" + "Qualcomm® aptX™ Adaptive ‑ääni" + "Qualcomm® aptX™ TWS+ ‑ääni" "Käytä järjestelmän valintaa (oletus)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ‑ääni" "Qualcomm® aptX™ HD ‑ääni" "LDAC" + "Qualcomm® aptX™ Adaptive ‑ääni" + "Qualcomm® aptX™ TWS+ ‑ääni" "Käytä järjestelmän valintaa (oletus)" diff --git a/packages/SettingsLib/res/values-fr-rCA/arrays.xml b/packages/SettingsLib/res/values-fr-rCA/arrays.xml index c566e6ec1f21..1a4d80419bde 100644 --- a/packages/SettingsLib/res/values-fr-rCA/arrays.xml +++ b/packages/SettingsLib/res/values-fr-rCA/arrays.xml @@ -92,6 +92,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Utiliser sélect. du système (par défaut)" @@ -100,6 +102,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Utiliser sélect. du système (par défaut)" diff --git a/packages/SettingsLib/res/values-fr/arrays.xml b/packages/SettingsLib/res/values-fr/arrays.xml index 909324079661..41a793ee0a63 100644 --- a/packages/SettingsLib/res/values-fr/arrays.xml +++ b/packages/SettingsLib/res/values-fr/arrays.xml @@ -92,6 +92,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Utiliser la sélection du système (par défaut)" @@ -100,6 +102,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Utiliser la sélection du système (par défaut)" diff --git a/packages/SettingsLib/res/values-gl/arrays.xml b/packages/SettingsLib/res/values-gl/arrays.xml index bd197e5014f7..14346666b3c0 100644 --- a/packages/SettingsLib/res/values-gl/arrays.xml +++ b/packages/SettingsLib/res/values-gl/arrays.xml @@ -92,6 +92,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Usa a selección do sistema (predeterminado)" @@ -100,6 +102,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Usar selección do sistema (predeterminado)" diff --git a/packages/SettingsLib/res/values-gu/arrays.xml b/packages/SettingsLib/res/values-gu/arrays.xml index 6154d20fd490..a923f6c3a147 100644 --- a/packages/SettingsLib/res/values-gu/arrays.xml +++ b/packages/SettingsLib/res/values-gu/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ઑડિયો" "Qualcomm® aptX™ HD ઑડિયો" "LDAC" + "Qualcomm® aptX™ Adaptive ઑડિઓ" + "Qualcomm® aptX™ TWS+ ઑડિઓ" "સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ઑડિયો" "Qualcomm® aptX™ HD ઑડિયો" "LDAC" + "Qualcomm® aptX™ Adaptive ઑડિઓ" + "Qualcomm® aptX™ TWS+ ઑડિઓ" "સિસ્ટમ પસંદગીનો ઉપયોગ કરો (ડિફૉલ્ટ)" diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml index f8522aa2eb2d..e6e9f051c5be 100644 --- a/packages/SettingsLib/res/values-hi/arrays.xml +++ b/packages/SettingsLib/res/values-hi/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ऑडियो" "Qualcomm® aptX™ HD ऑडियो" "LDAC" + "Qualcomm® aptX™ Adaptiveऑडियो" + "Qualcomm® aptX™ TWS+ ऑडियो" "सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ऑडियो" "Qualcomm® aptX™ HD ऑडियो" "LDAC" + "Qualcomm® aptX™ Adaptiveऑडियो" + "Qualcomm® aptX™ TWS+ ऑडियो" "सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)" diff --git a/packages/SettingsLib/res/values-hr/arrays.xml b/packages/SettingsLib/res/values-hr/arrays.xml index e185f0467c1d..8a6967dd86f8 100644 --- a/packages/SettingsLib/res/values-hr/arrays.xml +++ b/packages/SettingsLib/res/values-hr/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Upotreba odabira sustava (zadano)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Upotreba odabira sustava (zadano)" diff --git a/packages/SettingsLib/res/values-hu/arrays.xml b/packages/SettingsLib/res/values-hu/arrays.xml index 2369aefdbb3d..cf299cb7d078 100644 --- a/packages/SettingsLib/res/values-hu/arrays.xml +++ b/packages/SettingsLib/res/values-hu/arrays.xml @@ -92,6 +92,8 @@ "Hang: Qualcomm® aptX™" "Hang: Qualcomm® aptX™ HD" "LDAC" + "Hang: Qualcomm® aptX™ Adaptive" + "Hang: Qualcomm® aptX™ TWS+" "Rendszerérték (alapértelmezett)" @@ -100,6 +102,8 @@ "Hang: Qualcomm® aptX™" "Hang: Qualcomm® aptX™ HD" "LDAC" + "Hang: Qualcomm® aptX™ Adaptive" + "Hang: Qualcomm® aptX™ TWS+" "Rendszerérték (alapértelmezett)" diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml index d16027af6209..b20989664300 100644 --- a/packages/SettingsLib/res/values-hy/arrays.xml +++ b/packages/SettingsLib/res/values-hy/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ աուդիո" "Qualcomm® aptX™ HD աուդիո" "LDAC" + "Qualcomm® aptX™ Adaptive աուդիո" + "Qualcomm® aptX™ TWS+ աուդիո" "Օգտագործել համակարգի կարգավորումը (կանխադրված)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ աուդիո" "Qualcomm® aptX™ HD աուդիո" "LDAC" + "Qualcomm® aptX™ Adaptive աուդիո" + "Qualcomm® aptX™ TWS+ աուդիո" "Օգտագործել համակարգի կարգավորումը (կանխադրված)" diff --git a/packages/SettingsLib/res/values-in/arrays.xml b/packages/SettingsLib/res/values-in/arrays.xml index 7b4c9b6790ef..20662db52076 100644 --- a/packages/SettingsLib/res/values-in/arrays.xml +++ b/packages/SettingsLib/res/values-in/arrays.xml @@ -92,6 +92,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Gunakan Pilihan Sistem (Default)" @@ -100,6 +102,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Gunakan Pilihan Sistem (Default)" diff --git a/packages/SettingsLib/res/values-is/arrays.xml b/packages/SettingsLib/res/values-is/arrays.xml index b3e7cb90d3b1..2ccca2b0ee4e 100644 --- a/packages/SettingsLib/res/values-is/arrays.xml +++ b/packages/SettingsLib/res/values-is/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ hljóð" "Qualcomm® aptX™ HD hljóð" "LDAC" + "Qualcomm® aptX™ Adaptive hljóð" + "Qualcomm® aptX™ TWS+ hljóð" "Nota val kerfisins (sjálfgefið)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ hljóð" "Qualcomm® aptX™ HD hljóð" "LDAC" + "Qualcomm® aptX™ Adaptive hljóð" + "Qualcomm® aptX™ TWS+ hljóð" "Nota val kerfisins (sjálfgefið)" diff --git a/packages/SettingsLib/res/values-it/arrays.xml b/packages/SettingsLib/res/values-it/arrays.xml index 80b78747a54c..7d279c47d589 100644 --- a/packages/SettingsLib/res/values-it/arrays.xml +++ b/packages/SettingsLib/res/values-it/arrays.xml @@ -92,6 +92,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Usa selezione di sistema (predefinita)" @@ -100,6 +102,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Usa selezione di sistema (predefinita)" diff --git a/packages/SettingsLib/res/values-iw/arrays.xml b/packages/SettingsLib/res/values-iw/arrays.xml index 31abe2beaf49..e89d462bd1db 100644 --- a/packages/SettingsLib/res/values-iw/arrays.xml +++ b/packages/SettingsLib/res/values-iw/arrays.xml @@ -92,6 +92,8 @@ "אודיו Qualcomm® aptX™" "אודיו Qualcomm® aptX™ HD" "LDAC" + "אודיו Qualcomm® aptX™ Adaptive" + "אודיו Qualcomm® aptX™ TWS+" "שימוש בבחירת המערכת (ברירת המחדל)" @@ -100,6 +102,8 @@ "אודיו Qualcomm® aptX™" "אודיו Qualcomm® aptX™ HD" "LDAC" + "אודיו Qualcomm® aptX™ Adaptive" + "אודיו Qualcomm® aptX™ TWS+" "שימוש בבחירת המערכת (ברירת המחדל)" diff --git a/packages/SettingsLib/res/values-ja/arrays.xml b/packages/SettingsLib/res/values-ja/arrays.xml index ff5f3c227a82..f41183201c70 100644 --- a/packages/SettingsLib/res/values-ja/arrays.xml +++ b/packages/SettingsLib/res/values-ja/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ オーディオ" "Qualcomm® aptX™ HD オーディオ" "LDAC" + "Qualcomm® aptX™ Adaptive オーディオ" + "Qualcomm® aptX™ TWS+ オーディオ" "システムの選択(デフォルト)を使用" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ オーディオ" "Qualcomm® aptX™ HD オーディオ" "LDAC" + "Qualcomm® aptX™ Adaptive オーディオ" + "Qualcomm® aptX™ TWS+ オーディオ" "システムの選択(デフォルト)を使用" diff --git a/packages/SettingsLib/res/values-ka/arrays.xml b/packages/SettingsLib/res/values-ka/arrays.xml index 327a77f62496..25792b26849a 100644 --- a/packages/SettingsLib/res/values-ka/arrays.xml +++ b/packages/SettingsLib/res/values-ka/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ აუდიო" "Qualcomm® aptX™ HD აუდიო" "LDAC" + "Qualcomm® aptX™ Adaptive აუდიო" + "Qualcomm® aptX™ TWS+ აუდიო" "სისტემის არჩეულის გამოყენება (ნაგულისხმევი)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ აუდიო" "Qualcomm® aptX™ HD აუდიო" "LDAC" + "Qualcomm® aptX™ Adaptive აუდიო" + "Qualcomm® aptX™ TWS+ აუდიო" "სისტემის არჩეულის გამოყენება (ნაგულისხმევი)" diff --git a/packages/SettingsLib/res/values-kk/arrays.xml b/packages/SettingsLib/res/values-kk/arrays.xml index 28f91dab214e..f45561b8917c 100644 --- a/packages/SettingsLib/res/values-kk/arrays.xml +++ b/packages/SettingsLib/res/values-kk/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ аудиокодегі" "Qualcomm® aptX™ HD аудиокодегі" "LDAC" + "Qualcomm® aptX™ Adaptive аудиокодегі" + "Qualcomm® aptX™ TWS+ аудиокодегі" "Жүйенің таңдағанын алу (әдепкі)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ аудиокодегі" "Qualcomm® aptX™ HD аудиокодегі" "LDAC" + "Qualcomm® aptX™ Adaptive аудиокодегі" + "Qualcomm® aptX™ TWS+ аудиокодегі" "Жүйенің таңдағанын алу (әдепкі)" diff --git a/packages/SettingsLib/res/values-km/arrays.xml b/packages/SettingsLib/res/values-km/arrays.xml index 136f3a558b0f..8ca45cc00069 100644 --- a/packages/SettingsLib/res/values-km/arrays.xml +++ b/packages/SettingsLib/res/values-km/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm®សំឡេង aptX™" "Qualcomm®សំឡេង aptX™ HD" "LDAC" + "Qualcomm® aptX™ Adaptive" + "Qualcomm® aptX™ TWS+" "ប្រើ​ការ​ជ្រើសរើស​ប្រព័ន្ធ (លំនាំ​ដើម)" @@ -100,6 +102,8 @@ "Qualcomm®សំឡេង aptX™" "Qualcomm®សំឡេង aptX™ HD" "LDAC" + "Qualcomm® aptX™ Adaptive" + "Qualcomm® aptX™ TWS+" "ប្រើ​ការ​ជ្រើសរើស​ប្រព័ន្ធ (លំនាំ​ដើម)" diff --git a/packages/SettingsLib/res/values-kn/arrays.xml b/packages/SettingsLib/res/values-kn/arrays.xml index 9bf335076b40..0ed1ca993b13 100644 --- a/packages/SettingsLib/res/values-kn/arrays.xml +++ b/packages/SettingsLib/res/values-kn/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ಆಡಿಯೋ" "Qualcomm® aptX™ HD ಆಡಿಯೋ" "LDAC" + "Qualcomm® aptX™ Adaptive ಆಡಿಯೋ" + "Qualcomm® aptX™ TWS+ ಆಡಿಯೋ" "ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ಆಡಿಯೋ" "Qualcomm® aptX™ HD ಆಡಿಯೋ" "LDAC" + "Qualcomm® aptX™ Adaptive ಆಡಿಯೋ" + "Qualcomm® aptX™ TWS+ ಆಡಿಯೋ" "ಸಿಸ್ಟಂ ಆಯ್ಕೆಯನ್ನು ಬಳಸಿ (ಡಿಫಾಲ್ಟ್)" diff --git a/packages/SettingsLib/res/values-ko/arrays.xml b/packages/SettingsLib/res/values-ko/arrays.xml index 195fc47e8c64..26d0c1043924 100644 --- a/packages/SettingsLib/res/values-ko/arrays.xml +++ b/packages/SettingsLib/res/values-ko/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ 오디오" "Qualcomm® aptX™ HD 오디오" "LDAC" + "Qualcomm® aptX™ Adaptive 오디오" + "Qualcomm® aptX™ TWS+ 오디오" "시스템 설정 사용(기본)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ 오디오" "Qualcomm® aptX™ HD 오디오" "LDAC" + "Qualcomm® aptX™ Adaptive 오디오" + "Qualcomm® aptX™ TWS+ 오디오" "시스템 설정 사용(기본)" diff --git a/packages/SettingsLib/res/values-ky/arrays.xml b/packages/SettingsLib/res/values-ky/arrays.xml index 7c0fbaee2768..993d81e570b5 100644 --- a/packages/SettingsLib/res/values-ky/arrays.xml +++ b/packages/SettingsLib/res/values-ky/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ аудио" "Qualcomm® aptX™ HD аудио" "LDAC" + "Qualcomm® aptX™ Adaptive аудио" + "Qualcomm® aptX™ TWS+ аудио" "Система тандаганды колдонуу (демейки)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ аудио" "Qualcomm® aptX™ HD аудио" "LDAC" + "Qualcomm® aptX™ Adaptive аудио" + "Qualcomm® aptX™ TWS+ аудио" "Система тандаганды колдонуу (демейки)" diff --git a/packages/SettingsLib/res/values-lo/arrays.xml b/packages/SettingsLib/res/values-lo/arrays.xml index 2487ebe2b8d5..287ba9e3ae18 100644 --- a/packages/SettingsLib/res/values-lo/arrays.xml +++ b/packages/SettingsLib/res/values-lo/arrays.xml @@ -92,6 +92,8 @@ "ສຽງ Qualcomm® aptX™" "ສຽງ Qualcomm® aptX™ HD" "LDAC" + "ສຽງ Qualcomm® aptX™ Adaptive" + "ສຽງ Qualcomm® aptX™ TWS+" "ໃຊ້ການເລືອກຂອງລະບົບ (ຄ່າເລີ່ມຕົ້ນ)" @@ -100,6 +102,8 @@ "ສຽງ Qualcomm® aptX™" "ສຽງ Qualcomm® aptX™ HD" "LDAC" + "ສຽງ Qualcomm® aptX™ Adaptive" + "ສຽງ Qualcomm® aptX™ TWS+" "ໃຊ້ການເລືອກຂອງລະບົບ (ຄ່າເລີ່ມຕົ້ນ)" diff --git a/packages/SettingsLib/res/values-lt/arrays.xml b/packages/SettingsLib/res/values-lt/arrays.xml index 72e55ff7cece..04d02aaf6c69 100644 --- a/packages/SettingsLib/res/values-lt/arrays.xml +++ b/packages/SettingsLib/res/values-lt/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ garsas" "Qualcomm® aptX™ HD garsas" "LDAC" + "Qualcomm® aptX™ Adaptive garsas" + "Qualcomm® aptX™ TWS+ garsas" "Naudoti sistemos pasirink. (numatytasis)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ garsas" "Qualcomm® aptX™ HD garsas" "LDAC" + "Qualcomm® aptX™ Adaptive garsas" + "Qualcomm® aptX™ TWS+ garsas" "Naudoti sistemos pasirink. (numatytasis)" diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml index da3325c5d0cf..66935c48ecbb 100644 --- a/packages/SettingsLib/res/values-lv/arrays.xml +++ b/packages/SettingsLib/res/values-lv/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Sistēmas atlases izmantošana (nokl.)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Sistēmas atlases izmantošana (nokl.)" diff --git a/packages/SettingsLib/res/values-mk/arrays.xml b/packages/SettingsLib/res/values-mk/arrays.xml index 908571dd9480..d204529bcfe6 100644 --- a/packages/SettingsLib/res/values-mk/arrays.xml +++ b/packages/SettingsLib/res/values-mk/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ аудио" "Qualcomm® aptX™ HD аудио" "LDAC" + "Qualcomm® aptX™ Adaptive аудио" + "Qualcomm® aptX™ TWS+ аудио" "Користи избор на системот (стандардно)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ аудио" "Qualcomm® aptX™ HD аудио" "LDAC" + "Qualcomm® aptX™ Adaptive аудио" + "Qualcomm® aptX™ TWS+ аудио" "Користи избор на системот (стандардно)" diff --git a/packages/SettingsLib/res/values-ml/arrays.xml b/packages/SettingsLib/res/values-ml/arrays.xml index fd0c2e5b0dd8..48fe1e41aa0a 100644 --- a/packages/SettingsLib/res/values-ml/arrays.xml +++ b/packages/SettingsLib/res/values-ml/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ഓഡിയോ" "Qualcomm® aptX™ HD ഓഡിയോ" "LDAC" + "Qualcomm® aptX™ Adaptive ഓഡിയോ" + "Qualcomm® aptX™ TWS+ ഓഡിയോ" "സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ഓഡിയോ" "Qualcomm® aptX™ HD ഓഡിയോ" "LDAC" + "Qualcomm® aptX™ Adaptive ഓഡിയോ" + "Qualcomm® aptX™ TWS+ ഓഡിയോ" "സിസ്റ്റം സെലക്ഷൻ ഉപയോഗിക്കൂ ‌(ഡിഫോൾട്ട്)" diff --git a/packages/SettingsLib/res/values-mn/arrays.xml b/packages/SettingsLib/res/values-mn/arrays.xml index 2b02d620ac54..476c311c30e2 100644 --- a/packages/SettingsLib/res/values-mn/arrays.xml +++ b/packages/SettingsLib/res/values-mn/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ аудио" "Qualcomm® aptX™ HD аудио" "LDAC" + "Qualcomm® aptX™ Adaptive аудио" + "Qualcomm® aptX™ TWS+ аудио" "Системийн сонголтыг ашиглах (Өгөгдмөл)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ аудио" "Qualcomm® aptX™ HD аудио" "LDAC" + "Qualcomm® aptX™ Adaptive аудио" + "Qualcomm® aptX™ TWS+ аудио" "Системийн сонголтыг ашиглах (Өгөгдмөл)" diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml index 6cc013071223..cb89f693e311 100644 --- a/packages/SettingsLib/res/values-mr/arrays.xml +++ b/packages/SettingsLib/res/values-mr/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ऑडिओ" "Qualcomm® aptX™ HD ऑडिओ" "LDAC" + "Qualcomm® aptX™ Adaptive ऑडिओ" + "Qualcomm® aptX™ TWS+ ऑडिओ" "सिस्टम निवड वापरा (डीफॉल्ट)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ऑडिओ" "Qualcomm® aptX™ HD ऑडिओ" "LDAC" + "Qualcomm® aptX™ Adaptive ऑडिओ" + "Qualcomm® aptX™ TWS+ ऑडिओ" "सिस्टम निवड वापरा (डीफॉल्ट)" diff --git a/packages/SettingsLib/res/values-ms/arrays.xml b/packages/SettingsLib/res/values-ms/arrays.xml index af6d3c27a371..7757fb81ea23 100644 --- a/packages/SettingsLib/res/values-ms/arrays.xml +++ b/packages/SettingsLib/res/values-ms/arrays.xml @@ -92,6 +92,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Gunakan Pilihan Sistem (Lalai)" @@ -100,6 +102,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Gunakan Pilihan Sistem (Lalai)" diff --git a/packages/SettingsLib/res/values-my/arrays.xml b/packages/SettingsLib/res/values-my/arrays.xml index cb97802039d5..726f7db364a9 100644 --- a/packages/SettingsLib/res/values-my/arrays.xml +++ b/packages/SettingsLib/res/values-my/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ အသံ" "Qualcomm® aptX™ HD အသံ" "LDAC" + "Qualcomm® aptX™ Adaptive အသံ" + "Qualcomm® aptX™ TWS+ အသံ" "စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ အသံ" "Qualcomm® aptX™ HD အသံ" "LDAC" + "Qualcomm® aptX™ Adaptive အသံ" + "Qualcomm® aptX™ TWS+ အသံ" "စနစ်ရွေးချယ်မှုကို အသုံးပြုပါ (မူရင်း)" diff --git a/packages/SettingsLib/res/values-nb/arrays.xml b/packages/SettingsLib/res/values-nb/arrays.xml index 79d3587ac366..54b5127b1f87 100644 --- a/packages/SettingsLib/res/values-nb/arrays.xml +++ b/packages/SettingsLib/res/values-nb/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™-lyd" "Qualcomm® aptX™ HD-lyd" "LDAC" + "Qualcomm® aptX™ Adaptive-lyd" + "Qualcomm® aptX™ TWS+-lyd" "Bruk systemvalg (standard)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™-lyd" "Qualcomm® aptX™ HD-lyd" "LDAC" + "Qualcomm® aptX™ Adaptive-lyd" + "Qualcomm® aptX™ TWS+-lyd" "Bruk systemvalg (standard)" diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml index 3699bf3ec59c..cb0724ee6704 100644 --- a/packages/SettingsLib/res/values-ne/arrays.xml +++ b/packages/SettingsLib/res/values-ne/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ अडियो" "Qualcomm® aptX™ HD अडियो" "LDAC" + "Qualcomm® aptX™ Adaptive अडियो" + "Qualcomm® aptX™ TWS+ अडियो" "सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ अडियो" "Qualcomm® aptX™ HD अडियो" "LDAC" + "Qualcomm® aptX™ Adaptive अडियो" + "Qualcomm® aptX™ TWS+ अडियो" "सिस्टमको छनौट प्रयोग गरियोस् (डिफल्ट)" diff --git a/packages/SettingsLib/res/values-nl/arrays.xml b/packages/SettingsLib/res/values-nl/arrays.xml index e73a33b18753..1fefbb2ad114 100644 --- a/packages/SettingsLib/res/values-nl/arrays.xml +++ b/packages/SettingsLib/res/values-nl/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Gebruik systeemselectie (standaard)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ audio" "Qualcomm® aptX™ HD audio" "LDAC" + "Qualcomm® aptX™ Adaptive audio" + "Qualcomm® aptX™ TWS+ audio" "Systeemselectie gebruiken (standaard)" diff --git a/packages/SettingsLib/res/values-or/arrays.xml b/packages/SettingsLib/res/values-or/arrays.xml index 801e6976435d..1365b6ab8a7e 100644 --- a/packages/SettingsLib/res/values-or/arrays.xml +++ b/packages/SettingsLib/res/values-or/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ଅଡିଓ" "Qualcomm® aptX™ HD ଅଡିଓ" "LDAC" + "Qualcomm® aptX™ Adaptive ଅଡିଓ" + "Qualcomm® aptX™ TWS+ ଅଡିଓ" "ସିଷ୍ଟମ୍‌ର ଚୟନ (ଡିଫଲ୍ଟ୍) ବ୍ୟବହାର କରନ୍ତୁ" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ଅଡିଓ" "Qualcomm® aptX™ HD ଅଡିଓ" "LDAC" + "Qualcomm® aptX™ Adaptive ଅଡିଓ" + "Qualcomm® aptX™ TWS+ ଅଡିଓ" "ସିଷ୍ଟମ୍‌ର ଚୟନ (ଡିଫଲ୍ଟ୍) ବ୍ୟବହାର କରନ୍ତୁ" diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml index a2daef2052b3..b6668920623e 100644 --- a/packages/SettingsLib/res/values-pa/arrays.xml +++ b/packages/SettingsLib/res/values-pa/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ਆਡੀਓ" "Qualcomm® aptX™ HD ਆਡੀਓ" "LDAC" + "Qualcomm® aptX™ Adaptive ਆਡੀਓ" + "Qualcomm® aptX™ TWS+ ਆਡੀਓ" "ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ਆਡੀਓ" "Qualcomm® aptX™ HD ਆਡੀਓ" "LDAC" + "Qualcomm® aptX™ Adaptive ਆਡੀਓ" + "Qualcomm® aptX™ TWS+ ਆਡੀਓ" "ਸਿਸਟਮ ਚੋਣ ਦੀ ਵਰਤੋਂ ਕਰੋ (ਪੂਰਵ-ਨਿਰਧਾਰਿਤ)" diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml index 22b05399346b..95ec079429e3 100644 --- a/packages/SettingsLib/res/values-pl/arrays.xml +++ b/packages/SettingsLib/res/values-pl/arrays.xml @@ -92,6 +92,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Użyj wyboru systemu (domyślnie)" @@ -100,6 +102,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Użyj wyboru systemu (domyślnie)" diff --git a/packages/SettingsLib/res/values-pt-rBR/arrays.xml b/packages/SettingsLib/res/values-pt-rBR/arrays.xml index 5155311b24a4..93f868e4ab75 100644 --- a/packages/SettingsLib/res/values-pt-rBR/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rBR/arrays.xml @@ -92,6 +92,8 @@ "Áudio Qualcomm® aptX™" "Áudio Qualcomm® aptX™ HD" "LDAC" + "Áudio Qualcomm® aptX™ Adaptive" + "Áudio Qualcomm® aptX™ TWS+" "Usar seleção do sistema (padrão)" @@ -100,6 +102,8 @@ "Áudio Qualcomm® aptX™" "Áudio Qualcomm® aptX™ HD" "LDAC" + "Áudio Qualcomm® aptX™ Adaptive" + "Áudio Qualcomm® aptX™ TWS+" "Usar seleção do sistema (padrão)" diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml index ff8e202815c6..9c14e8c6a6a3 100644 --- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml @@ -92,6 +92,8 @@ "Áudio Qualcomm® aptX™" "Áudio Qualcomm® aptX™ HD" "LDAC" + "Áudio Qualcomm® aptX™ Adaptive" + "Áudio Qualcomm® aptX™ TWS+" "Utilizar seleção do sistema (predefinido)" @@ -100,6 +102,8 @@ "Áudio Qualcomm® aptX™" "Áudio Qualcomm® aptX™ HD" "LDAC" + "Áudio Qualcomm® aptX™ Adaptive" + "Áudio Qualcomm® aptX™ TWS+" "Utilizar seleção do sistema (predefinido)" diff --git a/packages/SettingsLib/res/values-pt/arrays.xml b/packages/SettingsLib/res/values-pt/arrays.xml index 5155311b24a4..dd60d9ea8aa8 100644 --- a/packages/SettingsLib/res/values-pt/arrays.xml +++ b/packages/SettingsLib/res/values-pt/arrays.xml @@ -92,6 +92,8 @@ "Áudio Qualcomm® aptX™" "Áudio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Usar seleção do sistema (padrão)" @@ -100,6 +102,8 @@ "Áudio Qualcomm® aptX™" "Áudio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Usar seleção do sistema (padrão)" diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml index c4d61c3f0f73..9d7f22c3077c 100644 --- a/packages/SettingsLib/res/values-ro/arrays.xml +++ b/packages/SettingsLib/res/values-ro/arrays.xml @@ -92,6 +92,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Folosiți selectarea sistemului (prestabilit)" @@ -100,6 +102,8 @@ "Audio Qualcomm® aptX™" "Audio Qualcomm® aptX™ HD" "LDAC" + "Audio Qualcomm® aptX™ Adaptive" + "Audio Qualcomm® aptX™ TWS+" "Folosiți selectarea sistemului (prestabilit)" diff --git a/packages/SettingsLib/res/values-ru/arrays.xml b/packages/SettingsLib/res/values-ru/arrays.xml index 318944d748fc..ffb6b539c212 100644 --- a/packages/SettingsLib/res/values-ru/arrays.xml +++ b/packages/SettingsLib/res/values-ru/arrays.xml @@ -92,6 +92,8 @@ "Аудиокодек: Qualcomm® aptX™" "Аудиокодек: Qualcomm® aptX™ HD" "LDAC" + "Аудиокодек: Qualcomm® aptX™ Adaptive" + "Аудиокодек: Qualcomm® aptX™ TWS+" "Выбор системы (по умолчанию)" @@ -100,6 +102,8 @@ "Аудиокодек: Qualcomm® aptX™" "Аудиокодек: Qualcomm® aptX™ HD" "LDAC" + "Аудиокодек: Qualcomm® aptX™ Adaptive" + "Аудиокодек: Qualcomm® aptX™ TWS+" "Выбор системы (по умолчанию)" diff --git a/packages/SettingsLib/res/values-si/arrays.xml b/packages/SettingsLib/res/values-si/arrays.xml index b0fc7531e6d4..9f114291f793 100644 --- a/packages/SettingsLib/res/values-si/arrays.xml +++ b/packages/SettingsLib/res/values-si/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ශ්‍රව්‍යය" "Qualcomm® aptX™ HD ශ්‍රව්‍යය" "LDAC" + "Qualcomm® aptX™ Adaptive ශ්‍රව්‍යය" + "Qualcomm® aptX™ TWS+ ශ්‍රව්‍යය" "පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ශ්‍රව්‍යය" "Qualcomm® aptX™ HD ශ්‍රව්‍යය" "LDAC" + "Qualcomm® aptX™ Adaptive ශ්‍රව්‍යය" + "Qualcomm® aptX™ TWS+ ශ්‍රව්‍යය" "පද්ධති තේරීම භාවිත කරන්න (පෙරනිමි)" diff --git a/packages/SettingsLib/res/values-sk/arrays.xml b/packages/SettingsLib/res/values-sk/arrays.xml index 35cc015616bf..fc4915f3e261 100644 --- a/packages/SettingsLib/res/values-sk/arrays.xml +++ b/packages/SettingsLib/res/values-sk/arrays.xml @@ -92,6 +92,8 @@ "Zvuk: Qualcomm® aptX™" "Zvuk: Qualcomm® aptX™ HD" "LDAC" + "Zvuk: Qualcomm® aptX™ Adaptive" + "Zvuk: Qualcomm® aptX™ TWS+" "Použiť voľbu systému (predvolené)" @@ -100,6 +102,8 @@ "Zvuk: Qualcomm® aptX™" "Zvuk: Qualcomm® aptX™ HD" "LDAC" + "Zvuk: Qualcomm® aptX™ Adaptive" + "Zvuk: Qualcomm® aptX™ TWS+" "Použiť voľbu systému (predvolené)" diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml index a0ff15e554c7..95119d439f4d 100644 --- a/packages/SettingsLib/res/values-sl/arrays.xml +++ b/packages/SettingsLib/res/values-sl/arrays.xml @@ -92,6 +92,8 @@ "Zvok Qualcomm® aptX™" "Zvok Qualcomm® aptX™ HD" "LDAC" + "Zvok Qualcomm® aptX™ Adaptive" + "Zvok Qualcomm® aptX™ TWS+" "Uporabi sistemsko izbiro (privzeto)" @@ -100,6 +102,8 @@ "Zvok Qualcomm® aptX™" "Zvok Qualcomm® aptX™ HD" "LDAC" + "Zvok Qualcomm® aptX™ Adaptive" + "Zvok Qualcomm® aptX™ TWS+" "Uporabi sistemsko izbiro (privzeto)" diff --git a/packages/SettingsLib/res/values-sq/arrays.xml b/packages/SettingsLib/res/values-sq/arrays.xml index d04c6ce9bec6..3cbb53045479 100644 --- a/packages/SettingsLib/res/values-sq/arrays.xml +++ b/packages/SettingsLib/res/values-sq/arrays.xml @@ -92,6 +92,8 @@ "Audioja e Qualcomm® aptX™" "Audioja e Qualcomm® aptX™ HD" "LDAC" + "Audioja e Qualcomm® aptX™ Adaptive" + "Audioja e Qualcomm® aptX™ TWS+" "Përdor përzgjedhjen e sistemit (e parazgjedhur)" @@ -100,6 +102,8 @@ "Audioja e Qualcomm® aptX™" "Audioja e Qualcomm® aptX™ HD" "LDAC" + "Audioja e Qualcomm® aptX™ Adaptive" + "Audioja e Qualcomm® aptX™ TWS+" "Përdor përzgjedhjen e sistemit (e parazgjedhur)" diff --git a/packages/SettingsLib/res/values-sr/arrays.xml b/packages/SettingsLib/res/values-sr/arrays.xml index 6fbc563313b6..5711eed9b10e 100644 --- a/packages/SettingsLib/res/values-sr/arrays.xml +++ b/packages/SettingsLib/res/values-sr/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ аудио" "Qualcomm® aptX™ HD аудио" "LDAC" + "Qualcomm® aptX™ Adaptive аудио" + "Qualcomm® aptX™ TWS+ аудио" "Користи избор система (подразумевано)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ аудио" "Qualcomm® aptX™ HD аудио" "LDAC" + "Qualcomm® aptX™ Adaptive аудио" + "Qualcomm® aptX™ TWS+ аудио" "Користи избор система (подразумевано)" diff --git a/packages/SettingsLib/res/values-sv/arrays.xml b/packages/SettingsLib/res/values-sv/arrays.xml index 721553e7e2b7..08d6462d1af1 100644 --- a/packages/SettingsLib/res/values-sv/arrays.xml +++ b/packages/SettingsLib/res/values-sv/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™-ljud" "Qualcomm® aptX™ HD-ljud" "LDAC" + "Qualcomm® aptX™ Adaptive-ljud" + "Qualcomm® aptX™ TWS+-ljud" "Använd systemval (standardinställning)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™-ljud" "Qualcomm® aptX™ HD-ljud" "LDAC" + "Qualcomm® aptX™ Adaptive-ljud" + "Qualcomm® aptX™ TWS+-ljud" "Använd systemval (standardinställning)" diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml index b95d69c95fc7..ba2a2939d1a2 100644 --- a/packages/SettingsLib/res/values-sw/arrays.xml +++ b/packages/SettingsLib/res/values-sw/arrays.xml @@ -92,6 +92,8 @@ "Sauti ya Qualcomm® aptX™" "Sauti ya Qualcomm® aptX™ HD" "LDAC" + "Sauti ya Qualcomm® aptX™ Adaptive" + "Sauti ya Qualcomm® aptX™ TWS+" "Tumia Uteuzi wa Mfumo (Chaguomsingi)" @@ -100,6 +102,8 @@ "Sauti ya Qualcomm® aptX™" "Sauti ya Qualcomm® aptX™ HD" "LDAC" + "Sauti ya Qualcomm® aptX™ Adaptive" + "Sauti ya Qualcomm® aptX™ TWS+" "Tumia Uteuzi wa Mfumo (Chaguomsingi)" diff --git a/packages/SettingsLib/res/values-ta/arrays.xml b/packages/SettingsLib/res/values-ta/arrays.xml index 79d7c575e7fd..85ed73a9d89e 100644 --- a/packages/SettingsLib/res/values-ta/arrays.xml +++ b/packages/SettingsLib/res/values-ta/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ஆடியோ" "Qualcomm® aptX™ HD ஆடியோ" "LDAC" + "Qualcomm® aptX™ Adaptive ஆடியோ" + "Qualcomm® aptX™ TWS+ ஆடியோ" "சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ஆடியோ" "Qualcomm® aptX™ HD ஆடியோ" "LDAC" + "Qualcomm® aptX™ Adaptive ஆடியோ" + "Qualcomm® aptX™ TWS+ ஆடியோ" "சாதனத் தேர்வைப் பயன்படுத்து (இயல்பு)" diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml index 67decc9eff68..1ecbb3cbbcc2 100644 --- a/packages/SettingsLib/res/values-te/arrays.xml +++ b/packages/SettingsLib/res/values-te/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ఆడియో" "Qualcomm® aptX™ HD ఆడియో" "LDAC" + "Qualcomm® aptX™ Adaptive ఆడియో" + "Qualcomm® aptX™ TWS+ ఆడియో" "సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ఆడియో" "Qualcomm® aptX™ HD ఆడియో" "LDAC" + "Qualcomm® aptX™ Adaptive ఆడియో" + "Qualcomm® aptX™ TWS+ ఆడియో" "సిస్టమ్ ఎంపికను ఉపయోగించండి (ఆటోమేటిక్)" diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml index 9f428e4be5d5..84dfd29f1a72 100644 --- a/packages/SettingsLib/res/values-th/arrays.xml +++ b/packages/SettingsLib/res/values-th/arrays.xml @@ -92,6 +92,8 @@ "เสียง Qualcomm® aptX™" "เสียง Qualcomm® aptX™ HD" "LDAC" + "เสียง Qualcomm® aptX™ Adaptive" + "เสียง Qualcomm® aptX™ TWS+" "ใช้การเลือกของระบบ (ค่าเริ่มต้น)" @@ -100,6 +102,8 @@ "เสียง Qualcomm® aptX™" "เสียง Qualcomm® aptX™ HD" "LDAC" + "เสียง Qualcomm® aptX™ Adaptive" + "เสียง Qualcomm® aptX™ TWS+" "ใช้การเลือกของระบบ (ค่าเริ่มต้น)" diff --git a/packages/SettingsLib/res/values-tl/arrays.xml b/packages/SettingsLib/res/values-tl/arrays.xml index 18800666c774..83cb71e46faf 100644 --- a/packages/SettingsLib/res/values-tl/arrays.xml +++ b/packages/SettingsLib/res/values-tl/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ na audio" "Qualcomm® aptX™ HD na audio" "LDAC" + "Qualcomm® aptX™ Adaptive na audio" + "Qualcomm® aptX™ TWS+ na audio" "Gamitin ang Pagpili ng System (Default)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ na audio" "Qualcomm® aptX™ HD na audio" "LDAC" + "Qualcomm® aptX™ Adaptive na audio" + "Qualcomm® aptX™ TWS+ na audio" "Gamitin ang Pagpili ng System (Default)" diff --git a/packages/SettingsLib/res/values-tr/arrays.xml b/packages/SettingsLib/res/values-tr/arrays.xml index b4dcfba953b3..a1e8ac0ee4ba 100644 --- a/packages/SettingsLib/res/values-tr/arrays.xml +++ b/packages/SettingsLib/res/values-tr/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ ses" "Qualcomm® aptX™ HD ses" "LDAC" + "Qualcomm® aptX™ Adaptive ses" + "Qualcomm® aptX™ TWS+ ses" "Sistem Seçimini Kullan (Varsayılan)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ ses" "Qualcomm® aptX™ HD ses" "LDAC" + "Qualcomm® aptX™ Adaptive ses" + "Qualcomm® aptX™ TWS+ ses" "Sistem Seçimini Kullan (Varsayılan)" diff --git a/packages/SettingsLib/res/values-uk/arrays.xml b/packages/SettingsLib/res/values-uk/arrays.xml index d48e20a2a613..210f28a15001 100644 --- a/packages/SettingsLib/res/values-uk/arrays.xml +++ b/packages/SettingsLib/res/values-uk/arrays.xml @@ -92,6 +92,8 @@ "Аудіо Qualcomm® aptX™" "Аудіо Qualcomm® aptX™ HD" "LDAC" + "Аудіо Qualcomm® aptX™ Adaptive" + "Аудіо Qualcomm® aptX™ TWS+" "Використовувати вибір системи (за умовчанням)" @@ -100,6 +102,8 @@ "Аудіо Qualcomm® aptX™" "Аудіо Qualcomm® aptX™ HD" "LDAC" + "Аудіо Qualcomm® aptX™ Adaptive" + "Аудіо Qualcomm® aptX™ TWS+" "Використовувати вибір системи (за умовчанням)" diff --git a/packages/SettingsLib/res/values-ur/arrays.xml b/packages/SettingsLib/res/values-ur/arrays.xml index ea6fdda370bd..903ef6233b2d 100644 --- a/packages/SettingsLib/res/values-ur/arrays.xml +++ b/packages/SettingsLib/res/values-ur/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ آڈیو" "Qualcomm® aptX™ HD آڈیو" "LDAC" + "Qualcomm® aptX™ Adaptive ﺁڈیﻭ" + "Qualcomm® aptX™ TWS+ ﺁڈیﻭ" "سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ آڈیو" "Qualcomm® aptX™ HD آڈیو" "LDAC" + "Qualcomm® aptX™ Adaptive ﺁڈیﻭ" + "Qualcomm® aptX™ TWS+ ﺁڈیﻭ" "سسٹم انتخاب کا استعمال کریں (ڈیفالٹ)" diff --git a/packages/SettingsLib/res/values-uz/arrays.xml b/packages/SettingsLib/res/values-uz/arrays.xml index 2fa8b0e1e525..d093b5c8668b 100644 --- a/packages/SettingsLib/res/values-uz/arrays.xml +++ b/packages/SettingsLib/res/values-uz/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ audiokodeki" "Qualcomm® aptX™ HD audiokodeki" "LDAC" + "Qualcomm® aptX™ Adaptive audiokodeki" + "Qualcomm® aptX™ TWS+ audiokodeki" "Tizim tanlovi (birlamchi)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ audiokodeki" "Qualcomm® aptX™ HD audiokodeki" "LDAC" + "Qualcomm® aptX™ Adaptive audiokodeki" + "Qualcomm® aptX™ TWS+ audiokodeki" "Tizim tanlovi (birlamchi)" diff --git a/packages/SettingsLib/res/values-vi/arrays.xml b/packages/SettingsLib/res/values-vi/arrays.xml index 5b59f4006a94..a53678879556 100644 --- a/packages/SettingsLib/res/values-vi/arrays.xml +++ b/packages/SettingsLib/res/values-vi/arrays.xml @@ -92,6 +92,8 @@ "Âm thanh Qualcomm® aptX™" "Âm thanh Qualcomm® aptX™ HD" "LDAC" + "Âm thanh Qualcomm® aptX™ Adaptive" + "Âm thanh Qualcomm® aptX™ TWS+" "Sử dụng lựa chọn của hệ thống (Mặc định)" @@ -100,6 +102,8 @@ "Âm thanh Qualcomm® aptX™" "Âm thanh Qualcomm® aptX™ HD" "LDAC" + "Âm thanh Qualcomm® aptX™ Adaptive" + "Âm thanh Qualcomm® aptX™ TWS+" "Sử dụng lựa chọn của hệ thống (Mặc định)" diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml index 400973b4e095..d10481bbce2e 100644 --- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ 音频" "Qualcomm® aptX™ HD 音频" "LDAC" + "Qualcomm® aptX™ Adaptive 音频" + "Qualcomm® aptX™ TWS+ 音频" "使用系统选择(默认)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ 音频" "Qualcomm® aptX™ HD 音频" "LDAC" + "Qualcomm® aptX™ Adaptive 音频" + "Qualcomm® aptX™ TWS+ 音频" "使用系统选择(默认)" diff --git a/packages/SettingsLib/res/values-zh-rHK/arrays.xml b/packages/SettingsLib/res/values-zh-rHK/arrays.xml index 17e45c5ea423..8c68b16ac6c0 100644 --- a/packages/SettingsLib/res/values-zh-rHK/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rHK/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ 音訊" "Qualcomm® aptX™ HD 音訊" "LDAC" + "Qualcomm® aptX™ Adaptive 音訊" + "Qualcomm® aptX™ TWS+ 音訊" "使用系統選擇 (預設)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ 音訊" "Qualcomm® aptX™ HD 音訊" "LDAC" + "Qualcomm® aptX™ Adaptive 音訊" + "Qualcomm® aptX™ TWS+ 音訊" "使用系統選擇 (預設)" diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml index 0728f1aad28e..13fafed5c0b6 100644 --- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml +++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ 音訊" "Qualcomm® aptX™ HD 音訊" "LDAC" + "Qualcomm® aptX™ Adaptive 音訊" + "Qualcomm® aptX™ TWS+ 音訊" "系統自動選擇 (預設)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ 音訊" "Qualcomm® aptX™ HD 音訊" "LDAC" + "Qualcomm® aptX™ Adaptive 音訊" + "Qualcomm® aptX™ TWS+ 音訊" "系統自動選擇 (預設)" diff --git a/packages/SettingsLib/res/values-zu/arrays.xml b/packages/SettingsLib/res/values-zu/arrays.xml index 06a029a52de6..4be9dd582aa5 100644 --- a/packages/SettingsLib/res/values-zu/arrays.xml +++ b/packages/SettingsLib/res/values-zu/arrays.xml @@ -92,6 +92,8 @@ "Qualcomm® aptX™ umsindo" "Qualcomm® aptX™ HD umsindo" "I-LDAC" + "Qualcomm® aptX™ Adaptive umsindo" + "Qualcomm® aptX™ TWS+ umsindo" "Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)" @@ -100,6 +102,8 @@ "Qualcomm® aptX™ umsindo" "Qualcomm® aptX™ HD umsindo" "I-LDAC" + "Qualcomm® aptX™ Adaptive umsindo" + "Qualcomm® aptX™ TWS+ umsindo" "Sebenzisa ukukhetha kwesistimu (Okuzenzakalelayo)" From c9e0db7753074e9537ac1cf6cc1abdcce1ee362b Mon Sep 17 00:00:00 2001 From: Manisha Agarwal Date: Mon, 20 Jul 2020 20:01:59 +0530 Subject: [PATCH 68/92] Audio: Add support for lc3 codec. Add support for lc3 codec. CRs-Fixed: 2789473 Change-Id: Ica900e6546642e8c945170645f129e3a83301ddd --- media/java/android/media/AudioSystem.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 59179615cf8c..f1197edd453d 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -236,6 +236,8 @@ public static String modeToString(int mode) { public static final int AUDIO_FORMAT_APTX_ADAPTIVE = 0x27000000; /** @hide */ public static final int AUDIO_FORMAT_APTX_TWSP = 0x2A000000; + /** @hide */ + public static final int VX_AUDIO_FORMAT_LC3 = 0x2B000000; /** @hide */ @IntDef(flag = false, prefix = "AUDIO_FORMAT_", value = { @@ -267,6 +269,8 @@ public static int audioFormatToBluetoothSourceCodec( return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE; case AUDIO_FORMAT_APTX_TWSP: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_TWSP; + case VX_AUDIO_FORMAT_LC3: + return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3; default: Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat) + " for conversion to BT codec"); @@ -298,6 +302,8 @@ public static int audioFormatToBluetoothSourceCodec( return AudioSystem.AUDIO_FORMAT_APTX_ADAPTIVE; case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_TWSP: return AudioSystem.AUDIO_FORMAT_APTX_TWSP; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3: + return AudioSystem.VX_AUDIO_FORMAT_LC3; default: Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec) + " for conversion to audio format"); @@ -398,6 +404,8 @@ public static String audioFormatToString(int audioFormat) { return "AUDIO_FORMAT_LHDC_LL"; case /* AUDIO_FORMAT_APTX_TWSP */ 0x2A000000: return "AUDIO_FORMAT_APTX_TWSP"; + case /* VX_AUDIO_FORMAT_LC3 */ 0x2B000000: + return "VX_AUDIO_FORMAT_LC3"; /* Aliases */ case /* AUDIO_FORMAT_PCM_16_BIT */ 0x1: From c31cda599b3c211f8fa8b18992e4fac8dfd7465d Mon Sep 17 00:00:00 2001 From: pramod kotreshappa Date: Tue, 6 Oct 2020 22:15:16 -0700 Subject: [PATCH 69/92] Add LC3 codec type. - Add LC3 codec type - Add additional sample rate and channel mode CRs-Fixed: 2789473 Change-Id: I13d0c6944a12422c13e2069697817c0f26330179 --- .../android/bluetooth/BluetoothCodecConfig.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index c87785eba643..88682e69287b 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -82,6 +82,9 @@ public final class BluetoothCodecConfig implements Parcelable { @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_CELT = 8; + @UnsupportedAppUsage + public static final int SOURCE_CODEC_TYPE_LC3 = 9; + @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; @@ -138,6 +141,17 @@ public final class BluetoothCodecConfig implements Parcelable { @UnsupportedAppUsage public static final int SAMPLE_RATE_192000 = 0x1 << 5; + @UnsupportedAppUsage + public static final int SAMPLE_RATE_16000 = 0x1 << 6; + + @UnsupportedAppUsage + public static final int SAMPLE_RATE_24000 = 0x1 << 7; + + @UnsupportedAppUsage + public static final int SAMPLE_RATE_32000 = 0x1 << 8; + + @UnsupportedAppUsage + public static final int SAMPLE_RATE_8000 = 0x1 << 9; /** @hide */ @IntDef(prefix = "BITS_PER_SAMPLE_", value = { @@ -180,6 +194,9 @@ public final class BluetoothCodecConfig implements Parcelable { @UnsupportedAppUsage public static final int CHANNEL_MODE_STEREO = 0x1 << 1; + @UnsupportedAppUsage + public static final int CHANNEL_MODE_JOINT_STEREO = 0x1 << 2; + private final @SourceCodecType int mCodecType; private @CodecPriority int mCodecPriority; private final @SampleRate int mSampleRate; From 8cce8afc749421a1987d5c7280d262ea6f724346 Mon Sep 17 00:00:00 2001 From: Sumit Deshmukh Date: Thu, 25 Jun 2020 12:16:05 +0530 Subject: [PATCH 70/92] DeviceGroup: Framework changes for Group Device operations. This change contains below implementations - BluetoothDeviceGroup profile proxy object implementation. - GroupClientProfile as LocalBluetoothProfile. - Group Callbacks to be given to registered application. CRs-Fixed: 2826578 Change-Id: I0a8186e800e9d2701319db1adc97bdcf0441cc12 --- .../android/bluetooth/BluetoothAdapter.java | 7 + .../bluetooth/BluetoothDeviceGroup.java | 812 ++++++++++++++++++ .../bluetooth/BluetoothGroupCallback.java | 132 +++ .../android/bluetooth/BluetoothProfile.java | 8 +- core/java/android/bluetooth/DeviceGroup.java | 175 ++++ .../bluetooth/BluetoothCallback.java | 25 + .../bluetooth/BluetoothEventManager.java | 20 + .../bluetooth/DeviceGroupClientProfile.java | 377 ++++++++ .../LocalBluetoothProfileManager.java | 13 +- 9 files changed, 1567 insertions(+), 2 deletions(-) create mode 100644 core/java/android/bluetooth/BluetoothDeviceGroup.java create mode 100644 core/java/android/bluetooth/BluetoothGroupCallback.java create mode 100644 core/java/android/bluetooth/DeviceGroup.java create mode 100644 packages/SettingsLib/src/com/android/settingslib/bluetooth/DeviceGroupClientProfile.java diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 7e64f65aa811..e7a98b0b48d4 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -3065,6 +3065,9 @@ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener } else if (profile == BluetoothProfile.LE_AUDIO) { BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this); return true; + } else if (profile == BluetoothProfile.GROUP_CLIENT) { + BluetoothDeviceGroup groupClient = new BluetoothDeviceGroup(context, listener); + return true; } else { return false; } @@ -3157,6 +3160,10 @@ public void closeProfileProxy(int profile, BluetoothProfile proxy) { case BluetoothProfile.LE_AUDIO: BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy; leAudio.close(); + case BluetoothProfile.GROUP_CLIENT: + BluetoothDeviceGroup groupClient = (BluetoothDeviceGroup) proxy; + groupClient.close(); + break; } } diff --git a/core/java/android/bluetooth/BluetoothDeviceGroup.java b/core/java/android/bluetooth/BluetoothDeviceGroup.java new file mode 100644 index 000000000000..a171e00df100 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothDeviceGroup.java @@ -0,0 +1,812 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package android.bluetooth; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.bluetooth.IBluetoothGroupCallback; +import android.content.Context; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.ParcelUuid; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + + +/** + * This class provides the public APIs to perform operations of + * the Group Identification Profile. + * + *

    This class provides functionalities to enable communication with remote + * devices which are grouped together to achieve common use cases in + * synchronized manner. + *

    BluetoothDeviceGroup is a proxy object for controlling the Bluetooth Group + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothDeviceGroup + * proxy object. Use {@link BluetoothAdapter#closeProfileProxy} to close connection + * of the BluetoothDeviceGroup proxy object with the profile service. + *

    BluetoothDeviceGroup proxy object can be used to identify and fetch Device Group. + * Also, API’s are exposed to get exclusive access of group devices for critical + * operations. Implement BluetoothGroupCallback to get results invoked API's. + * + * @hide + */ + + +public final class BluetoothDeviceGroup implements BluetoothProfile { + private static final String TAG = "BluetoothDeviceGroup"; + private static final boolean DBG = true; + private static final boolean VDBG = false; + + /** Group Client App is registerd for callbacks successfully */ + public static final int APP_REGISTRATION_SUCCESSFUL = 0; + /** Group Client App registration failed for callbacks */ + public static final int APP_REGISTRATION_FAILED = 1; + + /** Group Discovery Status when discovery is started */ + public static final int GROUP_DISCOVERY_STARTED = 0x00; + + /** Group Discovery Status when discovery is stopped */ + public static final int GROUP_DISCOVERY_STOPPED = 0x01; + + /** When Application starts Group discovery */ + public static final int DISCOVERY_STARTED_BY_APPL = 0x00; + + /** When Application stops Group discovery */ + public static final int DISCOVERY_STOPPED_BY_APPL = 0x01; + + /** When Group discovery is started as a result of + * change in Group property. */ + public static final int DISCOVERY_STARTED_GROUP_PROP_CHANGED = 0x02; + + /** When all devices of Group are discovered */ + public static final int DISCOVERY_COMPLETED = 0x03; + + /** Group discovery by timeeut. Group device not found in 10 sec. */ + public static final int DISCOVERY_STOPPED_BY_TIMEOUT = 0x04; + + /** Invalid params are provided for Group discovery */ + public static final int DISCOVERY_NOT_STARTED_INVALID_PARAMS = 0x05; + + /** Value to release Exclusive Access */ + public static final int ACCESS_RELEASED = 0x01; + + /** Value to acquire Exclusive Access */ + public static final int ACCESS_GRANTED = 0x02; + + /** When exclusive access is changed to #ACCESS_RELEASED for all reqested Group devices */ + public static final int EXCLUSIVE_ACCESS_RELEASED = 0x00; + + /** When exclusive access of the Group device is changed to #ACCESS_RELEASED by timeout */ + public static final int EXCLUSIVE_ACCESS_RELEASED_BY_TIMEOUT = 0x01; + + /** When exclusive access of all requested Group devices is changed to #ACCESS_GRANTED */ + public static final int ALL_DEVICES_GRANTED_ACCESS = 0x02; + + /** When exclusive access of some of the requested Group devices is changed to #ACCESS_GRANTED + * because of timeout in #setExclusiveAccess operation */ + public static final int SOME_GRANTED_ACCESS_REASON_TIMEOUT = 0x03; + + /** When access value of some of the requested Group devices is changed to #ACCESS_GRANTED + * because some of the Group devices were disconnected */ + public static final int SOME_GRANTED_ACCESS_REASON_DISCONNECTION = 0x04; + + /** When Exclusive Access couldnt be fetched as one of the Group devices denied + * to set value to #ACCESS_DENIED*/ + public static final int ACCESS_DENIED = 0x05; + + /** Suggests that invalid parameters are passed in #setExclusiveAccess request*/ + public static final int INVALID_ACCESS_REQ_PARAMS = 0x06; + + /** Invalid Group ID */ + public static final int INVALID_GROUP_ID = 0x10; + + /** MIN GROUP_ID Value*/ + public static final int GROUP_ID_MIN = 0x00; + /** MAX GROUP_ID Value*/ + public static final int GROUP_ID_MAX = 0x0F; + + /** Invalid APP ID */ + public static final int INVALID_APP_ID = 0x10; + + /** MIN APP_ID Value*/ + public static final int APP_ID_MIN = 0x00; + /** MAX APP_ID Value*/ + public static final int APP_ID_MAX = 0x0F; + + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.group.profile.action.CONNECTION_STATE_CHANGED"; + + private int mAppId; + private boolean mAppRegistered = false; + private Handler mHandler; + private BluetoothGroupCallback mCallback; + + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.GROUP_CLIENT, + "BluetoothDeviceGroup", IBluetoothDeviceGroup.class.getName()) { + @Override + public IBluetoothDeviceGroup getServiceInterface(IBinder service) { + return IBluetoothDeviceGroup.Stub.asInterface(Binder.allowBlocking(service)); + } + }; + + /** + * Creates a BluetoothDeviceGroup proxy object for interacting with the local + * Bluetooth Service which handles Group operations. + * @hide + */ + /*package*/ BluetoothDeviceGroup(Context context, ServiceListener listener) { + mProfileConnector.connect(context, listener); + mAdapter = BluetoothAdapter.getDefaultAdapter(); + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException re) { + Log.e(TAG, "", re); + } + } + } + + private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (!up) { + mAppRegistered = false; + } + } + }; + + /** + * Close this BluetoothGroupDevice client object. + * + * Application should call this method as soon as it is done with + * Group operations. + */ + /*package*/ void close() { + if (VDBG) log("close()"); + + mAppRegistered = false; + final IBluetoothDeviceGroup service = getService(); + if (service != null) { + try { + service.unregisterGroupClientApp(mAppId); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + } + + mProfileConnector.disconnect(); + } + + /** + * @hide + */ + private IBluetoothDeviceGroup getService() { + return mProfileConnector.getService(); + } + + /** + * {@inheritDoc} + */ + @Override + public void finalize() { + close(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public List getDevicesMatchingConnectionStates(int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + + return null; + } + + private boolean isEnabled() { + return mAdapter.getState() == BluetoothAdapter.STATE_ON; + } + + private static boolean isValidDevice(BluetoothDevice device) { + return device != null && + BluetoothAdapter.checkBluetoothAddress(device.getAddress()); + } + + /** + * {@inheritDoc} + */ + @Override + public int getConnectionState(BluetoothDevice device) { + if (VDBG) log("getState(" + device + ")"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + private final IBluetoothGroupCallback.Stub mBluetoothGroupCallback = + new IBluetoothGroupCallback.Stub() { + + @Override + public void onGroupClientAppRegistered(int status, int appId) { + if (DBG) { + Log.d(TAG, "onGroupClientAppRegistered() - status=" + status + + " appId = " + appId); + } + + if (status != APP_REGISTRATION_SUCCESSFUL) { + mAppRegistered = false; + } + + mAppId = appId; + runOrQueueCallback(new Runnable() { + @Override + public void run() { + final BluetoothGroupCallback callback = mCallback; + if (callback != null) { + callback.onGroupClientAppRegistered(status, appId); + } + } + }); + } + + @Override + public void onGroupClientAppUnregistered(int status) { + if (DBG) { + Log.d(TAG, "onGroupClientAppUnregistered() - status=" + status + + " mAppId=" + mAppId); + } + } + + @Override + public void onConnectionStateChanged (int state, BluetoothDevice device) { + if (DBG) { + Log.d(TAG, "onConnectionStateChanged() - state = " + state + + " device = " + device); + } + + runOrQueueCallback(new Runnable() { + @Override + public void run() { + final BluetoothGroupCallback callback = mCallback; + if (callback != null) { + callback.onConnectionStateChanged(state, device); + } + } + }); + } + + @Override + public void onNewGroupFound(int groupId, BluetoothDevice device, + ParcelUuid uuid) { + if (DBG) { + Log.d(TAG, "onNewGroupFound() - appId = " + mAppId + + ", groupId = " + groupId + ", device: " + device + + ", Including service UUID: " + uuid.toString()); + } + + runOrQueueCallback(new Runnable() { + @Override + public void run() { + final BluetoothGroupCallback callback = mCallback; + if (callback != null) { + callback.onNewGroupFound(groupId, device, uuid.getUuid()); + } + } + }); + } + + @Override + public void onGroupDiscoveryStatusChanged(int groupId, int status, int reason) { + if (DBG) { + Log.d(TAG, "onGroupDiscoveryStatusChanged() - appId = " + mAppId + + ", groupId = " + groupId + ", status: " + status + + ", reason: " + reason); + } + + runOrQueueCallback(new Runnable() { + @Override + public void run() { + final BluetoothGroupCallback callback = mCallback; + if (callback != null) { + callback.onGroupDiscoveryStatusChanged(groupId, status, reason); + } + } + }); + } + + @Override + public void onGroupDeviceFound(int groupId, BluetoothDevice device) { + if (DBG) { + Log.d(TAG, "onGroupDeviceFound() - appId = " + mAppId + ", device = " + device); + } + + runOrQueueCallback(new Runnable() { + @Override + public void run() { + final BluetoothGroupCallback callback = mCallback; + if (callback != null) { + callback.onGroupDeviceFound(groupId, device); + } + } + }); + } + + @Override + public void onExclusiveAccessChanged(int groupId, int value, int status, + List devices) { + if (DBG) { + Log.d(TAG, "onExclusiveAccessChanged() - appId = " + mAppId + + ", groupId = " + groupId + ", value = " + value + + " accessStatus = " + status + ", devices: " + devices); + } + + runOrQueueCallback(new Runnable() { + @Override + public void run() { + final BluetoothGroupCallback callback = mCallback; + if (callback != null) { + callback.onExclusiveAccessChanged(groupId, value, status, devices); + } + } + }); + } + + @Override + public void onExclusiveAccessStatusFetched(int groupId, int accessValue) { + } + + @Override + public void onExclusiveAccessAvailable (int groupId, BluetoothDevice device) { + if (DBG) { + Log.d(TAG, "onExclusiveAccessAvailable() - appId = " + mAppId + + ", groupId = " + groupId + ", device: " + device); + } + + runOrQueueCallback(new Runnable() { + @Override + public void run() { + final BluetoothGroupCallback callback = mCallback; + if (callback != null) { + callback.onExclusiveAccessAvailable(groupId, device); + } + } + }); + } + }; + + /** + * Registers callbacks to be received by application on completion of + * required operations. + * + * @param callbacks Reference of BluetoothGroupCallback implemented in + * application. + * @param handler handler that will receive asynchronous callbacks. + * @return true, if operation was initiated successfully. + */ + public boolean registerGroupClientApp(BluetoothGroupCallback callbacks, Handler handler) { + if (DBG) log("registerGroupClientApp() mAppRegistered = " + mAppRegistered); + + /* Check if app is trying multiple registrations */ + if (mAppRegistered) { + Log.e(TAG, "App already registered."); + return false; + } + + mHandler = handler; + mCallback = callbacks; + + final IBluetoothDeviceGroup service = getService(); + if (service == null) { + Log.e(TAG, "Proxy not attached to Profile Service. Can't register App."); + return false; + } + + mAppRegistered = true; + try { + UUID uuid = UUID.randomUUID(); + service.registerGroupClientApp(new ParcelUuid(uuid), mBluetoothGroupCallback); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + return true; + } + + /** + * Starts discovery of the remaining Group devices which are part of the group. + * + *

    This API should be called when onNewGroupFound() is received in the + * application and when given group is the required device group. This + * API can also be used to rediscover the undiscovered Group devices. + * + *

    To the application that started group discovery, + * {@link BluetoothGroupCallback#onGroupDeviceFound} callback will be given when + * a new Group device is found and {@link BluetoothGroupCallback#onGroupDiscoveryStatusChanged} + * callback will be given when discovery is started. + * + * @param groupId Identifier of the Group for which group + * discovery has to be started. + * @return true, if operation was initiated successfully. + */ + public boolean startGroupDiscovery(int groupId) { + if (DBG) log("startGroupDiscovery() : groupId = " + groupId); + + if (!mAppRegistered) { + Log.e(TAG, "App not registered for Group operations." + + " Register App using registerGroupClientApp"); + return false; + } + + final IBluetoothDeviceGroup service = getService(); + if (service == null) { + Log.e(TAG, "Proxy is not attached to Profile Service. Can't start group discovery"); + return false; + } + + try { + UUID uuid = UUID.randomUUID(); + service.startGroupDiscovery(mAppId ,groupId); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + return true; + } + + /** + * Stops ongoing group discovery for Group identified by groupId. + * + *

    {@link BluetoothGroupCallback#onGroupDiscoveryStatusChanged} is given + * when group discovery is stopped. + * + * @param groupId Identifier of the Group for which group + * discovery has to be stopped. + * @return true, if operation was initiated successfully. + */ + public boolean stopGroupDiscovery(int groupId) { + if (DBG) log("stopGroupDiscovery() : groupId = " + groupId); + + if (!mAppRegistered) { + Log.e(TAG, "App not registered for Group operations." + + " Register App using registerGroupClientApp"); + return false; + } + + final IBluetoothDeviceGroup service = getService(); + if (service == null) { + Log.e(TAG, "Proxy is not attached to Profile Service. Can't Stop group discovery"); + return false; + } + + try { + service.stopGroupDiscovery(mAppId ,groupId); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + return true; + } + + /** + * Fetches already discovered Groups. + * + * @return List of DeviceGroup that are already discovered. + */ + public List getDiscoveredGroups() { + if (DBG) log("getDiscoveredGroups()"); + + if (!mAppRegistered) { + Log.e(TAG, "App not registered for Group operations." + + " Register App using registerGroupClientApp"); + return null; + } + + final IBluetoothDeviceGroup service = getService(); + if (service == null) { + Log.e(TAG, "Proxy is not attached to Profile Service. Can't fetch Groups."); + return null; + } + + try { + List groups = service.getDiscoveredGroups(); + return groups; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + + return null; + } + + /** + * Fetch details of a already discovered Group identified by groupId. + * + * @param groupId Identifier of the Group for which Group details are required. + * @return Required DeviceGroup. + */ + public DeviceGroup getGroup(int groupId) { + if (DBG) log("getGroup() : groupId = " + groupId); + + if (!mAppRegistered) { + Log.e(TAG, "App not registered for Group operations." + + " Register App using registerGroupClientApp"); + return null; + } + + final IBluetoothDeviceGroup service = getService(); + if (service == null) { + Log.e(TAG, "Proxy is not attached to Profile Service. Can't fetch Group."); + return null; + } + + try { + DeviceGroup group = service.getDeviceGroup(groupId); + return group; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + + return null; + } + + /** + * Get Group Identifier of the remote device to which it belongs. + * + * @param device BluetoothDevice instance of the remote device. + * @param uuid ParcelUuid of the primary service in which this + * Group Service is included. + * @return Group identifier of the required device. + */ + public int getRemoteDeviceGroupId (BluetoothDevice device, ParcelUuid uuid) { + if (DBG) log("getRemoteDeviceGroupId() : device = " + device); + + if (!mAppRegistered) { + Log.e(TAG, "App not registered for Group operations." + + " Register App using registerGroupClientApp"); + return INVALID_GROUP_ID; + } + + final IBluetoothDeviceGroup service = getService(); + if (service == null) { + Log.e(TAG, "Proxy is not attached to Profile Service." + + "Can't get group id for device."); + return INVALID_GROUP_ID; + } + + try { + return service.getRemoteDeviceGroupId(device, uuid); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + return INVALID_GROUP_ID; + } + + /** + * Suggests whether discovery for a given Group is ongoing. + * + * @param groupId Identifier of the Group for which discovery + * status is to be known. + * @return true, if group discovery is ongoing for mentioned group. + * Otherwise, false. + */ + public boolean isGroupDiscoveryInProgress (int groupId) { + if (DBG) log("isGroupDiscoveryInProgress() : groupId = " + groupId); + + if (!mAppRegistered) { + Log.e(TAG, "App not registered for Group operations." + + " Register App using registerGroupClientApp"); + return false; + } + + final IBluetoothDeviceGroup service = getService(); + if (service == null) { + Log.e(TAG, "Proxy is not attached to Profile Service.Can't get discovery status."); + return false; + } + + try { + return service.isGroupDiscoveryInProgress(groupId); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Acquires/Releases exclusive access of a given Group or subgroup. + * The result of this operation is returned in + * {@link BluetoothGroupCallback#onExclusiveAccessChanged} callback. + * + * @param groupId Identifier of the Group. + * @param devices List of BluetoothDevice for which access has to be changed. + * If this parameter is passed as null, all Group devices in the + * mentioned group will be considered for request. + * @param value Access which required to be changed. + * 0x01 – Access released ({@link #ACCESS_RELEASED}). + * 0x02 - Access granted ({@link #ACCESS_GRANTED}). + * @return true, if operation was initiated successfully. + */ + public boolean setExclusiveAccess(int groupId, List devices, int value) { + if (DBG) log("setExclusiveAccess() : groupId = " + groupId + + ", access value: " + value); + + if (!mAppRegistered) { + Log.e(TAG, "App not registered for Group operations." + + " Register App using registerGroupClientApp"); + return false; + } + + final IBluetoothDeviceGroup service = getService(); + if (service == null) { + Log.e(TAG, "Proxy is not attached to Profile Service. Can't proceed."); + return false; + } + + try { + service.setExclusiveAccess(mAppId, groupId, devices, value); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + return true; + } + + /** + * Returns Status of the exclusive access for mentioned Group. + * + * @param groupId Identifier of the Group. + * @param devices List of BluetoothDevice for which access value has to be known. + * If this parameter is passed as null, all Group devices in the + * mentioned group will be queried for access status. + * @return true, if operation was initiated successfully. + * @hide + */ + public boolean getExclusiveAccessStatus (int groupId, List devices) { + if (DBG) log("getExclusiveAccessStatus() : groupId = " + groupId); + + if (!mAppRegistered) { + Log.e(TAG, "App not registered for Group operations." + + " Register App using registerGroupClientApp"); + return false; + } + + final IBluetoothDeviceGroup service = getService(); + if (service == null) { + Log.e(TAG, "Proxy is not attached to Profile Service." + + " Can't get exclusive access status."); + return false; + } + + try { + service.getExclusiveAccessStatus(mAppId, groupId, devices); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + return true; + } + + /** + * Creates GATT Connection with remote device for Group Operations. + * + *

    This API acts as trigger to start service discovery to identify + * new device group on remote device once connection has been established + * successfully. Application calling connect will get + * {@link BluetoothGroupCallback#onNewGroupFoundcallback} after + * {@link #onConnectionStateChanged} (once connection has been established + * and group discovery is completed.) + * + * @param device BluetoothDevice instance od remote device with which + * Connection is required to be established. + * @return true, if operation was initiated successfully. + */ + public boolean connect (BluetoothDevice device) { + if (DBG) log("connect : device = " + device); + + if (!mAppRegistered) { + Log.e(TAG, "App not registered for Group operations." + + " Register App using registerGroupClientApp"); + return false; + } + + final IBluetoothDeviceGroup service = getService(); + if (service == null) { + Log.e(TAG, "Proxy is not attached to Profile Service. Can't connect."); + return false; + } + + try { + service.connect(mAppId, device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + return true; + } + + /** + * Initiates GATT disconnection for Group Operations. + * + * @param device BluetoothDevice instance of remote device. + * This API must be called if application is not + * interested in any Group operations. + * @return true, if operation was initiated successfully. + */ + public boolean disconnect (BluetoothDevice device) { + if (DBG) log("disconnect : device = " + device); + + if (!mAppRegistered) { + Log.e(TAG, "App not registered for Group operations." + + " Register App using registerGroupClientApp"); + return false; + } + + final IBluetoothDeviceGroup service = getService(); + if (service == null) { + Log.e(TAG, "Proxy is not attached to Profile Service. Can't disconnect"); + return false; + } + + try { + service.disconnect(mAppId, device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + } + return true; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } + + /** + * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable + * immediately if no Handler was provided. + */ + private void runOrQueueCallback(final Runnable cb) { + if (mHandler == null) { + try { + cb.run(); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception in callback", ex); + } + } else { + mHandler.post(cb); + } + } +} diff --git a/core/java/android/bluetooth/BluetoothGroupCallback.java b/core/java/android/bluetooth/BluetoothGroupCallback.java new file mode 100644 index 000000000000..a818cc77fd3a --- /dev/null +++ b/core/java/android/bluetooth/BluetoothGroupCallback.java @@ -0,0 +1,132 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package android.bluetooth; + +import java.util.UUID; +import java.util.List; +/** + * This abstract class is used to implement {@link BluetoothDeviceGroup} callbacks. + * @hide + */ +public abstract class BluetoothGroupCallback { + /** + * This Callback gives connection state changed with specific group device. + * + * @param state Connection state of the {@link BluetoothProfile} group device. + * @param device Remote device for which connection state has changed. + */ + public void onConnectionStateChanged (int state, BluetoothDevice device) { + } + + /** + * This callback is given when application is registered for Group operation + * callbacks. This callback is given after {@link BluetoothDeviceGroup#registerGroupClientApp} + * is called. + * + * @param status Status of the group client app registration. + * @param appId Identifier of the application for group operations. + */ + public void onGroupClientAppRegistered(int status, int appId) { + } + + /** + * This callback is triggered when a new device group has been identified + * from one of the connected device. After this callback is received, application + * can choose to trigger discovery of device group using API + * {@link BluetoothDeviceGroup#startGroupDiscovery} + * + * @param groupId Identifier of the Device Group. + * @param device Remote device with which Device Group is found. + * @param uuid UUID of the primary Service for this Device Group Service. + */ + public void onNewGroupFound (int groupId, BluetoothDevice device, UUID uuid) { + } + + /** + * This Callback is triggered when device group discovery is either started/stopped. + * + * @param groupId Identifier of the device group. + * @param status Device Group Discovery status. + * {@link BluetoothDeviceGroup#GROUP_DISCOVERY_STARTED} + * or {@link BluetoothDeviceGroup#GROUP_DISCOVERY_STOPPED}. + * @param reason Reason for change in the discovery status. + */ + public void onGroupDiscoveryStatusChanged (int groupId, int status, int reason) { + } + + /** + * This callback is triggered when new group device has been found after group + * discovery has been started. This callback is given on discovery of every + * new group device. + * + * @param groupId Identifier of the device group. + * @param device {@link BluetoothDevice} instance of discovered group device. + */ + public void onGroupDeviceFound (int groupId, BluetoothDevice device) { + } + + /** + * This callback is triggered after exclusive access status of the group + * or subgroup has been changed after the request from application. + * + * @param groupId Identifier of the device group. + * @param value Changed value of the exclusive access. + * @param status Status associated with the exclusive access. + * @param devices List of devices for which exclusive access has been changed. + */ + public void onExclusiveAccessChanged (int groupId, int value, int status, + List devices) { + } + + /** + * This callback gives access status of requested group/subgroup once + * it is fetched. + * + * @param groupId Identifier of the device group. + * @param accessStatus Value of the Exclusive Access. + */ + public void onExclusiveAccessStatusFetched (int groupId, int accessStatus) { + } + + /** + * This callback is given to application when exclusive access is available + * for the device of a given group for which was denied earlier. + *

    Exclusive Access is considered available when group device sends notification + * for access changed to BluetoothDeviceGroup#ACCESS_RELEASED. This callback is + * given to the application which has requested the access earlier and the request + * had failed as one of the group device had DENIED the access. + * + * @param groupId Identifier of the device group. + * @param device {@link BluetoothDevice} which has exclusive access available. + */ + public void onExclusiveAccessAvailable (int groupId, BluetoothDevice device) { + } + +} \ No newline at end of file diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 161c843f0398..000819f794fe 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -212,13 +212,19 @@ public interface BluetoothProfile { */ int LE_AUDIO = 22; + /** + * Group Operation Profile (Client Role) + * @hide + */ + public int GROUP_CLIENT = 23; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 22; + int MAX_PROFILE_ID = 23; /** * Default priority for devices that we try to auto-connect to and diff --git a/core/java/android/bluetooth/DeviceGroup.java b/core/java/android/bluetooth/DeviceGroup.java new file mode 100644 index 000000000000..f1bd2c310197 --- /dev/null +++ b/core/java/android/bluetooth/DeviceGroup.java @@ -0,0 +1,175 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package android.bluetooth; + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelUuid; + +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +/** + * Provides Device Group details. + * + * {@see BluetoothDeviceGroup} + * @hide + * + */ + +public final class DeviceGroup implements Parcelable { + /** Identifier of the Device Group */ + private int mGroupId; + /** Size of the Device Group. */ + private int mSize; + /** List of all group devices {@link BluetoothDevice} */ + private List mGroupDevices = new ArrayList(); + /** Primary Service UUID which has included required Device Group service*/ + private final ParcelUuid mIncludingSrvcUUID; + /** Suggests whether exclusive access can be taken for this device group */ + private final boolean mExclusiveAccessSupport; + + /** + * Constructor. + * @hide + */ + public DeviceGroup(int groupId, int size, List groupDevices, + ParcelUuid includingSrvcUUID, boolean exclusiveAccessSupport) { + mGroupId = groupId; + mSize = size; + mGroupDevices = groupDevices; + mIncludingSrvcUUID = includingSrvcUUID; + mExclusiveAccessSupport = exclusiveAccessSupport; + } + + public DeviceGroup(Parcel in) { + mGroupId = in.readInt(); + mSize = in.readInt(); + in.readList(mGroupDevices, BluetoothDevice.class.getClassLoader()); + mIncludingSrvcUUID = in.readParcelable(ParcelUuid.class.getClassLoader()); + mExclusiveAccessSupport = in.readBoolean(); + } + + /** + * Used to retrieve identifier of the Device Group. + * + * @return Identifier of the Device Group. + */ + public int getDeviceGroupId() { + return mGroupId; + } + + /** + * Used to know total number group devices which are part of this Device Group. + * + * @return size of the Device Group + */ + public int getDeviceGroupSize() { + return mSize; + } + + /** + * Indicates total number of group devices discovered in Group Discovery procedure. + * + * @return total group devices discovered in the Device Group. + */ + public int getTotalDiscoveredGroupDevices() { + return mGroupDevices.size(); + } + + + /** + * Used to fetch group devices of the Device Group. + * + *@return List of group devices {@link BluetoothDevice} in the Device Group. + */ + public List getDeviceGroupMembers() { + return mGroupDevices; + } + + /** + * Suggests primary GATT service which has included this DeviceGroup Service + * for this device group. If remote device is part of multiple Device Groups then + * this uuid cant be null. If remote device is part of only one device froup + * then this returned parameter can be null. + * + *@return UUID of the GATT primary Service which has included this device group. + */ + public ParcelUuid getIncludingServiceUUID() { + return mIncludingSrvcUUID; + } + + /** + * Suggests whether exclusive access is supported by this Device Group. + * + * @return true, if exclusive access operation is supported by this Device Group. + * Otherwise, false. + */ + public boolean isExclusiveAccessSupported() { + return mExclusiveAccessSupport; + } + + /** + * Indicates whether all devices of this Device Group are discovered. + * + * @return true, if all group devices are discovered. Otherwise, false. + */ + public boolean isGroupDiscoveredCompleted() { + return (mSize == getTotalDiscoveredGroupDevices()); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mGroupId); + dest.writeInt(mSize); + dest.writeList(mGroupDevices); + dest.writeParcelable(mIncludingSrvcUUID, 0); + dest.writeBoolean(mExclusiveAccessSupport); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public DeviceGroup createFromParcel(Parcel in) { + return new DeviceGroup(in); + } + + public DeviceGroup[] newArray(int size) { + return new DeviceGroup[size]; + } + }; +} diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java index f0c12a3b5ace..b62f1c35d82f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java @@ -18,6 +18,8 @@ import android.bluetooth.BluetoothCodecStatus; +import java.util.UUID; + /** * BluetoothCallback provides a callback interface for the settings * UI to receive events from {@link BluetoothEventManager}. @@ -152,4 +154,27 @@ default void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int default void onA2dpCodecConfigChanged(CachedBluetoothDevice cachedDevice, BluetoothCodecStatus codecStatus) { } + + /** + * Called when new device group has been identified with the bonded remote device + * + * @param cachedDevice Bluetooth device with which device group has been found. + * @param groupId Identifier of the device group. + * @param setPrimaryServiceUuid Primary service with which this Device Group + * is associated. + */ + default void onNewGroupFound(CachedBluetoothDevice cachedDevice, int groupId, + UUID setPrimaryServiceUuid) { + } + + /** + * Called when Group Discovery status has been changed. + * + * @param groupId Identifier of the coordinated set. + * @param status Status of the group discovery procedure. + * @param reason Reason for the change in status of discovery. + */ + default void onGroupDiscoveryStatusChanged (int groupId, int status, int reason) { + } + } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 52874ca09bca..d8fde90ceb4c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -42,6 +42,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.UUID; /** * BluetoothEventManager receives broadcasts and callbacks from the Bluetooth @@ -250,6 +251,25 @@ private void dispatchA2dpCodecConfigChanged(CachedBluetoothDevice cachedDevice, } } + protected void dispatchNewGroupFound( + CachedBluetoothDevice cachedDevice, int groupId, UUID setPrimaryServiceUuid) { + synchronized(mCallbacks) { + for (BluetoothCallback callback : mCallbacks) { + callback.onNewGroupFound(cachedDevice, groupId, + setPrimaryServiceUuid); + } + } + } + + protected void dispatchGroupDiscoveryStatusChanged(int groupId, + int status, int reason) { + synchronized(mCallbacks) { + for (BluetoothCallback callback : mCallbacks) { + callback.onGroupDiscoveryStatusChanged(groupId, status, reason); + } + } + } + @VisibleForTesting void addHandler(String action, Handler handler) { mHandlerMap.put(action, handler); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/DeviceGroupClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/DeviceGroupClientProfile.java new file mode 100644 index 000000000000..a98cb3c9ea87 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/DeviceGroupClientProfile.java @@ -0,0 +1,377 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.android.settingslib.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothClass; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothDeviceGroup; +import android.bluetooth.BluetoothGroupCallback; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.DeviceGroup; +import android.content.Context; +import android.app.ActivityThread; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import com.android.settingslib.R; + +import java.util.List; +import java.util.UUID; + +/** + * DeviceGroupClientProfile handles group operations required in client Role. + */ +public class DeviceGroupClientProfile implements LocalBluetoothProfile { + private static final String TAG = "DeviceGroupClientProfile"; + + private BluetoothDeviceGroup mService; + private boolean mIsProfileReady; + + private final CachedBluetoothDeviceManager mDeviceManager; + private final LocalBluetoothProfileManager mProfileManager; + + static final String NAME = "DeviceGroup Client"; + private static final String GROUP_APP = "com.android.settings"; + private String mCallingPackage; + + // Order of this profile in device profiles list + private static final int ORDINAL = 3; + + DeviceGroupClientProfile(Context context, + CachedBluetoothDeviceManager deviceManager, + LocalBluetoothProfileManager profileManager) { + mDeviceManager = deviceManager; + mProfileManager = profileManager; + mCallingPackage = ActivityThread.currentOpPackageName(); + BluetoothAdapter.getDefaultAdapter() + .getProfileProxy(context, new GroupClientServiceListener(), + BluetoothProfile.GROUP_CLIENT); + } + + // These callbacks run on the main thread. + private final class GroupClientServiceListener + implements BluetoothProfile.ServiceListener { + + public void onServiceConnected(int profile, BluetoothProfile proxy) { + mService = (BluetoothDeviceGroup) proxy; + mIsProfileReady = true; + Log.d(TAG, "onServiceConnected: mCallingPackage = " + mCallingPackage); + // register Group Client App + if (GROUP_APP.equals(mCallingPackage)) { + mService.registerGroupClientApp(mGroupCallback, + new Handler(Looper.getMainLooper())); + } + } + + public void onServiceDisconnected(int profile) { + mIsProfileReady=false; + } + } + + private final BluetoothGroupCallback mGroupCallback = new BluetoothGroupCallback() { + + @Override + public void onNewGroupFound (int groupId, BluetoothDevice device, UUID uuid) { + Log.d(TAG, "onNewGroupFound()"); + + CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); + if (cachedDevice == null) { + cachedDevice = mDeviceManager.addDevice(device); + } + + mProfileManager.mEventManager.dispatchNewGroupFound( + cachedDevice, groupId, uuid); + Log.d(TAG, "Start Group Discovery for Audio capable device"); + //if (device.isAdvAudioDevice()) + mService.startGroupDiscovery(groupId); + } + + @Override + public void onGroupDiscoveryStatusChanged (int groupId, + int status, int reason) { + Log.d(TAG, "onGroupDiscoveryStatusChanged()"); + + mProfileManager.mEventManager.dispatchGroupDiscoveryStatusChanged( + groupId, status, reason); + } + + }; + + public boolean connectGroup (int groupId) { + Log.d(TAG, "connectGroup(): groupId = " + groupId); + boolean isTriggered = false; + + if(mService == null || mIsProfileReady == false) { + Log.e(TAG, "connectGroup: mService = " + mService + + " mIsProfileReady = " + mIsProfileReady); + return false; + } + + DeviceGroup mGroup = mService.getGroup(groupId); + + if (mGroup == null || mGroup.getDeviceGroupMembers().size() == 0) { + Log.e(TAG, "Requested device group not found"); + return false; + } + + for (BluetoothDevice device: mGroup.getDeviceGroupMembers()) { + CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); + if (cachedDevice == null) { + Log.w(TAG, "CachedBluetoothDevice not found for device: " + device); + continue; + } + + if (!cachedDevice.isConnected()) { + cachedDevice.connect(true); + isTriggered = true; + } + } + return isTriggered; + } + + public boolean disconnectGroup (int groupId) { + Log.d(TAG, "disconnectGroup(): groupId = " + groupId); + boolean isTriggered = false; + + if(mService == null || mIsProfileReady == false) { + Log.e(TAG, "connectGroup: mService = " + mService + + " mIsProfileReady = " + mIsProfileReady); + return false; + } + + DeviceGroup mGroup = mService.getGroup(groupId); + + if (mGroup == null || mGroup.getDeviceGroupMembers().size() == 0) { + Log.e(TAG, "Requested device group is not found"); + return false; + } + + for (BluetoothDevice device: mGroup.getDeviceGroupMembers()) { + CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); + if (cachedDevice == null) { + Log.w(TAG, "CachedBluetoothDevice not found for device: " + device); + continue; + } + + if (cachedDevice.isConnected()) { + cachedDevice.disconnect(); + isTriggered = true; + } + } + + return isTriggered; + } + + public boolean forgetGroup(int groupId) { + Log.d(TAG, "forgetGroup(): groupId = " + groupId); + + if(mService == null || mIsProfileReady == false) { + Log.e(TAG, "forgetGroup: mService = " + mService + + " mIsProfileReady = " + mIsProfileReady); + return false; + } + + DeviceGroup mGroup = mService.getGroup(groupId); + if (mGroup == null || mGroup.getDeviceGroupMembers().size() == 0) { + Log.e(TAG, "Requested device group is not found"); + return false; + } + + for (BluetoothDevice device: mGroup.getDeviceGroupMembers()) { + CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); + if (cachedDevice == null) { + Log.w(TAG, "CachedBluetoothDevice not found for device: " + device); + continue; + } + cachedDevice.unpair(); + } + + return true; + } + + public boolean startGroupDiscovery (int groupId) { + Log.d(TAG, "startGroupDiscovery: groupId = " + groupId); + + if(mService == null || mIsProfileReady == false) { + Log.e(TAG, "startGroupDiscovery: mService = " + mService + + " mIsProfileReady = " + mIsProfileReady); + return false; + } + + return mService.startGroupDiscovery(groupId); + } + + public boolean stopGroupDiscovery (int groupId) { + Log.d(TAG, "stopGroupDiscovery: groupId = " + groupId); + + if(mService == null || mIsProfileReady == false) { + Log.e(TAG, "stopGroupDiscovery: mService = " + mService + + " mIsProfileReady = " + mIsProfileReady); + return false; + } + + return mService.stopGroupDiscovery(groupId); + } + + public DeviceGroup getGroup (int groupId) { + Log.d(TAG, "getGroup: groupId = " + groupId); + + if(mService == null || mIsProfileReady == false) { + Log.e(TAG, "getGroup: mService = " + mService + + " mIsProfileReady = " + mIsProfileReady); + return null; + } + + return mService.getGroup(groupId); + } + + public List getDiscoveredGroups () { + Log.d(TAG, "getDiscoveredGroups"); + + if(mService == null || mIsProfileReady == false) { + Log.e(TAG, "getDiscoveredGroups: mService = " + mService + + " mIsProfileReady = " + mIsProfileReady); + return null; + } + + return mService.getDiscoveredGroups(); + } + + public boolean isGroupDiscoveryInProgress (int groupId) { + Log.d(TAG, "isGroupDiscoveryInProgress: groupId = " + groupId); + + if (mService == null) { + Log.e(TAG, "Not connected to Profile Service. Return."); + return false; + } + + return mService.isGroupDiscoveryInProgress(groupId); + } + + public int getRemoteDeviceGroupId (BluetoothDevice device) { + Log.d(TAG, "getRemoteDeviceGroupId: device = " + device); + + if(mService == null || mIsProfileReady == false) { + Log.e(TAG, "getRemoteDeviceGroupId: mService = " + mService + + " mIsProfileReady = " + mIsProfileReady); + return BluetoothDeviceGroup.INVALID_GROUP_ID; + } + + return mService.getRemoteDeviceGroupId(device, null); + } + + public boolean isProfileReady() { + return mIsProfileReady; + } + + @Override + public int getProfileId() { + return BluetoothProfile.GROUP_CLIENT; + } + + public boolean accessProfileEnabled() { + return false; + } + + public boolean isAutoConnectable() { + return false; + } + + public void setPreferred(BluetoothDevice device, boolean preferred) {} + + public int getPreferred(BluetoothDevice device) { return BluetoothProfile.PRIORITY_OFF;} + + public boolean isPreferred(BluetoothDevice device) {return false;} + + public boolean connect(BluetoothDevice device) { return false;} + + public boolean disconnect(BluetoothDevice device) {return false;} + + public int getConnectionStatus(BluetoothDevice device) { + return BluetoothProfile.STATE_DISCONNECTED; + } + + @Override + public boolean isEnabled(BluetoothDevice device) { + if (mService == null) { + return false; + } + return true; + } + + @Override + public int getConnectionPolicy(BluetoothDevice device) { + return 0; + } + + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; + + return isEnabled; + } + + public String toString() { + return NAME; + } + + public int getOrdinal() { + return ORDINAL; + } + + public int getNameResource(BluetoothDevice device) { + return 0;//R.string.bluetooth_profile_group_client; + } + + public int getSummaryResourceForDevice(BluetoothDevice device) { + return 0; + } + + public int getDrawableResource(BluetoothClass btClass) { + return 0; + } + + protected void finalize() { + Log.d(TAG, "finalize()"); + if (mService != null) { + try { + BluetoothAdapter.getDefaultAdapter() + .closeProfileProxy(BluetoothProfile.GROUP_CLIENT, + mService); + mService = null; + }catch (Throwable t) { + Log.w(TAG, "Error cleaning up BluetoothDeviceGroup proxy Object", t); + } + } + } +} + diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index c7953568353c..4f5b945f74cc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -19,6 +19,7 @@ import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothA2dpSink; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDeviceGroup; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadsetClient; @@ -85,10 +86,11 @@ public interface ServiceListener { private final Context mContext; private final CachedBluetoothDeviceManager mDeviceManager; - private final BluetoothEventManager mEventManager; + protected final BluetoothEventManager mEventManager; private A2dpProfile mA2dpProfile; private A2dpSinkProfile mA2dpSinkProfile; + private DeviceGroupClientProfile mGroupClientProfile; private HeadsetProfile mHeadsetProfile; private HfpClientProfile mHfpClientProfile; private MapProfile mMapProfile; @@ -220,6 +222,12 @@ void updateLocalProfiles() { mSapProfile = new SapProfile(mContext, mDeviceManager, this); addProfile(mSapProfile, SapProfile.NAME, BluetoothSap.ACTION_CONNECTION_STATE_CHANGED); } + if (mGroupClientProfile == null && supportedList.contains(BluetoothProfile.GROUP_CLIENT)) { + if (DEBUG) Log.d(TAG, "Adding local GROUP CLIENT profile"); + mGroupClientProfile = new DeviceGroupClientProfile(mContext, mDeviceManager, this); + addProfile(mGroupClientProfile, mGroupClientProfile.NAME, + BluetoothDeviceGroup.ACTION_CONNECTION_STATE_CHANGED); + } mEventManager.registerProfileIntentReceiver(); } @@ -460,6 +468,9 @@ HidDeviceProfile getHidDeviceProfile() { return mHidDeviceProfile; } + public DeviceGroupClientProfile getDeviceGroupClientProfile() { + return mGroupClientProfile; + } /** * Fill in a list of LocalBluetoothProfile objects that are supported by * the local device and the remote device. From a5e385f362b7654ba9e23690bf484897f3e866ca Mon Sep 17 00:00:00 2001 From: Sravan voleti Date: Thu, 3 Dec 2020 15:39:06 +0530 Subject: [PATCH 71/92] DeviceGroup: UI frameworks changes Add system APIS to support Group UI CRs-Fixed: 2829452 Change-Id: Id7de6e48f3c8be05be0af172055c35cab322e5fe --- .../android/bluetooth/BluetoothDevice.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 629e84f2a30b..e95f9333bd64 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -2897,4 +2897,41 @@ public byte[] getMetadata(@MetadataKey int key) { public static @MetadataKey int getMaxMetadataKey() { return METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD; } + + /** + * Returns Device type. + * + * @return device type. + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public int getDeviceType() { + if (sService == null) { + Log.e(TAG, "getDeviceType query remote device info failed"); + return -1; + } + try { + return sService.getDeviceType(this); + } catch (RemoteException e) { + Log.e(TAG, "getDeviceType fail ", e); + } + return -1; + } + + /** + * Used as a String extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. + * It contains the Group ID of IOT device. + * @hide + */ + public static final String EXTRA_GROUP_ID = "android.bluetooth.qti.extra.GROUP_ID"; + + /** + * Used as a String extra field in {@link #ACTION_BOND_STATE_CHANGED} intents. + * It contains the IGNORE DEVICE flag of IOT device. + * @hide + */ + public static final String EXTRA_IS_PRIVATE_ADDRESS = + "android.bluetooth.qti.extra.IS_PRIVATE_ADDRESS"; + + } From 32d6e9652df885677989e988e57800bd452a17c8 Mon Sep 17 00:00:00 2001 From: Sumit Deshmukh Date: Thu, 22 Oct 2020 15:41:02 +0530 Subject: [PATCH 72/92] DeviceGroup: Frameworks changes (for dual mode devices). This change contains implementation of overloaded API's to get device details based on public address. Note: API's are hidden and should be used only from settingslibs LocalBluetoothProfile. CRs-Fixed: 2826578 Change-Id: I9b1f08f4bd5eee8fd66fc032745d3ec5b56e7c2f --- .../bluetooth/BluetoothDeviceGroup.java | 46 +++++++++++++++++-- .../bluetooth/DeviceGroupClientProfile.java | 6 +-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothDeviceGroup.java b/core/java/android/bluetooth/BluetoothDeviceGroup.java index a171e00df100..0a0fea5b7f2a 100644 --- a/core/java/android/bluetooth/BluetoothDeviceGroup.java +++ b/core/java/android/bluetooth/BluetoothDeviceGroup.java @@ -532,6 +532,17 @@ public boolean stopGroupDiscovery(int groupId) { * @return List of DeviceGroup that are already discovered. */ public List getDiscoveredGroups() { + return getDiscoveredGroups(false); + } + + /** + * Fetches already discovered device groups. + * + * @param mPublicAddr All discovered device groups with public address of devices. + * @return List of Device Groups that are already discovered. + * @hide + */ + public List getDiscoveredGroups(boolean mPublicAddr) { if (DBG) log("getDiscoveredGroups()"); if (!mAppRegistered) { @@ -547,7 +558,7 @@ public List getDiscoveredGroups() { } try { - List groups = service.getDiscoveredGroups(); + List groups = service.getDiscoveredGroups(mPublicAddr); return groups; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); @@ -563,6 +574,19 @@ public List getDiscoveredGroups() { * @return Required DeviceGroup. */ public DeviceGroup getGroup(int groupId) { + return getGroup(groupId, false); + } + + /** + * Fetch details of a already discovered Group identified by groupId. + * + * @param groupId Identifier of the device group for which group + * details are required. + * @param mPublicAddr DeviceGroup with Public Address of the group devices. + * @return Required DeviceGroup. + * @hide + */ + public DeviceGroup getGroup(int groupId, boolean mPublicAddr) { if (DBG) log("getGroup() : groupId = " + groupId); if (!mAppRegistered) { @@ -578,7 +602,7 @@ public DeviceGroup getGroup(int groupId) { } try { - DeviceGroup group = service.getDeviceGroup(groupId); + DeviceGroup group = service.getDeviceGroup(groupId, mPublicAddr); return group; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); @@ -596,6 +620,22 @@ public DeviceGroup getGroup(int groupId) { * @return Group identifier of the required device. */ public int getRemoteDeviceGroupId (BluetoothDevice device, ParcelUuid uuid) { + return getRemoteDeviceGroupId(device, uuid, false); + } + + /** + * Get Group Identifier of the remote device to which it belongs. + * + * @param device BluetoothDevice instance of the remote device. + * @param uuid ParcelUuid of the primary service in which this + * Group Service is included. + * @param mPublicAddr Suggests that group identifier is required for passed + * public address of the remote device. + * @return Group identifier of the required group for the device + * @hide + */ + public int getRemoteDeviceGroupId (BluetoothDevice device, ParcelUuid uuid, + boolean mPublicAddr) { if (DBG) log("getRemoteDeviceGroupId() : device = " + device); if (!mAppRegistered) { @@ -612,7 +652,7 @@ public int getRemoteDeviceGroupId (BluetoothDevice device, ParcelUuid uuid) { } try { - return service.getRemoteDeviceGroupId(device, uuid); + return service.getRemoteDeviceGroupId(device, uuid, mPublicAddr); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/DeviceGroupClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/DeviceGroupClientProfile.java index a98cb3c9ea87..5a3f33a7c433 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/DeviceGroupClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/DeviceGroupClientProfile.java @@ -251,7 +251,7 @@ public DeviceGroup getGroup (int groupId) { return null; } - return mService.getGroup(groupId); + return mService.getGroup(groupId, true); } public List getDiscoveredGroups () { @@ -263,7 +263,7 @@ public List getDiscoveredGroups () { return null; } - return mService.getDiscoveredGroups(); + return mService.getDiscoveredGroups(true); } public boolean isGroupDiscoveryInProgress (int groupId) { @@ -286,7 +286,7 @@ public int getRemoteDeviceGroupId (BluetoothDevice device) { return BluetoothDeviceGroup.INVALID_GROUP_ID; } - return mService.getRemoteDeviceGroupId(device, null); + return mService.getRemoteDeviceGroupId(device, null, true); } public boolean isProfileReady() { From 440a961d416b85e4de86d6660f5cf95394b2b8b6 Mon Sep 17 00:00:00 2001 From: M Safoorah Banu Date: Tue, 16 Oct 2018 10:41:25 +0530 Subject: [PATCH 73/92] BT: API to update quiet mode status in Adapter service [2/3] - In BLE on use case, quiet mode status is not updated to Adapter service which results in not initiating auto connection of hfp & a2dp though quiet mode is disabled for next turn On. - Added new API to update quiet mode status in Adapter service. CRs-Fixed: 2337538 Change-Id: I49a604b7996024fd9fd498fb7cc6aafc208517a5 --- .../core/java/com/android/server/BluetoothManagerService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 11b3062ddfc5..2c322c9a47c1 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -1080,6 +1080,7 @@ private void continueFromBleOnState() { } if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { // This triggers transition to STATE_ON + mBluetooth.updateQuietModeStatus(mQuietEnable); mBluetooth.onLeServiceUp(mContext.getAttributionSource()); persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); } @@ -1889,6 +1890,8 @@ public void handleMessage(Message msg) { mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; + mQuietEnable = (msg.arg1 == 1); + if (isBle == 0) { persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); } @@ -1905,6 +1908,7 @@ public void handleMessage(Message msg) { Slog.i(TAG, "Already at BLE_ON State"); } else { Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); + mBluetooth.updateQuietModeStatus(mQuietEnable); mBluetooth.onLeServiceUp(mContext.getAttributionSource()); } break; From 8c02afa8e3bcceafca495be0de2c838210a1d022 Mon Sep 17 00:00:00 2001 From: Sravan voleti Date: Tue, 24 Aug 2021 22:45:02 +0530 Subject: [PATCH 74/92] Bluetooth: Update Bluetooth permissions for internal APIS. (2/4) This adds attribution source as a parameter to Value added APIS. Update Bluetooth API annotations. CRs-Fixed: 3044065 Change-Id: Icf9c0242f928ce2f26f66350ddb3937fda35220d --- .../android/bluetooth/BluetoothDevice.java | 22 +++++-- .../bluetooth/BluetoothDeviceGroup.java | 64 +++++++++++++++---- .../server/BluetoothManagerService.java | 6 +- 3 files changed, 73 insertions(+), 19 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index e95f9333bd64..36c3ff228c4f 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1677,6 +1677,9 @@ public boolean isBondingInitiatedLocally() { /** @hide */ @UnsupportedAppUsage + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setBondingInitiatedLocally(boolean localInitiated) { final IBluetooth service = sService; if (service == null) { @@ -1684,7 +1687,7 @@ public void setBondingInitiatedLocally(boolean localInitiated) { return; } try { - service.setBondingInitiatedLocally(this, localInitiated); + service.setBondingInitiatedLocally(this, localInitiated, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -2005,13 +2008,16 @@ public boolean sdpSearch(ParcelUuid uuid) { * @return True if the devcie is TWS+ device. * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isTwsPlusDevice() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot query remote device sdp records"); return false; } try { - return sService.isTwsPlusDevice(this); + return sService.isTwsPlusDevice(this, mAttributionSource); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -2023,13 +2029,16 @@ public boolean isTwsPlusDevice() { * null. * @hide */ + @RequiresLegacyBluetoothPermission + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getTwsPlusPeerAddress() { if (sService == null) { Log.e(TAG, "BT not enabled. Cannot get Remote Device name"); return null; } try { - return sService.getTwsPlusPeerAddress(this); + return sService.getTwsPlusPeerAddress(this, mAttributionSource); } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } @@ -2904,14 +2913,17 @@ public byte[] getMetadata(@MetadataKey int key) { * @return device type. * @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public int getDeviceType() { if (sService == null) { Log.e(TAG, "getDeviceType query remote device info failed"); return -1; } try { - return sService.getDeviceType(this); + return sService.getDeviceType(this, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "getDeviceType fail ", e); } diff --git a/core/java/android/bluetooth/BluetoothDeviceGroup.java b/core/java/android/bluetooth/BluetoothDeviceGroup.java index 0a0fea5b7f2a..e271fac12b45 100644 --- a/core/java/android/bluetooth/BluetoothDeviceGroup.java +++ b/core/java/android/bluetooth/BluetoothDeviceGroup.java @@ -29,6 +29,11 @@ package android.bluetooth; +import android.annotation.RequiresPermission; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.bluetooth.annotations.RequiresBluetoothScanPermission; +import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.IBluetoothGroupCallback; @@ -154,6 +159,7 @@ public final class BluetoothDeviceGroup implements BluetoothProfile { private BluetoothGroupCallback mCallback; private BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.GROUP_CLIENT, "BluetoothDeviceGroup", IBluetoothDeviceGroup.class.getName()) { @@ -171,6 +177,7 @@ public IBluetoothDeviceGroup getServiceInterface(IBinder service) { /*package*/ BluetoothDeviceGroup(Context context, ServiceListener listener) { mProfileConnector.connect(context, listener); mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAttributionSource = mAdapter.getAttributionSource(); IBluetoothManager mgr = mAdapter.getBluetoothManager(); if (mgr != null) { try { @@ -203,7 +210,7 @@ public void onBluetoothStateChange(boolean up) { final IBluetoothDeviceGroup service = getService(); if (service != null) { try { - service.unregisterGroupClientApp(mAppId); + service.unregisterGroupClientApp(mAppId, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -425,6 +432,8 @@ public void run() { * @param handler handler that will receive asynchronous callbacks. * @return true, if operation was initiated successfully. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean registerGroupClientApp(BluetoothGroupCallback callbacks, Handler handler) { if (DBG) log("registerGroupClientApp() mAppRegistered = " + mAppRegistered); @@ -446,7 +455,8 @@ public boolean registerGroupClientApp(BluetoothGroupCallback callbacks, Handler mAppRegistered = true; try { UUID uuid = UUID.randomUUID(); - service.registerGroupClientApp(new ParcelUuid(uuid), mBluetoothGroupCallback); + service.registerGroupClientApp(new ParcelUuid(uuid), mBluetoothGroupCallback, + mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -469,6 +479,8 @@ public boolean registerGroupClientApp(BluetoothGroupCallback callbacks, Handler * discovery has to be started. * @return true, if operation was initiated successfully. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean startGroupDiscovery(int groupId) { if (DBG) log("startGroupDiscovery() : groupId = " + groupId); @@ -486,7 +498,7 @@ public boolean startGroupDiscovery(int groupId) { try { UUID uuid = UUID.randomUUID(); - service.startGroupDiscovery(mAppId ,groupId); + service.startGroupDiscovery(mAppId ,groupId, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -503,6 +515,8 @@ public boolean startGroupDiscovery(int groupId) { * discovery has to be stopped. * @return true, if operation was initiated successfully. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean stopGroupDiscovery(int groupId) { if (DBG) log("stopGroupDiscovery() : groupId = " + groupId); @@ -519,7 +533,7 @@ public boolean stopGroupDiscovery(int groupId) { } try { - service.stopGroupDiscovery(mAppId ,groupId); + service.stopGroupDiscovery(mAppId ,groupId, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -531,6 +545,8 @@ public boolean stopGroupDiscovery(int groupId) { * * @return List of DeviceGroup that are already discovered. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDiscoveredGroups() { return getDiscoveredGroups(false); } @@ -542,6 +558,8 @@ public List getDiscoveredGroups() { * @return List of Device Groups that are already discovered. * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public List getDiscoveredGroups(boolean mPublicAddr) { if (DBG) log("getDiscoveredGroups()"); @@ -558,7 +576,7 @@ public List getDiscoveredGroups(boolean mPublicAddr) { } try { - List groups = service.getDiscoveredGroups(mPublicAddr); + List groups = service.getDiscoveredGroups(mPublicAddr, mAttributionSource); return groups; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); @@ -573,6 +591,8 @@ public List getDiscoveredGroups(boolean mPublicAddr) { * @param groupId Identifier of the Group for which Group details are required. * @return Required DeviceGroup. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public DeviceGroup getGroup(int groupId) { return getGroup(groupId, false); } @@ -586,6 +606,8 @@ public DeviceGroup getGroup(int groupId) { * @return Required DeviceGroup. * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public DeviceGroup getGroup(int groupId, boolean mPublicAddr) { if (DBG) log("getGroup() : groupId = " + groupId); @@ -602,7 +624,7 @@ public DeviceGroup getGroup(int groupId, boolean mPublicAddr) { } try { - DeviceGroup group = service.getDeviceGroup(groupId, mPublicAddr); + DeviceGroup group = service.getDeviceGroup(groupId, mPublicAddr, mAttributionSource); return group; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); @@ -619,6 +641,8 @@ public DeviceGroup getGroup(int groupId, boolean mPublicAddr) { * Group Service is included. * @return Group identifier of the required device. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getRemoteDeviceGroupId (BluetoothDevice device, ParcelUuid uuid) { return getRemoteDeviceGroupId(device, uuid, false); } @@ -634,6 +658,8 @@ public int getRemoteDeviceGroupId (BluetoothDevice device, ParcelUuid uuid) { * @return Group identifier of the required group for the device * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getRemoteDeviceGroupId (BluetoothDevice device, ParcelUuid uuid, boolean mPublicAddr) { if (DBG) log("getRemoteDeviceGroupId() : device = " + device); @@ -652,7 +678,7 @@ public int getRemoteDeviceGroupId (BluetoothDevice device, ParcelUuid uuid, } try { - return service.getRemoteDeviceGroupId(device, uuid, mPublicAddr); + return service.getRemoteDeviceGroupId(device, uuid, mPublicAddr, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -667,6 +693,8 @@ public int getRemoteDeviceGroupId (BluetoothDevice device, ParcelUuid uuid, * @return true, if group discovery is ongoing for mentioned group. * Otherwise, false. */ + @RequiresBluetoothScanPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean isGroupDiscoveryInProgress (int groupId) { if (DBG) log("isGroupDiscoveryInProgress() : groupId = " + groupId); @@ -683,7 +711,7 @@ public boolean isGroupDiscoveryInProgress (int groupId) { } try { - return service.isGroupDiscoveryInProgress(groupId); + return service.isGroupDiscoveryInProgress(groupId, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -704,6 +732,10 @@ public boolean isGroupDiscoveryInProgress (int groupId) { * 0x02 - Access granted ({@link #ACCESS_GRANTED}). * @return true, if operation was initiated successfully. */ + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean setExclusiveAccess(int groupId, List devices, int value) { if (DBG) log("setExclusiveAccess() : groupId = " + groupId + ", access value: " + value); @@ -721,7 +753,7 @@ public boolean setExclusiveAccess(int groupId, List devices, in } try { - service.setExclusiveAccess(mAppId, groupId, devices, value); + service.setExclusiveAccess(mAppId, groupId, devices, value, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -738,6 +770,10 @@ public boolean setExclusiveAccess(int groupId, List devices, in * @return true, if operation was initiated successfully. * @hide */ + @RequiresPermission(allOf = { + android.Manifest.permission.BLUETOOTH_CONNECT, + android.Manifest.permission.BLUETOOTH_PRIVILEGED, + }) public boolean getExclusiveAccessStatus (int groupId, List devices) { if (DBG) log("getExclusiveAccessStatus() : groupId = " + groupId); @@ -755,7 +791,7 @@ public boolean getExclusiveAccessStatus (int groupId, List devi } try { - service.getExclusiveAccessStatus(mAppId, groupId, devices); + service.getExclusiveAccessStatus(mAppId, groupId, devices, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -776,6 +812,8 @@ public boolean getExclusiveAccessStatus (int groupId, List devi * Connection is required to be established. * @return true, if operation was initiated successfully. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean connect (BluetoothDevice device) { if (DBG) log("connect : device = " + device); @@ -792,7 +830,7 @@ public boolean connect (BluetoothDevice device) { } try { - service.connect(mAppId, device); + service.connect(mAppId, device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } @@ -807,6 +845,8 @@ public boolean connect (BluetoothDevice device) { * interested in any Group operations. * @return true, if operation was initiated successfully. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disconnect (BluetoothDevice device) { if (DBG) log("disconnect : device = " + device); @@ -823,7 +863,7 @@ public boolean disconnect (BluetoothDevice device) { } try { - service.disconnect(mAppId, device); + service.disconnect(mAppId, device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); } diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 2c322c9a47c1..d449d173bf5d 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -1080,7 +1080,8 @@ private void continueFromBleOnState() { } if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) { // This triggers transition to STATE_ON - mBluetooth.updateQuietModeStatus(mQuietEnable); + mBluetooth.updateQuietModeStatus(mQuietEnable, + mContext.getAttributionSource()); mBluetooth.onLeServiceUp(mContext.getAttributionSource()); persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH); } @@ -1908,7 +1909,8 @@ public void handleMessage(Message msg) { Slog.i(TAG, "Already at BLE_ON State"); } else { Slog.w(TAG, "BT Enable in BLE_ON State, going to ON"); - mBluetooth.updateQuietModeStatus(mQuietEnable); + mBluetooth.updateQuietModeStatus(mQuietEnable, + mContext.getAttributionSource()); mBluetooth.onLeServiceUp(mContext.getAttributionSource()); } break; From 32fc0cd1beb8b14de778488d4eb35b86f2850e05 Mon Sep 17 00:00:00 2001 From: pramod kotreshappa Date: Wed, 27 May 2020 04:31:03 -0700 Subject: [PATCH 75/92] Periodic Advertisment Sync Transfer feature support CRs-fixed: 2814447 Change-Id: Ic77e8ddbe57e415f7fd9c11943da50e4d86230d2 --- .../le/PeriodicAdvertisingCallback.java | 9 +++ .../le/PeriodicAdvertisingManager.java | 69 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java b/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java index 14ac911fcb7f..b3fd484f699f 100644 --- a/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java +++ b/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java @@ -78,4 +78,13 @@ public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { */ public void onSyncLost(int syncHandle) { } + + /** + * Callback when periodic sync transfered. + * + * @param device + * @param status + */ + public void onSyncTransfered(BluetoothDevice device, int status) { + } } diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java index dea686d18ea2..bf690b948848 100644 --- a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java +++ b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java @@ -215,6 +215,63 @@ public void unregisterSync(PeriodicAdvertisingCallback callback) { } } + public void transferSync(BluetoothDevice bda, int service_data, int sync_handle) { + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + PeriodicAdvertisingCallback callback = null; + for (PeriodicAdvertisingCallback cb : mCallbackWrappers.keySet()) { + callback = cb; + } + if (callback != null) { + callback.onSyncTransfered(bda, + PeriodicAdvertisingCallback.SYNC_NO_RESOURCES); + } + return; + } + try { + gatt.transferSync(bda, service_data , sync_handle); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register sync - ", e); + return; + } + } + + public void transferSetInfo(BluetoothDevice bda, int service_data, + int adv_handle, PeriodicAdvertisingCallback callback) { + transferSetInfo(bda, service_data, adv_handle, callback, null); + } + + public void transferSetInfo (BluetoothDevice bda, int service_data, + int adv_handle, PeriodicAdvertisingCallback callback, Handler handler) { + if (callback == null) { + throw new IllegalArgumentException("callback can't be null"); + } + IBluetoothGatt gatt; + try { + gatt = mBluetoothManager.getBluetoothGatt(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get Bluetooth gatt - ", e); + return; + } + if (handler == null) { + handler = new Handler(Looper.getMainLooper()); + } + IPeriodicAdvertisingCallback wrapper = wrap(callback, handler); + if (wrapper == null) { + throw new IllegalArgumentException("callback was not properly registered"); + } + try { + gatt.transferSetInfo(bda, service_data , adv_handle, wrapper); + } catch (RemoteException e) { + Log.e(TAG, "Failed to register sync - ", e); + return; + } + + } + @SuppressLint("AndroidFrameworkBluetoothPermission") private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback, Handler handler) { @@ -259,6 +316,18 @@ public void run() { } }); } + + public void onSyncTransfered(BluetoothDevice device, int status) { + handler.post(new Runnable() { + @Override + public void run() { + callback.onSyncTransfered(device, status); + // App can still unregister the sync until notified it's lost. + // Remove callback after app was notifed. + //mCallbackWrappers.remove(callback); + } + }); + } }; } } From 6db7e7026ba5472858e77d22b4eecbcdcc009fb0 Mon Sep 17 00:00:00 2001 From: Satheesh Kumar Pallemoni Date: Wed, 31 Mar 2021 15:17:10 +0530 Subject: [PATCH 76/92] Revert "Matching codec index between frameworks and stack" This reverts commit 6e7b6cffb26610a7335e8e9c6b6451bd03468630. CRs-Fixed: 2914887 Change-Id: Ic1cf1a81bba8f0f54a5dc404cfe5fec0bcc850cd --- core/java/android/bluetooth/BluetoothCodecConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index 88682e69287b..34e95b9c2d3c 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -65,10 +65,10 @@ public final class BluetoothCodecConfig implements Parcelable { public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; @UnsupportedAppUsage - public static final int SOURCE_CODEC_TYPE_APTX_ADAPTIVE = 4; + public static final int SOURCE_CODEC_TYPE_LDAC = 4; @UnsupportedAppUsage - public static final int SOURCE_CODEC_TYPE_LDAC = 5; + public static final int SOURCE_CODEC_TYPE_APTX_ADAPTIVE = 5; @UnsupportedAppUsage public static final int SOURCE_CODEC_TYPE_APTX_TWSP = 6; From fd695f128ccf019e09d0f129adc98796ea1216fb Mon Sep 17 00:00:00 2001 From: Satheesh Kumar Pallemoni Date: Wed, 13 May 2020 23:12:45 +0530 Subject: [PATCH 77/92] A2dpSrc: Align legacy codec enumeration with AOSP(4/7) To avoid any future CTS failures, need to align our QC codec enumeration with AOSP. On top of that enumeraion extaned to QVA codecs. CRs-Fixed: 2708798 Change-Id: Idc8dbcdc6454586031ce7431f52a2c031cfd1871 --- .../android/bluetooth/BluetoothCodecConfig.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index 34e95b9c2d3c..6715a1d56730 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -46,7 +46,9 @@ public final class BluetoothCodecConfig implements Parcelable { SOURCE_CODEC_TYPE_APTX, SOURCE_CODEC_TYPE_APTX_HD, SOURCE_CODEC_TYPE_LDAC, - SOURCE_CODEC_TYPE_MAX, + SOURCE_CODEC_TYPE_APTX_ADAPTIVE, + SOURCE_CODEC_TYPE_APTX_TWSP, + SOURCE_QVA_CODEC_TYPE_MAX, SOURCE_CODEC_TYPE_INVALID }) @Retention(RetentionPolicy.SOURCE) @@ -68,13 +70,16 @@ public final class BluetoothCodecConfig implements Parcelable { public static final int SOURCE_CODEC_TYPE_LDAC = 4; @UnsupportedAppUsage - public static final int SOURCE_CODEC_TYPE_APTX_ADAPTIVE = 5; + public static final int SOURCE_CODEC_TYPE_MAX = 5; @UnsupportedAppUsage - public static final int SOURCE_CODEC_TYPE_APTX_TWSP = 6; + public static final int SOURCE_CODEC_TYPE_APTX_ADAPTIVE = SOURCE_CODEC_TYPE_MAX; @UnsupportedAppUsage - public static final int SOURCE_CODEC_TYPE_MAX = 7; + public static final int SOURCE_CODEC_TYPE_APTX_TWSP = SOURCE_CODEC_TYPE_MAX + 1; + + @UnsupportedAppUsage + public static final int SOURCE_QVA_CODEC_TYPE_MAX = SOURCE_CODEC_TYPE_MAX + 2; /* CELT is not an A2DP Codec and only used to fetch encoder ** format for BA usecase, moving out of a2dp codec value list From 5c1696aca67746ba9003f3980c6933cca1ec9b2a Mon Sep 17 00:00:00 2001 From: pramod kotreshappa Date: Mon, 11 Jan 2021 15:36:54 -0800 Subject: [PATCH 78/92] Add Broadcast profile id Add broadcast profile to BluetoothProfile CRs-fixed: 2856233 Change-Id: I17a4eb21ea9cc74a28349a8e0883ed2249838f3c --- .../android/bluetooth/BluetoothAdapter.java | 82 +++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 9 +- .../server/BluetoothModeChangeHelper.java | 7 +- 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index e7a98b0b48d4..cb8ee477b939 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -84,6 +84,9 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; /** * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter} @@ -2004,6 +2007,22 @@ public boolean setActiveDevice(@NonNull BluetoothDevice device, return false; } + /** @hide */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public boolean isBroadcastActive() { + try { + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.isBroadcastActive(); + } + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + return false; + } + /** * Connects all enabled and supported bluetooth profiles between the local and remote device. * Connection is asynchronous and you should listen to each profile's broadcast intent @@ -3056,6 +3075,8 @@ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener } else if (profile == BluetoothProfile.HID_DEVICE) { BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener, this); return true; + } else if (profile == BluetoothProfile.BROADCAST) { + return getBroadcastProfile(context, listener); } else if (profile == BluetoothProfile.HEARING_AID) { if (isHearingAidProfileSupported()) { BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this); @@ -3153,6 +3174,9 @@ public void closeProfileProxy(int profile, BluetoothProfile proxy) { BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy; hidDevice.close(); break; + case BluetoothProfile.BROADCAST: + closeBroadcastProfile(proxy); + break; case BluetoothProfile.HEARING_AID: BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy; hearingAid.close(); @@ -3167,6 +3191,64 @@ public void closeProfileProxy(int profile, BluetoothProfile proxy) { } } + private boolean getBroadcastProfile(Context context, + BluetoothProfile.ServiceListener listener) { + boolean ret = true; + Class broadcastClass = null; + Constructor bcastConstructor = null; + Object broadcastObj = null; + try { + broadcastClass = Class.forName("android.bluetooth.BluetoothBroadcast"); + } catch (ClassNotFoundException ex) { + Log.e(TAG, "no BluetoothBroadcast: exists"); + } + if (broadcastClass != null) { + try { + bcastConstructor = + broadcastClass.getDeclaredConstructor(new Class[]{Context.class, + BluetoothProfile.ServiceListener.class}); + } catch (NoSuchMethodException ex) { + Log.e(TAG, "bcastConstructor: NoSuchMethodException: gdm" + ex); + } + } + if (bcastConstructor != null) { + try { + broadcastObj = bcastConstructor.newInstance(context, listener); + } catch (InstantiationException | IllegalAccessException | + InvocationTargetException ex) { + ex.printStackTrace(); + } + } + if (broadcastObj == null) { + return false; + } + return true; + } + + private void closeBroadcastProfile(BluetoothProfile proxy) { + Class broadcastClass = null; + Method broadcastClose = null; + try { + broadcastClass = Class.forName("android.bluetooth.BluetootBroadcast"); + } catch (ClassNotFoundException ex) { + Log.e(TAG, "no BluetoothBroadcast: exists"); + } + if (broadcastClass != null) { + try { + broadcastClose = broadcastClass.getDeclaredMethod("close", null); + } catch (NoSuchMethodException e) { + Log.e(TAG, "no Broadcast:close method exists"); + } + if (broadcastClose != null) { + try { + broadcastClose.invoke(proxy, null); + } catch(IllegalAccessException | InvocationTargetException ex) { + ex.printStackTrace(); + } + } + } + } + private static final IBluetoothManagerCallback sManagerCallback = new IBluetoothManagerCallback.Stub() { public void onBluetoothServiceUp(IBluetooth bluetoothService) { diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 000819f794fe..1c1b26402541 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -218,13 +218,18 @@ public interface BluetoothProfile { */ public int GROUP_CLIENT = 23; + /** + * Broadcast + * @hide + */ + public int BROADCAST = 24; /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 23; + int MAX_PROFILE_ID = 24; /** * Default priority for devices that we try to auto-connect to and @@ -422,6 +427,8 @@ static String getProfileName(int profile) { return "OPP"; case HEARING_AID: return "HEARING_AID"; + case BROADCAST: + return "BROADCAST"; default: return "UNKNOWN_PROFILE"; } diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java index 3642e4dccf34..2003ebec4a15 100644 --- a/services/core/java/com/android/server/BluetoothModeChangeHelper.java +++ b/services/core/java/com/android/server/BluetoothModeChangeHelper.java @@ -83,7 +83,8 @@ public void onServiceDisconnected(int profile) { @VisibleForTesting public boolean isA2dpOrHearingAidConnected() { - return isA2dpConnected() || isHearingAidConnected(); + return isA2dpConnected() || isHearingAidConnected() || + isBroadcastActive(); } @VisibleForTesting @@ -142,4 +143,8 @@ private boolean isHearingAidConnected() { } return hearingAid.getConnectedDevices().size() > 0; } + + private boolean isBroadcastActive() { + return mAdapter.isBroadcastActive(); + } } From c6fe92e51344a672443457dd0fc0eb6d66d7730e Mon Sep 17 00:00:00 2001 From: Bhakthavatsala Raghavendra Date: Tue, 22 Dec 2020 16:38:39 -0800 Subject: [PATCH 79/92] Bluetooth: Add BC profile entry Add BC profile entry and override constructor for CachedBluetoothDevice CRs-fixed: 2853618 Change-Id: I750139a482280ce3409989582ffad50aed20e081 --- .../android/bluetooth/BluetoothAdapter.java | 91 +++++++++++++++++++ .../android/bluetooth/BluetoothProfile.java | 21 ++++- .../SettingsLib/res/values/strings_ba.xml | 43 +++++++++ .../bluetooth/BluetoothEventManager.java | 21 +++++ .../bluetooth/CachedBluetoothDevice.java | 54 ++++++++++- .../LocalBluetoothProfileManager.java | 34 +++++++ 6 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 packages/SettingsLib/res/values/strings_ba.xml diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index cb8ee477b939..440973078986 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -3006,6 +3006,92 @@ public Pair readOutOfBandData() { return null; } + private void closeBCProfile(BluetoothProfile proxy) { + Class bshClass = null; + Method bshClose = null; + try { + bshClass = Class.forName("android.bluetooth.BluetoothSyncHelper"); + } catch (ClassNotFoundException ex) { + Log.e(TAG, "no BSH: exists"); + bshClass = null; + } + if (bshClass != null) { + Log.d(TAG, "Able to get BSH class handle"); + try { + bshClose = bshClass.getDeclaredMethod("close", null); + } catch (NoSuchMethodException e) { + Log.e(TAG, "no BSH:isSupported method exists"); + } + if (bshClose != null) { + try { + bshClose.invoke(proxy, null); + } catch(IllegalAccessException e) { + Log.e(TAG, "bshClose IllegalAccessException"); + } catch (InvocationTargetException e) { + Log.e(TAG, "bshClose InvocationTargetException"); + } + } + } + Log.d(TAG, "CloseBCProfile returns"); + } + + private boolean getBCProfile(Context context, BluetoothProfile.ServiceListener sl) { + boolean ret = true; + boolean isProfileSupported = false; + Class bshClass = null; + Method bshSupported = null; + Constructor bshCons = null; + Object bshObj = null; + try { + bshClass = Class.forName("android.bluetooth.BluetoothSyncHelper"); + } catch (ClassNotFoundException ex) { + Log.e(TAG, "no BSH: exists"); + bshClass = null; + } + if (bshClass != null) { + Log.d(TAG, "Able to get BSH class handle"); + try { + bshSupported = bshClass.getDeclaredMethod("isSupported", null); + } catch (NoSuchMethodException e) { + Log.e(TAG, "no BSH:isSupported method exists: gdm"); + } + try { + bshCons = + bshClass.getDeclaredConstructor( + new Class[]{Context.class, + BluetoothProfile.ServiceListener.class}); + } catch (NoSuchMethodException ex) { + Log.e(TAG, "bshCons: NoSuchMethodException: gdm" + ex); + } + } + if (bshClass != null && bshSupported != null && bshCons != null) { + try { + isProfileSupported = (boolean)bshSupported.invoke(null, null); + } catch(IllegalAccessException e) { + Log.e(TAG, "BSH:isSupported IllegalAccessException"); + } catch (InvocationTargetException e) { + Log.e(TAG, "BSH:isSupported InvocationTargetException"); + } + if (isProfileSupported) { + try { + bshObj = bshCons.newInstance( + context, sl); + } catch (InstantiationException ex) { + Log.e(TAG, "bshCons InstantiationException:" + ex); + } catch (IllegalAccessException ex) { + Log.e(TAG, "bshCons InstantiationException:" + ex); + } catch (InvocationTargetException ex) { + Log.e(TAG, "bshCons InvocationTargetException:" + ex); + } + } + } + if (bshObj == null) { + ret = false; + } + Log.d(TAG, "getBCService returns" + ret); + return ret; + } + /** * Get the profile proxy object associated with the profile. * @@ -3077,6 +3163,8 @@ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener return true; } else if (profile == BluetoothProfile.BROADCAST) { return getBroadcastProfile(context, listener); + } else if (profile == BluetoothProfile.BC_PROFILE) { + return getBCProfile(context, listener); } else if (profile == BluetoothProfile.HEARING_AID) { if (isHearingAidProfileSupported()) { BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this); @@ -3177,6 +3265,9 @@ public void closeProfileProxy(int profile, BluetoothProfile proxy) { case BluetoothProfile.BROADCAST: closeBroadcastProfile(proxy); break; + case BluetoothProfile.BC_PROFILE: + closeBCProfile(proxy); + break; case BluetoothProfile.HEARING_AID: BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy; hearingAid.close(); diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 1c1b26402541..51a321b406a5 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -223,13 +223,32 @@ public interface BluetoothProfile { * @hide */ public int BROADCAST = 24; + + /** + * BC_PROFILE + * @hide + */ + public static final int BC_PROFILE = 25; + + /** + * PC_PROFILE + * @hide + */ + public static final int PC_PROFILE = 26; + + /** + * CC_SERVER + * @hide + */ + public static final int CC_SERVER = 27; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 24; + int MAX_PROFILE_ID = 27; /** * Default priority for devices that we try to auto-connect to and diff --git a/packages/SettingsLib/res/values/strings_ba.xml b/packages/SettingsLib/res/values/strings_ba.xml new file mode 100644 index 000000000000..34035048be1e --- /dev/null +++ b/packages/SettingsLib/res/values/strings_ba.xml @@ -0,0 +1,43 @@ + + + + + + BC Profile + + Connected to BA server + + + Use for BA + diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index d8fde90ceb4c..08a2ec67766c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -42,6 +42,8 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.UUID; /** @@ -63,6 +65,8 @@ public class BluetoothEventManager { private final android.os.Handler mReceiverHandler; private final UserHandle mUserHandle; private final Context mContext; + private final String ACT_BROADCAST_SOURCE_INFO = + "android.bluetooth.BroadcastAudioSAManager.action.BROADCAST_SOURCE_INFO"; interface Handler { void onReceive(Context context, Intent intent, BluetoothDevice device); @@ -130,6 +134,23 @@ interface Handler { addHandler(BluetoothDevice.ACTION_ACL_CONNECTED, new AclStateChangedHandler()); addHandler(BluetoothDevice.ACTION_ACL_DISCONNECTED, new AclStateChangedHandler()); addHandler(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED, new A2dpCodecConfigChangedHandler()); + Object sourceInfoHandler = null; + try { + Class classSourceInfoHandler = + Class.forName("com.android.settingslib.bluetooth.BroadcastSourceInfoHandler"); + Constructor ctor; + ctor = classSourceInfoHandler.getDeclaredConstructor( + new Class[] {CachedBluetoothDeviceManager.class}); + sourceInfoHandler = ctor.newInstance(mDeviceManager); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException + | InstantiationException | InvocationTargetException e) { + e.printStackTrace(); + } + if (sourceInfoHandler != null) { + Log.d(TAG, "adding SourceInfo Handler"); + addHandler(ACT_BROADCAST_SOURCE_INFO, + (Handler)sourceInfoHandler); + } registerAdapterIntentReceiver(); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 0f918e8562fd..f2d4b5e3cc6e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -51,6 +51,9 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; /** * CachedBluetoothDevice represents a remote Bluetooth device. It contains @@ -150,6 +153,17 @@ public void handleMessage(Message msg) { mTwspBatteryLevel = -1; } + CachedBluetoothDevice(CachedBluetoothDevice cachedDevice) { + mContext = cachedDevice.mContext; + mLocalAdapter = BluetoothAdapter.getDefaultAdapter(); + mProfileManager = cachedDevice.mProfileManager; + mDevice = cachedDevice.mDevice; + fillData(); + mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID; + mTwspBatteryState = -1; + mTwspBatteryLevel = -1; + } + private void initDrawableCache() { int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; @@ -811,13 +825,49 @@ public List getProfiles() { return new ArrayList<>(mProfiles); } + public boolean isBASeeker() { + if (mDevice == null) { + Log.e(TAG, "isBASeeker: mDevice is null"); + return false; + } + boolean ret = false; + Class bCProfileClass = null; + String BC_PROFILE_CLASS = "com.android.settingslib.bluetooth.BCProfile"; + Method baSeeker; + try { + bCProfileClass = Class.forName(BC_PROFILE_CLASS); + baSeeker = bCProfileClass.getDeclaredMethod("isBASeeker", BluetoothDevice.class); + ret = (boolean)baSeeker.invoke(null, mDevice); + } catch (ClassNotFoundException | NoSuchMethodException + | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return ret; + } + public List getConnectableProfiles() { List connectableProfiles = new ArrayList(); + Class bCProfileClass = null; + String BC_PROFILE_CLASS = "com.android.settingslib.bluetooth.BCProfile"; + try { + bCProfileClass = Class.forName(BC_PROFILE_CLASS); + } catch (ClassNotFoundException ex) { + Log.e(TAG, "no BCProfileClass: exists"); + bCProfileClass = null; + } synchronized (mProfileLock) { for (LocalBluetoothProfile profile : mProfiles) { - if (profile.accessProfileEnabled()) { - connectableProfiles.add(profile); + if (bCProfileClass != null && bCProfileClass.isInstance(profile)) { + if (isBASeeker()) { + connectableProfiles.add(profile); + } else { + Log.d(TAG, "BC profile is not enabled for" + mDevice); + } + } else { + if (profile.accessProfileEnabled()) { + connectableProfiles.add(profile); + } } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 4f5b945f74cc..8b8a7c653024 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -37,6 +37,7 @@ import android.content.Context; import android.content.Intent; import android.os.ParcelUuid; +import android.os.SystemProperties; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -50,6 +51,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; /** @@ -96,6 +99,7 @@ public interface ServiceListener { private MapProfile mMapProfile; private MapClientProfile mMapClientProfile; private HidProfile mHidProfile; + private LocalBluetoothProfile mBCProfile; private HidDeviceProfile mHidDeviceProfile; private OppProfile mOppProfile; private PanProfile mPanProfile; @@ -104,6 +108,9 @@ public interface ServiceListener { private HearingAidProfile mHearingAidProfile; private SapProfile mSapProfile; + private static final String BC_CONNECTION_STATE_CHANGED = + "android.bluetooth.bc.profile.action.CONNECTION_STATE_CHANGED"; + /** * Mapping from profile name, e.g. "HEADSET" to profile object. */ @@ -215,6 +222,23 @@ void updateLocalProfiles() { addProfile(mPbapClientProfile, PbapClientProfile.NAME, BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED); } + if (mBCProfile == null && supportedList.contains(BluetoothProfile.BC_PROFILE)) { + if (DEBUG) Log.d(TAG, "Adding local BC profile"); + try { + Class classBCProfile = + Class.forName("com.android.settingslib.bluetooth.BCProfile"); + Constructor ctor; + ctor = classBCProfile.getDeclaredConstructor(new Class[] {Context.class, + CachedBluetoothDeviceManager.class, + LocalBluetoothProfileManager.class}); + mBCProfile = (LocalBluetoothProfile)ctor.newInstance(mContext, mDeviceManager, this); + addProfile(mBCProfile, "BCProfile", + BC_CONNECTION_STATE_CHANGED); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException + | InstantiationException | InvocationTargetException e) { + e.printStackTrace(); + } + } if (mSapProfile == null && supportedList.contains(BluetoothProfile.SAP)) { if (DEBUG) { Log.d(TAG, "Adding local SAP profile"); @@ -458,6 +482,11 @@ SapProfile getSapProfile() { return mSapProfile; } + public LocalBluetoothProfile getBCProfile() { + Log.d(TAG, "getBCProfile returning: " + mBCProfile); + return mBCProfile; + } + @VisibleForTesting HidProfile getHidProfile() { return mHidProfile; @@ -585,6 +614,11 @@ synchronized void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids, removedProfiles.remove(mSapProfile); } + if (mBCProfile != null) { + profiles.add(mBCProfile); + removedProfiles.remove(mBCProfile); + if(DEBUG) Log.d(TAG, "BC profile removed"); + } if (DEBUG) { Log.d(TAG,"New Profiles" + profiles.toString()); } From 4a2d4ec6a284dd3e919c6c29f6a29d10d57e417c Mon Sep 17 00:00:00 2001 From: Sunny Kapdi Date: Thu, 14 Jan 2021 19:52:42 -0800 Subject: [PATCH 80/92] Bluetooth: Broadcast UI: Changes to existing files CRs-fixed: 2856233 Change-Id: I04ecf35e9f6d8e778b3e8f90e8f1ab0cd34eeb9b --- packages/SettingsLib/res/values/strings.xml | 2 ++ .../bluetooth/BluetoothCallback.java | 21 ++++++++++++ .../bluetooth/BluetoothEventManager.java | 34 +++++++++++++++++++ .../LocalBluetoothProfileManager.java | 25 ++++++++++++++ 4 files changed, 82 insertions(+) diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 6b840bd7901d..d8f0d43997ed 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -234,6 +234,8 @@ Phone calls File transfer + + Broadcast Input device diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java index b62f1c35d82f..7475685de281 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java @@ -177,4 +177,25 @@ default void onNewGroupFound(CachedBluetoothDevice cachedDevice, int groupId, default void onGroupDiscoveryStatusChanged (int groupId, int status, int reason) { } + /** + * Called when Broadcast state is changed. It listens to + * {@link android.bluetooth.BluetoothBroadcast#ACTION_BROADCAST_STATE_CHANGED} + * + * @param state the Bluetooth device connection state, the possible values are: + * {@link android.bluetooth.BluetoothBroadcast#STATE_DISABLED}, + * {@link android.bluetooth.BluetoothBroadcast#STATE_ENABLING}, + * {@link android.bluetooth.BluetoothBroadcast#STATE_ENABLED}, + * {@link android.bluetooth.BluetoothBroadcast#STATE_DISABLING}, + * {@link android.bluetooth.BluetoothBroadcast#STATE_STREAMING} + */ + default void onBroadcastStateChanged(int state) { + } + + /** + * Called when Broadcast key is changed. It listens to + * {@link android.bluetooth.BluetoothBroadcast#ACTION_BROADCAST_ENCRYPTION_KEY_GENERATED} + * + */ + default void onBroadcastKeyGenerated() { + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 08a2ec67766c..56d8138d58bb 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -152,6 +152,12 @@ interface Handler { (Handler)sourceInfoHandler); } + // Broadcast broadcasts + addHandler("android.bluetooth.broadcast.profile.action.BROADCAST_STATE_CHANGED", + new BroadcastStateChangedHandler()); + addHandler("android.bluetooth.broadcast.profile.action.BROADCAST_ENCRYPTION_KEY_GENERATED", + new BroadcastKeyGeneratedHandler()); + registerAdapterIntentReceiver(); } @@ -247,6 +253,18 @@ private void dispatchAudioModeChanged() { } } + private void dispatchBroadcastStateChanged(int state) { + for (BluetoothCallback callback : mCallbacks) { + callback.onBroadcastStateChanged(state); + } + } + + private void dispatchBroadcastKeyGenerated() { + for (BluetoothCallback callback : mCallbacks) { + callback.onBroadcastKeyGenerated(); + } + } + @VisibleForTesting void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { @@ -373,6 +391,22 @@ public void onReceive(Context context, Intent intent, BluetoothDevice device) { } } + private class BroadcastStateChangedHandler implements Handler { + //@Override + public void onReceive(Context context, Intent intent, BluetoothDevice device) { + int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, + BluetoothAdapter.ERROR); + dispatchBroadcastStateChanged(state); + } + } + + private class BroadcastKeyGeneratedHandler implements Handler { + //@Override + public void onReceive(Context context, Intent intent, BluetoothDevice device) { + dispatchBroadcastKeyGenerated(); + } + } + private class NameChangedHandler implements Handler { public void onReceive(Context context, Intent intent, BluetoothDevice device) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 8b8a7c653024..9dc81e944f28 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -45,6 +45,8 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -107,6 +109,7 @@ public interface ServiceListener { private PbapServerProfile mPbapProfile; private HearingAidProfile mHearingAidProfile; private SapProfile mSapProfile; + private Object mBroadcastProfileObject; private static final String BC_CONNECTION_STATE_CHANGED = "android.bluetooth.bc.profile.action.CONNECTION_STATE_CHANGED"; @@ -246,6 +249,24 @@ void updateLocalProfiles() { mSapProfile = new SapProfile(mContext, mDeviceManager, this); addProfile(mSapProfile, SapProfile.NAME, BluetoothSap.ACTION_CONNECTION_STATE_CHANGED); } + if (mBroadcastProfileObject == null && supportedList.contains(BluetoothProfile.BROADCAST)) { + if (DEBUG) { + Log.d(TAG, "Adding local Broadcast profile"); + } + try { + //mBroadcastProfileObject = new BroadcastProfile(mContext); + Class classBroadcastProfile = + Class.forName("com.android.settingslib.bluetooth.BroadcastProfile"); + Constructor ctor; + ctor = classBroadcastProfile.getDeclaredConstructor(new Class[] {Context.class}); + mBroadcastProfileObject = ctor.newInstance(mContext); + mProfileNameMap.put("Broadcast", + (LocalBluetoothProfile) mBroadcastProfileObject); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException + | InstantiationException | InvocationTargetException e) { + e.printStackTrace(); + } + } if (mGroupClientProfile == null && supportedList.contains(BluetoothProfile.GROUP_CLIENT)) { if (DEBUG) Log.d(TAG, "Adding local GROUP CLIENT profile"); mGroupClientProfile = new DeviceGroupClientProfile(mContext, mDeviceManager, this); @@ -482,6 +503,10 @@ SapProfile getSapProfile() { return mSapProfile; } + public Object getBroadcastProfile() { + return mBroadcastProfileObject; + } + public LocalBluetoothProfile getBCProfile() { Log.d(TAG, "getBCProfile returning: " + mBCProfile); return mBCProfile; From 8fb812fb11cca7e40c196e8256a498b9b853ce45 Mon Sep 17 00:00:00 2001 From: Sravan voleti Date: Thu, 29 Oct 2020 15:49:12 +0530 Subject: [PATCH 81/92] Group-UI: UI frameworks changes Add system APIS to support Group UI Add AUDIO MCOD related flags Add new icon for Group supported Adv Audio device CRs-Fixed: 2832643 Change-Id: Iab3b23115fb7b9917faed3cbb30f32538824e7db --- .../android/bluetooth/BluetoothClass.java | 5 ++ .../SettingsLib/res/drawable/ic_adv_audio.xml | 76 +++++++++++++++++++ packages/SettingsLib/res/values/strings.xml | 3 + .../bluetooth/BluetoothEventManager.java | 38 ++++++++++ .../settingslib/bluetooth/BluetoothUtils.java | 6 ++ .../bluetooth/CachedBluetoothDevice.java | 60 +++++++++++++++ 6 files changed, 188 insertions(+) mode change 100755 => 100644 core/java/android/bluetooth/BluetoothClass.java create mode 100644 packages/SettingsLib/res/drawable/ic_adv_audio.xml diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java old mode 100755 new mode 100644 index 7fe18a0704ba..1d19c9431e69 --- a/core/java/android/bluetooth/BluetoothClass.java +++ b/core/java/android/bluetooth/BluetoothClass.java @@ -119,6 +119,11 @@ public static final class Service { private static final int BITMASK = 0xFFE000; public static final int LIMITED_DISCOVERABILITY = 0x002000; + /** + * @hide + */ + public static final int GROUP = 0x004000; + public static final int POSITIONING = 0x010000; public static final int NETWORKING = 0x020000; public static final int RENDER = 0x040000; diff --git a/packages/SettingsLib/res/drawable/ic_adv_audio.xml b/packages/SettingsLib/res/drawable/ic_adv_audio.xml new file mode 100644 index 000000000000..34f670fcb369 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_adv_audio.xml @@ -0,0 +1,76 @@ + + + + \ No newline at end of file diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index d8f0d43997ed..0f0e01934168 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -334,6 +334,9 @@ Imaging + + Group Devices + Headphone diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 56d8138d58bb..500d70f5bec3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -293,6 +293,7 @@ private void dispatchA2dpCodecConfigChanged(CachedBluetoothDevice cachedDevice, protected void dispatchNewGroupFound( CachedBluetoothDevice cachedDevice, int groupId, UUID setPrimaryServiceUuid) { synchronized(mCallbacks) { + updateCacheDeviceInfo(groupId, cachedDevice); for (BluetoothCallback callback : mCallbacks) { callback.onNewGroupFound(cachedDevice, groupId, setPrimaryServiceUuid); @@ -429,6 +430,21 @@ public void onReceive(Context context, Intent intent, BluetoothDevice device) { cachedDevice = mDeviceManager.addDevice(device); } + if(bondState == BluetoothDevice.BOND_BONDED) { + int groupId = intent.getIntExtra(BluetoothDevice.EXTRA_GROUP_ID, + BluetoothDevice.ERROR); + if (groupId != BluetoothDevice.ERROR && groupId >= 0) { + updateCacheDeviceInfo(groupId, cachedDevice); + } else if (intent.getBooleanExtra(BluetoothDevice.EXTRA_IS_PRIVATE_ADDRESS, + false)) { + /* + * Do not show private address in UI, just ignore assuming remaining + * events will receive on public address (connection, disconnection) + */ + Log.d(TAG, "Hide showing private address in UI " + cachedDevice); + updateIgnoreDeviceFlag(cachedDevice); + } + } for (BluetoothCallback callback : mCallbacks) { callback.onDeviceBondStateChanged(cachedDevice, bondState); } @@ -628,4 +644,26 @@ public void onReceive(Context context, Intent intent, BluetoothDevice device) { dispatchA2dpCodecConfigChanged(cachedDevice, codecStatus); } } + + private void updateCacheDeviceInfo(int groupId, CachedBluetoothDevice cachedDevice) { + BluetoothDevice device = cachedDevice.getDevice(); + boolean isGroup = cachedDevice.isGroupDevice(); + Log.d(TAG, "updateCacheDeviceInfo groupId " + groupId + + ", cachedDevice :" + cachedDevice + ", name :" + cachedDevice.getName() + +" isGroup :" + isGroup + " groupId " + cachedDevice.getSetId()); + if (isGroup) { + if (groupId != cachedDevice.getSetId()) { + Log.d(TAG, "groupId mismatch ignore" + cachedDevice.getSetId()); + return; + } + Log.d(TAG, "updateCacheDeviceInfo update ignored "); + } else { + cachedDevice.setDeviceType(groupId); + } + } + + private void updateIgnoreDeviceFlag(CachedBluetoothDevice cachedDevice) { + cachedDevice.setDeviceType(CachedBluetoothDevice.PRIVATE_ADDR); + } + } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index 253629c8d128..e86c8402d517 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -100,6 +100,12 @@ public static Pair getBtClassDrawableWithDescription(Context c default: // unrecognized device class; continue } + int tmpBtClass = btClass.getClassOfDevice() & BluetoothClass.Service.GROUP; + if (tmpBtClass == BluetoothClass.Service.GROUP) { + return new Pair<>( + getBluetoothDrawable(context, R.drawable.ic_adv_audio), + context.getString(R.string.bluetooth_talkback_group)); + } } List profiles = cachedDevice.getProfiles(); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index f2d4b5e3cc6e..9995056da942 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -118,6 +118,17 @@ public class CachedBluetoothDevice implements Comparable @VisibleForTesting LruCache mDrawableCache; + private int mGroupId = -1; + + private boolean mIsGroupDevice = false; + + private boolean mIsIgnore = false; + + private final int UNKNOWN = -1, BREDR = 100, GROUPID_START = 0, GROUPID_END = 15; + private int mType = UNKNOWN; + static final int PRIVATE_ADDR = 101; + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { @@ -1369,4 +1380,53 @@ void releaseLruCache() { boolean getUnpairing() { return mUnpairing; } + + public int getSetId(){ + return mGroupId; + } + + public boolean isGroupDevice() { + return mIsGroupDevice; + } + + public boolean isPrivateAddr() { + return mIsIgnore; + } + + public void setDeviceType(int deviceType) { + if (deviceType!= mType) { + // Log.d(TAG, "setDeviceType deviceType " + deviceType + " type " + mType); + mType = deviceType; + if (mType == UNKNOWN || mType == BREDR) { + mIsGroupDevice = false; + mGroupId = UNKNOWN; + mIsIgnore = false; + } else if (mType == PRIVATE_ADDR) { + mIsGroupDevice = false; + mGroupId = UNKNOWN; + mIsIgnore = true; + } else if (mType >= GROUPID_START && mType <= GROUPID_END ) { + mGroupId = mType; + mIsIgnore = false; + mIsGroupDevice = true; + } else { + Log.e(TAG, "setDeviceType error type " + mType); + } + } + /* Log.d(TAG, "setDeviceType mType " + mType + " mIsGroupDevice " + mIsGroupDevice + + " mGroupId " + mGroupId + " mIsIgnore " + mIsIgnore + + " name " + getName() + " addr " + getAddress()); */ + } + + public boolean isTypeUnKnown() { + if (mType == UNKNOWN) { + return true; + } else { + return false; + } + } + + public int getmType() { + return mType; + } } From 324d8520431372ed3d0a9acf99542cab800d20a5 Mon Sep 17 00:00:00 2001 From: zhenchao Date: Fri, 6 Nov 2020 22:12:19 +0800 Subject: [PATCH 82/92] VCP: frameworks and settingsLib changes for VCP Profile This change contains below implementations: - Bluetooth VCP profile proxy object implementation. - VcpProfile implementation in SettingsLib - VCP Refactor changes in framework and settingsLib CRs-fixed: 2857030 Change-Id: If7c607d51c92e4215a1bef58dab77a7479131a31 --- .../android/bluetooth/BluetoothAdapter.java | 7 + .../android/bluetooth/BluetoothProfile.java | 16 +- core/java/android/bluetooth/BluetoothVcp.java | 337 ++++++++++++++++++ packages/SettingsLib/res/values/strings.xml | 3 + .../bluetooth/BluetoothEventManager.java | 27 ++ .../LocalBluetoothProfileManager.java | 13 + .../settingslib/bluetooth/VcpProfile.java | 199 +++++++++++ 7 files changed, 598 insertions(+), 4 deletions(-) create mode 100644 core/java/android/bluetooth/BluetoothVcp.java create mode 100644 packages/SettingsLib/src/com/android/settingslib/bluetooth/VcpProfile.java diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 440973078986..0b4d841117c9 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -3177,6 +3177,9 @@ public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener } else if (profile == BluetoothProfile.GROUP_CLIENT) { BluetoothDeviceGroup groupClient = new BluetoothDeviceGroup(context, listener); return true; + } else if (profile == BluetoothProfile.VCP) { + BluetoothVcp vcp = new BluetoothVcp(context, listener); + return true; } else { return false; } @@ -3279,6 +3282,10 @@ public void closeProfileProxy(int profile, BluetoothProfile proxy) { BluetoothDeviceGroup groupClient = (BluetoothDeviceGroup) proxy; groupClient.close(); break; + case BluetoothProfile.VCP: + BluetoothVcp vcp = (BluetoothVcp) proxy; + vcp.close(); + break; } } diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 51a321b406a5..c61ecc557e3c 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -224,23 +224,29 @@ public interface BluetoothProfile { */ public int BROADCAST = 24; + /** + * VCP + * @hide + */ + public static final int VCP = 25; + /** * BC_PROFILE * @hide */ - public static final int BC_PROFILE = 25; + public static final int BC_PROFILE = 26; /** * PC_PROFILE * @hide */ - public static final int PC_PROFILE = 26; + public static final int PC_PROFILE = 27; /** * CC_SERVER * @hide */ - public static final int CC_SERVER = 27; + public static final int CC_SERVER = 28; /** * Max profile ID. This value should be updated whenever a new profile is added to match @@ -248,7 +254,7 @@ public interface BluetoothProfile { * * @hide */ - int MAX_PROFILE_ID = 27; + int MAX_PROFILE_ID = 28; /** * Default priority for devices that we try to auto-connect to and @@ -448,6 +454,8 @@ static String getProfileName(int profile) { return "HEARING_AID"; case BROADCAST: return "BROADCAST"; + case VCP: + return "VCP"; default: return "UNKNOWN_PROFILE"; } diff --git a/core/java/android/bluetooth/BluetoothVcp.java b/core/java/android/bluetooth/BluetoothVcp.java new file mode 100644 index 000000000000..bb30d46f9fd1 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothVcp.java @@ -0,0 +1,337 @@ +/* + *Copyright (c) 2020, The Linux Foundation. All rights reserved. + *Not a contribution + */ + +/* + * Copyright 2018 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.bluetooth; + +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the public APIs to control the Bluetooth VCP profile. + * + *

    BluetoothVcp is a proxy object for controlling the Bluetooth VolumeControl + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothVcp proxy object. + * + * {@hide} + */ +public final class BluetoothVcp implements BluetoothProfile { + private static final String TAG = "BluetoothVcp"; + private static final boolean DBG = true; + private static final boolean VDBG = true; + + /** + * Intent used to broadcast the change in connection state of the VCP + * profile. + * + *

    This intent will have 3 extras: + *

      + *
    • {@link #EXTRA_STATE} - The current state of the profile.
    • + *
    • {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
    • + *
    • {@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
    • + *
    + * + *

    {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + *

    Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.vcp.profile.action.CONNECTION_STATE_CHANGED"; + + /** + * Intent used to broadcast the volume change of the Volume Renderer device + * + *

    This intent will have 1 extras: + *

      + *
    • {@link #EXTRA_VOLUME} - Current volume settings of Renderer device + * device. Range: 0 - 255. + * + * @hide + */ + public static final String ACTION_VOLUME_CHANGED = + "android.bluetooth.vcp.profile.action.VOLUME_CHANGED"; + + /** + * A int extra field in {@link #ACTION_VOLUME_CHANGED} + * intents that contains the volume of the Volume Renderer device. + */ + public static final String EXTRA_VOLUME = + "android.bluetooth.vcp.profile.extra.VOLUME"; + + /** + * Intent used to broadcast the mute change of the Volume Renderer device + * + *

      This intent will have 1 extras: + *

        + *
      • {@link #EXTRA_MUTE} - Current mute status of Renderer device + * device. False: unmute, True: mute. + * + * @hide + */ + public static final String ACTION_MUTE_CHANGED = + "android.bluetooth.vcp.profile.action.MUTE_CHANGED"; + + /** + * A boolean extra field in {@link #ACTION_MUTE_CHANGED} + * intents that contains the mute status of the Volume Renderer device. + */ + public static final String EXTRA_MUTE = + "android.bluetooth.vcp.profile.extra.MUTE"; + + /** + * Intent used to broadcast the connection mode change of the VCP + * + *

        This intent will have 1 extras: + *

          + *
        • {@link #EXTRA_MODE} - Current connection mode of VCP + * can be any of {@link #MODE_NONE}, {@link #MODE_UNICAST}, + * {@link #MODE_BROADCAST}, {@link #MODE_UNICAST_BROADCAST}, + * + * @hide + */ + public static final String ACTION_CONNECTION_MODE_CHANGED = + "android.bluetooth.vcp.profile.action.CONNECTION_MODE_CHANGED"; + + /** + * A int extra field in {@link #ACTION_CONNECTION_MODE_CHANGED} + * intents that contains the connection mode of the VCP. + */ + public static final String EXTRA_MODE = + "android.bluetooth.vcp.profile.extra.MODE"; + + /** None VCP connection */ + public static final int MODE_NONE = 0x00; + /** VCP connection setup with unicast mode */ + public static final int MODE_UNICAST = 0x01; + /** VCP connection setup with broadcast mode */ + public static final int MODE_BROADCAST = 0x02; + /** VCP connection setup with unicast and broadcast mode */ + public static final int MODE_UNICAST_BROADCAST = 0x03; + + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.VCP, + "BluetoothVcp", IBluetoothVcp.class.getName()) { + @Override + public IBluetoothVcp getServiceInterface(IBinder service) { + return IBluetoothVcp.Stub.asInterface( + Binder.allowBlocking(service)); + } + }; + + /** + * Create a BluetoothVcp proxy object for interacting with the local + * Bluetooth VCP service. + */ + /*package*/ BluetoothVcp(Context context, ServiceListener listener) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mProfileConnector.connect(context, listener); + } + + /*package*/ void close() { + mProfileConnector.disconnect(); + } + + private IBluetoothVcp getService() { + return mProfileConnector.getService(); + } + + @Override + public void finalize() { + close(); + } + + /** + * {@inheritDoc} + */ + @Override + public List getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public List getDevicesMatchingConnectionStates(int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public int getConnectionState(BluetoothDevice device) { + if (VDBG) log("getConnectionState(" + device + ")"); + final IBluetoothVcp service = + getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * Get current VCP Connection mode + * + * @param device: remote device instance + * @return current connection mode of VCP: + * {@link #BluetoothVcp.MODE_NONE} if none VCP connection + * {@link #BluetoothVcp.MODE_UNICAST} if VCP is connected for unicast + * {@link #BluetoothVcp.MODE_BROADCAST} if VCP is connected for broadcast + * {@link #BluetoothVcp.MODE_UNICAST_BROADCAST} if VCP + * is connected for unicast and broadcast + */ + public int getConnectionMode(BluetoothDevice device) { + if (VDBG) log("getConnectionMode(" + device + ")"); + final IBluetoothVcp service = + getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.getConnectionMode(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return MODE_NONE; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return MODE_NONE; + } + + /** + * Set absolute volume to remote device via VCP connection + * + * @param device: remote device instance + * @prarm volume: requested volume settings for remote device + */ + public void setAbsoluteVolume(BluetoothDevice device, int volume) { + if (VDBG) log("setAbsoluteVolume(" + device + ")"); + final IBluetoothVcp service = + getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + service.setAbsoluteVolume(device, volume); + return; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + } + + /** + * Get current absolute volume of the remote device + * + * @param device: remote device instance + * @return current absolute volume of the remote device + */ + public int getAbsoluteVolume(BluetoothDevice device) { + if (VDBG) log("getAbsoluteVolume(" + device + ")"); + final IBluetoothVcp service = + getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.getAbsoluteVolume(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return -1; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return -1; + } + + /** + * Mute or unmute remote device via VCP connection + * + * @param device: remote device instance + * @prarm enableMute: true if mute, false if unmute + */ + public void setMute(BluetoothDevice device, boolean enableMute) { + if (VDBG) log("setMute(" + device + ")" +" enableMute: " + enableMute); + final IBluetoothVcp service = + getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + service.setMute(device, enableMute); + return; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + } + + /** + * Get mute status of remote device + * + * @param device: remote device instance + * @return current mute status of the remote device + * true if mute status, false if unmute status + */ + public boolean isMute(BluetoothDevice device) { + if (VDBG) log("isMute(" + device + ")"); + final IBluetoothVcp service = + getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.isMute(device); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + private boolean isEnabled() { + return mAdapter.getState() == BluetoothAdapter.STATE_ON; + } + + private static boolean isValidDevice(BluetoothDevice device) { + return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} + diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 0f0e01934168..edbde761af72 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -250,6 +250,9 @@ Text Messages SIM Access + + Volume control HD audio: %1$s diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 500d70f5bec3..a20f1dd25cc5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -23,6 +23,7 @@ import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothVcp; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -157,6 +158,9 @@ interface Handler { new BroadcastStateChangedHandler()); addHandler("android.bluetooth.broadcast.profile.action.BROADCAST_ENCRYPTION_KEY_GENERATED", new BroadcastKeyGeneratedHandler()); + // VCP state changed broadcasts + addHandler(BluetoothVcp.ACTION_CONNECTION_MODE_CHANGED, new VcpModeChangedHandler()); + addHandler(BluetoothVcp.ACTION_VOLUME_CHANGED, new VcpVolumeChangedHandler()); registerAdapterIntentReceiver(); } @@ -645,6 +649,29 @@ public void onReceive(Context context, Intent intent, BluetoothDevice device) { } } + private class VcpModeChangedHandler implements Handler { + @Override + public void onReceive(Context context, Intent intent, BluetoothDevice device) { + CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); + int mode = intent.getIntExtra(BluetoothVcp.EXTRA_MODE, 0); + if (cachedDevice != null) { + Log.i(TAG, cachedDevice + " Vcp connection mode change to " + mode); + cachedDevice.refresh(); + } + } + } + + private class VcpVolumeChangedHandler implements Handler { + @Override + public void onReceive(Context context, Intent intent, BluetoothDevice device) { + CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); + if (cachedDevice != null) { + Log.i(TAG, cachedDevice + " Vcp volume change"); + cachedDevice.refresh(); + } + } + } + private void updateCacheDeviceInfo(int groupId, CachedBluetoothDevice cachedDevice) { BluetoothDevice device = cachedDevice.getDevice(); boolean isGroup = cachedDevice.isGroupDevice(); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 9dc81e944f28..8151d4aa93fe 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -34,6 +34,7 @@ import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothSap; import android.bluetooth.BluetoothUuid; +import android.bluetooth.BluetoothVcp; import android.content.Context; import android.content.Intent; import android.os.ParcelUuid; @@ -110,6 +111,7 @@ public interface ServiceListener { private HearingAidProfile mHearingAidProfile; private SapProfile mSapProfile; private Object mBroadcastProfileObject; + private VcpProfile mVcpProfile; private static final String BC_CONNECTION_STATE_CHANGED = "android.bluetooth.bc.profile.action.CONNECTION_STATE_CHANGED"; @@ -273,6 +275,12 @@ void updateLocalProfiles() { addProfile(mGroupClientProfile, mGroupClientProfile.NAME, BluetoothDeviceGroup.ACTION_CONNECTION_STATE_CHANGED); } + if (mVcpProfile == null && supportedList.contains(BluetoothProfile.VCP)) { + if(DEBUG) Log.d(TAG, "Adding local VCP profile"); + mVcpProfile = new VcpProfile(mContext, mDeviceManager, this); + addProfile(mVcpProfile, VcpProfile.NAME, + BluetoothVcp.ACTION_CONNECTION_STATE_CHANGED); + } mEventManager.registerProfileIntentReceiver(); } @@ -525,6 +533,11 @@ HidDeviceProfile getHidDeviceProfile() { public DeviceGroupClientProfile getDeviceGroupClientProfile() { return mGroupClientProfile; } + + public VcpProfile getVcpProfile() { + return mVcpProfile; + } + /** * Fill in a list of LocalBluetoothProfile objects that are supported by * the local device and the remote device. diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VcpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VcpProfile.java new file mode 100644 index 000000000000..eedb05d0e2df --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VcpProfile.java @@ -0,0 +1,199 @@ +/****************************************************************************** + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.android.settingslib.bluetooth; + +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothClass; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothVcp; +import android.bluetooth.BluetoothProfile; +import android.content.Context; +import android.util.Log; + +import com.android.settingslib.R; + +import java.util.ArrayList; +import java.util.List; + +public class VcpProfile implements LocalBluetoothProfile { + private static final String TAG = "VcpProfile"; + private static boolean V = true; + + private Context mContext; + + private BluetoothVcp mService; + private boolean mIsProfileReady; + + private final CachedBluetoothDeviceManager mDeviceManager; + + static final String NAME = "VCP"; + private final LocalBluetoothProfileManager mProfileManager; + private final BluetoothAdapter mBluetoothAdapter; + + // Order of this profile in device profiles list + private static final int ORDINAL = 1; + + // These callbacks run on the main thread. + private final class VcpServiceListener + implements BluetoothProfile.ServiceListener { + + public void onServiceConnected(int profile, BluetoothProfile proxy) { + mService = (BluetoothVcp) proxy; + Log.w(TAG, "Bluetooth service Connected"); + mIsProfileReady=true; + mProfileManager.callServiceConnectedListeners(); + } + + public void onServiceDisconnected(int profile) { + Log.w(TAG, "Bluetooth service Disconnected"); + mIsProfileReady=false; + } + } + + public boolean isProfileReady() { + return mIsProfileReady; + } + + @Override + public int getProfileId() { + return BluetoothProfile.VCP; + } + + VcpProfile(Context context, CachedBluetoothDeviceManager deviceManager, + LocalBluetoothProfileManager profileManager) { + mContext = context; + mDeviceManager = deviceManager; + mProfileManager = profileManager; + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mBluetoothAdapter.getProfileProxy(context, + new VcpServiceListener(), BluetoothProfile.VCP); + } + + public boolean accessProfileEnabled() { + return false; + } + + public boolean isAutoConnectable() { + return false; + } + + public int getConnectionStatus(BluetoothDevice device) { + if (mService == null) { + return BluetoothProfile.STATE_DISCONNECTED; + } + return mService.getConnectionState(device); + } + + public int getConnectionMode(BluetoothDevice device) { + if (mService == null) { + return BluetoothProfile.STATE_DISCONNECTED; + } + return mService.getConnectionMode(device); + } + + @Override + public boolean isEnabled(BluetoothDevice device) { + return false; + } + + @Override + public int getConnectionPolicy(BluetoothDevice device) { + return CONNECTION_POLICY_UNKNOWN; + } + + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + return false; + } + + public void setAbsoluteVolume(BluetoothDevice device, int volume) { + if (mService == null) { + return; + } + mService.setAbsoluteVolume(device, volume); + } + + public int getAbsoluteVolume(BluetoothDevice device) { + if (mService == null) { + return -1; + } + return mService.getAbsoluteVolume(device); + } + + public void setMute(BluetoothDevice device, boolean enableMute) { + if (mService == null) { + return; + } + mService.setMute(device, enableMute); + } + + public boolean isMute(BluetoothDevice device) { + if (mService == null) { + return false; + } + return mService.isMute(device); + } + + public String toString() { + return NAME; + } + + public int getOrdinal() { + return ORDINAL; + } + + public int getNameResource(BluetoothDevice device) { + return R.string.bluetooth_profile_vcp; + } + + public int getSummaryResourceForDevice(BluetoothDevice device) { + return 0; // VCP profile not displayed in UI + } + + public int getDrawableResource(BluetoothClass btClass) { + return 0; // no icon for VCP + } + + protected void finalize() { + Log.d(TAG, "finalize()"); + if (mService != null) { + try { + BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.VCP, + mService); + mService = null; + }catch (Throwable t) { + Log.w(TAG, "Error cleaning up Vcp proxy", t); + } + } + } +} + From 6861a6d33de8ac36ca221016cb8d0f2e61bf99cf Mon Sep 17 00:00:00 2001 From: zhenchao Date: Thu, 23 Sep 2021 19:13:46 +0800 Subject: [PATCH 83/92] Update Bluetooth permissions for VCP APIS (1/3) - This adds attribution source as a parameter to Value added APIS, matching aidl changes also made in this topic. - This is now required to allow the app ops for the new bluetooth permissions (BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE, and BLUETOOTH_SCAN) to be noted. - Update Bluetooth API annotations. - Add missing permission checkes for binder APIS. Change-Id: I20cbc69fbfe86b510dfc6d6335de31072191b491 CRs-Fixed: 3046326 --- core/java/android/bluetooth/BluetoothVcp.java | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothVcp.java b/core/java/android/bluetooth/BluetoothVcp.java index bb30d46f9fd1..8132ce2025a8 100644 --- a/core/java/android/bluetooth/BluetoothVcp.java +++ b/core/java/android/bluetooth/BluetoothVcp.java @@ -21,6 +21,9 @@ package android.bluetooth; +import android.annotation.RequiresPermission; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import android.content.AttributionSource; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -62,6 +65,8 @@ public final class BluetoothVcp implements BluetoothProfile { *

          Requires {@link android.Manifest.permission#BLUETOOTH} permission to * receive. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.vcp.profile.action.CONNECTION_STATE_CHANGED"; @@ -75,6 +80,8 @@ public final class BluetoothVcp implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_VOLUME_CHANGED = "android.bluetooth.vcp.profile.action.VOLUME_CHANGED"; @@ -95,6 +102,8 @@ public final class BluetoothVcp implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_MUTE_CHANGED = "android.bluetooth.vcp.profile.action.MUTE_CHANGED"; @@ -116,6 +125,8 @@ public final class BluetoothVcp implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_MODE_CHANGED = "android.bluetooth.vcp.profile.action.CONNECTION_MODE_CHANGED"; @@ -136,6 +147,7 @@ public final class BluetoothVcp implements BluetoothProfile { public static final int MODE_UNICAST_BROADCAST = 0x03; private BluetoothAdapter mAdapter; + private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.VCP, "BluetoothVcp", IBluetoothVcp.class.getName()) { @@ -153,6 +165,7 @@ public IBluetoothVcp getServiceInterface(IBinder service) { /*package*/ BluetoothVcp(Context context, ServiceListener listener) { mAdapter = BluetoothAdapter.getDefaultAdapter(); mProfileConnector.connect(context, listener); + mAttributionSource = mAdapter.getAttributionSource(); } /*package*/ void close() { @@ -190,13 +203,15 @@ public List getDevicesMatchingConnectionStates(int[] states) { * {@inheritDoc} */ @Override + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { if (VDBG) log("getConnectionState(" + device + ")"); final IBluetoothVcp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionState(device); + return service.getConnectionState(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return BluetoothProfile.STATE_DISCONNECTED; @@ -217,13 +232,15 @@ public int getConnectionState(BluetoothDevice device) { * {@link #BluetoothVcp.MODE_UNICAST_BROADCAST} if VCP * is connected for unicast and broadcast */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionMode(BluetoothDevice device) { if (VDBG) log("getConnectionMode(" + device + ")"); final IBluetoothVcp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getConnectionMode(device); + return service.getConnectionMode(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return MODE_NONE; @@ -239,13 +256,15 @@ public int getConnectionMode(BluetoothDevice device) { * @param device: remote device instance * @prarm volume: requested volume settings for remote device */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setAbsoluteVolume(BluetoothDevice device, int volume) { if (VDBG) log("setAbsoluteVolume(" + device + ")"); final IBluetoothVcp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - service.setAbsoluteVolume(device, volume); + service.setAbsoluteVolume(device, volume, mAttributionSource); return; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); @@ -261,13 +280,15 @@ public void setAbsoluteVolume(BluetoothDevice device, int volume) { * @param device: remote device instance * @return current absolute volume of the remote device */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getAbsoluteVolume(BluetoothDevice device) { if (VDBG) log("getAbsoluteVolume(" + device + ")"); final IBluetoothVcp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.getAbsoluteVolume(device); + return service.getAbsoluteVolume(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return -1; @@ -283,13 +304,15 @@ public int getAbsoluteVolume(BluetoothDevice device) { * @param device: remote device instance * @prarm enableMute: true if mute, false if unmute */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setMute(BluetoothDevice device, boolean enableMute) { if (VDBG) log("setMute(" + device + ")" +" enableMute: " + enableMute); final IBluetoothVcp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - service.setMute(device, enableMute); + service.setMute(device, enableMute, mAttributionSource); return; } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); @@ -306,13 +329,15 @@ public void setMute(BluetoothDevice device, boolean enableMute) { * @return current mute status of the remote device * true if mute status, false if unmute status */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isMute(BluetoothDevice device) { if (VDBG) log("isMute(" + device + ")"); final IBluetoothVcp service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { try { - return service.isMute(device); + return service.isMute(device, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); return false; From 69b9b5eb63adf6f639e1af184e63c78ea3bab54f Mon Sep 17 00:00:00 2001 From: Gurpreet Ghai Date: Fri, 17 Sep 2021 16:24:44 +0530 Subject: [PATCH 84/92] BT: Add APIs to dynamically control Active profile - Add APIs to dynamically switch between BREDR and LE profiles for call and media Audio - These APIs are introduced as SDK APIs which can be called from external apps like Settings Change-Id: Iafb514fcf833ccd77ba300f60277ac30eae89943 CRs-Fixed: 3072404 --- core/java/android/bluetooth/BluetoothVcp.java | 58 +++++++++++++++++++ .../settingslib/bluetooth/VcpProfile.java | 16 ++++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/core/java/android/bluetooth/BluetoothVcp.java b/core/java/android/bluetooth/BluetoothVcp.java index 8132ce2025a8..1f8498c62fb4 100644 --- a/core/java/android/bluetooth/BluetoothVcp.java +++ b/core/java/android/bluetooth/BluetoothVcp.java @@ -146,6 +146,14 @@ public final class BluetoothVcp implements BluetoothProfile { /** VCP connection setup with unicast and broadcast mode */ public static final int MODE_UNICAST_BROADCAST = 0x03; + public static final int A2DP = 0x0001; + public static final int HFP = 0x0002; + public static final int LE_MEDIA = 0x0010; + public static final int LE_VOICE = 0x2000; + + public static final int CALL_STREAM = 0; + public static final int MEDIA_STREAM = 1; + private BluetoothAdapter mAdapter; private final AttributionSource mAttributionSource; private final BluetoothProfileConnector mProfileConnector = @@ -347,6 +355,56 @@ public boolean isMute(BluetoothDevice device) { return false; } + /** + * set active stream for a DuMo device + * + * @param device: remote device instance + * @param audioType: call/media audio + * @param profile: profile that is needed to be active + * @return success/failure + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + public boolean setActiveProfile(BluetoothDevice device, int audioType, int profile) { + if (VDBG) log("setActiveProfile(" + device + ")"); + final IBluetoothVcp service = + getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.setActiveProfile(device, audioType, profile, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * set active stream for a DuMo device + * + * @param audioType: call/media audio + * @return ID of current active profile + */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + public int getActiveProfile(int audioType) { + if (VDBG) log("getActiveProfile(" + audioType + ")"); + final IBluetoothVcp service = + getService(); + if (service != null && isEnabled()) { + try { + return service.getActiveProfile(audioType, mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return -1; + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return -1; + } + private boolean isEnabled() { return mAdapter.getState() == BluetoothAdapter.STATE_ON; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VcpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VcpProfile.java index eedb05d0e2df..1ed9f27b4e82 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/VcpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/VcpProfile.java @@ -163,6 +163,20 @@ public boolean isMute(BluetoothDevice device) { return mService.isMute(device); } + public boolean setActiveProfile(BluetoothDevice device, int audioType, int profile) { + if(mService != null) { + return mService.setActiveProfile(device, audioType, profile); + } + return false; + } + + public int getActiveProfile(int audioType) { + if(mService != null) { + return mService.getActiveProfile(audioType); + } + return -1; + } + public String toString() { return NAME; } @@ -176,7 +190,7 @@ public int getNameResource(BluetoothDevice device) { } public int getSummaryResourceForDevice(BluetoothDevice device) { - return 0; // VCP profile not displayed in UI + return 0; // VCP profile not displayed in UI } public int getDrawableResource(BluetoothClass btClass) { From 06600a7e03d3407125bc62c733bbf7f1f6bd033f Mon Sep 17 00:00:00 2001 From: himta ram Date: Fri, 31 Jan 2020 11:22:41 +0530 Subject: [PATCH 85/92] hfp: define STATE_AUDIO_DISCONNECTING audio state Define Headset state when SCO audio is disconnecting. CRs-Fixed: 2634664 Change-Id: Ie1200d4e9ce0b38ba71bd43c76e50720cb1ddc3f --- core/java/android/bluetooth/BluetoothHeadset.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 59ebd21f7eaa..2854068c3fd4 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -269,6 +269,13 @@ public final class BluetoothHeadset implements BluetoothProfile { */ public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY"; + /** + * Headset state when SCO audio is disconnecting. + * + * @hide + */ + public static final int STATE_AUDIO_DISCONNECTING = 13; + /** * Headset state when SCO audio is not connected. * This state can be one of From 2384f60662c5dbb5ea4f538b1704836bcdd07824 Mon Sep 17 00:00:00 2001 From: Bhakthavatsala Raghavendra Date: Wed, 9 Dec 2020 16:12:52 -0800 Subject: [PATCH 86/92] Bluetooth: Add constructor for sending address type Override the constructor to send addresstype as part of Scan results. These addresstype information will be sent only to previleged applications CRs-fixed: 2834981 Change-Id: I47acaf1fcc1cfdd35aa9fe2b913f588cfc51cc70 --- .../java/android/bluetooth/le/ScanResult.java | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/core/java/android/bluetooth/le/ScanResult.java b/core/java/android/bluetooth/le/ScanResult.java index 522845628487..40ff3bec817e 100644 --- a/core/java/android/bluetooth/le/ScanResult.java +++ b/core/java/android/bluetooth/le/ScanResult.java @@ -93,6 +93,7 @@ public final class ScanResult implements Parcelable, Attributable { private int mAdvertisingSid; private int mTxPower; private int mPeriodicAdvertisingInterval; + private int mAddressType; /** * Constructs a new ScanResult. @@ -117,6 +118,7 @@ public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi, mAdvertisingSid = SID_NOT_PRESENT; mTxPower = 127; mPeriodicAdvertisingInterval = 0; + mAddressType = -1; } /** @@ -146,6 +148,42 @@ public ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int sec mPeriodicAdvertisingInterval = periodicAdvertisingInterval; mScanRecord = scanRecord; mTimestampNanos = timestampNanos; + mAddressType = -1; + } + + /** + * Constructs a new ScanResult. + * + * @param device Remote Bluetooth device found. + * @param addressType addressType for the Scan result + * @param eventType Event type. + * @param primaryPhy Primary advertising phy. + * @param secondaryPhy Secondary advertising phy. + * @param advertisingSid Advertising set ID. + * @param txPower Transmit power. + * @param rssi Received signal strength. + * @param periodicAdvertisingInterval Periodic advertising interval. + * @param scanRecord Scan record including both advertising data and scan response data. + * @param timestampNanos Timestamp at which the scan result was observed. + * @param addressType addressType for the Scan result + * + *@hide + */ + public ScanResult(BluetoothDevice device, int addressType, int eventType, int primaryPhy, + int secondaryPhy, + int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval, + ScanRecord scanRecord, long timestampNanos) { + mDevice = device; + mEventType = eventType; + mPrimaryPhy = primaryPhy; + mSecondaryPhy = secondaryPhy; + mAdvertisingSid = advertisingSid; + mTxPower = txPower; + mRssi = rssi; + mPeriodicAdvertisingInterval = periodicAdvertisingInterval; + mScanRecord = scanRecord; + mTimestampNanos = timestampNanos; + mAddressType = addressType; } private ScanResult(Parcel in) { @@ -174,6 +212,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mAdvertisingSid); dest.writeInt(mTxPower); dest.writeInt(mPeriodicAdvertisingInterval); + dest.writeInt(mAddressType); } private void readFromParcel(Parcel in) { @@ -191,6 +230,7 @@ private void readFromParcel(Parcel in) { mAdvertisingSid = in.readInt(); mTxPower = in.readInt(); mPeriodicAdvertisingInterval = in.readInt(); + mAddressType = in.readInt(); } @Override @@ -308,6 +348,14 @@ public int getPeriodicAdvertisingInterval() { return mPeriodicAdvertisingInterval; } + /** + * + *@hide + */ + public int getAddressType() { + return mAddressType; + } + @Override public int hashCode() { return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos, @@ -333,7 +381,8 @@ public boolean equals(@Nullable Object obj) { && mSecondaryPhy == other.mSecondaryPhy && mAdvertisingSid == other.mAdvertisingSid && mTxPower == other.mTxPower - && mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval; + && mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval + && mAddressType == other.mAddressType; } @Override From 2b5bafb8584d329ecc2635f6c20f0bdbcd32d8d8 Mon Sep 17 00:00:00 2001 From: Sumit Deshmukh Date: Wed, 16 Sep 2020 03:08:34 +0530 Subject: [PATCH 87/92] BLE: Add support for Group AD Type based scan filtering. This change contains - API's to do filtered scan based on Group AD Type. - Extract Group identifier data from ScanRecord. CRs-Fixed: 2826578 Change-Id: I75982a1acf982c03234d9e1084aee287d3d3623d --- .../java/android/bluetooth/le/ScanFilter.java | 51 +++++++++++++++++-- .../java/android/bluetooth/le/ScanRecord.java | 31 +++++++++-- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java index b5d56be9a1ce..dd3126d78a1b 100644 --- a/core/java/android/bluetooth/le/ScanFilter.java +++ b/core/java/android/bluetooth/le/ScanFilter.java @@ -96,6 +96,10 @@ public final class ScanFilter implements Parcelable { private final int mTDSFlagsMask; private final byte[] mWifiNANHash; + private final boolean mGroupBasedFiltering; + + private static final int GROUP_DATA_LEN = 6; + /** @hide */ public static final ScanFilter EMPTY = new ScanFilter.Builder().build(); @@ -105,7 +109,8 @@ private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, byte[] serviceData, byte[] serviceDataMask, int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask, @AddressType int addressType, @Nullable byte[] irk, - int orgId, int TDSFlags, int TDSFlagsMask, byte[] wifiNANHash) { + int orgId, int TDSFlags, int TDSFlagsMask, byte[] wifiNANHash, + boolean groupBasedFiltering) { mDeviceName = name; mServiceUuid = uuid; mServiceUuidMask = uuidMask; @@ -124,6 +129,7 @@ private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, mTDSFlags = TDSFlags; mTDSFlagsMask = TDSFlagsMask; mWifiNANHash = wifiNANHash; + mGroupBasedFiltering = groupBasedFiltering; } @Override @@ -205,6 +211,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeByteArray(mWifiNANHash); } } + dest.writeBoolean(mGroupBasedFiltering); } /** @@ -312,6 +319,8 @@ public ScanFilter createFromParcel(Parcel in) { } } + boolean groupBasedFiltering = in.readBoolean(); + builder.setGroupBasedFiltering(groupBasedFiltering); return builder.build(); } }; @@ -438,6 +447,14 @@ public byte[] getWifiNANHash() { return mWifiNANHash; } + /** + * @hide + * Returns true, if Group AD Type based filtering is enabled. Otherwise, false. + */ + public boolean getGroupFilteringValue() { + return mGroupBasedFiltering; + } + /** * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match * if it matches all the field filters. @@ -507,6 +524,13 @@ public boolean matches(ScanResult scanResult) { } } + // Group AD Type filter match + if (mGroupBasedFiltering) { + if (scanRecord.getGroupIdentifierData().length != GROUP_DATA_LEN) { + return false; + } + } + // All filters match. return true; } @@ -604,7 +628,8 @@ public String toString() { + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + ", mOrganizationId=" + mOrgId + ", mTDSFlags=" + mTDSFlags + ", mTDSFlagsMask=" + mTDSFlagsMask - + ", mWifiNANHash=" + Arrays.toString(mWifiNANHash) +"]"; + + ", mWifiNANHash=" + Arrays.toString(mWifiNANHash) +"]" + + ", mGroupBasedFiltering=" + mGroupBasedFiltering; } @Override @@ -617,7 +642,8 @@ public int hashCode() { Arrays.hashCode(mServiceDataMask), mServiceUuid, mServiceUuidMask, mServiceSolicitationUuid, mServiceSolicitationUuidMask, - mOrgId, mTDSFlags, mTDSFlagsMask, Arrays.hashCode(mWifiNANHash)); + mOrgId, mTDSFlags, mTDSFlagsMask, Arrays.hashCode(mWifiNANHash), + mGroupBasedFiltering); } @Override @@ -645,7 +671,8 @@ public boolean equals(@Nullable Object obj) { && mOrgId == other.mOrgId && mTDSFlags == other.mTDSFlags && mTDSFlagsMask == other.mTDSFlagsMask - && Objects.deepEquals(mWifiNANHash, other.mWifiNANHash); + && Objects.deepEquals(mWifiNANHash, other.mWifiNANHash) + && mGroupBasedFiltering == other.mGroupBasedFiltering; } /** @@ -692,6 +719,8 @@ public static final class Builder { private int mTDSFlagsMask = -1; private byte[] mWifiNANHash; + private boolean mGroupBasedFiltering; + /** * Set filter on device name. */ @@ -1017,6 +1046,17 @@ public Builder setTransportDiscoveryData(int orgId, int TDSFlags, int TDSFlagsMa mWifiNANHash = wifiNANHash; return this; } + + /** + * @hide + * Enable filter on Group AD Type. + */ + public @NonNull Builder setGroupBasedFiltering( + boolean enable) { + mGroupBasedFiltering = enable; + return this; + } + /** * Build {@link ScanFilter}. * @@ -1029,7 +1069,8 @@ public ScanFilter build() { mServiceDataUuid, mServiceData, mServiceDataMask, mManufacturerId, mManufacturerData, mManufacturerDataMask, mAddressType, mIrk, - mOrgId, mTDSFlags, mTDSFlagsMask, mWifiNANHash); + mOrgId, mTDSFlags, mTDSFlagsMask, mWifiNANHash, + mGroupBasedFiltering); } } } diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java index da96b142ecd1..28dbfbef993f 100644 --- a/core/java/android/bluetooth/le/ScanRecord.java +++ b/core/java/android/bluetooth/le/ScanRecord.java @@ -60,6 +60,10 @@ public final class ScanRecord { private static final int DATA_TYPE_SERVICE_SOLICITATION_UUIDS_128_BIT = 0x15; private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF; private static final int DATA_TYPE_TRANSPORT_DISCOVERY_DATA = 0x26; + /** + * @hide + */ + public static int DATA_TYPE_GROUP_AD_TYPE = 0x00; // Flags of the advertising data. private final int mAdvertiseFlags; @@ -85,6 +89,9 @@ public final class ScanRecord { // Transport Discovery data. private final byte[] mTDSData; + // Group Identifier Data + private final byte[] mGroupIdentifierData; + /** * Returns the advertising flags indicating the discoverable mode and capability of the device. * Returns -1 if the flag field is not set. @@ -176,6 +183,14 @@ public byte[] getTDSData() { return mTDSData; } + /** + * @hide + * Returns Group Identifier data + */ + public byte[] getGroupIdentifierData() { + return mGroupIdentifierData; + } + /** * Returns raw bytes of scan record. */ @@ -209,7 +224,7 @@ private ScanRecord(List serviceUuids, SparseArray manufacturerData, Map serviceData, int advertiseFlags, int txPowerLevel, - String localName, byte[] tdsData, byte[] bytes) { + String localName, byte[] tdsData, byte[] groupIdentifierData, byte[] bytes) { mServiceSolicitationUuids = serviceSolicitationUuids; mServiceUuids = serviceUuids; mManufacturerSpecificData = manufacturerData; @@ -218,6 +233,7 @@ private ScanRecord(List serviceUuids, mAdvertiseFlags = advertiseFlags; mTxPowerLevel = txPowerLevel; mTDSData = tdsData; + mGroupIdentifierData = groupIdentifierData; mBytes = bytes; } @@ -249,6 +265,7 @@ public static ScanRecord parseFromBytes(byte[] scanRecord) { Map serviceData = new ArrayMap(); byte[] tdsData = null; + byte[] groupIdentifierData = null; try { while (currentPos < scanRecord.length) { @@ -330,8 +347,12 @@ public static ScanRecord parseFromBytes(byte[] scanRecord) { case DATA_TYPE_TRANSPORT_DISCOVERY_DATA: tdsData = extractBytes(scanRecord, currentPos, dataLength); break; + default: - // Just ignore, we don't handle such data type. + if (fieldType == DATA_TYPE_GROUP_AD_TYPE) { + Log.d(TAG, "Parsing Group Identifier data"); + groupIdentifierData = extractBytes(scanRecord, currentPos, dataLength); + } break; } currentPos += dataLength; @@ -341,12 +362,14 @@ public static ScanRecord parseFromBytes(byte[] scanRecord) { serviceUuids = null; } return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData, - serviceData, advertiseFlag, txPowerLevel, localName, tdsData, scanRecord); + serviceData, advertiseFlag, txPowerLevel, localName, tdsData, + groupIdentifierData, scanRecord); } catch (Exception e) { Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord)); // As the record is invalid, ignore all the parsed results for this packet // and return an empty record with raw scanRecord bytes in results - return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null, null, scanRecord); + return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null, null, + null, scanRecord); } } From 445cff9067949e3d74d04903a432f3e274409a37 Mon Sep 17 00:00:00 2001 From: Gurpreet Ghai Date: Wed, 22 Sep 2021 09:42:14 +0530 Subject: [PATCH 88/92] BT: Add entry for MCP Service in Frameworks - Add Profile entry for MCP Service CRs-Fixed: 3033146 Change-Id: I7f89b6ab2c84f8681d953c0f5c65750946962fc3 --- core/java/android/bluetooth/BluetoothProfile.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index c61ecc557e3c..5169142ba0b0 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -248,13 +248,19 @@ public interface BluetoothProfile { */ public static final int CC_SERVER = 28; + /** + * MCP_SERVER + * @hide + */ + public static final int MCP_SERVER = 29; + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 28; + int MAX_PROFILE_ID = 29; /** * Default priority for devices that we try to auto-connect to and From a6a0f9bfeac30f150d4019b54f5d5a5e934d623f Mon Sep 17 00:00:00 2001 From: pramod kotreshappa Date: Fri, 24 Sep 2021 00:15:12 -0700 Subject: [PATCH 89/92] Use BLUETOOTH_CONNECT permission instead of legacy permission CRs-Fixed: 3043842 Change-Id: I70f3c109140bf9634e2d0a293a323cae3c9cd476 --- core/java/android/bluetooth/BluetoothAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 0b4d841117c9..7bb3317a1329 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -2008,12 +2008,12 @@ public boolean setActiveDevice(@NonNull BluetoothDevice device, } /** @hide */ - @RequiresPermission(Manifest.permission.BLUETOOTH) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isBroadcastActive() { try { mServiceLock.readLock().lock(); if (mService != null) { - return mService.isBroadcastActive(); + return mService.isBroadcastActive(mAttributionSource); } } catch (RemoteException e) { Log.e(TAG, "", e); From bb712dee351912198bdeb5745339ec1ceb34e660 Mon Sep 17 00:00:00 2001 From: Gautam Manam Date: Wed, 7 Jul 2021 14:16:18 +0530 Subject: [PATCH 90/92] AudioService: Music resumed to speaker after call Observed disconnect of A2DP is coming in and scheduled with delay of 500msecs as non supress intent and around same time connect is coming where it founds device is already connected and triggers device config change intent. And after timeout,scheduled disconnect event gets triggered and removes A2DP device causing music to route to speaker Fix is to remove scheduled events when recent event comes CRs-Fixed: 2974286 Change-Id: I625226269474a4f4b122275bcaa5dddc19cfeaad --- .../java/com/android/server/audio/AudioDeviceBroker.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index c59bcb996df8..23f513bce9d1 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -621,7 +621,10 @@ private void removeScheduledA2dpEvents(@NonNull BluetoothDevice device) { boolean suppressNoisyIntent, int a2dpVolume) { final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile, suppressNoisyIntent, a2dpVolume); - sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE, info); + synchronized (mDeviceStateLock) { + removeScheduledA2dpEvents(info.mDevice, info.mProfile); + sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE, info); + } } private static final class HearingAidDeviceConnectionInfo { From 8a0dbfa73711dd61a3b1d7efb4a27067fea01df1 Mon Sep 17 00:00:00 2001 From: Zhou Song Date: Tue, 11 May 2021 23:21:24 +0800 Subject: [PATCH 91/92] Check for a2dp profile when removing stale connection events During disconnecting both a2dp output and input device, same device is passed to handler but with different profile, to avoid removing existing event in the handler, check the a2dp profile as well. CRs-Fixed: 2943846 Change-Id: I9d0eced33d57c46d6e49a74e958c488f3a07249c --- .../java/com/android/server/audio/AudioDeviceBroker.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 23f513bce9d1..7168d579505f 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -547,7 +547,8 @@ public boolean equals(Object o) { return true; } if (o instanceof BtDeviceConnectionInfo) { - return mDevice.equals(((BtDeviceConnectionInfo) o).mDevice); + return mDevice.equals(((BtDeviceConnectionInfo) o).mDevice) && + mProfile == (((BtDeviceConnectionInfo) o).mProfile); } return false; } @@ -580,7 +581,7 @@ public String toString() { // 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); + removeScheduledA2dpEvents(info.mDevice, info.mProfile); sendLMsgNoDelay( info.mState == BluetoothProfile.STATE_CONNECTED @@ -592,14 +593,14 @@ public String toString() { /** remove all previously scheduled connection and state change events for the given device */ @GuardedBy("mDeviceStateLock") - private void removeScheduledA2dpEvents(@NonNull BluetoothDevice device) { + private void removeScheduledA2dpEvents(@NonNull BluetoothDevice device, int profile) { 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); + BluetoothProfile.STATE_CONNECTED, profile, false, -1); mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, connectionInfoToRemove); mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION, From 9d59e2356c9bf87bd19de39f79b699cc89108b51 Mon Sep 17 00:00:00 2001 From: LuK1337 Date: Mon, 28 Dec 2020 20:43:22 +0100 Subject: [PATCH 92/92] audio: Discard QTI only codecs when AOSP BT stack is in use * Fixes a2dp offload on devices not using QTI BT stack. Change-Id: I5044d02f8be74bbf050b16083e753bd392dac0a6 --- media/java/android/media/AudioSystem.java | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index f1197edd453d..9b3ade524a4b 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -29,6 +29,7 @@ import android.media.audiopolicy.AudioMix; import android.os.Build; import android.os.IBinder; +import android.os.SystemProperties; import android.os.Vibrator; import android.telephony.TelephonyManager; import android.util.Log; @@ -252,6 +253,9 @@ public static String modeToString(int mode) { @Retention(RetentionPolicy.SOURCE) public @interface AudioFormatNativeEnumForBtCodec {} + private static final boolean IS_QTI_BT = + SystemProperties.get("ro.bluetooth.library_name", "").equals("libbluetooth_qti.so"); + /** * @hide * Convert audio format enum values to Bluetooth codec values @@ -264,13 +268,14 @@ public static int audioFormatToBluetoothSourceCodec( case AUDIO_FORMAT_APTX: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX; case AUDIO_FORMAT_APTX_HD: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD; case AUDIO_FORMAT_LDAC: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC; - case AUDIO_FORMAT_CELT: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_CELT; + case AUDIO_FORMAT_CELT: + if (IS_QTI_BT) return BluetoothCodecConfig.SOURCE_CODEC_TYPE_CELT; case AUDIO_FORMAT_APTX_ADAPTIVE: - return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE; + if (IS_QTI_BT) return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE; case AUDIO_FORMAT_APTX_TWSP: - return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_TWSP; + if (IS_QTI_BT) return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_TWSP; case VX_AUDIO_FORMAT_LC3: - return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3; + if (IS_QTI_BT) return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3; default: Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat) + " for conversion to BT codec"); @@ -297,13 +302,13 @@ public static int audioFormatToBluetoothSourceCodec( case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC: return AudioSystem.AUDIO_FORMAT_LDAC; case BluetoothCodecConfig.SOURCE_CODEC_TYPE_CELT: - return AudioSystem.AUDIO_FORMAT_CELT; + if (IS_QTI_BT) return AudioSystem.AUDIO_FORMAT_CELT; case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_ADAPTIVE: - return AudioSystem.AUDIO_FORMAT_APTX_ADAPTIVE; + if (IS_QTI_BT) return AudioSystem.AUDIO_FORMAT_APTX_ADAPTIVE; case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_TWSP: - return AudioSystem.AUDIO_FORMAT_APTX_TWSP; + if (IS_QTI_BT) return AudioSystem.AUDIO_FORMAT_APTX_TWSP; case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3: - return AudioSystem.VX_AUDIO_FORMAT_LC3; + if (IS_QTI_BT) return AudioSystem.VX_AUDIO_FORMAT_LC3; default: Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec) + " for conversion to audio format");