Merge "Fix `deprecated-this-capture` warnings in SF" into main
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 9347ce4..dc5b1a1 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -62,8 +62,13 @@
/**
* Returns the PID of the process which has made the current binder
- * call. If not in a binder call, this will return getpid. If the
- * call is oneway, this will return 0.
+ * call. If not in a binder call, this will return getpid.
+ *
+ * Warning: oneway transactions do not receive PID. Even if you expect
+ * a transaction to be synchronous, a misbehaving client could send it
+ * as an asynchronous call and result in a 0 PID here. Additionally, if
+ * there is a race and the calling process dies, the PID may still be
+ * 0 for a synchronous call.
*/
[[nodiscard]] pid_t getCallingPid() const;
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index db2d2c1..b1ab7b0 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -390,6 +390,12 @@
* calling process dies and is replaced with another process with elevated permissions and the same
* PID.
*
+ * Warning: oneway transactions do not receive PID. Even if you expect
+ * a transaction to be synchronous, a misbehaving client could send it
+ * as a synchronous call and result in a 0 PID here. Additionally, if
+ * there is a race and the calling process dies, the PID may still be
+ * 0 for a synchronous call.
+ *
* Available since API level 29.
*
* \return calling pid or the current process's PID if this thread isn't processing a transaction.
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index a3a2562..8a06274 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -101,13 +101,16 @@
/// dies and is replaced with another process with elevated permissions and
/// the same PID.
///
+ /// Warning: oneway transactions do not receive PID. Even if you expect
+ /// a transaction to be synchronous, a misbehaving client could send it
+ /// as a synchronous call and result in a 0 PID here. Additionally, if
+ /// there is a race and the calling process dies, the PID may still be
+ /// 0 for a synchronous call.
+ ///
/// Available since API level 29.
///
/// \return calling pid or the current process's PID if this thread isn't
/// processing a transaction.
- ///
- /// If the transaction being processed is a oneway transaction, then this
- /// method will return 0.
pub fn get_calling_pid() -> pid_t {
// Safety: Safe FFI
unsafe { sys::AIBinder_getCallingPid() }
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index e7b2195..745b6f3 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -113,6 +113,22 @@
AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM,
"HAL and AHardwareBuffer pixel format don't match");
+static enum AHardwareBufferStatus filterStatus(status_t status) {
+ switch (status) {
+ case STATUS_OK:
+ return AHARDWAREBUFFER_STATUS_OK;
+ case STATUS_NO_MEMORY:
+ return AHARDWAREBUFFER_STATUS_NO_MEMORY;
+ case STATUS_BAD_VALUE:
+ return AHARDWAREBUFFER_STATUS_BAD_VALUE;
+ case STATUS_UNKNOWN_TRANSACTION:
+ case STATUS_INVALID_OPERATION:
+ return AHARDWAREBUFFER_STATUS_UNSUPPORTED;
+ default:
+ return AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR;
+ }
+}
+
// ----------------------------------------------------------------------------
// Public functions
// ----------------------------------------------------------------------------
@@ -511,6 +527,24 @@
return AParcel_viewPlatformParcel(parcel)->write(*gb);
}
+ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer) {
+ const GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+ if (!gb) return ADATASPACE_UNKNOWN;
+ ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
+ status_t status = gb->getDataspace(&dataspace);
+ if (status != OK) {
+ return ADATASPACE_UNKNOWN;
+ }
+ return static_cast<ADataSpace>(dataspace);
+}
+
+enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* buffer,
+ ADataSpace dataspace) {
+ GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+ auto& mapper = GraphicBufferMapper::get();
+ return filterStatus(mapper.setDataspace(gb->handle, static_cast<ui::Dataspace>(dataspace)));
+}
+
// ----------------------------------------------------------------------------
// VNDK functions
// ----------------------------------------------------------------------------
@@ -552,6 +586,56 @@
return NO_ERROR;
}
+enum AHardwareBufferStatus AHardwareBuffer_allocate2(
+ const AHardwareBuffer_Desc* desc, const AHardwareBufferLongOptions* additionalOptions,
+ size_t additionalOptionsSize, AHardwareBuffer** outBuffer) {
+ (void)additionalOptions;
+ (void)additionalOptionsSize;
+ if (!outBuffer || !desc) return AHARDWAREBUFFER_STATUS_BAD_VALUE;
+ if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) {
+ return AHARDWAREBUFFER_STATUS_BAD_VALUE;
+ }
+
+ int format = AHardwareBuffer_convertToPixelFormat(desc->format);
+ uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
+
+ std::vector<GraphicBufferAllocator::AdditionalOptions> extras;
+ extras.reserve(additionalOptionsSize);
+ for (size_t i = 0; i < additionalOptionsSize; i++) {
+ extras.push_back(GraphicBufferAllocator::AdditionalOptions{additionalOptions[i].name,
+ additionalOptions[i].value});
+ }
+
+ const auto extrasCount = extras.size();
+ auto gbuffer = sp<GraphicBuffer>::make(GraphicBufferAllocator::AllocationRequest{
+ .importBuffer = true,
+ .width = desc->width,
+ .height = desc->height,
+ .format = format,
+ .layerCount = desc->layers,
+ .usage = usage,
+ .requestorName = std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]",
+ .extras = std::move(extras),
+ });
+
+ status_t err = gbuffer->initCheck();
+ if (err != 0 || gbuffer->handle == nullptr) {
+ if (err == NO_MEMORY) {
+ GraphicBuffer::dumpAllocationsToSystemLog();
+ }
+ ALOGE("GraphicBuffer(w=%u, h=%u, lc=%u, extrasCount=%zd) failed (%s), handle=%p",
+ desc->width, desc->height, desc->layers, extrasCount, strerror(-err),
+ gbuffer->handle);
+ return filterStatus(err == 0 ? UNKNOWN_ERROR : err);
+ }
+
+ *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get());
+
+ // Ensure the buffer doesn't get destroyed when the sp<> goes away.
+ AHardwareBuffer_acquire(*outBuffer);
+ return AHARDWAREBUFFER_STATUS_OK;
+}
+
// ----------------------------------------------------------------------------
// Helpers implementation
// ----------------------------------------------------------------------------
@@ -652,12 +736,9 @@
return ahardwarebuffer_format;
}
+// TODO: Remove, this is just to make an overly aggressive ABI checker happy
int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer) {
- GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
- auto& mapper = GraphicBufferMapper::get();
- ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
- mapper.getDataspace(gb->handle, &dataspace);
- return static_cast<int32_t>(dataspace);
+ return ::AHardwareBuffer_getDataSpace(buffer);
}
uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) {
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index dd5958d..f97eed5 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -152,31 +152,56 @@
int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) {
static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN));
- static_assert(static_cast<int>(STANDARD_MASK) == static_cast<int>(HAL_DATASPACE_STANDARD_MASK));
- static_assert(static_cast<int>(STANDARD_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED));
- static_assert(static_cast<int>(STANDARD_BT709) == static_cast<int>(HAL_DATASPACE_STANDARD_BT709));
- static_assert(static_cast<int>(STANDARD_BT601_625) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625));
- static_assert(static_cast<int>(STANDARD_BT601_625_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED));
- static_assert(static_cast<int>(STANDARD_BT601_525) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525));
- static_assert(static_cast<int>(STANDARD_BT601_525_UNADJUSTED) == static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED));
- static_assert(static_cast<int>(STANDARD_BT470M) == static_cast<int>(HAL_DATASPACE_STANDARD_BT470M));
- static_assert(static_cast<int>(STANDARD_FILM) == static_cast<int>(HAL_DATASPACE_STANDARD_FILM));
- static_assert(static_cast<int>(STANDARD_DCI_P3) == static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3));
- static_assert(static_cast<int>(STANDARD_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB));
- static_assert(static_cast<int>(TRANSFER_MASK) == static_cast<int>(HAL_DATASPACE_TRANSFER_MASK));
- static_assert(static_cast<int>(TRANSFER_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED));
- static_assert(static_cast<int>(TRANSFER_LINEAR) == static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR));
- static_assert(static_cast<int>(TRANSFER_SMPTE_170M) == static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M));
- static_assert(static_cast<int>(TRANSFER_GAMMA2_2) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2));
- static_assert(static_cast<int>(TRANSFER_GAMMA2_6) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6));
- static_assert(static_cast<int>(TRANSFER_GAMMA2_8) == static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8));
- static_assert(static_cast<int>(TRANSFER_ST2084) == static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084));
- static_assert(static_cast<int>(TRANSFER_HLG) == static_cast<int>(HAL_DATASPACE_TRANSFER_HLG));
- static_assert(static_cast<int>(RANGE_MASK) == static_cast<int>(HAL_DATASPACE_RANGE_MASK));
- static_assert(static_cast<int>(RANGE_UNSPECIFIED) == static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED));
- static_assert(static_cast<int>(RANGE_FULL) == static_cast<int>(HAL_DATASPACE_RANGE_FULL));
- static_assert(static_cast<int>(RANGE_LIMITED) == static_cast<int>(HAL_DATASPACE_RANGE_LIMITED));
- static_assert(static_cast<int>(RANGE_EXTENDED) == static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_MASK) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_MASK));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_UNSPECIFIED) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_UNSPECIFIED));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT709) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT709));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_625_UNADJUSTED) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT601_525_UNADJUSTED) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_BT470M) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_BT470M));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_FILM) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_FILM));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_DCI_P3) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_DCI_P3));
+ static_assert(static_cast<int>(ADATASPACE_STANDARD_ADOBE_RGB) ==
+ static_cast<int>(HAL_DATASPACE_STANDARD_ADOBE_RGB));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_MASK) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_MASK));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_UNSPECIFIED) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_UNSPECIFIED));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_LINEAR) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_LINEAR));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_SMPTE_170M) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_SMPTE_170M));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_2) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_2));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_6) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_6));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_GAMMA2_8) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_GAMMA2_8));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_ST2084) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_ST2084));
+ static_assert(static_cast<int>(ADATASPACE_TRANSFER_HLG) ==
+ static_cast<int>(HAL_DATASPACE_TRANSFER_HLG));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_MASK) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_MASK));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_UNSPECIFIED) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_UNSPECIFIED));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_FULL) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_FULL));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_LIMITED) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_LIMITED));
+ static_assert(static_cast<int>(ADATASPACE_RANGE_EXTENDED) ==
+ static_cast<int>(HAL_DATASPACE_RANGE_EXTENDED));
static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB));
static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index 880c694..f145a2f 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -27,8 +27,8 @@
#include <stdint.h>
-struct AHardwareBuffer;
-struct AHardwareBuffer_Desc;
+#include <vndk/hardware_buffer.h>
+
struct ANativeWindowBuffer;
namespace android {
@@ -46,11 +46,6 @@
// convert HAL format to AHardwareBuffer format (note: this is a no-op)
uint32_t AHardwareBuffer_convertToPixelFormat(uint32_t format);
-// retrieves a dataspace from the AHardwareBuffer metadata, if the device
-// support gralloc metadata. Returns UNKNOWN if gralloc metadata is not
-// supported.
-int32_t AHardwareBuffer_getDataSpace(AHardwareBuffer* buffer);
-
// convert AHardwareBuffer usage bits to HAL usage bits (note: this is a no-op)
uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage);
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index eab21fb..d4278be 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -37,7 +37,7 @@
/**
* ADataSpace.
*/
-enum ADataSpace {
+enum ADataSpace : int32_t {
/**
* Default-assumption data space, when not explicitly specified.
*
@@ -63,7 +63,7 @@
* Defines the chromaticity coordinates of the source primaries in terms of
* the CIE 1931 definition of x and y specified in ISO 11664-1.
*/
- STANDARD_MASK = 63 << 16,
+ ADATASPACE_STANDARD_MASK = 63 << 16,
/**
* Chromacity coordinates are unknown or are determined by the application.
@@ -78,7 +78,7 @@
* For all other formats standard is undefined, and implementations should use
* an appropriate standard for the data represented.
*/
- STANDARD_UNSPECIFIED = 0 << 16,
+ ADATASPACE_STANDARD_UNSPECIFIED = 0 << 16,
/**
* <pre>
@@ -91,7 +91,7 @@
* Use the unadjusted KR = 0.2126, KB = 0.0722 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT709 = 1 << 16,
+ ADATASPACE_STANDARD_BT709 = 1 << 16,
/**
* <pre>
@@ -106,7 +106,7 @@
* to minimize the color shift into RGB space that uses BT.709
* primaries.
*/
- STANDARD_BT601_625 = 2 << 16,
+ ADATASPACE_STANDARD_BT601_625 = 2 << 16,
/**
* <pre>
@@ -119,7 +119,7 @@
* Use the unadjusted KR = 0.222, KB = 0.071 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT601_625_UNADJUSTED = 3 << 16,
+ ADATASPACE_STANDARD_BT601_625_UNADJUSTED = 3 << 16,
/**
* <pre>
@@ -134,7 +134,7 @@
* to minimize the color shift into RGB space that uses BT.709
* primaries.
*/
- STANDARD_BT601_525 = 4 << 16,
+ ADATASPACE_STANDARD_BT601_525 = 4 << 16,
/**
* <pre>
@@ -147,7 +147,7 @@
* Use the unadjusted KR = 0.212, KB = 0.087 luminance interpretation
* for RGB conversion (as in SMPTE 240M).
*/
- STANDARD_BT601_525_UNADJUSTED = 5 << 16,
+ ADATASPACE_STANDARD_BT601_525_UNADJUSTED = 5 << 16,
/**
* <pre>
@@ -160,7 +160,7 @@
* Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT2020 = 6 << 16,
+ ADATASPACE_STANDARD_BT2020 = 6 << 16,
/**
* <pre>
@@ -173,7 +173,7 @@
* Use the unadjusted KR = 0.2627, KB = 0.0593 luminance interpretation
* for RGB conversion using the linear domain.
*/
- STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16,
+ ADATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16,
/**
* <pre>
@@ -186,7 +186,7 @@
* Use the unadjusted KR = 0.30, KB = 0.11 luminance interpretation
* for RGB conversion.
*/
- STANDARD_BT470M = 8 << 16,
+ ADATASPACE_STANDARD_BT470M = 8 << 16,
/**
* <pre>
@@ -199,7 +199,7 @@
* Use the unadjusted KR = 0.254, KB = 0.068 luminance interpretation
* for RGB conversion.
*/
- STANDARD_FILM = 9 << 16,
+ ADATASPACE_STANDARD_FILM = 9 << 16,
/**
* SMPTE EG 432-1 and SMPTE RP 431-2. (DCI-P3)
@@ -210,7 +210,7 @@
* red 0.680 0.320
* white (D65) 0.3127 0.3290</pre>
*/
- STANDARD_DCI_P3 = 10 << 16,
+ ADATASPACE_STANDARD_DCI_P3 = 10 << 16,
/**
* Adobe RGB
@@ -221,7 +221,7 @@
* red 0.640 0.330
* white (D65) 0.3127 0.3290</pre>
*/
- STANDARD_ADOBE_RGB = 11 << 16,
+ ADATASPACE_STANDARD_ADOBE_RGB = 11 << 16,
/**
* Transfer aspect
@@ -236,7 +236,7 @@
* component. Implementation may apply the transfer function in RGB space
* for all pixel formats if desired.
*/
- TRANSFER_MASK = 31 << 22,
+ ADATASPACE_TRANSFER_MASK = 31 << 22,
/**
* Transfer characteristics are unknown or are determined by the
@@ -244,13 +244,13 @@
*
* Implementations should use the following transfer functions:
*
- * For YCbCr formats: use TRANSFER_SMPTE_170M
- * For RGB formats: use TRANSFER_SRGB
+ * For YCbCr formats: use ADATASPACE_TRANSFER_SMPTE_170M
+ * For RGB formats: use ADATASPACE_TRANSFER_SRGB
*
* For all other formats transfer function is undefined, and implementations
* should use an appropriate standard for the data represented.
*/
- TRANSFER_UNSPECIFIED = 0 << 22,
+ ADATASPACE_TRANSFER_UNSPECIFIED = 0 << 22,
/**
* Linear transfer.
@@ -260,7 +260,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_LINEAR = 1 << 22,
+ ADATASPACE_TRANSFER_LINEAR = 1 << 22,
/**
* sRGB transfer.
@@ -271,7 +271,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_SRGB = 2 << 22,
+ ADATASPACE_TRANSFER_SRGB = 2 << 22,
/**
* SMPTE 170M transfer.
@@ -282,7 +282,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_SMPTE_170M = 3 << 22,
+ ADATASPACE_TRANSFER_SMPTE_170M = 3 << 22,
/**
* Display gamma 2.2.
@@ -292,7 +292,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_GAMMA2_2 = 4 << 22,
+ ADATASPACE_TRANSFER_GAMMA2_2 = 4 << 22,
/**
* Display gamma 2.6.
@@ -302,7 +302,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_GAMMA2_6 = 5 << 22,
+ ADATASPACE_TRANSFER_GAMMA2_6 = 5 << 22,
/**
* Display gamma 2.8.
@@ -312,7 +312,7 @@
* L - luminance of image 0 <= L <= 1 for conventional colorimetry
* E - corresponding electrical signal</pre>
*/
- TRANSFER_GAMMA2_8 = 6 << 22,
+ ADATASPACE_TRANSFER_GAMMA2_8 = 6 << 22,
/**
* SMPTE ST 2084 (Dolby Perceptual Quantizer).
@@ -328,7 +328,7 @@
* L = 1 corresponds to 10000 cd/m2
* E - corresponding electrical signal</pre>
*/
- TRANSFER_ST2084 = 7 << 22,
+ ADATASPACE_TRANSFER_ST2084 = 7 << 22,
/**
* ARIB STD-B67 Hybrid Log Gamma.
@@ -344,7 +344,7 @@
* to reference white level of 100 cd/m2
* E - corresponding electrical signal</pre>
*/
- TRANSFER_HLG = 8 << 22,
+ ADATASPACE_TRANSFER_HLG = 8 << 22,
/**
* Range aspect
@@ -352,7 +352,7 @@
* Defines the range of values corresponding to the unit range of 0-1.
* This is defined for YCbCr only, but can be expanded to RGB space.
*/
- RANGE_MASK = 7 << 27,
+ ADATASPACE_RANGE_MASK = 7 << 27,
/**
* Range is unknown or are determined by the application. Implementations
@@ -365,13 +365,13 @@
* For all other formats range is undefined, and implementations should use
* an appropriate range for the data represented.
*/
- RANGE_UNSPECIFIED = 0 << 27,
+ ADATASPACE_RANGE_UNSPECIFIED = 0 << 27,
/**
* Full range uses all values for Y, Cb and Cr from
* 0 to 2^b-1, where b is the bit depth of the color format.
*/
- RANGE_FULL = 1 << 27,
+ ADATASPACE_RANGE_FULL = 1 << 27,
/**
* Limited range uses values 16/256*2^b to 235/256*2^b for Y, and
@@ -386,7 +386,7 @@
* Luma (Y) samples should range from 64 to 940, inclusive
* Chroma (Cb, Cr) samples should range from 64 to 960, inclusive
*/
- RANGE_LIMITED = 2 << 27,
+ ADATASPACE_RANGE_LIMITED = 2 << 27,
/**
* Extended range is used for scRGB. Intended for use with
@@ -395,7 +395,7 @@
* color outside the sRGB gamut.
* Used to blend / merge multiple dataspaces on a single display.
*/
- RANGE_EXTENDED = 3 << 27,
+ ADATASPACE_RANGE_EXTENDED = 3 << 27,
/**
* scRGB linear encoding
@@ -410,7 +410,8 @@
*
* Uses extended range, linear transfer and BT.709 standard.
*/
- ADATASPACE_SCRGB_LINEAR = 406913024, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_EXTENDED
+ ADATASPACE_SCRGB_LINEAR = 406913024, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR |
+ // ADATASPACE_RANGE_EXTENDED
/**
* sRGB gamma encoding
@@ -425,7 +426,8 @@
*
* Uses full range, sRGB transfer BT.709 standard.
*/
- ADATASPACE_SRGB = 142671872, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_FULL
+ ADATASPACE_SRGB = 142671872, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB |
+ // ADATASPACE_RANGE_FULL
/**
* scRGB
@@ -440,14 +442,16 @@
*
* Uses extended range, sRGB transfer and BT.709 standard.
*/
- ADATASPACE_SCRGB = 411107328, // STANDARD_BT709 | TRANSFER_SRGB | RANGE_EXTENDED
+ ADATASPACE_SCRGB = 411107328, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SRGB |
+ // ADATASPACE_RANGE_EXTENDED
/**
* Display P3
*
* Uses full range, sRGB transfer and D65 DCI-P3 standard.
*/
- ADATASPACE_DISPLAY_P3 = 143261696, // STANDARD_DCI_P3 | TRANSFER_SRGB | RANGE_FULL
+ ADATASPACE_DISPLAY_P3 = 143261696, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_SRGB |
+ // ADATASPACE_RANGE_FULL
/**
* ITU-R Recommendation 2020 (BT.2020)
@@ -456,7 +460,8 @@
*
* Uses full range, SMPTE 2084 (PQ) transfer and BT2020 standard.
*/
- ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
+ ADATASPACE_BT2020_PQ = 163971072, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084 |
+ // ADATASPACE_RANGE_FULL
/**
* ITU-R Recommendation 2020 (BT.2020)
@@ -465,7 +470,8 @@
*
* Uses limited range, SMPTE 2084 (PQ) transfer and BT2020 standard.
*/
- ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
+ ADATASPACE_BT2020_ITU_PQ = 298188800, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_ST2084
+ // | ADATASPACE_RANGE_LIMITED
/**
* Adobe RGB
@@ -475,7 +481,8 @@
* Note: Application is responsible for gamma encoding the data as
* a 2.2 gamma encoding is not supported in HW.
*/
- ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
+ ADATASPACE_ADOBE_RGB = 151715840, // ADATASPACE_STANDARD_ADOBE_RGB |
+ // ADATASPACE_TRANSFER_GAMMA2_2 | ADATASPACE_RANGE_FULL
/**
* JPEG File Interchange Format (JFIF)
@@ -484,7 +491,8 @@
*
* Uses full range, SMPTE 170M transfer and BT.601_625 standard.
*/
- ADATASPACE_JFIF = 146931712, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_FULL
+ ADATASPACE_JFIF = 146931712, // ADATASPACE_STANDARD_BT601_625 | ADATASPACE_TRANSFER_SMPTE_170M |
+ // ADATASPACE_RANGE_FULL
/**
* ITU-R Recommendation 601 (BT.601) - 625-line
@@ -493,7 +501,8 @@
*
* Uses limited range, SMPTE 170M transfer and BT.601_625 standard.
*/
- ADATASPACE_BT601_625 = 281149440, // STANDARD_BT601_625 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+ ADATASPACE_BT601_625 = 281149440, // ADATASPACE_STANDARD_BT601_625 |
+ // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED
/**
* ITU-R Recommendation 601 (BT.601) - 525-line
@@ -502,7 +511,8 @@
*
* Uses limited range, SMPTE 170M transfer and BT.601_525 standard.
*/
- ADATASPACE_BT601_525 = 281280512, // STANDARD_BT601_525 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+ ADATASPACE_BT601_525 = 281280512, // ADATASPACE_STANDARD_BT601_525 |
+ // ADATASPACE_TRANSFER_SMPTE_170M | ADATASPACE_RANGE_LIMITED
/**
* ITU-R Recommendation 2020 (BT.2020)
@@ -511,7 +521,8 @@
*
* Uses full range, SMPTE 170M transfer and BT2020 standard.
*/
- ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL
+ ADATASPACE_BT2020 = 147193856, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_SMPTE_170M |
+ // ADATASPACE_RANGE_FULL
/**
* ITU-R Recommendation 709 (BT.709)
@@ -520,7 +531,8 @@
*
* Uses limited range, SMPTE 170M transfer and BT.709 standard.
*/
- ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+ ADATASPACE_BT709 = 281083904, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_SMPTE_170M |
+ // ADATASPACE_RANGE_LIMITED
/**
* SMPTE EG 432-1 and SMPTE RP 431-2
@@ -532,7 +544,8 @@
* Note: Application is responsible for gamma encoding the data as
* a 2.6 gamma encoding is not supported in HW.
*/
- ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL
+ ADATASPACE_DCI_P3 = 155844608, // ADATASPACE_STANDARD_DCI_P3 | ADATASPACE_TRANSFER_GAMMA2_6 |
+ // ADATASPACE_RANGE_FULL
/**
* sRGB linear encoding
@@ -546,21 +559,24 @@
*
* Uses full range, linear transfer and BT.709 standard.
*/
- ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
+ ADATASPACE_SRGB_LINEAR = 138477568, // ADATASPACE_STANDARD_BT709 | ADATASPACE_TRANSFER_LINEAR |
+ // ADATASPACE_RANGE_FULL
/**
* Hybrid Log Gamma encoding
*
* Uses full range, hybrid log gamma transfer and BT2020 standard.
*/
- ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL
+ ADATASPACE_BT2020_HLG = 168165376, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG |
+ // ADATASPACE_RANGE_FULL
/**
* ITU Hybrid Log Gamma encoding
*
* Uses limited range, hybrid log gamma transfer and BT2020 standard.
*/
- ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED
+ ADATASPACE_BT2020_ITU_HLG = 302383104, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG |
+ // ADATASPACE_RANGE_LIMITED
/**
* Depth
@@ -574,7 +590,37 @@
*
* Embedded depth metadata following the dynamic depth specification.
*/
- ADATASPACE_DYNAMIC_DEPTH = 4098
+ ADATASPACE_DYNAMIC_DEPTH = 4098,
+
+#ifndef ADATASPACE_SKIP_LEGACY_DEFINES
+ STANDARD_MASK = ADATASPACE_STANDARD_MASK,
+ STANDARD_UNSPECIFIED = ADATASPACE_STANDARD_UNSPECIFIED,
+ STANDARD_BT709 = ADATASPACE_STANDARD_BT709,
+ STANDARD_BT601_625 = ADATASPACE_STANDARD_BT601_625,
+ STANDARD_BT601_625_UNADJUSTED = ADATASPACE_STANDARD_BT601_625_UNADJUSTED,
+ STANDARD_BT601_525 = ADATASPACE_STANDARD_BT601_525,
+ STANDARD_BT601_525_UNADJUSTED = ADATASPACE_STANDARD_BT601_525_UNADJUSTED,
+ STANDARD_BT470M = ADATASPACE_STANDARD_BT470M,
+ STANDARD_BT2020 = ADATASPACE_STANDARD_BT2020,
+ STANDARD_FILM = ADATASPACE_STANDARD_FILM,
+ STANDARD_DCI_P3 = ADATASPACE_STANDARD_DCI_P3,
+ STANDARD_ADOBE_RGB = ADATASPACE_STANDARD_ADOBE_RGB,
+ TRANSFER_MASK = ADATASPACE_TRANSFER_MASK,
+ TRANSFER_UNSPECIFIED = ADATASPACE_TRANSFER_UNSPECIFIED,
+ TRANSFER_LINEAR = ADATASPACE_TRANSFER_LINEAR,
+ TRANSFER_SMPTE_170M = ADATASPACE_TRANSFER_SMPTE_170M,
+ TRANSFER_GAMMA2_2 = ADATASPACE_TRANSFER_GAMMA2_2,
+ TRANSFER_GAMMA2_6 = ADATASPACE_TRANSFER_GAMMA2_6,
+ TRANSFER_GAMMA2_8 = ADATASPACE_TRANSFER_GAMMA2_8,
+ TRANSFER_SRGB = ADATASPACE_TRANSFER_SRGB,
+ TRANSFER_ST2084 = ADATASPACE_TRANSFER_ST2084,
+ TRANSFER_HLG = ADATASPACE_TRANSFER_HLG,
+ RANGE_MASK = ADATASPACE_RANGE_MASK,
+ RANGE_UNSPECIFIED = ADATASPACE_RANGE_UNSPECIFIED,
+ RANGE_FULL = ADATASPACE_RANGE_FULL,
+ RANGE_LIMITED = ADATASPACE_RANGE_LIMITED,
+ RANGE_EXTENDED = ADATASPACE_RANGE_EXTENDED,
+#endif
};
__END_DECLS
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 21798d0..e0e30c3 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -46,6 +46,9 @@
#define ANDROID_HARDWARE_BUFFER_H
#include <android/rect.h>
+#define ADATASPACE_SKIP_LEGACY_DEFINES
+#include <android/data_space.h>
+#undef ADATASPACE_SKIP_LEGACY_DEFINES
#include <inttypes.h>
#include <sys/cdefs.h>
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h
index 21931bb..4918601 100644
--- a/libs/nativewindow/include/vndk/hardware_buffer.h
+++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -21,6 +21,7 @@
#include <android/hardware_buffer.h>
#include <cutils/native_handle.h>
+#include <errno.h>
__BEGIN_DECLS
@@ -105,6 +106,76 @@
AHARDWAREBUFFER_USAGE_CAMERA_MASK = 6UL << 16,
};
+/**
+ * Additional options for AHardwareBuffer_allocate2. These correspond to
+ * android.hardware.graphics.common.ExtendableType
+ */
+typedef struct {
+ const char* _Nonnull name;
+ int64_t value;
+} AHardwareBufferLongOptions;
+
+enum AHardwareBufferStatus : int32_t {
+ /* Success, no error */
+ AHARDWAREBUFFER_STATUS_OK = 0,
+ /* There's insufficient memory to satisfy the request */
+ AHARDWAREBUFFER_STATUS_NO_MEMORY = -ENOMEM,
+ /* The given argument is invalid */
+ AHARDWAREBUFFER_STATUS_BAD_VALUE = -EINVAL,
+ /* The requested operation is not supported by the device */
+ AHARDWAREBUFFER_STATUS_UNSUPPORTED = -ENOSYS,
+ /* An unknown error occurred */
+ AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR = (-2147483647 - 1),
+};
+
+/**
+ * Allocates a buffer that matches the passed AHardwareBuffer_Desc with additional options
+ *
+ * If allocation succeeds, the buffer can be used according to the
+ * usage flags specified in its description. If a buffer is used in ways
+ * not compatible with its usage flags, the results are undefined and
+ * may include program termination.
+ *
+ * @param desc The AHardwareBuffer_Desc that describes the allocation to request. Note that `stride`
+ * is ignored.
+ * @param additionalOptions A pointer to an array of AHardwareBufferLongOptions with additional
+ * string key + long value options that may be specified. May be null if
+ * `additionalOptionsSize` is 0
+ * @param additionalOptionsSize The number of additional options to pass
+ * @param outBuffer The resulting buffer allocation
+ * @return AHARDWAREBUFFER_STATUS_OK on success
+ * AHARDWAREBUFFER_STATUS_NO_MEMORY if there's insufficient resources for the allocation
+ * AHARDWAREBUFFER_STATUS_BAD_VALUE if the provided description & options are not supported
+ * by the device
+ * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other error
+ * any reason. The returned buffer has a reference count of 1.
+ */
+enum AHardwareBufferStatus AHardwareBuffer_allocate2(
+ const AHardwareBuffer_Desc* _Nonnull desc,
+ const AHardwareBufferLongOptions* _Nullable additionalOptions, size_t additionalOptionsSize,
+ AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Queries the dataspace of the given AHardwareBuffer.
+ *
+ * @param buffer The non-null buffer for which to query the Dataspace
+ * @return The dataspace of the buffer, or ADATASPACE_UNKNOWN if one hasn't been set
+ */
+enum ADataSpace AHardwareBuffer_getDataSpace(const AHardwareBuffer* _Nonnull buffer)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets the dataspace of the given AHardwareBuffer
+ * @param buffer The non-null buffer for which to set the dataspace
+ * @param dataSpace The dataspace to set
+ * @return AHARDWAREBUFFER_STATUS_OK on success,
+ * AHARDWAREBUFFER_STATUS_UNSUPPORTED if the device doesn't support setting the dataspace,
+ * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other failure.
+ */
+enum AHardwareBufferStatus AHardwareBuffer_setDataSpace(AHardwareBuffer* _Nonnull buffer,
+ enum ADataSpace dataSpace)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
__END_DECLS
#endif /* ANDROID_VNDK_NATIVEWINDOW_AHARDWAREBUFFER_H */
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index dcb5068..a185a59 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -2,6 +2,7 @@
global:
AHardwareBuffer_acquire;
AHardwareBuffer_allocate;
+ AHardwareBuffer_allocate2; # llndk # systemapi
AHardwareBuffer_createFromHandle; # llndk # systemapi
AHardwareBuffer_describe;
AHardwareBuffer_getId; # introduced=31
@@ -16,6 +17,8 @@
AHardwareBuffer_unlock;
AHardwareBuffer_readFromParcel; # introduced=34
AHardwareBuffer_writeToParcel; # introduced=34
+ AHardwareBuffer_getDataSpace; # llndk # systemapi
+ AHardwareBuffer_setDataSpace; # llndk # systemapi
ANativeWindowBuffer_getHardwareBuffer; # llndk
ANativeWindow_OemStorageGet; # llndk
ANativeWindow_OemStorageSet; # llndk
diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp
index ef863b6..1f0128a 100644
--- a/libs/nativewindow/tests/AHardwareBufferTest.cpp
+++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "AHardwareBuffer_test"
//#define LOG_NDEBUG 0
+#include <android-base/properties.h>
+#include <android/data_space.h>
#include <android/hardware/graphics/common/1.0/types.h>
#include <gtest/gtest.h>
#include <private/android/AHardwareBufferHelpers.h>
@@ -26,6 +28,10 @@
using namespace android;
using android::hardware::graphics::common::V1_0::BufferUsage;
+static bool IsCuttlefish() {
+ return ::android::base::GetProperty("ro.product.board", "") == "cutf";
+}
+
static ::testing::AssertionResult BuildHexFailureMessage(uint64_t expected,
uint64_t actual, const char* type) {
std::ostringstream ss;
@@ -170,3 +176,83 @@
EXPECT_NE(id1, id2);
}
+
+TEST(AHardwareBufferTest, Allocate2NoExtras) {
+ AHardwareBuffer_Desc desc{
+ .width = 64,
+ .height = 1,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_BLOB,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ .stride = 0,
+ };
+
+ AHardwareBuffer* buffer = nullptr;
+ ASSERT_EQ(0, AHardwareBuffer_allocate2(&desc, nullptr, 0, &buffer));
+ uint64_t id = 0;
+ EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id));
+ EXPECT_NE(0, id);
+ AHardwareBuffer_Desc desc2{};
+ AHardwareBuffer_describe(buffer, &desc2);
+ EXPECT_EQ(desc.width, desc2.width);
+ EXPECT_EQ(desc.height, desc2.height);
+ EXPECT_GE(desc2.stride, desc2.width);
+
+ AHardwareBuffer_release(buffer);
+}
+
+TEST(AHardwareBufferTest, Allocate2WithExtras) {
+ if (!IsCuttlefish()) {
+ GTEST_SKIP() << "Unknown gralloc HAL, cannot test extras";
+ }
+
+ AHardwareBuffer_Desc desc{
+ .width = 64,
+ .height = 48,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ .stride = 0,
+ };
+
+ AHardwareBuffer* buffer = nullptr;
+ std::array<AHardwareBufferLongOptions, 1> extras = {{
+ {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3},
+ }};
+ ASSERT_EQ(0, AHardwareBuffer_allocate2(&desc, extras.data(), extras.size(), &buffer));
+ uint64_t id = 0;
+ EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id));
+ EXPECT_NE(0, id);
+ AHardwareBuffer_Desc desc2{};
+ AHardwareBuffer_describe(buffer, &desc2);
+ EXPECT_EQ(desc.width, desc2.width);
+ EXPECT_EQ(desc.height, desc2.height);
+ EXPECT_GE(desc2.stride, desc2.width);
+
+ EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer));
+
+ AHardwareBuffer_release(buffer);
+}
+
+TEST(AHardwareBufferTest, GetSetDataspace) {
+ AHardwareBuffer_Desc desc{
+ .width = 64,
+ .height = 48,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+ .stride = 0,
+ };
+
+ AHardwareBuffer* buffer = nullptr;
+ ASSERT_EQ(0, AHardwareBuffer_allocate(&desc, &buffer));
+
+ EXPECT_EQ(ADATASPACE_UNKNOWN, AHardwareBuffer_getDataSpace(buffer));
+ AHardwareBufferStatus status = AHardwareBuffer_setDataSpace(buffer, ADATASPACE_DISPLAY_P3);
+ if (status != AHARDWAREBUFFER_STATUS_UNSUPPORTED) {
+ EXPECT_EQ(0, status);
+ EXPECT_EQ(ADATASPACE_DISPLAY_P3, AHardwareBuffer_getDataSpace(buffer));
+ }
+
+ AHardwareBuffer_release(buffer);
+}
\ No newline at end of file
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
index 30737c1..d7c7eb3 100644
--- a/libs/nativewindow/tests/Android.bp
+++ b/libs/nativewindow/tests/Android.bp
@@ -31,6 +31,7 @@
"device-tests",
],
shared_libs: [
+ "libbase",
"libgui",
"liblog",
"libnativewindow",
@@ -44,5 +45,8 @@
"ANativeWindowTest.cpp",
"c_compatibility.c",
],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 2053c6a..d688b51 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -236,7 +236,13 @@
err = selectEGLConfig(display, format, 0, &config);
if (err != NO_ERROR) {
// this EGL is too lame for android
- LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
+ LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"
+ " (format: %d, vendor: %s, version: %s, extensions: %s, Client"
+ " API: %s)",
+ format, eglQueryString(display, EGL_VENDOR),
+ eglQueryString(display, EGL_VERSION),
+ eglQueryString(display, EGL_EXTENSIONS),
+ eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
}
}
}
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index e9b5dec..a5aca99 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -384,8 +384,8 @@
status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount, uint64_t usage,
- uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles, bool importBuffers) const {
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo = {};
descriptorInfo.width = width;
descriptorInfo.height = height;
@@ -400,6 +400,8 @@
return error;
}
+ constexpr auto bufferCount = 1;
+
auto ret = mAllocator->allocate(descriptor, bufferCount,
[&](const auto& tmpError, const auto& tmpStride,
const auto& tmpBuffers) {
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index 474d381..152b35a 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -371,7 +371,7 @@
status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
android::PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ uint64_t usage, uint32_t* outStride,
buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
@@ -383,6 +383,8 @@
return error;
}
+ constexpr auto bufferCount = 1;
+
auto ret = mAllocator->allocate(descriptor, bufferCount,
[&](const auto& tmpError, const auto& tmpStride,
const auto& tmpBuffers) {
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 03ff58a..d6970e0 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -1069,7 +1069,7 @@
status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
android::PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ uint64_t usage, uint32_t* outStride,
buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
if (auto error = sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage,
@@ -1084,6 +1084,8 @@
return error;
}
+ constexpr auto bufferCount = 1;
+
if (mAidlAllocator) {
AllocationResult result;
#pragma clang diagnostic push
diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp
index 2ec6d18..b07e155 100644
--- a/libs/ui/Gralloc5.cpp
+++ b/libs/ui/Gralloc5.cpp
@@ -19,6 +19,7 @@
#include <ui/Gralloc5.h>
+#include <aidl/android/hardware/graphics/allocator/AllocationError.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <android/binder_manager.h>
#include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h>
@@ -215,55 +216,75 @@
status_t Gralloc5Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
android::PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t *outStride,
- buffer_handle_t *outBufferHandles, bool importBuffers) const {
- auto descriptorInfo = makeDescriptor(requestorName, width, height, format, layerCount, usage);
+ uint64_t usage, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers) const {
+ auto result = allocate(GraphicBufferAllocator::AllocationRequest{
+ .importBuffer = importBuffers,
+ .width = width,
+ .height = height,
+ .format = format,
+ .layerCount = layerCount,
+ .usage = usage,
+ .requestorName = requestorName,
+ });
+
+ *outStride = result.stride;
+ outBufferHandles[0] = result.handle;
+ return result.status;
+}
+
+GraphicBufferAllocator::AllocationResult Gralloc5Allocator::allocate(
+ const GraphicBufferAllocator::AllocationRequest& request) const {
+ auto descriptorInfo = makeDescriptor(request.requestorName, request.width, request.height,
+ request.format, request.layerCount, request.usage);
if (!descriptorInfo) {
- return BAD_VALUE;
+ return GraphicBufferAllocator::AllocationResult{BAD_VALUE};
+ }
+
+ descriptorInfo->additionalOptions.reserve(request.extras.size());
+ for (const auto& option : request.extras) {
+ ExtendableType type;
+ type.name = option.name;
+ type.value = option.value;
+ descriptorInfo->additionalOptions.push_back(std::move(type));
}
AllocationResult result;
- auto status = mAllocator->allocate2(*descriptorInfo, bufferCount, &result);
+ auto status = mAllocator->allocate2(*descriptorInfo, 1, &result);
if (!status.isOk()) {
auto error = status.getExceptionCode();
if (error == EX_SERVICE_SPECIFIC) {
- error = status.getServiceSpecificError();
+ switch (static_cast<AllocationError>(status.getServiceSpecificError())) {
+ case AllocationError::BAD_DESCRIPTOR:
+ error = BAD_VALUE;
+ break;
+ case AllocationError::NO_RESOURCES:
+ error = NO_MEMORY;
+ break;
+ default:
+ error = UNKNOWN_ERROR;
+ break;
+ }
}
- if (error == OK) {
- error = UNKNOWN_ERROR;
- }
- return error;
+ return GraphicBufferAllocator::AllocationResult{error};
}
- if (importBuffers) {
- for (uint32_t i = 0; i < bufferCount; i++) {
- auto handle = makeFromAidl(result.buffers[i]);
- auto error = mMapper.importBuffer(handle, &outBufferHandles[i]);
- native_handle_delete(handle);
- if (error != NO_ERROR) {
- for (uint32_t j = 0; j < i; j++) {
- mMapper.freeBuffer(outBufferHandles[j]);
- outBufferHandles[j] = nullptr;
- }
- return error;
- }
+ GraphicBufferAllocator::AllocationResult ret{OK};
+ if (request.importBuffer) {
+ auto handle = makeFromAidl(result.buffers[0]);
+ auto error = mMapper.importBuffer(handle, &ret.handle);
+ native_handle_delete(handle);
+ if (error != NO_ERROR) {
+ return GraphicBufferAllocator::AllocationResult{error};
}
} else {
- for (uint32_t i = 0; i < bufferCount; i++) {
- outBufferHandles[i] = dupFromAidl(result.buffers[i]);
- if (!outBufferHandles[i]) {
- for (uint32_t j = 0; j < i; j++) {
- auto buffer = const_cast<native_handle_t *>(outBufferHandles[j]);
- native_handle_close(buffer);
- native_handle_delete(buffer);
- outBufferHandles[j] = nullptr;
- }
- return NO_MEMORY;
- }
+ ret.handle = dupFromAidl(result.buffers[0]);
+ if (!ret.handle) {
+ return GraphicBufferAllocator::AllocationResult{NO_MEMORY};
}
}
- *outStride = result.stride;
+ ret.stride = result.stride;
// Release all the resources held by AllocationResult (specifically any remaining FDs)
result = {};
@@ -272,7 +293,7 @@
// is marked apex_available (b/214400477) and libbinder isn't (which of course is correct)
// IPCThreadState::self()->flushCommands();
- return OK;
+ return ret;
}
void Gralloc5Mapper::preload() {
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 429760f..c007fdb 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -106,6 +106,26 @@
inUsage, inStride);
}
+GraphicBuffer::GraphicBuffer(const GraphicBufferAllocator::AllocationRequest& request)
+ : GraphicBuffer() {
+ GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();
+ auto result = allocator.allocate(request);
+ mInitCheck = result.status;
+ if (result.status == NO_ERROR) {
+ handle = result.handle;
+ stride = result.stride;
+
+ mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts);
+
+ width = static_cast<int>(request.width);
+ height = static_cast<int>(request.height);
+ format = request.format;
+ layerCount = request.layerCount;
+ usage = request.usage;
+ usage_deprecated = int(usage);
+ }
+}
+
GraphicBuffer::~GraphicBuffer()
{
ATRACE_CALL();
@@ -143,6 +163,10 @@
const_cast<GraphicBuffer*>(this));
}
+status_t GraphicBuffer::getDataspace(ui::Dataspace* outDataspace) const {
+ return mBufferMapper.getDataspace(handle, outDataspace);
+}
+
status_t GraphicBuffer::reallocate(uint32_t inWidth, uint32_t inHeight,
PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage)
{
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index eb0bd4e..98082fb 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -113,6 +113,79 @@
ALOGD("%s", s.c_str());
}
+auto GraphicBufferAllocator::allocate(const AllocationRequest& request) -> AllocationResult {
+ ATRACE_CALL();
+ if (!request.width || !request.height) {
+ return AllocationResult(BAD_VALUE);
+ }
+
+ const auto width = request.width;
+ const auto height = request.height;
+
+ const uint32_t bpp = bytesPerPixel(request.format);
+ if (std::numeric_limits<size_t>::max() / width / height < static_cast<size_t>(bpp)) {
+ ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+ "usage %" PRIx64 ": Requesting too large a buffer size",
+ request.width, request.height, request.layerCount, request.format, request.usage);
+ return AllocationResult(BAD_VALUE);
+ }
+
+ if (request.layerCount < 1) {
+ return AllocationResult(BAD_VALUE);
+ }
+
+ auto result = mAllocator->allocate(request);
+ if (result.status == UNKNOWN_TRANSACTION) {
+ if (!request.extras.empty()) {
+ ALOGE("Failed to allocate with additional options, allocator version mis-match? "
+ "gralloc version = %d",
+ (int)mMapper.getMapperVersion());
+ return result;
+ }
+ // If there's no additional options, fall back to previous allocate version
+ result.status = mAllocator->allocate(request.requestorName, request.width, request.height,
+ request.format, request.layerCount, request.usage,
+ &result.stride, &result.handle, request.importBuffer);
+ }
+
+ if (result.status != NO_ERROR) {
+ ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+ "usage %" PRIx64 ": %d",
+ request.width, request.height, request.layerCount, request.format, request.usage,
+ result.status);
+ return result;
+ }
+
+ if (!request.importBuffer) {
+ return result;
+ }
+ size_t bufSize;
+
+ // if stride has no meaning or is too large,
+ // approximate size with the input width instead
+ if ((result.stride) != 0 &&
+ std::numeric_limits<size_t>::max() / height / (result.stride) < static_cast<size_t>(bpp)) {
+ bufSize = static_cast<size_t>(width) * height * bpp;
+ } else {
+ bufSize = static_cast<size_t>((result.stride)) * height * bpp;
+ }
+
+ Mutex::Autolock _l(sLock);
+ KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+ alloc_rec_t rec;
+ rec.width = width;
+ rec.height = height;
+ rec.stride = result.stride;
+ rec.format = request.format;
+ rec.layerCount = request.layerCount;
+ rec.usage = request.usage;
+ rec.size = bufSize;
+ rec.requestorName = request.requestorName;
+ list.add(result.handle, rec);
+
+ return result;
+}
+
status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
buffer_handle_t* handle, uint32_t* stride,
@@ -141,7 +214,7 @@
usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage,
- 1, stride, handle, importBuffer);
+ stride, handle, importBuffer);
if (error != NO_ERROR) {
ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
"usage %" PRIx64 ": %d",
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index 496ba57..e6015e0 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -23,6 +23,7 @@
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/StrongPointer.h>
+#include "GraphicBufferAllocator.h"
#include <string>
@@ -218,9 +219,13 @@
*/
virtual status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount, uint64_t usage,
- uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
bool importBuffers = true) const = 0;
+
+ virtual GraphicBufferAllocator::AllocationResult allocate(
+ const GraphicBufferAllocator::AllocationRequest&) const {
+ return GraphicBufferAllocator::AllocationResult(UNKNOWN_TRANSACTION);
+ }
};
} // namespace android
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index a7b6f492..e50bb3a 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -81,9 +81,8 @@
std::string dumpDebugInfo(bool less = true) const override;
status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers = true) const override;
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
private:
const Gralloc2Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
index 7367549..035684a 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -82,9 +82,8 @@
std::string dumpDebugInfo(bool less = true) const override;
status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers = true) const override;
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
private:
const Gralloc3Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index df43be8..0f469c0 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -174,9 +174,8 @@
std::string dumpDebugInfo(bool less = true) const override;
status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
- PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers = true) const override;
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
private:
const Gralloc4Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc5.h b/libs/ui/include/ui/Gralloc5.h
index 44b97d1..f9e8f5e 100644
--- a/libs/ui/include/ui/Gralloc5.h
+++ b/libs/ui/include/ui/Gralloc5.h
@@ -172,10 +172,12 @@
[[nodiscard]] status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount, uint64_t usage,
- uint32_t bufferCount, uint32_t *outStride,
- buffer_handle_t *outBufferHandles,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
bool importBuffers) const override;
+ [[nodiscard]] GraphicBufferAllocator::AllocationResult allocate(
+ const GraphicBufferAllocator::AllocationRequest&) const override;
+
private:
const Gralloc5Mapper &mMapper;
std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAllocator;
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index f859848..652d8ba 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -26,6 +26,7 @@
#include <android/hardware_buffer.h>
#include <ui/ANativeObjectBase.h>
+#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
@@ -103,6 +104,8 @@
uint32_t inLayerCount, uint64_t inUsage,
std::string requestorName = "<Unknown>");
+ GraphicBuffer(const GraphicBufferAllocator::AllocationRequest&);
+
// Create a GraphicBuffer from an existing handle.
enum HandleWrapMethod : uint8_t {
// Wrap and use the handle directly. It assumes the handle has been
@@ -169,6 +172,8 @@
mGenerationNumber = generation;
}
+ status_t getDataspace(ui::Dataspace* outDataspace) const;
+
// This function is privileged. It requires access to the allocator
// device or service, which usually involves adding suitable selinux
// rules.
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 3ed988c..8f461e1 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -22,6 +22,7 @@
#include <memory>
#include <string>
+#include <vector>
#include <cutils/native_handle.h>
@@ -42,6 +43,35 @@
public:
static inline GraphicBufferAllocator& get() { return getInstance(); }
+ struct AdditionalOptions {
+ const char* name;
+ int64_t value;
+ };
+
+ struct AllocationRequest {
+ bool importBuffer;
+ uint32_t width;
+ uint32_t height;
+ PixelFormat format;
+ uint32_t layerCount;
+ uint64_t usage;
+ std::string requestorName;
+ std::vector<AdditionalOptions> extras;
+ };
+
+ struct AllocationResult {
+ status_t status;
+ buffer_handle_t handle = nullptr;
+ uint32_t stride = 0;
+
+ explicit AllocationResult(status_t status) : status(status) {}
+
+ explicit AllocationResult(buffer_handle_t handle, uint32_t stride)
+ : status(OK), handle(handle), stride(stride) {}
+ };
+
+ AllocationResult allocate(const AllocationRequest&);
+
/**
* Allocates and imports a gralloc buffer.
*
diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp
index f4c0afa..efca083 100644
--- a/libs/ui/tests/GraphicBufferAllocator_test.cpp
+++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp
@@ -51,7 +51,7 @@
std::cout << "Setting expected stride to " << stride << std::endl;
EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())),
allocate)
- .WillOnce(DoAll(SetArgPointee<7>(stride), Return(err)));
+ .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err)));
}
std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; }
};
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h
index d62e3e2..d02b387 100644
--- a/libs/ui/tests/mock/MockGrallocAllocator.h
+++ b/libs/ui/tests/mock/MockGrallocAllocator.h
@@ -35,7 +35,7 @@
MOCK_METHOD(std::string, dumpDebugInfo, (bool less), (const, override));
MOCK_METHOD(status_t, allocate,
(std::string requestorName, uint32_t width, uint32_t height, PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ uint32_t layerCount, uint64_t usage, uint32_t* outStride,
buffer_handle_t* outBufferHandles, bool less),
(const, override));
};
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 45c9b5c..69f42bc 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -77,6 +77,7 @@
"InputCommonConverter.cpp",
"InputDeviceMetricsCollector.cpp",
"InputFilter.cpp",
+ "InputFilterCallbacks.cpp",
"InputProcessor.cpp",
"PointerChoreographer.cpp",
"PreferStylusOverTouchBlocker.cpp",
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index 9c4a3eb..5d87d34 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -44,31 +44,9 @@
return event;
}
-NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) {
- return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId,
- static_cast<uint32_t>(event.source), event.displayId, event.policyFlags,
- static_cast<int32_t>(event.action), event.flags, event.keyCode,
- event.scanCode, event.metaState, event.downTime);
-}
-
-namespace {
-
-class RustCallbacks : public IInputFilter::BnInputFilterCallbacks {
-public:
- RustCallbacks(InputListenerInterface& nextListener) : mNextListener(nextListener) {}
- ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override {
- mNextListener.notifyKey(keyEventToNotifyKeyArgs(event));
- return ndk::ScopedAStatus::ok();
- }
-
-private:
- InputListenerInterface& mNextListener;
-};
-
-} // namespace
-
InputFilter::InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust)
- : mNextListener(listener), mCallbacks(ndk::SharedRefBase::make<RustCallbacks>(listener)) {
+ : mNextListener(listener),
+ mCallbacks(ndk::SharedRefBase::make<InputFilterCallbacks>(listener)) {
LOG_ALWAYS_FATAL_IF(!rust.createInputFilter(mCallbacks, &mInputFilterRust).isOk());
LOG_ALWAYS_FATAL_IF(!mInputFilterRust);
}
@@ -92,11 +70,11 @@
}
void InputFilter::notifyKey(const NotifyKeyArgs& args) {
- if (!isFilterEnabled()) {
- mNextListener.notifyKey(args);
+ if (isFilterEnabled()) {
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk());
return;
}
- LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyKey(notifyKeyArgsToKeyEvent(args)).isOk());
+ mNextListener.notifyKey(args);
}
void InputFilter::notifyMotion(const NotifyMotionArgs& args) {
@@ -138,6 +116,15 @@
}
}
+void InputFilter::setAccessibilityStickyKeysEnabled(bool enabled) {
+ std::scoped_lock _l(mLock);
+
+ if (mConfig.stickyKeysEnabled != enabled) {
+ mConfig.stickyKeysEnabled = enabled;
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyConfigurationChanged(mConfig).isOk());
+ }
+}
+
void InputFilter::dump(std::string& dump) {
dump += "InputFilter:\n";
}
diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h
index 06f7d0e..9fa7a87 100644
--- a/services/inputflinger/InputFilter.h
+++ b/services/inputflinger/InputFilter.h
@@ -18,6 +18,7 @@
#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
#include <utils/Mutex.h>
+#include "InputFilterCallbacks.h"
#include "InputListener.h"
#include "NotifyArgs.h"
@@ -33,6 +34,7 @@
*/
virtual void dump(std::string& dump) = 0;
virtual void setAccessibilityBounceKeysThreshold(nsecs_t threshold) = 0;
+ virtual void setAccessibilityStickyKeysEnabled(bool enabled) = 0;
};
class InputFilter : public InputFilterInterface {
@@ -56,11 +58,12 @@
void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
void setAccessibilityBounceKeysThreshold(nsecs_t threshold) override;
+ void setAccessibilityStickyKeysEnabled(bool enabled) override;
void dump(std::string& dump) override;
private:
InputListenerInterface& mNextListener;
- std::shared_ptr<IInputFilterCallbacks> mCallbacks;
+ std::shared_ptr<InputFilterCallbacks> mCallbacks;
std::shared_ptr<IInputFilter> mInputFilterRust;
mutable std::mutex mLock;
InputFilterConfiguration mConfig GUARDED_BY(mLock);
diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp
new file mode 100644
index 0000000..8c8f5e8
--- /dev/null
+++ b/services/inputflinger/InputFilterCallbacks.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InputFilterCallbacks"
+
+#include "InputFilterCallbacks.h"
+
+namespace android {
+
+using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;
+
+NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) {
+ return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId,
+ static_cast<uint32_t>(event.source), event.displayId, event.policyFlags,
+ static_cast<int32_t>(event.action), event.flags, event.keyCode,
+ event.scanCode, event.metaState, event.downTime);
+}
+
+InputFilterCallbacks::InputFilterCallbacks(InputListenerInterface& listener)
+ : mNextListener(listener) {}
+
+ndk::ScopedAStatus InputFilterCallbacks::sendKeyEvent(const AidlKeyEvent& event) {
+ mNextListener.notifyKey(keyEventToNotifyKeyArgs(event));
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InputFilterCallbacks::onModifierStateChanged(int32_t modifierState,
+ int32_t lockedModifierState) {
+ std::scoped_lock _l(mLock);
+ mStickyModifierState.modifierState = modifierState;
+ mStickyModifierState.lockedModifierState = lockedModifierState;
+ ALOGI("Sticky keys modifier state changed: modifierState=%d, lockedModifierState=%d",
+ modifierState, lockedModifierState);
+ return ndk::ScopedAStatus::ok();
+}
+
+uint32_t InputFilterCallbacks::getModifierState() {
+ std::scoped_lock _l(mLock);
+ return mStickyModifierState.modifierState;
+}
+
+uint32_t InputFilterCallbacks::getLockedModifierState() {
+ std::scoped_lock _l(mLock);
+ return mStickyModifierState.lockedModifierState;
+}
+
+} // namespace android
diff --git a/services/inputflinger/InputFilterCallbacks.h b/services/inputflinger/InputFilterCallbacks.h
new file mode 100644
index 0000000..c0a80fb
--- /dev/null
+++ b/services/inputflinger/InputFilterCallbacks.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/com/android/server/inputflinger/IInputFlingerRust.h>
+#include <android/binder_auto_utils.h>
+#include <utils/Mutex.h>
+#include <mutex>
+#include "InputListener.h"
+#include "NotifyArgs.h"
+
+/**
+ * The C++ component of InputFilter designed as a wrapper around the rust callback implementation.
+ */
+namespace android {
+
+using IInputFilter = aidl::com::android::server::inputflinger::IInputFilter;
+using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent;
+
+class InputFilterCallbacks : public IInputFilter::BnInputFilterCallbacks {
+public:
+ explicit InputFilterCallbacks(InputListenerInterface& listener);
+ ~InputFilterCallbacks() override = default;
+
+ uint32_t getModifierState();
+ uint32_t getLockedModifierState();
+
+private:
+ InputListenerInterface& mNextListener;
+ mutable std::mutex mLock;
+ struct StickyModifierState {
+ uint32_t modifierState;
+ uint32_t lockedModifierState;
+ } mStickyModifierState GUARDED_BY(mLock);
+
+ ndk::ScopedAStatus sendKeyEvent(const AidlKeyEvent& event) override;
+ ndk::ScopedAStatus onModifierStateChanged(int32_t modifierState,
+ int32_t lockedModifierState) override;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
index 14b41cd..2921d30 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
@@ -33,6 +33,9 @@
interface IInputFilterCallbacks {
/** Sends back a filtered key event */
void sendKeyEvent(in KeyEvent event);
+
+ /** Sends back modifier state */
+ void onModifierStateChanged(int modifierState, int lockedModifierState);
}
/** Returns if InputFilter is enabled */
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
index 3b2e88b..38b1612 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/InputFilterConfiguration.aidl
@@ -22,4 +22,6 @@
parcelable InputFilterConfiguration {
// Threshold value for Bounce keys filter (check bounce_keys_filter.rs)
long bounceKeysThresholdNs;
+ // If sticky keys filter is enabled
+ boolean stickyKeysEnabled;
}
\ No newline at end of file
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index 340ff8e..e94a71f 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -27,6 +27,7 @@
};
use crate::bounce_keys_filter::BounceKeysFilter;
+use crate::sticky_keys_filter::StickyKeysFilter;
use log::{error, info};
use std::sync::{Arc, Mutex, RwLock};
@@ -91,6 +92,14 @@
let mut state = self.state.lock().unwrap();
let mut first_filter: Box<dyn Filter + Send + Sync> =
Box::new(BaseFilter::new(self.callbacks.clone()));
+ if config.stickyKeysEnabled {
+ first_filter = Box::new(StickyKeysFilter::new(
+ first_filter,
+ ModifierStateListener::new(self.callbacks.clone()),
+ ));
+ state.enabled = true;
+ info!("Sticky keys filter is installed");
+ }
if config.bounceKeysThresholdNs > 0 {
first_filter =
Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs));
@@ -125,34 +134,43 @@
}
}
+pub struct ModifierStateListener {
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+}
+
+impl ModifierStateListener {
+ /// Create a new InputFilter instance.
+ pub fn new(callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>) -> ModifierStateListener {
+ Self { callbacks }
+ }
+
+ pub fn modifier_state_changed(&self, modifier_state: u32, locked_modifier_state: u32) {
+ let _ = self
+ .callbacks
+ .read()
+ .unwrap()
+ .onModifierStateChanged(modifier_state as i32, locked_modifier_state as i32);
+ }
+}
+
#[cfg(test)]
mod tests {
- use crate::input_filter::{test_filter::TestFilter, Filter, InputFilter};
+ use crate::input_filter::{
+ test_callbacks::TestCallbacks, test_filter::TestFilter, InputFilter,
+ };
use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
- use binder::{Interface, Strong};
+ use binder::Strong;
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
DeviceInfo::DeviceInfo, IInputFilter::IInputFilter,
- IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent,
KeyEventAction::KeyEventAction,
};
use std::sync::{Arc, RwLock};
- struct FakeCallbacks {}
-
- impl Interface for FakeCallbacks {}
-
- impl IInputFilterCallbacks for FakeCallbacks {
- fn sendKeyEvent(&self, _event: &KeyEvent) -> binder::Result<()> {
- Result::Ok(())
- }
- }
-
#[test]
fn test_not_enabled_with_default_filter() {
- let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
- Strong::new(Box::new(FakeCallbacks {}));
- let input_filter = InputFilter::new(fake_callbacks);
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks)));
let result = input_filter.isEnabled();
assert!(result.is_ok());
assert!(!result.unwrap());
@@ -160,17 +178,21 @@
#[test]
fn test_notify_key_with_no_filters() {
- let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
- Strong::new(Box::new(FakeCallbacks {}));
- let input_filter = InputFilter::new(fake_callbacks);
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks.clone())));
let event = create_key_event();
assert!(input_filter.notifyKey(&event).is_ok());
+ assert_eq!(test_callbacks.last_event().unwrap(), event);
}
#[test]
fn test_notify_key_with_filter() {
let test_filter = TestFilter::new();
- let input_filter = create_input_filter(Box::new(test_filter.clone()));
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::create_input_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))),
+ );
let event = create_key_event();
assert!(input_filter.notifyKey(&event).is_ok());
assert_eq!(test_filter.last_event().unwrap(), event);
@@ -179,7 +201,11 @@
#[test]
fn test_notify_devices_changed() {
let test_filter = TestFilter::new();
- let input_filter = create_input_filter(Box::new(test_filter.clone()));
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::create_input_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))),
+ );
assert!(input_filter
.notifyInputDevicesChanged(&[DeviceInfo { deviceId: 0, external: true }])
.is_ok());
@@ -188,21 +214,30 @@
#[test]
fn test_notify_configuration_changed_enabled_bounce_keys() {
- let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
- Strong::new(Box::new(FakeCallbacks {}));
- let input_filter = InputFilter::new(fake_callbacks);
- let result = input_filter
- .notifyConfigurationChanged(&InputFilterConfiguration { bounceKeysThresholdNs: 100 });
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks)));
+ let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration {
+ bounceKeysThresholdNs: 100,
+ stickyKeysEnabled: false,
+ });
assert!(result.is_ok());
let result = input_filter.isEnabled();
assert!(result.is_ok());
assert!(result.unwrap());
}
- fn create_input_filter(filter: Box<dyn Filter + Send + Sync>) -> InputFilter {
- let fake_callbacks: Strong<dyn IInputFilterCallbacks> =
- Strong::new(Box::new(FakeCallbacks {}));
- InputFilter::create_input_filter(filter, Arc::new(RwLock::new(fake_callbacks)))
+ #[test]
+ fn test_notify_configuration_changed_enabled_sticky_keys() {
+ let test_callbacks = TestCallbacks::new();
+ let input_filter = InputFilter::new(Strong::new(Box::new(test_callbacks)));
+ let result = input_filter.notifyConfigurationChanged(&InputFilterConfiguration {
+ bounceKeysThresholdNs: 0,
+ stickyKeysEnabled: true,
+ });
+ assert!(result.is_ok());
+ let result = input_filter.isEnabled();
+ assert!(result.is_ok());
+ assert!(result.unwrap());
}
fn create_key_event() -> KeyEvent {
@@ -272,3 +307,69 @@
}
}
}
+
+#[cfg(test)]
+pub mod test_callbacks {
+ use binder::Interface;
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, KeyEvent::KeyEvent,
+ };
+ use std::sync::{Arc, RwLock, RwLockWriteGuard};
+
+ #[derive(Default)]
+ struct TestCallbacksInner {
+ last_modifier_state: u32,
+ last_locked_modifier_state: u32,
+ last_event: Option<KeyEvent>,
+ }
+
+ #[derive(Default, Clone)]
+ pub struct TestCallbacks(Arc<RwLock<TestCallbacksInner>>);
+
+ impl Interface for TestCallbacks {}
+
+ impl TestCallbacks {
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ fn inner(&self) -> RwLockWriteGuard<'_, TestCallbacksInner> {
+ self.0.write().unwrap()
+ }
+
+ pub fn last_event(&self) -> Option<KeyEvent> {
+ self.0.read().unwrap().last_event
+ }
+
+ pub fn clear(&mut self) {
+ self.inner().last_event = None;
+ self.inner().last_modifier_state = 0;
+ self.inner().last_locked_modifier_state = 0;
+ }
+
+ pub fn get_last_modifier_state(&self) -> u32 {
+ self.0.read().unwrap().last_modifier_state
+ }
+
+ pub fn get_last_locked_modifier_state(&self) -> u32 {
+ self.0.read().unwrap().last_locked_modifier_state
+ }
+ }
+
+ impl IInputFilterCallbacks for TestCallbacks {
+ fn sendKeyEvent(&self, event: &KeyEvent) -> binder::Result<()> {
+ self.inner().last_event = Some(*event);
+ Result::Ok(())
+ }
+
+ fn onModifierStateChanged(
+ &self,
+ modifier_state: i32,
+ locked_modifier_state: i32,
+ ) -> std::result::Result<(), binder::Status> {
+ self.inner().last_modifier_state = modifier_state as u32;
+ self.inner().last_locked_modifier_state = locked_modifier_state as u32;
+ Result::Ok(())
+ }
+ }
+}
diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs
index 68cd480..fa16898 100644
--- a/services/inputflinger/rust/lib.rs
+++ b/services/inputflinger/rust/lib.rs
@@ -21,6 +21,7 @@
mod bounce_keys_filter;
mod input_filter;
+mod sticky_keys_filter;
use crate::input_filter::InputFilter;
use binder::{
diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs
new file mode 100644
index 0000000..da581b8
--- /dev/null
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Sticky keys input filter implementation.
+//! Sticky keys is an accessibility feature that assists users who have physical disabilities or
+//! helps users reduce repetitive strain injury. It serializes keystrokes instead of pressing
+//! multiple keys at a time, allowing the user to press and release a modifier key, such as Shift,
+//! Ctrl, Alt, or any other modifier key, and have it remain active until any other key is pressed.
+use crate::input_filter::{Filter, ModifierStateListener};
+use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+};
+use std::collections::HashSet;
+
+// Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h
+const KEYCODE_ALT_LEFT: i32 = 57;
+const KEYCODE_ALT_RIGHT: i32 = 58;
+const KEYCODE_SHIFT_LEFT: i32 = 59;
+const KEYCODE_SHIFT_RIGHT: i32 = 60;
+const KEYCODE_SYM: i32 = 63;
+const KEYCODE_CTRL_LEFT: i32 = 113;
+const KEYCODE_CTRL_RIGHT: i32 = 114;
+const KEYCODE_CAPS_LOCK: i32 = 115;
+const KEYCODE_SCROLL_LOCK: i32 = 116;
+const KEYCODE_META_LEFT: i32 = 117;
+const KEYCODE_META_RIGHT: i32 = 118;
+const KEYCODE_FUNCTION: i32 = 119;
+const KEYCODE_NUM_LOCK: i32 = 143;
+
+// Modifier states: values are from /frameworks/native/include/android/input.h
+const META_ALT_ON: u32 = 0x02;
+const META_ALT_LEFT_ON: u32 = 0x10;
+const META_ALT_RIGHT_ON: u32 = 0x20;
+const META_SHIFT_ON: u32 = 0x01;
+const META_SHIFT_LEFT_ON: u32 = 0x40;
+const META_SHIFT_RIGHT_ON: u32 = 0x80;
+const META_CTRL_ON: u32 = 0x1000;
+const META_CTRL_LEFT_ON: u32 = 0x2000;
+const META_CTRL_RIGHT_ON: u32 = 0x4000;
+const META_META_ON: u32 = 0x10000;
+const META_META_LEFT_ON: u32 = 0x20000;
+const META_META_RIGHT_ON: u32 = 0x40000;
+
+pub struct StickyKeysFilter {
+ next: Box<dyn Filter + Send + Sync>,
+ listener: ModifierStateListener,
+ /// Tracking devices that contributed to the modifier state.
+ contributing_devices: HashSet<i32>,
+ /// State describing the current enabled modifiers. This contain both locked and non-locked
+ /// modifier state bits.
+ modifier_state: u32,
+ /// State describing the current locked modifiers. These modifiers will not be cleared on a
+ /// non-modifier key press. They will be cleared only if the locked modifier key is pressed
+ /// again.
+ locked_modifier_state: u32,
+}
+
+impl StickyKeysFilter {
+ /// Create a new StickyKeysFilter instance.
+ pub fn new(
+ next: Box<dyn Filter + Send + Sync>,
+ listener: ModifierStateListener,
+ ) -> StickyKeysFilter {
+ Self {
+ next,
+ listener,
+ contributing_devices: HashSet::new(),
+ modifier_state: 0,
+ locked_modifier_state: 0,
+ }
+ }
+}
+
+impl Filter for StickyKeysFilter {
+ fn notify_key(&mut self, event: &KeyEvent) {
+ let up = event.action == KeyEventAction::UP;
+ let mut modifier_state = self.modifier_state;
+ let mut locked_modifier_state = self.locked_modifier_state;
+ if !is_ephemeral_modifier_key(event.keyCode) {
+ // If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like
+ // CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with
+ // the KeyEvent.
+ let old_modifier_state = event.metaState as u32;
+ let mut new_event = *event;
+ // Send the current modifier state with the key event before clearing non-locked
+ // modifier state
+ new_event.metaState =
+ (clear_ephemeral_modifier_state(old_modifier_state) | modifier_state) as i32;
+ self.next.notify_key(&new_event);
+ if up && !is_modifier_key(event.keyCode) {
+ modifier_state =
+ clear_ephemeral_modifier_state(modifier_state) | locked_modifier_state;
+ }
+ } else if up {
+ // Update contributing devices to track keyboards
+ self.contributing_devices.insert(event.deviceId);
+ // If ephemeral modifier key, capture the key and update the sticky modifier states
+ let modifier_key_mask = get_ephemeral_modifier_key_mask(event.keyCode);
+ let symmetrical_modifier_key_mask = get_symmetrical_modifier_key_mask(event.keyCode);
+ if locked_modifier_state & modifier_key_mask != 0 {
+ locked_modifier_state &= !symmetrical_modifier_key_mask;
+ modifier_state &= !symmetrical_modifier_key_mask;
+ } else if modifier_key_mask & modifier_state != 0 {
+ locked_modifier_state |= modifier_key_mask;
+ modifier_state =
+ (modifier_state & !symmetrical_modifier_key_mask) | modifier_key_mask;
+ } else {
+ modifier_state |= modifier_key_mask;
+ }
+ }
+ if self.modifier_state != modifier_state
+ || self.locked_modifier_state != locked_modifier_state
+ {
+ self.modifier_state = modifier_state;
+ self.locked_modifier_state = locked_modifier_state;
+ self.listener.modifier_state_changed(modifier_state, locked_modifier_state);
+ }
+ }
+
+ fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) {
+ // Clear state if all contributing devices removed
+ self.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId));
+ if self.contributing_devices.is_empty()
+ && (self.modifier_state != 0 || self.locked_modifier_state != 0)
+ {
+ self.modifier_state = 0;
+ self.locked_modifier_state = 0;
+ self.listener.modifier_state_changed(0, 0);
+ }
+ self.next.notify_devices_changed(device_infos);
+ }
+}
+
+fn is_modifier_key(keycode: i32) -> bool {
+ matches!(
+ keycode,
+ KEYCODE_ALT_LEFT
+ | KEYCODE_ALT_RIGHT
+ | KEYCODE_SHIFT_LEFT
+ | KEYCODE_SHIFT_RIGHT
+ | KEYCODE_CTRL_LEFT
+ | KEYCODE_CTRL_RIGHT
+ | KEYCODE_META_LEFT
+ | KEYCODE_META_RIGHT
+ | KEYCODE_SYM
+ | KEYCODE_FUNCTION
+ | KEYCODE_CAPS_LOCK
+ | KEYCODE_NUM_LOCK
+ | KEYCODE_SCROLL_LOCK
+ )
+}
+
+fn is_ephemeral_modifier_key(keycode: i32) -> bool {
+ matches!(
+ keycode,
+ KEYCODE_ALT_LEFT
+ | KEYCODE_ALT_RIGHT
+ | KEYCODE_SHIFT_LEFT
+ | KEYCODE_SHIFT_RIGHT
+ | KEYCODE_CTRL_LEFT
+ | KEYCODE_CTRL_RIGHT
+ | KEYCODE_META_LEFT
+ | KEYCODE_META_RIGHT
+ )
+}
+
+fn get_ephemeral_modifier_key_mask(keycode: i32) -> u32 {
+ match keycode {
+ KEYCODE_ALT_LEFT => META_ALT_LEFT_ON | META_ALT_ON,
+ KEYCODE_ALT_RIGHT => META_ALT_RIGHT_ON | META_ALT_ON,
+ KEYCODE_SHIFT_LEFT => META_SHIFT_LEFT_ON | META_SHIFT_ON,
+ KEYCODE_SHIFT_RIGHT => META_SHIFT_RIGHT_ON | META_SHIFT_ON,
+ KEYCODE_CTRL_LEFT => META_CTRL_LEFT_ON | META_CTRL_ON,
+ KEYCODE_CTRL_RIGHT => META_CTRL_RIGHT_ON | META_CTRL_ON,
+ KEYCODE_META_LEFT => META_META_LEFT_ON | META_META_ON,
+ KEYCODE_META_RIGHT => META_META_RIGHT_ON | META_META_ON,
+ _ => 0,
+ }
+}
+
+/// Modifier mask including both left and right versions of a modifier key.
+fn get_symmetrical_modifier_key_mask(keycode: i32) -> u32 {
+ match keycode {
+ KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => META_ALT_LEFT_ON | META_ALT_RIGHT_ON | META_ALT_ON,
+ KEYCODE_SHIFT_LEFT | KEYCODE_SHIFT_RIGHT => {
+ META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON | META_SHIFT_ON
+ }
+ KEYCODE_CTRL_LEFT | KEYCODE_CTRL_RIGHT => {
+ META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON | META_CTRL_ON
+ }
+ KEYCODE_META_LEFT | KEYCODE_META_RIGHT => {
+ META_META_LEFT_ON | META_META_RIGHT_ON | META_META_ON
+ }
+ _ => 0,
+ }
+}
+
+fn clear_ephemeral_modifier_state(modifier_state: u32) -> u32 {
+ modifier_state
+ & !(META_ALT_LEFT_ON
+ | META_ALT_RIGHT_ON
+ | META_ALT_ON
+ | META_SHIFT_LEFT_ON
+ | META_SHIFT_RIGHT_ON
+ | META_SHIFT_ON
+ | META_CTRL_LEFT_ON
+ | META_CTRL_RIGHT_ON
+ | META_CTRL_ON
+ | META_META_LEFT_ON
+ | META_META_RIGHT_ON
+ | META_META_ON)
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::input_filter::{
+ test_callbacks::TestCallbacks, test_filter::TestFilter, Filter, ModifierStateListener,
+ };
+ use crate::sticky_keys_filter::{
+ StickyKeysFilter, KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, KEYCODE_CAPS_LOCK,
+ KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, KEYCODE_FUNCTION, KEYCODE_META_LEFT,
+ KEYCODE_META_RIGHT, KEYCODE_NUM_LOCK, KEYCODE_SCROLL_LOCK, KEYCODE_SHIFT_LEFT,
+ KEYCODE_SHIFT_RIGHT, KEYCODE_SYM, META_ALT_LEFT_ON, META_ALT_ON, META_ALT_RIGHT_ON,
+ META_CTRL_LEFT_ON, META_CTRL_ON, META_CTRL_RIGHT_ON, META_META_LEFT_ON, META_META_ON,
+ META_META_RIGHT_ON, META_SHIFT_LEFT_ON, META_SHIFT_ON, META_SHIFT_RIGHT_ON,
+ };
+ use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+ use binder::Strong;
+ use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
+ DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
+ KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
+ };
+ use std::sync::{Arc, RwLock};
+
+ static DEVICE_ID: i32 = 1;
+ static KEY_A: i32 = 29;
+ static BASE_KEY_DOWN: KeyEvent = KeyEvent {
+ id: 1,
+ deviceId: DEVICE_ID,
+ downTime: 0,
+ readTime: 0,
+ eventTime: 0,
+ source: Source::KEYBOARD,
+ displayId: 0,
+ policyFlags: 0,
+ action: KeyEventAction::DOWN,
+ flags: 0,
+ keyCode: 0,
+ scanCode: 0,
+ metaState: 0,
+ };
+
+ static BASE_KEY_UP: KeyEvent = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_DOWN };
+
+ #[test]
+ fn test_notify_key_consumes_ephemeral_modifier_keys() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let key_codes = &[
+ KEYCODE_ALT_LEFT,
+ KEYCODE_ALT_RIGHT,
+ KEYCODE_CTRL_LEFT,
+ KEYCODE_CTRL_RIGHT,
+ KEYCODE_SHIFT_LEFT,
+ KEYCODE_SHIFT_RIGHT,
+ KEYCODE_META_LEFT,
+ KEYCODE_META_RIGHT,
+ ];
+ for key_code in key_codes.iter() {
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN });
+ assert!(test_filter.last_event().is_none());
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: *key_code, ..BASE_KEY_UP });
+ assert!(test_filter.last_event().is_none());
+ }
+ }
+
+ #[test]
+ fn test_notify_key_passes_non_ephemeral_modifier_keys() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let key_codes = &[
+ KEYCODE_CAPS_LOCK,
+ KEYCODE_NUM_LOCK,
+ KEYCODE_SCROLL_LOCK,
+ KEYCODE_FUNCTION,
+ KEYCODE_SYM,
+ ];
+ for key_code in key_codes.iter() {
+ let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_DOWN };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+ let event = KeyEvent { keyCode: *key_code, ..BASE_KEY_UP };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+ }
+ }
+
+ #[test]
+ fn test_notify_key_passes_non_modifier_keys() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+
+ let event = KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP };
+ sticky_keys_filter.notify_key(&event);
+ assert_eq!(test_filter.last_event().unwrap(), event);
+ }
+
+ #[test]
+ fn test_modifier_state_updated_on_modifier_key_press() {
+ let mut test_filter = TestFilter::new();
+ let mut test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ let test_states = &[
+ (KEYCODE_ALT_LEFT, META_ALT_ON | META_ALT_LEFT_ON),
+ (KEYCODE_ALT_RIGHT, META_ALT_ON | META_ALT_RIGHT_ON),
+ (KEYCODE_CTRL_LEFT, META_CTRL_ON | META_CTRL_LEFT_ON),
+ (KEYCODE_CTRL_RIGHT, META_CTRL_ON | META_CTRL_RIGHT_ON),
+ (KEYCODE_SHIFT_LEFT, META_SHIFT_ON | META_SHIFT_LEFT_ON),
+ (KEYCODE_SHIFT_RIGHT, META_SHIFT_ON | META_SHIFT_RIGHT_ON),
+ (KEYCODE_META_LEFT, META_META_ON | META_META_LEFT_ON),
+ (KEYCODE_META_RIGHT, META_META_ON | META_META_RIGHT_ON),
+ ];
+ for test_state in test_states.iter() {
+ test_filter.clear();
+ test_callbacks.clear();
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
+ assert_eq!(test_callbacks.get_last_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
+ assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+
+ // Re-send keys to lock it
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
+ assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
+ assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1);
+
+ // Re-send keys to clear
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
+ assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
+ assert_eq!(test_callbacks.get_last_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ }
+ }
+
+ #[test]
+ fn test_modifier_state_cleared_on_non_modifier_key_press() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
+
+ assert_eq!(test_callbacks.get_last_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ }
+
+ #[test]
+ fn test_locked_modifier_state_not_cleared_on_non_modifier_key_press() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_SHIFT_LEFT, ..BASE_KEY_UP });
+
+ assert_eq!(
+ test_callbacks.get_last_modifier_state(),
+ META_SHIFT_LEFT_ON | META_SHIFT_ON | META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+ assert_eq!(
+ test_callbacks.get_last_locked_modifier_state(),
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
+
+ assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+ assert_eq!(
+ test_callbacks.get_last_locked_modifier_state(),
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+ }
+
+ #[test]
+ fn test_key_events_have_sticky_modifier_state() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
+ assert_eq!(
+ test_filter.last_event().unwrap().metaState as u32,
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
+ assert_eq!(
+ test_filter.last_event().unwrap().metaState as u32,
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+ }
+
+ #[test]
+ fn test_modifier_state_not_cleared_until_all_devices_removed() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent {
+ deviceId: 1,
+ keyCode: KEYCODE_CTRL_LEFT,
+ ..BASE_KEY_DOWN
+ });
+ sticky_keys_filter.notify_key(&KeyEvent {
+ deviceId: 1,
+ keyCode: KEYCODE_CTRL_LEFT,
+ ..BASE_KEY_UP
+ });
+
+ sticky_keys_filter.notify_key(&KeyEvent {
+ deviceId: 2,
+ keyCode: KEYCODE_CTRL_LEFT,
+ ..BASE_KEY_DOWN
+ });
+ sticky_keys_filter.notify_key(&KeyEvent {
+ deviceId: 2,
+ keyCode: KEYCODE_CTRL_LEFT,
+ ..BASE_KEY_UP
+ });
+
+ sticky_keys_filter.notify_devices_changed(&[DeviceInfo { deviceId: 2, external: true }]);
+ assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+ assert_eq!(
+ test_callbacks.get_last_locked_modifier_state(),
+ META_CTRL_LEFT_ON | META_CTRL_ON
+ );
+
+ sticky_keys_filter.notify_devices_changed(&[]);
+ assert_eq!(test_callbacks.get_last_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ }
+
+ fn setup_filter(
+ next: Box<dyn Filter + Send + Sync>,
+ callbacks: Arc<RwLock<Strong<dyn IInputFilterCallbacks>>>,
+ ) -> StickyKeysFilter {
+ StickyKeysFilter::new(next, ModifierStateListener::new(callbacks))
+ }
+}
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 11c56a8..0dd4dd6 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -7,6 +7,18 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+aconfig_declarations {
+ name: "sensorservice_flags",
+ package: "com.android.frameworks.sensorservice.flags",
+ srcs: ["senserservice_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "sensorservice_flags_c_lib",
+ aconfig_declarations: "dynamic_sensors_flags",
+ host_supported: true,
+}
+
cc_library {
name: "libsensorservice",
diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/senserservice_flags.aconfig
new file mode 100644
index 0000000..6b74f6f
--- /dev/null
+++ b/services/sensorservice/senserservice_flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.frameworks.sensorservice.flags"
+
+flag {
+ name: "dynamic_sensor_hal_reconnect_handling"
+ namespace: "sensors"
+ description: "This flag controls if the dynamic sensor data will be clean up after HAL is disconnected."
+ bug: "307782607"
+}
\ No newline at end of file