diff options
117 files changed, 3261 insertions, 784 deletions
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index 23f185e305..4a77967bb2 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -37,9 +37,6 @@ cc_library_shared { name: "libdumpstateutil", defaults: ["dumpstate_cflag_defaults"], vendor_available: true, - vndk: { - enabled: true, - }, srcs: [ "DumpstateInternal.cpp", "DumpstateUtil.cpp", @@ -82,7 +79,10 @@ filegroup { cc_defaults { name: "dumpstate_defaults", - defaults: ["dumpstate_cflag_defaults"], + defaults: [ + "aconfig_lib_cc_static_link.defaults", + "dumpstate_cflag_defaults", + ], shared_libs: [ "android.hardware.dumpstate@1.0", "android.hardware.dumpstate@1.1", diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index a5c0c602c7..95a05cdcde 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -790,7 +790,8 @@ Status ServiceManager::registerClientCallback(const std::string& name, const sp< if (OK != IInterface::asBinder(cb)->linkToDeath(sp<ServiceManager>::fromExisting(this))) { - ALOGE("%s Could not linkToDeath when adding client callback for %s", name.c_str()); + ALOGE("%s Could not linkToDeath when adding client callback for %s", + ctx.toDebugString().c_str(), name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't linkToDeath."); } diff --git a/include/android/choreographer.h b/include/android/choreographer.h index f999708f04..6a91d9836e 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -17,8 +17,20 @@ /** * @addtogroup Choreographer * - * Choreographer coordinates the timing of frame rendering. This is the C version of the - * android.view.Choreographer object in Java. + * Choreographer coordinates the timing of frame rendering. This is the C + * version of the android.view.Choreographer object in Java. If you do not use + * Choreographer to pace your render loop, you may render too quickly for the + * display, increasing latency between frame submission and presentation. + * + * Input events are guaranteed to be processed before the frame callback is + * called, and will not be run concurrently. Input and sensor events should not + * be handled in the Choregrapher callback. + * + * The frame callback is also the appropriate place to run any per-frame state + * update logic. For example, in a game, the frame callback should be + * responsible for updating things like physics, AI, game state, and rendering + * the frame. Input and sensors should be handled separately via callbacks + * registered with AInputQueue and ASensorManager. * * As of API level 33, apps can follow proper frame pacing and even choose a future frame to render. * The API is used as follows: @@ -38,6 +50,11 @@ * 4. SurfaceFlinger attempts to follow the chosen frame timeline, by not applying transactions or * latching buffers before the desired presentation time. * + * On older devices, AChoreographer_postFrameCallback64 or + * AChoreographer_postFrameCallback can be used to lesser effect. They cannot be + * used to precisely plan your render timeline, but will rate limit to avoid + * overloading the display pipeline and increasing frame latency. + * * @{ */ @@ -119,23 +136,60 @@ typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, voi AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24); /** - * Deprecated: Use AChoreographer_postFrameCallback64 instead. + * Post a callback to be run when the application should begin rendering the + * next frame. The data pointer provided will be passed to the callback function + * when it's called. + * + * The callback will only be run for the next frame, not all subsequent frames, + * so to render continuously the callback should itself call + * AChoreographer_postFrameCallback. + * + * \bug The callback receives the frame time in nanoseconds as a long. On 32-bit + * systems, long is 32-bit, so the frame time will roll over roughly every two + * seconds. If your minSdkVersion is 29 or higher, switch to + * AChoreographer_postFrameCallback64, which uses a 64-bit frame time for all + * platforms. For older OS versions, you must combine the argument with the + * upper bits of clock_gettime(CLOCK_MONOTONIC, ...) on 32-bit systems. + * + * \deprecated Use AChoreographer_postFrameCallback64, which does not have the + * bug described above. */ void AChoreographer_postFrameCallback(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data) - __INTRODUCED_IN(24) __DEPRECATED_IN(29); + __INTRODUCED_IN(24) __DEPRECATED_IN(29, "Use AChoreographer_postFrameCallback64 instead"); /** - * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead. + * Post a callback to be run when the application should begin rendering the + * next frame following the specified delay. The data pointer provided will be + * passed to the callback function when it's called. + * + * The callback will only be run for the next frame after the delay, not all + * subsequent frames, so to render continuously the callback should itself call + * AChoreographer_postFrameCallbackDelayed. + * + * \bug The callback receives the frame time in nanoseconds as a long. On 32-bit + * systems, long is 32-bit, so the frame time will roll over roughly every two + * seconds. If your minSdkVersion is 29 or higher, switch to + * AChoreographer_postFrameCallbackDelayed64, which uses a 64-bit frame time for + * all platforms. For older OS versions, you must combine the argument with the + * upper bits of clock_gettime(CLOCK_MONOTONIC, ...) on 32-bit systems. + * + * \deprecated Use AChoreographer_postFrameCallbackDelayed64, which does not + * have the bug described above. */ void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, AChoreographer_frameCallback callback, void* data, long delayMillis) __INTRODUCED_IN(24) - __DEPRECATED_IN(29); + __DEPRECATED_IN(29, "Use AChoreographer_postFrameCallbackDelayed64 instead"); /** - * Post a callback to be run on the next frame. The data pointer provided will - * be passed to the callback function when it's called. + * Post a callback to be run when the application should begin rendering the + * next frame. The data pointer provided will be passed to the callback function + * when it's called. + * + * The callback will only be run on the next frame, not all subsequent frames, + * so to render continuously the callback should itself call + * AChoreographer_postFrameCallback64. * * Available since API level 29. */ @@ -144,9 +198,13 @@ void AChoreographer_postFrameCallback64(AChoreographer* choreographer, __INTRODUCED_IN(29); /** - * Post a callback to be run on the frame following the specified delay. The - * data pointer provided will be passed to the callback function when it's - * called. + * Post a callback to be run when the application should begin rendering the + * next frame following the specified delay. The data pointer provided will be + * passed to the callback function when it's called. + * + * The callback will only be run for the next frame after the delay, not all + * subsequent frames, so to render continuously the callback should itself call + * AChoreographer_postFrameCallbackDelayed64. * * Available since API level 29. */ @@ -155,8 +213,13 @@ void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, uint32_t delayMillis) __INTRODUCED_IN(29); /** - * Posts a callback to be run on the next frame. The data pointer provided will - * be passed to the callback function when it's called. + * Posts a callback to be run when the application should begin rendering the + * next frame. The data pointer provided will be passed to the callback function + * when it's called. + * + * The callback will only be run for the next frame, not all subsequent frames, + * so to render continuously the callback should itself call + * AChoreographer_postVsyncCallback. * * Available since API level 33. */ diff --git a/include/android/looper.h b/include/android/looper.h index e50730d5c1..d80a3660a6 100644 --- a/include/android/looper.h +++ b/include/android/looper.h @@ -35,7 +35,7 @@ extern "C" { // This file may also be built on glibc or on Windows/MacOS libc's, so // deprecated definitions are provided. #if !defined(__REMOVED_IN) -#define __REMOVED_IN(__api_level) __attribute__((__deprecated__)) +#define __REMOVED_IN(__api_level, msg) __attribute__((__deprecated__(msg))) #endif struct ALooper; @@ -182,23 +182,27 @@ typedef int (*ALooper_callbackFunc)(int fd, int events, void* data); * If the timeout is zero, returns immediately without blocking. * If the timeout is negative, waits indefinitely until an event appears. * - * Returns ALOOPER_POLL_WAKE if the poll was awoken using wake() before + * Returns ALOOPER_POLL_WAKE if the poll was awoken using ALooper_wake() before * the timeout expired and no callbacks were invoked and no other file - * descriptors were ready. + * descriptors were ready. **All return values may also imply + * ALOOPER_POLL_WAKE.** * - * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. + * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. The poll + * may also have been explicitly woken by ALooper_wake. * - * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given - * timeout expired. + * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given timeout + * expired. The poll may also have been explicitly woken by ALooper_wake. * - * Returns ALOOPER_POLL_ERROR if an error occurred. + * Returns ALOOPER_POLL_ERROR if the calling thread has no associated Looper or + * for unrecoverable internal errors. The poll may also have been explicitly + * woken by ALooper_wake. * - * Returns a value >= 0 containing an identifier (the same identifier - * `ident` passed to ALooper_addFd()) if its file descriptor has data - * and it has no callback function (requiring the caller here to - * handle it). In this (and only this) case outFd, outEvents and - * outData will contain the poll events and data associated with the - * fd, otherwise they will be set to NULL. + * Returns a value >= 0 containing an identifier (the same identifier `ident` + * passed to ALooper_addFd()) if its file descriptor has data and it has no + * callback function (requiring the caller here to handle it). In this (and + * only this) case outFd, outEvents and outData will contain the poll events and + * data associated with the fd, otherwise they will be set to NULL. The poll may + * also have been explicitly woken by ALooper_wake. * * This method does not return until it has finished invoking the appropriate callbacks * for all file descriptors that were signalled. @@ -210,10 +214,21 @@ int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa * data has been consumed or a file descriptor is available with no callback. * This function will never return ALOOPER_POLL_CALLBACK. * - * Removed in API 34 as ALooper_pollAll can swallow ALooper_wake calls. - * Use ALooper_pollOnce instead. + * This API cannot be used safely, but a safe alternative exists (see below). As + * such, new builds will not be able to call this API and must migrate to the + * safe API. Binary compatibility is preserved to support already-compiled apps. + * + * \bug ALooper_pollAll will not wake in response to ALooper_wake calls if it + * also handles another event at the same time. + * + * \deprecated Calls to ALooper_pollAll should be replaced with + * ALooper_pollOnce. If you call ALooper_pollOnce in a loop, you *must* treat + * all return values as if they also indicate ALOOPER_POLL_WAKE. */ -int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) __REMOVED_IN(1); +int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) + __REMOVED_IN(1, + "ALooper_pollAll may ignore wakes. Use ALooper_pollOnce instead. See " + "The API documentation for more information"); /** * Wakes the poll asynchronously. diff --git a/include/android/sensor.h b/include/android/sensor.h index a618393e66..e3c1ccfc6c 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -60,7 +60,7 @@ #define __INTRODUCED_IN(__api_level) /* nothing */ #endif #if !defined(__DEPRECATED_IN) -#define __DEPRECATED_IN(__api_level) __attribute__((__deprecated__)) +#define __DEPRECATED_IN(__api_level, msg) __attribute__((__deprecated__(msg))) #endif #ifdef __cplusplus @@ -748,7 +748,8 @@ typedef ASensorRef const* ASensorList; * ASensorManager* sensorManager = ASensorManager_getInstance(); * */ -ASensorManager* ASensorManager_getInstance() __DEPRECATED_IN(26); +ASensorManager* ASensorManager_getInstance() + __DEPRECATED_IN(26, "Use ASensorManager_getInstanceForPackage instead"); /** * Get a reference to the sensor manager. ASensorManager is a singleton diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 23461e27b5..14167e6467 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -62,16 +62,18 @@ typedef struct ASurfaceControl ASurfaceControl; * * Available since API level 29. */ -ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* parent, const char* debug_name) - __INTRODUCED_IN(29); +ASurfaceControl* _Nullable ASurfaceControl_createFromWindow(ANativeWindow* _Nonnull parent, + const char* _Nonnull debug_name) + __INTRODUCED_IN(29); /** * See ASurfaceControl_createFromWindow. * * Available since API level 29. */ -ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name) - __INTRODUCED_IN(29); +ASurfaceControl* _Nullable ASurfaceControl_create(ASurfaceControl* _Nonnull parent, + const char* _Nonnull debug_name) + __INTRODUCED_IN(29); /** * Acquires a reference on the given ASurfaceControl object. This prevents the object @@ -81,7 +83,7 @@ ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* deb * * Available since API level 31. */ -void ASurfaceControl_acquire(ASurfaceControl* surface_control) __INTRODUCED_IN(31); +void ASurfaceControl_acquire(ASurfaceControl* _Nonnull surface_control) __INTRODUCED_IN(31); /** * Removes a reference that was previously acquired with one of the following functions: @@ -92,7 +94,7 @@ void ASurfaceControl_acquire(ASurfaceControl* surface_control) __INTRODUCED_IN(3 * * Available since API level 29. */ -void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29); +void ASurfaceControl_release(ASurfaceControl* _Nonnull surface_control) __INTRODUCED_IN(29); struct ASurfaceTransaction; @@ -108,14 +110,14 @@ typedef struct ASurfaceTransaction ASurfaceTransaction; * * Available since API level 29. */ -ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29); +ASurfaceTransaction* _Nonnull ASurfaceTransaction_create() __INTRODUCED_IN(29); /** * Destroys the \a transaction object. * * Available since API level 29. */ -void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29); +void ASurfaceTransaction_delete(ASurfaceTransaction* _Nullable transaction) __INTRODUCED_IN(29); /** * Applies the updates accumulated in \a transaction. @@ -126,7 +128,7 @@ void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_I * * Available since API level 29. */ -void ASurfaceTransaction_apply(ASurfaceTransaction* transaction) __INTRODUCED_IN(29); +void ASurfaceTransaction_apply(ASurfaceTransaction* _Nonnull transaction) __INTRODUCED_IN(29); /** * An opaque handle returned during a callback that can be used to query general stats and stats for @@ -154,9 +156,9 @@ typedef struct ASurfaceTransactionStats ASurfaceTransactionStats; * * Available since API level 29. */ -typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats) - __INTRODUCED_IN(29); - +typedef void (*ASurfaceTransaction_OnComplete)(void* _Null_unspecified context, + ASurfaceTransactionStats* _Nonnull stats) + __INTRODUCED_IN(29); /** * The ASurfaceTransaction_OnCommit callback is invoked when transaction is applied and the updates @@ -183,8 +185,9 @@ typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactio * * Available since API level 31. */ -typedef void (*ASurfaceTransaction_OnCommit)(void* context, ASurfaceTransactionStats* stats) - __INTRODUCED_IN(31); +typedef void (*ASurfaceTransaction_OnCommit)(void* _Null_unspecified context, + ASurfaceTransactionStats* _Nonnull stats) + __INTRODUCED_IN(31); /** * Returns the timestamp of when the frame was latched by the framework. Once a frame is @@ -192,8 +195,8 @@ typedef void (*ASurfaceTransaction_OnCommit)(void* context, ASurfaceTransactionS * * Available since API level 29. */ -int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_transaction_stats) - __INTRODUCED_IN(29); +int64_t ASurfaceTransactionStats_getLatchTime( + ASurfaceTransactionStats* _Nonnull surface_transaction_stats) __INTRODUCED_IN(29); /** * Returns a sync fence that signals when the transaction has been presented. @@ -204,8 +207,8 @@ int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_ * * Available since API level 29. */ -int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats) - __INTRODUCED_IN(29); +int ASurfaceTransactionStats_getPresentFenceFd( + ASurfaceTransactionStats* _Nonnull surface_transaction_stats) __INTRODUCED_IN(29); /** * \a outASurfaceControls returns an array of ASurfaceControl pointers that were updated during the @@ -217,18 +220,18 @@ int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface * * \a outASurfaceControlsSize returns the size of the ASurfaceControls array. */ -void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats, - ASurfaceControl*** outASurfaceControls, - size_t* outASurfaceControlsSize) - __INTRODUCED_IN(29); +void ASurfaceTransactionStats_getASurfaceControls( + ASurfaceTransactionStats* _Nonnull surface_transaction_stats, + ASurfaceControl* _Nullable* _Nullable* _Nonnull outASurfaceControls, + size_t* _Nonnull outASurfaceControlsSize) __INTRODUCED_IN(29); /** * Releases the array of ASurfaceControls that were returned by * ASurfaceTransactionStats_getASurfaceControls(). * * Available since API level 29. */ -void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_controls) - __INTRODUCED_IN(29); +void ASurfaceTransactionStats_releaseASurfaceControls( + ASurfaceControl* _Nonnull* _Nonnull surface_controls) __INTRODUCED_IN(29); /** * Returns the timestamp of when the CURRENT buffer was acquired. A buffer is considered @@ -237,9 +240,9 @@ void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_ * * Available since API level 29. */ -int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats, - ASurfaceControl* surface_control) - __INTRODUCED_IN(29); +int64_t ASurfaceTransactionStats_getAcquireTime( + ASurfaceTransactionStats* _Nonnull surface_transaction_stats, + ASurfaceControl* _Nonnull surface_control) __INTRODUCED_IN(29); /** * The returns the fence used to signal the release of the PREVIOUS buffer set on @@ -264,9 +267,8 @@ int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surfac * Available since API level 29. */ int ASurfaceTransactionStats_getPreviousReleaseFenceFd( - ASurfaceTransactionStats* surface_transaction_stats, - ASurfaceControl* surface_control) - __INTRODUCED_IN(29); + ASurfaceTransactionStats* _Nonnull surface_transaction_stats, + ASurfaceControl* _Nonnull surface_control) __INTRODUCED_IN(29); /** * Sets the callback that will be invoked when the updates from this transaction @@ -275,8 +277,10 @@ int ASurfaceTransactionStats_getPreviousReleaseFenceFd( * * Available since API level 29. */ -void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* context, - ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29); +void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* _Nonnull transaction, + void* _Null_unspecified context, + ASurfaceTransaction_OnComplete _Nonnull func) + __INTRODUCED_IN(29); /** * Sets the callback that will be invoked when the updates from this transaction are applied and are @@ -285,8 +289,10 @@ void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* c * * Available since API level 31. */ -void ASurfaceTransaction_setOnCommit(ASurfaceTransaction* transaction, void* context, - ASurfaceTransaction_OnCommit func) __INTRODUCED_IN(31); +void ASurfaceTransaction_setOnCommit(ASurfaceTransaction* _Nonnull transaction, + void* _Null_unspecified context, + ASurfaceTransaction_OnCommit _Nonnull func) + __INTRODUCED_IN(31); /** * Reparents the \a surface_control from its old parent to the \a new_parent surface control. @@ -296,14 +302,14 @@ void ASurfaceTransaction_setOnCommit(ASurfaceTransaction* transaction, void* con * * Available since API level 29. */ -void ASurfaceTransaction_reparent(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, ASurfaceControl* new_parent) - __INTRODUCED_IN(29); +void ASurfaceTransaction_reparent(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + ASurfaceControl* _Nullable new_parent) __INTRODUCED_IN(29); /** * Parameter for ASurfaceTransaction_setVisibility(). */ -enum { +enum ASurfaceTransactionVisibility : int8_t { ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0, ASURFACE_TRANSACTION_VISIBILITY_SHOW = 1, }; @@ -314,9 +320,10 @@ enum { * * Available since API level 29. */ -void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, int8_t visibility) - __INTRODUCED_IN(29); +void ASurfaceTransaction_setVisibility(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + enum ASurfaceTransactionVisibility visibility) + __INTRODUCED_IN(29); /** * Updates the z order index for \a surface_control. Note that the z order for a surface @@ -327,9 +334,9 @@ void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction, * * Available since API level 29. */ -void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, int32_t z_order) - __INTRODUCED_IN(29); +void ASurfaceTransaction_setZOrder(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, int32_t z_order) + __INTRODUCED_IN(29); /** * Updates the AHardwareBuffer displayed for \a surface_control. If not -1, the @@ -344,9 +351,10 @@ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction, * * Available since API level 29. */ -void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, AHardwareBuffer* buffer, - int acquire_fence_fd) __INTRODUCED_IN(29); +void ASurfaceTransaction_setBuffer(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + AHardwareBuffer* _Nonnull buffer, int acquire_fence_fd) + __INTRODUCED_IN(29); /** * Updates the color for \a surface_control. This will make the background color for the @@ -356,23 +364,23 @@ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction, * * Available since API level 29. */ -void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, float r, float g, float b, - float alpha, ADataSpace dataspace) - __INTRODUCED_IN(29); +void ASurfaceTransaction_setColor(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, float r, float g, + float b, float alpha, enum ADataSpace dataspace) + __INTRODUCED_IN(29); /** * \param source The sub-rect within the buffer's content to be rendered inside the surface's area * The surface's source rect is clipped by the bounds of its current buffer. The source rect's width * and height must be > 0. * - * \param destination Specifies the rect in the parent's space where this surface will be drawn. The post - * source rect bounds are scaled to fit the destination rect. The surface's destination rect is + * \param destination Specifies the rect in the parent's space where this surface will be drawn. The + * post source rect bounds are scaled to fit the destination rect. The surface's destination rect is * clipped by the bounds of its parent. The destination rect's width and height must be > 0. * - * \param transform The transform applied after the source rect is applied to the buffer. This parameter - * should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_* - * enum. + * \param transform The transform applied after the source rect is applied to the buffer. This + * parameter should be set to 0 for no transform. To specify a transfrom use the + * NATIVE_WINDOW_TRANSFORM_* enum. * * Available since API level 29. * @@ -381,10 +389,10 @@ void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction, * to set different properties at different times, instead of having to specify all the desired * properties at once. */ -void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, const ARect& source, +void ASurfaceTransaction_setGeometry(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, const ARect& source, const ARect& destination, int32_t transform) - __INTRODUCED_IN(29); + __INTRODUCED_IN(29); /** * Bounds the surface and its children to the bounds specified. The crop and buffer size will be @@ -395,9 +403,9 @@ void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction, * * Available since API level 31. */ -void ASurfaceTransaction_setCrop(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, const ARect& crop) - __INTRODUCED_IN(31); +void ASurfaceTransaction_setCrop(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, const ARect& crop) + __INTRODUCED_IN(31); /** * Specifies the position in the parent's space where the surface will be drawn. @@ -407,9 +415,9 @@ void ASurfaceTransaction_setCrop(ASurfaceTransaction* transaction, * * Available since API level 31. */ -void ASurfaceTransaction_setPosition(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, int32_t x, int32_t y) - __INTRODUCED_IN(31); +void ASurfaceTransaction_setPosition(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, int32_t x, + int32_t y) __INTRODUCED_IN(31); /** * \param transform The transform applied after the source rect is applied to the buffer. This @@ -418,9 +426,9 @@ void ASurfaceTransaction_setPosition(ASurfaceTransaction* transaction, * * Available since API level 31. */ -void ASurfaceTransaction_setBufferTransform(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, int32_t transform) - __INTRODUCED_IN(31); +void ASurfaceTransaction_setBufferTransform(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + int32_t transform) __INTRODUCED_IN(31); /** * Sets an x and y scale of a surface with (0, 0) as the centerpoint of the scale. @@ -430,13 +438,13 @@ void ASurfaceTransaction_setBufferTransform(ASurfaceTransaction* transaction, * * Available since API level 31. */ -void ASurfaceTransaction_setScale(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, float xScale, float yScale) - __INTRODUCED_IN(31); +void ASurfaceTransaction_setScale(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, float xScale, + float yScale) __INTRODUCED_IN(31); /** * Parameter for ASurfaceTransaction_setBufferTransparency(). */ -enum { +enum ASurfaceTransactionTransparency : int8_t { ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT = 0, ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT = 1, ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE = 2, @@ -448,9 +456,9 @@ enum { * * Available since API level 29. */ -void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, - int8_t transparency) +void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + enum ASurfaceTransactionTransparency transparency) __INTRODUCED_IN(29); /** @@ -459,9 +467,10 @@ void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction, * * Available since API level 29. */ -void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, const ARect rects[], - uint32_t count) __INTRODUCED_IN(29); +void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + const ARect* _Nullable rects, uint32_t count) + __INTRODUCED_IN(29); /** * Specifies a desiredPresentTime for the transaction. The framework will try to present @@ -475,7 +484,7 @@ void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction, * * Available since API level 29. */ -void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction, +void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* _Nonnull transaction, int64_t desiredPresentTime) __INTRODUCED_IN(29); /** @@ -485,8 +494,8 @@ void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction, * * Available since API level 29. */ -void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, float alpha) +void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, float alpha) __INTRODUCED_IN(29); /** @@ -496,9 +505,9 @@ void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction, * * Available since API level 29. */ -void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, ADataSpace data_space) - __INTRODUCED_IN(29); +void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + enum ADataSpace data_space) __INTRODUCED_IN(29); /** * SMPTE ST 2086 "Mastering Display Color Volume" static metadata @@ -508,9 +517,9 @@ void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction, * * Available since API level 29. */ -void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, - struct AHdrMetadata_smpte2086* metadata) +void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + struct AHdrMetadata_smpte2086* _Nullable metadata) __INTRODUCED_IN(29); /** @@ -521,9 +530,9 @@ void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transacti * * Available since API level 29. */ -void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, - struct AHdrMetadata_cta861_3* metadata) +void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + struct AHdrMetadata_cta861_3* _Nullable metadata) __INTRODUCED_IN(29); /** @@ -568,10 +577,10 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transactio * * Available since API level 34. */ -void ASurfaceTransaction_setExtendedRangeBrightness(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, - float currentBufferRatio, - float desiredRatio) __INTRODUCED_IN(__ANDROID_API_U__); +void ASurfaceTransaction_setExtendedRangeBrightness(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + float currentBufferRatio, float desiredRatio) + __INTRODUCED_IN(__ANDROID_API_U__); /** * Same as ASurfaceTransaction_setFrameRateWithChangeStrategy(transaction, surface_control, @@ -581,8 +590,8 @@ void ASurfaceTransaction_setExtendedRangeBrightness(ASurfaceTransaction* transac * * Available since API level 30. */ -void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, float frameRate, +void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, float frameRate, int8_t compatibility) __INTRODUCED_IN(30); /** @@ -617,10 +626,11 @@ void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction, * * Available since API level 31. */ -void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, float frameRate, - int8_t compatibility, int8_t changeFrameRateStrategy) - __INTRODUCED_IN(31); +void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + float frameRate, int8_t compatibility, + int8_t changeFrameRateStrategy) + __INTRODUCED_IN(31); /** * Clears the frame rate which is set for \a surface_control. @@ -643,8 +653,8 @@ void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* tra * * Available since API level 34. */ -void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control) +void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control) __INTRODUCED_IN(__ANDROID_API_U__); /** @@ -673,10 +683,9 @@ void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* transaction, * \param surface_control The ASurfaceControl on which to control buffer backpressure behavior. * \param enableBackPressure Whether to enable back pressure. */ -void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, - bool enableBackPressure) - __INTRODUCED_IN(31); +void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + bool enableBackPressure) __INTRODUCED_IN(31); /** * Sets the frame timeline to use in SurfaceFlinger. @@ -696,7 +705,7 @@ void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* transaction, * to the corresponding expected presentation time and deadline from the frame to be rendered. A * stale or invalid value will be ignored. */ -void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* transaction, +void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* _Nonnull transaction, AVsyncId vsyncId) __INTRODUCED_IN(33); __END_DECLS diff --git a/services/inputflinger/BlockingQueue.h b/include/input/BlockingQueue.h index f848c82c42..f848c82c42 100644 --- a/services/inputflinger/BlockingQueue.h +++ b/include/input/BlockingQueue.h diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index eec12e4cd5..090e35bc37 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -229,6 +229,7 @@ cc_defaults { cc_library_headers { name: "trusty_mock_headers", host_supported: true, + vendor_available: true, export_include_dirs: [ "trusty/include", @@ -243,12 +244,18 @@ cc_library_headers { cc_defaults { name: "trusty_mock_defaults", host_supported: true, + vendor_available: true, header_libs: [ "libbinder_headers_base", "liblog_stub", "trusty_mock_headers", ], + export_header_lib_headers: [ + "libbinder_headers_base", + "liblog_stub", + "trusty_mock_headers", + ], shared_libs: [ "libutils_binder_sdk", @@ -358,9 +365,6 @@ cc_library { // for vndbinder vendor_available: true, - vndk: { - enabled: true, - }, recovery_available: true, double_loadable: true, // TODO(b/153609531): remove when no longer needed. @@ -442,7 +446,7 @@ cc_library_host_shared { }, } -cc_library_static { +cc_library { name: "libbinder_rpc_no_kernel", vendor_available: true, defaults: [ @@ -454,7 +458,7 @@ cc_library_static { ], } -cc_library_static { +cc_library { name: "libbinder_rpc_no_blob", vendor_available: true, defaults: [ @@ -470,7 +474,7 @@ cc_library_static { ], } -cc_library_static { +cc_library { name: "libbinder_rpc_no_native_handle", vendor_available: true, defaults: [ @@ -486,7 +490,7 @@ cc_library_static { ], } -cc_library_static { +cc_library { name: "libbinder_rpc_single_threaded", defaults: [ "libbinder_common_defaults", @@ -501,7 +505,7 @@ cc_library_static { ], } -cc_library_static { +cc_library { name: "libbinder_rpc_single_threaded_no_kernel", defaults: [ "libbinder_common_defaults", @@ -669,6 +673,7 @@ cc_library { "//packages/modules/Virtualization:__subpackages__", "//device/google/cuttlefish/shared/minidroid:__subpackages__", "//system/software_defined_vehicle:__subpackages__", + "//visibility:any_system_partition", ], } diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 42dd6916c7..54457fc568 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -44,6 +44,7 @@ std::unordered_map<int32_t, uint32_t> BpBinder::sLastLimitCallbackMap; int BpBinder::sNumTrackedUids = 0; std::atomic_bool BpBinder::sCountByUidEnabled(false); binder_proxy_limit_callback BpBinder::sLimitCallback; +binder_proxy_warning_callback BpBinder::sWarningCallback; bool BpBinder::sBinderProxyThrottleCreate = false; static StaticString16 kDescriptorUninit(u""); @@ -52,6 +53,9 @@ static StaticString16 kDescriptorUninit(u""); uint32_t BpBinder::sBinderProxyCountHighWatermark = 2500; // Another arbitrary value a binder count needs to drop below before another callback will be called uint32_t BpBinder::sBinderProxyCountLowWatermark = 2000; +// Arbitrary value between low and high watermark on a bad behaving app to +// trigger a warning callback. +uint32_t BpBinder::sBinderProxyCountWarningWatermark = 2250; std::atomic<uint32_t> BpBinder::sBinderProxyCount(0); std::atomic<uint32_t> BpBinder::sBinderProxyCountWarned(0); @@ -63,7 +67,8 @@ static constexpr uint32_t kBinderProxyCountWarnInterval = 5000; enum { LIMIT_REACHED_MASK = 0x80000000, // A flag denoting that the limit has been reached - COUNTING_VALUE_MASK = 0x7FFFFFFF, // A mask of the remaining bits for the count value + WARNING_REACHED_MASK = 0x40000000, // A flag denoting that the warning has been reached + COUNTING_VALUE_MASK = 0x3FFFFFFF, // A mask of the remaining bits for the count value }; BpBinder::ObjectManager::ObjectManager() @@ -181,7 +186,13 @@ sp<BpBinder> BpBinder::create(int32_t handle) { sLastLimitCallbackMap[trackedUid] = trackedValue; } } else { - if ((trackedValue & COUNTING_VALUE_MASK) >= sBinderProxyCountHighWatermark) { + uint32_t currentValue = trackedValue & COUNTING_VALUE_MASK; + if (currentValue >= sBinderProxyCountWarningWatermark + && currentValue < sBinderProxyCountHighWatermark + && ((trackedValue & WARNING_REACHED_MASK) == 0)) [[unlikely]] { + sTrackingMap[trackedUid] |= WARNING_REACHED_MASK; + if (sWarningCallback) sWarningCallback(trackedUid); + } else if (currentValue >= sBinderProxyCountHighWatermark) { ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)", getuid(), trackedUid, trackedValue); sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK; @@ -609,11 +620,11 @@ BpBinder::~BpBinder() { binderHandle()); } else { auto countingValue = trackedValue & COUNTING_VALUE_MASK; - if ((trackedValue & LIMIT_REACHED_MASK) && + if ((trackedValue & (LIMIT_REACHED_MASK | WARNING_REACHED_MASK)) && (countingValue <= sBinderProxyCountLowWatermark)) [[unlikely]] { ALOGI("Limit reached bit reset for uid %d (fewer than %d proxies from uid %d held)", getuid(), sBinderProxyCountLowWatermark, mTrackedUid); - sTrackingMap[mTrackedUid] &= ~LIMIT_REACHED_MASK; + sTrackingMap[mTrackedUid] &= ~(LIMIT_REACHED_MASK | WARNING_REACHED_MASK); sLastLimitCallbackMap.erase(mTrackedUid); } if (--sTrackingMap[mTrackedUid] == 0) { @@ -730,15 +741,18 @@ void BpBinder::enableCountByUid() { sCountByUidEnabled.store(true); } void BpBinder::disableCountByUid() { sCountByUidEnabled.store(false); } void BpBinder::setCountByUidEnabled(bool enable) { sCountByUidEnabled.store(enable); } -void BpBinder::setLimitCallback(binder_proxy_limit_callback cb) { +void BpBinder::setBinderProxyCountEventCallback(binder_proxy_limit_callback cbl, + binder_proxy_warning_callback cbw) { RpcMutexUniqueLock _l(sTrackingLock); - sLimitCallback = cb; + sLimitCallback = std::move(cbl); + sWarningCallback = std::move(cbw); } -void BpBinder::setBinderProxyCountWatermarks(int high, int low) { +void BpBinder::setBinderProxyCountWatermarks(int high, int low, int warning) { RpcMutexUniqueLock _l(sTrackingLock); sBinderProxyCountHighWatermark = high; sBinderProxyCountLowWatermark = low; + sBinderProxyCountWarningWatermark = warning; } // --------------------------------------------------------------------------- diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 2dd310e9ca..84ef489a6c 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -30,6 +30,7 @@ #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> +#include <algorithm> #include <binder/Binder.h> #include <binder/BpBinder.h> diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl index f8a8843309..3ddfefa311 100644 --- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl +++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl @@ -43,6 +43,18 @@ interface IPackageManagerNative { @utf8InCpp String[] getNamesForUids(in int[] uids); /** + * Return the UID associated with the given package name. + * Note that the same package will have different UIDs under different UserHandle on + * the same device. + * @param packageName The full name (i.e. com.google.apps.contacts) of the desired package. + * @param flags Additional option flags to modify the data returned. + * @param userId The user handle identifier to look up the package under. + * @return Returns an integer UID who owns the given package name, or -1 if no such package is + * available to the caller. + */ + int getPackageUid(in @utf8InCpp String packageName, in long flags, in int userId); + + /** * Returns the name of the installer (a package) which installed the named * package. Preloaded packages return the string "preload". Sideloaded packages * return an empty string. Unknown or unknowable are returned as empty strings. diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h index eef07aef2d..b3a2d9ec28 100644 --- a/libs/binder/binder_module.h +++ b/libs/binder/binder_module.h @@ -32,77 +32,4 @@ #include <linux/android/binder.h> #include <sys/ioctl.h> -#ifndef BR_FROZEN_REPLY -// Temporary definition of BR_FROZEN_REPLY. For production -// this will come from UAPI binder.h -#define BR_FROZEN_REPLY _IO('r', 18) -#endif // BR_FROZEN_REPLY - -#ifndef BINDER_FREEZE -/* - * Temporary definitions for freeze support. For the final version - * these will be defined in the UAPI binder.h file from upstream kernel. - */ -#define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info) - -struct binder_freeze_info { - // - // Group-leader PID of process to be frozen - // - uint32_t pid; - // - // Enable(1) / Disable(0) freeze for given PID - // - uint32_t enable; - // - // Timeout to wait for transactions to drain. - // 0: don't wait (ioctl will return EAGAIN if not drained) - // N: number of ms to wait - uint32_t timeout_ms; -}; -#endif // BINDER_FREEZE - -#ifndef BINDER_GET_FROZEN_INFO - -#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info) - -struct binder_frozen_status_info { - // - // Group-leader PID of process to be queried - // - __u32 pid; - // - // Indicates whether the process has received any sync calls since last - // freeze (cleared at freeze/unfreeze) - // bit 0: received sync transaction after being frozen - // bit 1: new pending sync transaction during freezing - // - __u32 sync_recv; - // - // Indicates whether the process has received any async calls since last - // freeze (cleared at freeze/unfreeze) - // - __u32 async_recv; -}; -#endif // BINDER_GET_FROZEN_INFO - -#ifndef BR_ONEWAY_SPAM_SUSPECT -// Temporary definition of BR_ONEWAY_SPAM_SUSPECT. For production -// this will come from UAPI binder.h -#define BR_ONEWAY_SPAM_SUSPECT _IO('r', 19) -#endif // BR_ONEWAY_SPAM_SUSPECT - -#ifndef BINDER_ENABLE_ONEWAY_SPAM_DETECTION -/* - * Temporary definitions for oneway spam detection support. For the final version - * these will be defined in the UAPI binder.h file from upstream kernel. - */ -#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32) -#endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION - -#ifndef BR_TRANSACTION_PENDING_FROZEN -// Temporary definition of BR_TRANSACTION_PENDING_FROZEN until UAPI binder.h includes it. -#define BR_TRANSACTION_PENDING_FROZEN _IO('r', 20) -#endif // BR_TRANSACTION_PENDING_FROZEN - #endif // _BINDER_MODULE_H_ diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 89a4d273ad..9f0390769b 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -35,7 +35,8 @@ class Stability; } class ProcessState; -using binder_proxy_limit_callback = void(*)(int); +using binder_proxy_limit_callback = std::function<void(int)>; +using binder_proxy_warning_callback = std::function<void(int)>; class BpBinder : public IBinder { @@ -86,8 +87,9 @@ public: static void enableCountByUid(); static void disableCountByUid(); static void setCountByUidEnabled(bool enable); - static void setLimitCallback(binder_proxy_limit_callback cb); - static void setBinderProxyCountWatermarks(int high, int low); + static void setBinderProxyCountEventCallback(binder_proxy_limit_callback cbl, + binder_proxy_warning_callback cbw); + static void setBinderProxyCountWatermarks(int high, int low, int warning); static uint32_t getBinderProxyCount(); std::optional<int32_t> getDebugBinderHandle() const; @@ -212,6 +214,8 @@ private: static std::unordered_map<int32_t,uint32_t> sLastLimitCallbackMap; static std::atomic<uint32_t> sBinderProxyCount; static std::atomic<uint32_t> sBinderProxyCountWarned; + static binder_proxy_warning_callback sWarningCallback; + static uint32_t sBinderProxyCountWarningWatermark; }; } // namespace android diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index 7d0acd1843..392ebb5b0a 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -73,6 +73,17 @@ void ARpcServer_setSupportedFileDescriptorTransportModes( const ARpcSession_FileDescriptorTransportMode modes[], size_t modes_len); +// Sets the maximum number of threads that the Server will use for +// incoming client connections. +// +// This must be called before adding a client session. This corresponds +// to the number of incoming connections to RpcSession objects in the +// server, which will correspond to the number of outgoing connections +// in client RpcSession objects. +// +// If this is not specified, this will be a single-threaded server. +void ARpcServer_setMaxThreads(ARpcServer* server, size_t threads); + // Runs ARpcServer_join() in a background thread. Immediately returns. void ARpcServer_start(ARpcServer* server); diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index cb44c58c2c..21537fc50d 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -167,6 +167,10 @@ void ARpcServer_setSupportedFileDescriptorTransportModes( server->setSupportedFileDescriptorTransportModes(modevec); } +void ARpcServer_setMaxThreads(ARpcServer* handle, size_t threads) { + handleToStrongPointer<RpcServer>(handle)->setMaxThreads(threads); +} + void ARpcServer_start(ARpcServer* handle) { handleToStrongPointer<RpcServer>(handle)->start(); } diff --git a/libs/binder/liblog_stub/include/log/log.h b/libs/binder/liblog_stub/include/log/log.h index 91c9632c1b..dad0020ace 100644 --- a/libs/binder/liblog_stub/include/log/log.h +++ b/libs/binder/liblog_stub/include/log/log.h @@ -54,16 +54,16 @@ int __android_log_print(int prio, const char* tag, const char* fmt, ...) #define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG) #define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG) -#define ALOG(priority, tag, fmt, ...) \ - do { \ - if (false)[[/*VERY*/ unlikely]] { /* ignore unused __VA_ARGS__ */ \ - std::fprintf(stderr, fmt __VA_OPT__(, ) __VA_ARGS__); \ - } \ - IF_ALOG(priority, tag) { \ - __android_log_print(ANDROID_##priority, tag, \ - tag ": " fmt "\n" __VA_OPT__(, ) __VA_ARGS__); \ - } \ - if constexpr (ANDROID_##priority == ANDROID_LOG_FATAL) std::abort(); \ +#define ALOG(priority, tag, fmt, ...) \ + do { \ + if (false)[[/*VERY*/ unlikely]] { /* ignore unused __VA_ARGS__ */ \ + std::fprintf(stderr, fmt __VA_OPT__(, ) __VA_ARGS__); \ + } \ + IF_ALOG(priority, tag) { \ + __android_log_print(ANDROID_##priority, tag, "%s: " fmt "\n", \ + (tag)__VA_OPT__(, ) __VA_ARGS__); \ + } \ + if constexpr (ANDROID_##priority == ANDROID_LOG_FATAL) std::abort(); \ } while (false) #define ALOGV(...) ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__) #define ALOGD(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 30dbdddc60..9a2d14af32 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -50,8 +50,10 @@ cc_library { ], cflags: [ + "-DBINDER_WITH_KERNEL_IPC", "-Wall", "-Wextra", + "-Wextra-semi", "-Werror", ], @@ -145,6 +147,46 @@ cc_library { afdo: true, } +cc_library { + name: "libbinder_ndk_on_trusty_mock", + defaults: [ + "trusty_mock_defaults", + ], + + export_include_dirs: [ + "include_cpp", + "include_ndk", + "include_platform", + ], + + srcs: [ + "ibinder.cpp", + "libbinder.cpp", + "parcel.cpp", + "stability.cpp", + "status.cpp", + ], + + shared_libs: [ + "libbinder_on_trusty_mock", + ], + + header_libs: [ + "libbinder_trusty_ndk_headers", + ], + export_header_lib_headers: [ + "libbinder_trusty_ndk_headers", + ], + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + + visibility: ["//frameworks/native/libs/binder:__subpackages__"], +} + cc_library_headers { name: "libbinder_headers_platform_shared", export_include_dirs: ["include_cpp"], diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index bf7a0ba5f0..cf5942059a 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -24,6 +24,7 @@ #include <private/android_filesystem_config.h> #endif +#include "../BuildFlags.h" #include "ibinder_internal.h" #include "parcel_internal.h" #include "status_internal.h" @@ -44,7 +45,9 @@ namespace ABBinderTag { static const void* kId = "ABBinder"; static void* kValue = static_cast<void*>(new bool{true}); -void clean(const void* /*id*/, void* /*obj*/, void* /*cookie*/){/* do nothing */}; +void clean(const void* /*id*/, void* /*obj*/, void* /*cookie*/) { + /* do nothing */ +} static void attach(const sp<IBinder>& binder) { auto alreadyAttached = binder->attachObject(kId, kValue, nullptr /*cookie*/, clean); @@ -69,7 +72,7 @@ void clean(const void* id, void* obj, void* cookie) { LOG_ALWAYS_FATAL_IF(id != kId, "%p %p %p", id, obj, cookie); delete static_cast<Value*>(obj); -}; +} } // namespace ABpBinderTag @@ -211,6 +214,12 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce binder_status_t status = getClass()->onTransact(this, code, &in, &out); return PruneStatusT(status); } else if (code == SHELL_COMMAND_TRANSACTION && getClass()->handleShellCommand != nullptr) { + if constexpr (!android::kEnableKernelIpc) { + // Non-IPC builds do not have getCallingUid(), + // so we have no way of authenticating the caller + return STATUS_PERMISSION_DENIED; + } + int in = data.readFileDescriptor(); int out = data.readFileDescriptor(); int err = data.readFileDescriptor(); @@ -569,6 +578,7 @@ binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient return recipient->unlinkToDeath(binder->getBinder(), cookie); } +#ifdef BINDER_WITH_KERNEL_IPC uid_t AIBinder_getCallingUid() { return ::android::IPCThreadState::self()->getCallingUid(); } @@ -580,6 +590,7 @@ pid_t AIBinder_getCallingPid() { bool AIBinder_isHandlingTransaction() { return ::android::IPCThreadState::self()->getServingStackPointer() != nullptr; } +#endif void AIBinder_incStrong(AIBinder* binder) { if (binder == nullptr) { @@ -797,9 +808,11 @@ void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) { localBinder->setRequestingSid(requestingSid); } +#ifdef BINDER_WITH_KERNEL_IPC const char* AIBinder_getCallingSid() { return ::android::IPCThreadState::self()->getCallingSid(); } +#endif void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) { binder->asABBinder()->setMinSchedulerPolicy(policy, priority); diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index b1ab7b0f9a..2929bce897 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -718,9 +718,17 @@ binder_status_t AIBinder_getExtension(AIBinder* binder, AIBinder** outExt) __INT * When registering the interface, add: * std::shared_ptr<MyFoo> foo = new MyFoo; // class in AOSP codebase * std::shared_ptr<MyBar> bar = new MyBar; // custom extension class - * ... = AIBinder_setExtension(foo->asBinder().get(), bar->asBinder().get()); + * SpAIBinder binder = foo->asBinder(); // target binder to extend + * ... = AIBinder_setExtension(binder.get(), bar->asBinder().get()); + * ... = AServiceManager_addService(binder.get(), instanceName); * // handle error * + * Do not use foo->asBinder().get() as the target binder argument to + * AIBinder_setExtensions because asBinder it creates a new binder + * object that will be destroyed after the function is called. The same + * binder object must be used for AIBinder_setExtension and + * AServiceManager_addService to register the service with an extension. + * * Then, clients of IFoo can get this extension: * SpAIBinder binder = ...; * std::shared_ptr<IFoo> foo = IFoo::fromBinder(binder); // handle if null diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index c665ad82ad..52edae4a38 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -18,11 +18,8 @@ #include <android/binder_ibinder.h> #include <android/binder_status.h> -#include <sys/cdefs.h> - -#ifndef __TRUSTY__ #include <android/llndk-versioning.h> -#endif +#include <sys/cdefs.h> __BEGIN_DECLS diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index 57a38dc480..ef556d7f5f 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -35,6 +35,21 @@ rust_library { } rust_library { + name: "libbinder_rs_on_trusty_mock", + crate_name: "binder", + srcs: ["src/lib.rs"], + cfgs: [ + "trusty", + ], + rustlibs: [ + "libbinder_ndk_sys_on_trusty_mock", + "libdowncast_rs", + "liblibc", + ], + vendor: true, +} + +rust_library { name: "libbinder_tokio_rs", crate_name: "binder_tokio", srcs: ["binder_tokio/lib.rs"], @@ -89,6 +104,26 @@ rust_library { visibility: [":__subpackages__"], } +rust_library { + name: "libbinder_ndk_sys_on_trusty_mock", + crate_name: "binder_ndk_sys", + srcs: [ + "sys/lib.rs", + ":libbinder_ndk_bindgen_on_trusty_mock", + ], + cfgs: [ + "trusty", + ], + shared_libs: [ + "libbinder_ndk_on_trusty_mock", + ], + vendor: true, + // Lints are checked separately for libbinder_ndk_sys. + // The Trusty mock copy pulls in extra headers that + // don't pass the lints for the bindgen output. + lints: "none", +} + rust_bindgen { name: "libbinder_ndk_bindgen", crate_name: "binder_ndk_bindgen", @@ -125,6 +160,28 @@ rust_bindgen { min_sdk_version: "Tiramisu", } +rust_bindgen { + name: "libbinder_ndk_bindgen_on_trusty_mock", + crate_name: "binder_ndk_bindgen", + wrapper_src: "sys/BinderBindings.hpp", + source_stem: "bindings", + defaults: [ + "trusty_mock_defaults", + ], + + bindgen_flag_files: [ + // Unfortunately the only way to specify the rust_non_exhaustive enum + // style for a type is to make it the default + // and then specify constified enums for the enums we don't want + // rustified + "libbinder_ndk_bindgen_flags.txt", + ], + shared_libs: [ + "libbinder_ndk_on_trusty_mock", + "libc++", + ], +} + rust_test { name: "libbinder_rs-internal_test", crate_name: "binder", diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs index 1dc0b2471d..71bb95bc0e 100644 --- a/libs/binder/rust/binder_tokio/lib.rs +++ b/libs/binder/rust/binder_tokio/lib.rs @@ -34,6 +34,7 @@ use std::future::Future; /// Retrieve an existing service for a particular interface, sleeping for a few /// seconds if it doesn't yet exist. +#[deprecated = "this polls 5s, use wait_for_interface or check_interface"] pub async fn get_interface<T: FromIBinder + ?Sized + 'static>( name: &str, ) -> Result<Strong<T>, StatusCode> { @@ -56,6 +57,32 @@ pub async fn get_interface<T: FromIBinder + ?Sized + 'static>( } } +/// Retrieve an existing service for a particular interface. Returns +/// `Err(StatusCode::NAME_NOT_FOUND)` immediately if the service is not available. +/// +/// NOTE: "immediately" above does not mean the future will complete the first time it is polled. +pub async fn check_interface<T: FromIBinder + ?Sized + 'static>( + name: &str, +) -> Result<Strong<T>, StatusCode> { + if binder::is_handling_transaction() { + // See comment in the BinderAsyncPool impl. + return binder::check_interface::<T>(name); + } + + let name = name.to_string(); + let res = tokio::task::spawn_blocking(move || binder::check_interface::<T>(&name)).await; + + // The `is_panic` branch is not actually reachable in Android as we compile + // with `panic = abort`. + match res { + Ok(Ok(service)) => Ok(service), + Ok(Err(err)) => Err(err), + Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()), + Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION), + Err(_) => Err(StatusCode::UNKNOWN_ERROR), + } +} + /// Retrieve an existing service for a particular interface, or start it if it /// is configured as a dynamic service and isn't yet started. pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>( diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp index 535ce010f7..2e463451f9 100644 --- a/libs/binder/rust/rpcbinder/Android.bp +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -18,6 +18,7 @@ rust_library { "libbinder_ndk_sys", "libbinder_rpc_unstable_bindgen_sys", "libbinder_rs", + "libcfg_if", "libdowncast_rs", "libforeign_types", "liblibc", diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs index 163f000ac8..7e5c9ddc35 100644 --- a/libs/binder/rust/rpcbinder/src/lib.rs +++ b/libs/binder/rust/rpcbinder/src/lib.rs @@ -16,10 +16,10 @@ //! API for RPC Binder services. -#[cfg(not(target_os = "trusty"))] mod server; mod session; +pub use server::RpcServer; #[cfg(not(target_os = "trusty"))] -pub use server::{RpcServer, RpcServerRef}; +pub use server::RpcServerRef; pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef}; diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs index 6fda878d07..d6bdbd831f 100644 --- a/libs/binder/rust/rpcbinder/src/server.rs +++ b/libs/binder/rust/rpcbinder/src/server.rs @@ -14,160 +14,12 @@ * limitations under the License. */ -use crate::session::FileDescriptorTransportMode; -use binder::{unstable_api::AsNative, SpIBinder}; -use binder_rpc_unstable_bindgen::ARpcServer; -use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; -use std::ffi::CString; -use std::io::{Error, ErrorKind}; -use std::os::unix::io::{IntoRawFd, OwnedFd}; - -foreign_type! { - type CType = binder_rpc_unstable_bindgen::ARpcServer; - fn drop = binder_rpc_unstable_bindgen::ARpcServer_free; - - /// A type that represents a foreign instance of RpcServer. - #[derive(Debug)] - pub struct RpcServer; - /// A borrowed RpcServer. - pub struct RpcServerRef; -} - -/// SAFETY: The opaque handle can be cloned freely. -unsafe impl Send for RpcServer {} -/// SAFETY: The underlying C++ RpcServer class is thread-safe. -unsafe impl Sync for RpcServer {} - -impl RpcServer { - /// Creates a binder RPC server, serving the supplied binder service implementation on the given - /// vsock port. Only connections from the given CID are accepted. - /// - // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client. - // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface. - pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> { - let service = service.as_native_mut(); - - // SAFETY: Service ownership is transferring to the server and won't be valid afterward. - // Plus the binder objects are threadsafe. - unsafe { - Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock( - service, cid, port, - )) - } - } - - /// Creates a binder RPC server, serving the supplied binder service implementation on the given - /// socket file descriptor. The socket should be bound to an address before calling this - /// function. - pub fn new_bound_socket( - mut service: SpIBinder, - socket_fd: OwnedFd, - ) -> Result<RpcServer, Error> { - let service = service.as_native_mut(); - - // SAFETY: Service ownership is transferring to the server and won't be valid afterward. - // Plus the binder objects are threadsafe. - // The server takes ownership of the socket FD. - unsafe { - Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newBoundSocket( - service, - socket_fd.into_raw_fd(), - )) - } - } - - /// Creates a binder RPC server that bootstraps sessions using an existing Unix domain socket - /// pair, with a given root IBinder object. Callers should create a pair of SOCK_STREAM Unix - /// domain sockets, pass one to the server and the other to the client. Multiple client session - /// can be created from the client end of the pair. - pub fn new_unix_domain_bootstrap( - mut service: SpIBinder, - bootstrap_fd: OwnedFd, - ) -> Result<RpcServer, Error> { - let service = service.as_native_mut(); - - // SAFETY: Service ownership is transferring to the server and won't be valid afterward. - // Plus the binder objects are threadsafe. - // The server takes ownership of the bootstrap FD. - unsafe { - Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newUnixDomainBootstrap( - service, - bootstrap_fd.into_raw_fd(), - )) - } - } - - /// Creates a binder RPC server, serving the supplied binder service implementation on the given - /// IP address and port. - pub fn new_inet(mut service: SpIBinder, address: &str, port: u32) -> Result<RpcServer, Error> { - let address = match CString::new(address) { - Ok(s) => s, - Err(e) => { - log::error!("Cannot convert {} to CString. Error: {:?}", address, e); - return Err(Error::from(ErrorKind::InvalidInput)); - } - }; - let service = service.as_native_mut(); - - // SAFETY: Service ownership is transferring to the server and won't be valid afterward. - // Plus the binder objects are threadsafe. - unsafe { - Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInet( - service, - address.as_ptr(), - port, - )) - } - } - - unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> { - if ptr.is_null() { - return Err(Error::new(ErrorKind::Other, "Failed to start server")); - } - // SAFETY: Our caller must pass us a valid or null pointer, and we've checked that it's not - // null. - Ok(unsafe { RpcServer::from_ptr(ptr) }) - } -} - -impl RpcServerRef { - /// Sets the list of file descriptor transport modes supported by this server. - pub fn set_supported_file_descriptor_transport_modes( - &self, - modes: &[FileDescriptorTransportMode], - ) { - // SAFETY: Does not keep the pointer after returning does, nor does it - // read past its boundary. Only passes the 'self' pointer as an opaque handle. - unsafe { - binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes( - self.as_ptr(), - modes.as_ptr(), - modes.len(), - ) - } - } - - /// Starts a new background thread and calls join(). Returns immediately. - pub fn start(&self) { - // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. - unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) }; - } - - /// Joins the RpcServer thread. The call blocks until the server terminates. - /// This must be called from exactly one thread. - pub fn join(&self) { - // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. - unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) }; - } - - /// Shuts down the running RpcServer. Can be called multiple times and from - /// multiple threads. Called automatically during drop(). - pub fn shutdown(&self) -> Result<(), Error> { - // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. - if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } { - Ok(()) - } else { - Err(Error::from(ErrorKind::UnexpectedEof)) - } +cfg_if::cfg_if! { + if #[cfg(target_os = "trusty")] { + mod trusty; + pub use trusty::*; + } else { + mod android; + pub use android::*; } } diff --git a/libs/binder/rust/rpcbinder/src/server/android.rs b/libs/binder/rust/rpcbinder/src/server/android.rs new file mode 100644 index 0000000000..2ab34472a9 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/server/android.rs @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2024 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. + */ + +use crate::session::FileDescriptorTransportMode; +use binder::{unstable_api::AsNative, SpIBinder}; +use binder_rpc_unstable_bindgen::ARpcServer; +use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; +use std::ffi::CString; +use std::io::{Error, ErrorKind}; +use std::os::unix::io::{IntoRawFd, OwnedFd}; + +foreign_type! { + type CType = binder_rpc_unstable_bindgen::ARpcServer; + fn drop = binder_rpc_unstable_bindgen::ARpcServer_free; + + /// A type that represents a foreign instance of RpcServer. + #[derive(Debug)] + pub struct RpcServer; + /// A borrowed RpcServer. + pub struct RpcServerRef; +} + +/// SAFETY: The opaque handle can be cloned freely. +unsafe impl Send for RpcServer {} +/// SAFETY: The underlying C++ RpcServer class is thread-safe. +unsafe impl Sync for RpcServer {} + +impl RpcServer { + /// Creates a binder RPC server, serving the supplied binder service implementation on the given + /// vsock port. Only connections from the given CID are accepted. + /// + // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client. + // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface. + pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> { + let service = service.as_native_mut(); + + // SAFETY: Service ownership is transferring to the server and won't be valid afterward. + // Plus the binder objects are threadsafe. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock( + service, cid, port, + )) + } + } + + /// Creates a binder RPC server, serving the supplied binder service implementation on the given + /// socket file descriptor. The socket should be bound to an address before calling this + /// function. + pub fn new_bound_socket( + mut service: SpIBinder, + socket_fd: OwnedFd, + ) -> Result<RpcServer, Error> { + let service = service.as_native_mut(); + + // SAFETY: Service ownership is transferring to the server and won't be valid afterward. + // Plus the binder objects are threadsafe. + // The server takes ownership of the socket FD. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newBoundSocket( + service, + socket_fd.into_raw_fd(), + )) + } + } + + /// Creates a binder RPC server that bootstraps sessions using an existing Unix domain socket + /// pair, with a given root IBinder object. Callers should create a pair of SOCK_STREAM Unix + /// domain sockets, pass one to the server and the other to the client. Multiple client session + /// can be created from the client end of the pair. + pub fn new_unix_domain_bootstrap( + mut service: SpIBinder, + bootstrap_fd: OwnedFd, + ) -> Result<RpcServer, Error> { + let service = service.as_native_mut(); + + // SAFETY: Service ownership is transferring to the server and won't be valid afterward. + // Plus the binder objects are threadsafe. + // The server takes ownership of the bootstrap FD. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newUnixDomainBootstrap( + service, + bootstrap_fd.into_raw_fd(), + )) + } + } + + /// Creates a binder RPC server, serving the supplied binder service implementation on the given + /// IP address and port. + pub fn new_inet(mut service: SpIBinder, address: &str, port: u32) -> Result<RpcServer, Error> { + let address = match CString::new(address) { + Ok(s) => s, + Err(e) => { + log::error!("Cannot convert {} to CString. Error: {:?}", address, e); + return Err(Error::from(ErrorKind::InvalidInput)); + } + }; + let service = service.as_native_mut(); + + // SAFETY: Service ownership is transferring to the server and won't be valid afterward. + // Plus the binder objects are threadsafe. + unsafe { + Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInet( + service, + address.as_ptr(), + port, + )) + } + } + + unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> { + if ptr.is_null() { + return Err(Error::new(ErrorKind::Other, "Failed to start server")); + } + // SAFETY: Our caller must pass us a valid or null pointer, and we've checked that it's not + // null. + Ok(unsafe { RpcServer::from_ptr(ptr) }) + } +} + +impl RpcServerRef { + /// Sets the list of file descriptor transport modes supported by this server. + pub fn set_supported_file_descriptor_transport_modes( + &self, + modes: &[FileDescriptorTransportMode], + ) { + // SAFETY: Does not keep the pointer after returning does, nor does it + // read past its boundary. Only passes the 'self' pointer as an opaque handle. + unsafe { + binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes( + self.as_ptr(), + modes.as_ptr(), + modes.len(), + ) + } + } + + /// Sets the max number of threads this Server uses for incoming client connections. + /// + /// This must be called before adding a client session. This corresponds + /// to the number of incoming connections to RpcSession objects in the + /// server, which will correspond to the number of outgoing connections + /// in client RpcSession objects. Specifically this is useful for handling + /// client-side callback connections. + /// + /// If this is not specified, this will be a single-threaded server. + pub fn set_max_threads(&self, count: usize) { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. + unsafe { binder_rpc_unstable_bindgen::ARpcServer_setMaxThreads(self.as_ptr(), count) }; + } + + /// Starts a new background thread and calls join(). Returns immediately. + pub fn start(&self) { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. + unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) }; + } + + /// Joins the RpcServer thread. The call blocks until the server terminates. + /// This must be called from exactly one thread. + pub fn join(&self) { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. + unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) }; + } + + /// Shuts down the running RpcServer. Can be called multiple times and from + /// multiple threads. Called automatically during drop(). + pub fn shutdown(&self) -> Result<(), Error> { + // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer. + if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } { + Ok(()) + } else { + Err(Error::from(ErrorKind::UnexpectedEof)) + } + } +} diff --git a/libs/binder/rust/rpcbinder/src/server/trusty.rs b/libs/binder/rust/rpcbinder/src/server/trusty.rs new file mode 100644 index 0000000000..fe45decf25 --- /dev/null +++ b/libs/binder/rust/rpcbinder/src/server/trusty.rs @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use binder::{unstable_api::AsNative, SpIBinder}; +use libc::size_t; +use std::ffi::{c_char, c_void}; +use std::ptr; +use tipc::{ConnectResult, Handle, MessageResult, PortCfg, TipcError, UnbufferedService, Uuid}; + +pub trait PerSessionCallback: Fn(Uuid) -> Option<SpIBinder> + Send + Sync + 'static {} +impl<T> PerSessionCallback for T where T: Fn(Uuid) -> Option<SpIBinder> + Send + Sync + 'static {} + +pub struct RpcServer { + inner: *mut binder_rpc_server_bindgen::ARpcServerTrusty, +} + +/// SAFETY: The opaque handle points to a heap allocation +/// that should be process-wide and not tied to the current thread. +unsafe impl Send for RpcServer {} +/// SAFETY: The underlying C++ RpcServer class is thread-safe. +unsafe impl Sync for RpcServer {} + +impl Drop for RpcServer { + fn drop(&mut self) { + // SAFETY: `ARpcServerTrusty_delete` is the correct destructor to call + // on pointers returned by `ARpcServerTrusty_new`. + unsafe { + binder_rpc_server_bindgen::ARpcServerTrusty_delete(self.inner); + } + } +} + +impl RpcServer { + /// Allocates a new RpcServer object. + pub fn new(service: SpIBinder) -> RpcServer { + Self::new_per_session(move |_uuid| Some(service.clone())) + } + + /// Allocates a new per-session RpcServer object. + /// + /// Per-session objects take a closure that gets called once + /// for every new connection. The closure gets the UUID of + /// the peer and can accept or reject that connection. + pub fn new_per_session<F: PerSessionCallback>(f: F) -> RpcServer { + // SAFETY: Takes ownership of the returned handle, which has correct refcount. + let inner = unsafe { + binder_rpc_server_bindgen::ARpcServerTrusty_newPerSession( + Some(per_session_callback_wrapper::<F>), + Box::into_raw(Box::new(f)).cast(), + Some(per_session_callback_deleter::<F>), + ) + }; + RpcServer { inner } + } +} + +unsafe extern "C" fn per_session_callback_wrapper<F: PerSessionCallback>( + uuid_ptr: *const c_void, + len: size_t, + cb_ptr: *mut c_char, +) -> *mut binder_rpc_server_bindgen::AIBinder { + // SAFETY: This callback should only get called while the RpcServer is alive. + let cb = unsafe { &mut *cb_ptr.cast::<F>() }; + + if len != std::mem::size_of::<Uuid>() { + return ptr::null_mut(); + } + + // SAFETY: On the previous lines we check that we got exactly the right amount of bytes. + let uuid = unsafe { + let mut uuid = std::mem::MaybeUninit::<Uuid>::uninit(); + uuid.as_mut_ptr().copy_from(uuid_ptr.cast(), 1); + uuid.assume_init() + }; + + cb(uuid).map_or_else(ptr::null_mut, |b| { + // Prevent AIBinder_decStrong from being called before AIBinder_toPlatformBinder. + // The per-session callback in C++ is supposed to call AIBinder_decStrong on the + // pointer we return here. + std::mem::ManuallyDrop::new(b).as_native_mut().cast() + }) +} + +unsafe extern "C" fn per_session_callback_deleter<F: PerSessionCallback>(cb: *mut c_char) { + // SAFETY: shared_ptr calls this to delete the pointer we gave it. + // It should only get called once the last shared reference goes away. + unsafe { + drop(Box::<F>::from_raw(cb.cast())); + } +} + +pub struct RpcServerConnection { + ctx: *mut c_void, +} + +impl Drop for RpcServerConnection { + fn drop(&mut self) { + // We do not need to close handle_fd since we do not own it. + unsafe { + binder_rpc_server_bindgen::ARpcServerTrusty_handleChannelCleanup(self.ctx); + } + } +} + +impl UnbufferedService for RpcServer { + type Connection = RpcServerConnection; + + fn on_connect( + &self, + _port: &PortCfg, + handle: &Handle, + peer: &Uuid, + ) -> tipc::Result<ConnectResult<Self::Connection>> { + let mut conn = RpcServerConnection { ctx: std::ptr::null_mut() }; + let rc = unsafe { + binder_rpc_server_bindgen::ARpcServerTrusty_handleConnect( + self.inner, + handle.as_raw_fd(), + peer.as_ptr().cast(), + &mut conn.ctx, + ) + }; + if rc < 0 { + Err(TipcError::from_uapi(rc.into())) + } else { + Ok(ConnectResult::Accept(conn)) + } + } + + fn on_message( + &self, + conn: &Self::Connection, + _handle: &Handle, + buffer: &mut [u8], + ) -> tipc::Result<MessageResult> { + assert!(buffer.is_empty()); + let rc = unsafe { binder_rpc_server_bindgen::ARpcServerTrusty_handleMessage(conn.ctx) }; + if rc < 0 { + Err(TipcError::from_uapi(rc.into())) + } else { + Ok(MessageResult::MaintainConnection) + } + } + + fn on_disconnect(&self, conn: &Self::Connection) { + unsafe { binder_rpc_server_bindgen::ARpcServerTrusty_handleDisconnect(conn.ctx) }; + } +} diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 16049f28c5..e70f4f0232 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -100,7 +100,9 @@ mod error; mod native; mod parcel; mod proxy; -#[cfg(not(target_os = "trusty"))] +#[cfg(not(trusty))] +mod service; +#[cfg(not(trusty))] mod state; use binder_ndk_sys as sys; @@ -108,16 +110,15 @@ use binder_ndk_sys as sys; pub use crate::binder_async::{BinderAsyncPool, BoxFuture}; pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak}; pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode}; -pub use native::{ - add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, - LazyServiceGuard, -}; pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder}; -pub use proxy::{ - get_declared_instances, get_interface, get_service, is_declared, wait_for_interface, - wait_for_service, DeathRecipient, SpIBinder, WpIBinder, +pub use proxy::{DeathRecipient, SpIBinder, WpIBinder}; +#[cfg(not(trusty))] +pub use service::{ + add_service, check_interface, check_service, force_lazy_services_persist, + get_declared_instances, get_interface, get_service, is_declared, is_handling_transaction, + register_lazy_service, wait_for_interface, wait_for_service, LazyServiceGuard, }; -#[cfg(not(target_os = "trusty"))] +#[cfg(not(trusty))] pub use state::{ProcessState, ThreadState}; /// Binder result containing a [`Status`] on error. diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index 8ae010ea88..c87cc94973 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -23,12 +23,11 @@ use crate::proxy::SpIBinder; use crate::sys; use std::convert::TryFrom; -use std::ffi::{c_void, CStr, CString}; +use std::ffi::{c_void, CStr}; use std::io::Write; use std::mem::ManuallyDrop; use std::ops::Deref; use std::os::raw::c_char; -use std::sync::Mutex; /// Rust wrapper around Binder remotable objects. /// @@ -328,7 +327,7 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { /// contains a `T` pointer in its user data. fd should be a non-owned file /// descriptor, and args must be an array of null-terminated string /// pointers with length num_args. - #[cfg(not(target_os = "trusty"))] + #[cfg(not(trusty))] unsafe extern "C" fn on_dump( binder: *mut sys::AIBinder, fd: i32, @@ -375,7 +374,7 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { } /// Called to handle the `dump` transaction. - #[cfg(target_os = "trusty")] + #[cfg(trusty)] unsafe extern "C" fn on_dump( _binder: *mut sys::AIBinder, _fd: i32, @@ -462,110 +461,6 @@ unsafe impl<B: Remotable> AsNative<sys::AIBinder> for Binder<B> { } } -/// Register a new service with the default service manager. -/// -/// Registers the given binder object with the given identifier. If successful, -/// this service can then be retrieved using that identifier. -/// -/// This function will panic if the identifier contains a 0 byte (NUL). -pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> { - let instance = CString::new(identifier).unwrap(); - let status = - // Safety: `AServiceManager_addService` expects valid `AIBinder` and C - // string pointers. Caller retains ownership of both pointers. - // `AServiceManager_addService` creates a new strong reference and copies - // the string, so both pointers need only be valid until the call returns. - unsafe { sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) }; - status_result(status) -} - -/// Register a dynamic service via the LazyServiceRegistrar. -/// -/// Registers the given binder object with the given identifier. If successful, -/// this service can then be retrieved using that identifier. The service process -/// will be shut down once all registered services are no longer in use. -/// -/// If any service in the process is registered as lazy, all should be, otherwise -/// the process may be shut down while a service is in use. -/// -/// This function will panic if the identifier contains a 0 byte (NUL). -pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> { - let instance = CString::new(identifier).unwrap(); - // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C - // string pointers. Caller retains ownership of both - // pointers. `AServiceManager_registerLazyService` creates a new strong reference - // and copies the string, so both pointers need only be valid until the - // call returns. - let status = unsafe { - sys::AServiceManager_registerLazyService(binder.as_native_mut(), instance.as_ptr()) - }; - status_result(status) -} - -/// Prevent a process which registers lazy services from being shut down even when none -/// of the services is in use. -/// -/// If persist is true then shut down will be blocked until this function is called again with -/// persist false. If this is to be the initial state, call this function before calling -/// register_lazy_service. -/// -/// Consider using [`LazyServiceGuard`] rather than calling this directly. -pub fn force_lazy_services_persist(persist: bool) { - // Safety: No borrowing or transfer of ownership occurs here. - unsafe { sys::AServiceManager_forceLazyServicesPersist(persist) } -} - -/// An RAII object to ensure a process which registers lazy services is not killed. During the -/// lifetime of any of these objects the service manager will not not kill the process even if none -/// of its lazy services are in use. -#[must_use] -#[derive(Debug)] -pub struct LazyServiceGuard { - // Prevent construction outside this module. - _private: (), -} - -// Count of how many LazyServiceGuard objects are in existence. -static GUARD_COUNT: Mutex<u64> = Mutex::new(0); - -impl LazyServiceGuard { - /// Create a new LazyServiceGuard to prevent the service manager prematurely killing this - /// process. - pub fn new() -> Self { - let mut count = GUARD_COUNT.lock().unwrap(); - *count += 1; - if *count == 1 { - // It's important that we make this call with the mutex held, to make sure - // that multiple calls (e.g. if the count goes 1 -> 0 -> 1) are correctly - // sequenced. (That also means we can't just use an AtomicU64.) - force_lazy_services_persist(true); - } - Self { _private: () } - } -} - -impl Drop for LazyServiceGuard { - fn drop(&mut self) { - let mut count = GUARD_COUNT.lock().unwrap(); - *count -= 1; - if *count == 0 { - force_lazy_services_persist(false); - } - } -} - -impl Clone for LazyServiceGuard { - fn clone(&self) -> Self { - Self::new() - } -} - -impl Default for LazyServiceGuard { - fn default() -> Self { - Self::new() - } -} - /// Tests often create a base BBinder instance; so allowing the unit /// type to be remotable translates nicely to Binder::new(()). impl Remotable for () { @@ -590,10 +485,3 @@ impl Remotable for () { } impl Interface for () {} - -/// Determine whether the current thread is currently executing an incoming -/// transaction. -pub fn is_handling_transaction() -> bool { - // Safety: This method is always safe to call. - unsafe { sys::AIBinder_isHandlingTransaction() } -} diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index 7434e9ddbd..340014aeaa 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -29,11 +29,10 @@ use crate::sys; use std::cmp::Ordering; use std::convert::TryInto; -use std::ffi::{c_void, CStr, CString}; +use std::ffi::{c_void, CString}; use std::fmt; use std::mem; use std::os::fd::AsRawFd; -use std::os::raw::c_char; use std::ptr; use std::sync::Arc; @@ -129,14 +128,6 @@ impl SpIBinder { } } -fn interface_cast<T: FromIBinder + ?Sized>(service: Option<SpIBinder>) -> Result<Strong<T>> { - if let Some(service) = service { - FromIBinder::try_from(service) - } else { - Err(StatusCode::NAME_NOT_FOUND) - } -} - pub mod unstable_api { use super::{sys, SpIBinder}; @@ -739,93 +730,6 @@ unsafe impl<T: Proxy> AsNative<sys::AIBinder> for T { } } -/// Retrieve an existing service, blocking for a few seconds if it doesn't yet -/// exist. -pub fn get_service(name: &str) -> Option<SpIBinder> { - let name = CString::new(name).ok()?; - // Safety: `AServiceManager_getService` returns either a null pointer or a - // valid pointer to an owned `AIBinder`. Either of these values is safe to - // pass to `SpIBinder::from_raw`. - unsafe { SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) } -} - -/// Retrieve an existing service, or start it if it is configured as a dynamic -/// service and isn't yet started. -pub fn wait_for_service(name: &str) -> Option<SpIBinder> { - let name = CString::new(name).ok()?; - // Safety: `AServiceManager_waitforService` returns either a null pointer or - // a valid pointer to an owned `AIBinder`. Either of these values is safe to - // pass to `SpIBinder::from_raw`. - unsafe { SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr())) } -} - -/// Retrieve an existing service for a particular interface, blocking for a few -/// seconds if it doesn't yet exist. -pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> { - interface_cast(get_service(name)) -} - -/// Retrieve an existing service for a particular interface, or start it if it -/// is configured as a dynamic service and isn't yet started. -pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> { - interface_cast(wait_for_service(name)) -} - -/// Check if a service is declared (e.g. in a VINTF manifest) -pub fn is_declared(interface: &str) -> Result<bool> { - let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?; - - // Safety: `interface` is a valid null-terminated C-style string and is only - // borrowed for the lifetime of the call. The `interface` local outlives - // this call as it lives for the function scope. - unsafe { Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) } -} - -/// Retrieve all declared instances for a particular interface -/// -/// For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo' -/// is passed here, then ["foo"] would be returned. -pub fn get_declared_instances(interface: &str) -> Result<Vec<String>> { - unsafe extern "C" fn callback(instance: *const c_char, opaque: *mut c_void) { - // Safety: opaque was a mutable pointer created below from a Vec of - // CString, and outlives this callback. The null handling here is just - // to avoid the possibility of unwinding across C code if this crate is - // ever compiled with panic=unwind. - if let Some(instances) = unsafe { opaque.cast::<Vec<CString>>().as_mut() } { - // Safety: instance is a valid null-terminated C string with a - // lifetime at least as long as this function, and we immediately - // copy it into an owned CString. - unsafe { - instances.push(CStr::from_ptr(instance).to_owned()); - } - } else { - eprintln!("Opaque pointer was null in get_declared_instances callback!"); - } - } - - let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?; - let mut instances: Vec<CString> = vec![]; - // Safety: `interface` and `instances` are borrowed for the length of this - // call and both outlive the call. `interface` is guaranteed to be a valid - // null-terminated C-style string. - unsafe { - sys::AServiceManager_forEachDeclaredInstance( - interface.as_ptr(), - &mut instances as *mut _ as *mut c_void, - Some(callback), - ); - } - - instances - .into_iter() - .map(CString::into_string) - .collect::<std::result::Result<Vec<String>, _>>() - .map_err(|e| { - eprintln!("An interface instance name was not a valid UTF-8 string: {}", e); - StatusCode::BAD_VALUE - }) -} - /// Safety: `SpIBinder` guarantees that `binder` always contains a valid pointer /// to an `AIBinder`, so we can trivially extract this pointer here. unsafe impl AsNative<sys::AIBinder> for SpIBinder { diff --git a/libs/binder/rust/src/service.rs b/libs/binder/rust/src/service.rs new file mode 100644 index 0000000000..29dd8e1f58 --- /dev/null +++ b/libs/binder/rust/src/service.rs @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2024 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. + */ + +use crate::binder::{AsNative, FromIBinder, Strong}; +use crate::error::{status_result, Result, StatusCode}; +use crate::proxy::SpIBinder; +use crate::sys; + +use std::ffi::{c_void, CStr, CString}; +use std::os::raw::c_char; +use std::sync::Mutex; + +/// Register a new service with the default service manager. +/// +/// Registers the given binder object with the given identifier. If successful, +/// this service can then be retrieved using that identifier. +/// +/// This function will panic if the identifier contains a 0 byte (NUL). +pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> { + let instance = CString::new(identifier).unwrap(); + let status = + // Safety: `AServiceManager_addService` expects valid `AIBinder` and C + // string pointers. Caller retains ownership of both pointers. + // `AServiceManager_addService` creates a new strong reference and copies + // the string, so both pointers need only be valid until the call returns. + unsafe { sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) }; + status_result(status) +} + +/// Register a dynamic service via the LazyServiceRegistrar. +/// +/// Registers the given binder object with the given identifier. If successful, +/// this service can then be retrieved using that identifier. The service process +/// will be shut down once all registered services are no longer in use. +/// +/// If any service in the process is registered as lazy, all should be, otherwise +/// the process may be shut down while a service is in use. +/// +/// This function will panic if the identifier contains a 0 byte (NUL). +pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> { + let instance = CString::new(identifier).unwrap(); + // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C + // string pointers. Caller retains ownership of both + // pointers. `AServiceManager_registerLazyService` creates a new strong reference + // and copies the string, so both pointers need only be valid until the + // call returns. + let status = unsafe { + sys::AServiceManager_registerLazyService(binder.as_native_mut(), instance.as_ptr()) + }; + status_result(status) +} + +/// Prevent a process which registers lazy services from being shut down even when none +/// of the services is in use. +/// +/// If persist is true then shut down will be blocked until this function is called again with +/// persist false. If this is to be the initial state, call this function before calling +/// register_lazy_service. +/// +/// Consider using [`LazyServiceGuard`] rather than calling this directly. +pub fn force_lazy_services_persist(persist: bool) { + // Safety: No borrowing or transfer of ownership occurs here. + unsafe { sys::AServiceManager_forceLazyServicesPersist(persist) } +} + +/// An RAII object to ensure a process which registers lazy services is not killed. During the +/// lifetime of any of these objects the service manager will not kill the process even if none +/// of its lazy services are in use. +#[must_use] +#[derive(Debug)] +pub struct LazyServiceGuard { + // Prevent construction outside this module. + _private: (), +} + +// Count of how many LazyServiceGuard objects are in existence. +static GUARD_COUNT: Mutex<u64> = Mutex::new(0); + +impl LazyServiceGuard { + /// Create a new LazyServiceGuard to prevent the service manager prematurely killing this + /// process. + pub fn new() -> Self { + let mut count = GUARD_COUNT.lock().unwrap(); + *count += 1; + if *count == 1 { + // It's important that we make this call with the mutex held, to make sure + // that multiple calls (e.g. if the count goes 1 -> 0 -> 1) are correctly + // sequenced. (That also means we can't just use an AtomicU64.) + force_lazy_services_persist(true); + } + Self { _private: () } + } +} + +impl Drop for LazyServiceGuard { + fn drop(&mut self) { + let mut count = GUARD_COUNT.lock().unwrap(); + *count -= 1; + if *count == 0 { + force_lazy_services_persist(false); + } + } +} + +impl Clone for LazyServiceGuard { + fn clone(&self) -> Self { + Self::new() + } +} + +impl Default for LazyServiceGuard { + fn default() -> Self { + Self::new() + } +} + +/// Determine whether the current thread is currently executing an incoming +/// transaction. +pub fn is_handling_transaction() -> bool { + // Safety: This method is always safe to call. + unsafe { sys::AIBinder_isHandlingTransaction() } +} + +fn interface_cast<T: FromIBinder + ?Sized>(service: Option<SpIBinder>) -> Result<Strong<T>> { + if let Some(service) = service { + FromIBinder::try_from(service) + } else { + Err(StatusCode::NAME_NOT_FOUND) + } +} + +/// Retrieve an existing service, blocking for a few seconds if it doesn't yet +/// exist. +#[deprecated = "this polls 5s, use wait_for_service or check_service"] +pub fn get_service(name: &str) -> Option<SpIBinder> { + let name = CString::new(name).ok()?; + // Safety: `AServiceManager_getService` returns either a null pointer or a + // valid pointer to an owned `AIBinder`. Either of these values is safe to + // pass to `SpIBinder::from_raw`. + unsafe { SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) } +} + +/// Retrieve an existing service. Returns `None` immediately if the service is not available. +pub fn check_service(name: &str) -> Option<SpIBinder> { + let name = CString::new(name).ok()?; + // Safety: `AServiceManager_checkService` returns either a null pointer or + // a valid pointer to an owned `AIBinder`. Either of these values is safe to + // pass to `SpIBinder::from_raw`. + unsafe { SpIBinder::from_raw(sys::AServiceManager_checkService(name.as_ptr())) } +} + +/// Retrieve an existing service, or start it if it is configured as a dynamic +/// service and isn't yet started. +pub fn wait_for_service(name: &str) -> Option<SpIBinder> { + let name = CString::new(name).ok()?; + // Safety: `AServiceManager_waitforService` returns either a null pointer or + // a valid pointer to an owned `AIBinder`. Either of these values is safe to + // pass to `SpIBinder::from_raw`. + unsafe { SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr())) } +} + +/// Retrieve an existing service for a particular interface, blocking for a few +/// seconds if it doesn't yet exist. +#[deprecated = "this polls 5s, use wait_for_interface or check_interface"] +pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> { + interface_cast(get_service(name)) +} + +/// Retrieve an existing service for a particular interface. Returns +/// `Err(StatusCode::NAME_NOT_FOUND)` immediately if the service is not available. +pub fn check_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> { + interface_cast(check_service(name)) +} + +/// Retrieve an existing service for a particular interface, or start it if it +/// is configured as a dynamic service and isn't yet started. +pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> { + interface_cast(wait_for_service(name)) +} + +/// Check if a service is declared (e.g. in a VINTF manifest) +pub fn is_declared(interface: &str) -> Result<bool> { + let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?; + + // Safety: `interface` is a valid null-terminated C-style string and is only + // borrowed for the lifetime of the call. The `interface` local outlives + // this call as it lives for the function scope. + unsafe { Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) } +} + +/// Retrieve all declared instances for a particular interface +/// +/// For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo' +/// is passed here, then ["foo"] would be returned. +pub fn get_declared_instances(interface: &str) -> Result<Vec<String>> { + unsafe extern "C" fn callback(instance: *const c_char, opaque: *mut c_void) { + // Safety: opaque was a mutable pointer created below from a Vec of + // CString, and outlives this callback. The null handling here is just + // to avoid the possibility of unwinding across C code if this crate is + // ever compiled with panic=unwind. + if let Some(instances) = unsafe { opaque.cast::<Vec<CString>>().as_mut() } { + // Safety: instance is a valid null-terminated C string with a + // lifetime at least as long as this function, and we immediately + // copy it into an owned CString. + unsafe { + instances.push(CStr::from_ptr(instance).to_owned()); + } + } else { + eprintln!("Opaque pointer was null in get_declared_instances callback!"); + } + } + + let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?; + let mut instances: Vec<CString> = vec![]; + // Safety: `interface` and `instances` are borrowed for the length of this + // call and both outlive the call. `interface` is guaranteed to be a valid + // null-terminated C-style string. + unsafe { + sys::AServiceManager_forEachDeclaredInstance( + interface.as_ptr(), + &mut instances as *mut _ as *mut c_void, + Some(callback), + ); + } + + instances + .into_iter() + .map(CString::into_string) + .collect::<std::result::Result<Vec<String>, _>>() + .map_err(|e| { + eprintln!("An interface instance name was not a valid UTF-8 string: {}", e); + StatusCode::BAD_VALUE + }) +} diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs index c5c847b874..5352473272 100644 --- a/libs/binder/rust/sys/lib.rs +++ b/libs/binder/rust/sys/lib.rs @@ -25,7 +25,9 @@ mod bindings { } // Trusty puts the full path to the auto-generated file in BINDGEN_INC_FILE -// and builds it with warnings-as-errors, so we need to use #[allow(bad_style)] +// and builds it with warnings-as-errors, so we need to use #[allow(bad_style)]. +// We need to use cfg(target_os) instead of cfg(trusty) here because of +// the difference between the two build systems, which we cannot mock. #[cfg(target_os = "trusty")] #[allow(bad_style)] mod bindings { diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index c87fa89756..15ae56fdd7 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -421,7 +421,7 @@ mod tests { } #[test] - fn check_services() { + fn check_get_service() { let mut sm = binder::get_service("manager").expect("Did not get manager binder service"); assert!(sm.is_binder_alive()); assert!(sm.ping_binder().is_ok()); @@ -445,7 +445,7 @@ mod tests { } #[tokio::test] - async fn check_services_async() { + async fn check_get_service_async() { let mut sm = binder::get_service("manager").expect("Did not get manager binder service"); assert!(sm.is_binder_alive()); assert!(sm.ping_binder().is_ok()); @@ -474,6 +474,62 @@ mod tests { } #[test] + fn check_check_service() { + let mut sm = binder::check_service("manager").expect("Did not find manager binder service"); + assert!(sm.is_binder_alive()); + assert!(sm.ping_binder().is_ok()); + + assert!(binder::check_service("this_service_does_not_exist").is_none()); + assert_eq!( + binder::check_interface::<dyn ITest>("this_service_does_not_exist").err(), + Some(StatusCode::NAME_NOT_FOUND) + ); + assert_eq!( + binder::check_interface::<dyn IATest<Tokio>>("this_service_does_not_exist").err(), + Some(StatusCode::NAME_NOT_FOUND) + ); + + // The service manager service isn't an ITest, so this must fail. + assert_eq!( + binder::check_interface::<dyn ITest>("manager").err(), + Some(StatusCode::BAD_TYPE) + ); + assert_eq!( + binder::check_interface::<dyn IATest<Tokio>>("manager").err(), + Some(StatusCode::BAD_TYPE) + ); + } + + #[tokio::test] + async fn check_check_service_async() { + let mut sm = binder::check_service("manager").expect("Did not find manager binder service"); + assert!(sm.is_binder_alive()); + assert!(sm.ping_binder().is_ok()); + + assert!(binder::check_service("this_service_does_not_exist").is_none()); + assert_eq!( + binder_tokio::check_interface::<dyn ITest>("this_service_does_not_exist").await.err(), + Some(StatusCode::NAME_NOT_FOUND) + ); + assert_eq!( + binder_tokio::check_interface::<dyn IATest<Tokio>>("this_service_does_not_exist") + .await + .err(), + Some(StatusCode::NAME_NOT_FOUND) + ); + + // The service manager service isn't an ITest, so this must fail. + assert_eq!( + binder_tokio::check_interface::<dyn ITest>("manager").await.err(), + Some(StatusCode::BAD_TYPE) + ); + assert_eq!( + binder_tokio::check_interface::<dyn IATest<Tokio>>("manager").await.err(), + Some(StatusCode::BAD_TYPE) + ); + } + + #[test] fn check_wait_for_service() { let mut sm = binder::wait_for_service("manager").expect("Did not get manager binder service"); diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 2f0987fd1a..6800a8d36c 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -200,9 +200,11 @@ cc_library_static { defaults: [ "binder_test_defaults", ], + header_libs: [ + "libbinder_headers_base", + ], shared_libs: [ "libbase", - "liblog", ], srcs: [ "FileUtils.cpp", @@ -435,6 +437,8 @@ cc_test { // Add the Trusty mock library as a fake dependency so it gets built required: [ "libbinder_on_trusty_mock", + "libbinder_ndk_on_trusty_mock", + "libbinder_rs_on_trusty_mock", "binderRpcTestService_on_trusty_mock", "binderRpcTest_on_trusty_mock", ], diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 0ee96e7317..1f61f1852a 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -115,6 +115,7 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_GET_SCHEDULING_POLICY, BINDER_LIB_TEST_NOP_TRANSACTION_WAIT, BINDER_LIB_TEST_GETPID, + BINDER_LIB_TEST_GETUID, BINDER_LIB_TEST_ECHO_VECTOR, BINDER_LIB_TEST_GET_NON_BLOCKING_FD, BINDER_LIB_TEST_REJECT_OBJECTS, @@ -505,10 +506,11 @@ TEST_F(BinderLibTest, Freeze) { // Pass test on devices where BINDER_FREEZE ioctl is not supported int ret = IPCThreadState::self()->freeze(pid, false, 0); - if (ret != 0) { + if (ret == -EINVAL) { GTEST_SKIP(); return; } + EXPECT_EQ(NO_ERROR, ret); EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0)); @@ -1477,6 +1479,86 @@ TEST_F(BinderLibTest, BinderProxyCount) { EXPECT_EQ(BpBinder::getBinderProxyCount(), initialCount); } +static constexpr int kBpCountHighWatermark = 20; +static constexpr int kBpCountLowWatermark = 10; +static constexpr int kBpCountWarningWatermark = 15; +static constexpr int kInvalidUid = -1; + +TEST_F(BinderLibTest, BinderProxyCountCallback) { + Parcel data, reply; + sp<IBinder> server = addServer(); + ASSERT_NE(server, nullptr); + + BpBinder::enableCountByUid(); + EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETUID, data, &reply), StatusEq(NO_ERROR)); + int32_t uid = reply.readInt32(); + ASSERT_NE(uid, kInvalidUid); + + uint32_t initialCount = BpBinder::getBinderProxyCount(); + { + uint32_t count = initialCount; + BpBinder::setBinderProxyCountWatermarks(kBpCountHighWatermark, + kBpCountLowWatermark, + kBpCountWarningWatermark); + int limitCallbackUid = kInvalidUid; + int warningCallbackUid = kInvalidUid; + BpBinder::setBinderProxyCountEventCallback([&](int uid) { limitCallbackUid = uid; }, + [&](int uid) { warningCallbackUid = uid; }); + + std::vector<sp<IBinder> > proxies; + auto createProxyOnce = [&](int expectedWarningCallbackUid, int expectedLimitCallbackUid) { + warningCallbackUid = limitCallbackUid = kInvalidUid; + ASSERT_THAT(server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply), + StatusEq(NO_ERROR)); + proxies.push_back(reply.readStrongBinder()); + EXPECT_EQ(BpBinder::getBinderProxyCount(), ++count); + EXPECT_EQ(warningCallbackUid, expectedWarningCallbackUid); + EXPECT_EQ(limitCallbackUid, expectedLimitCallbackUid); + }; + auto removeProxyOnce = [&](int expectedWarningCallbackUid, int expectedLimitCallbackUid) { + warningCallbackUid = limitCallbackUid = kInvalidUid; + proxies.pop_back(); + EXPECT_EQ(BpBinder::getBinderProxyCount(), --count); + EXPECT_EQ(warningCallbackUid, expectedWarningCallbackUid); + EXPECT_EQ(limitCallbackUid, expectedLimitCallbackUid); + }; + + // Test the increment/decrement of the binder proxies. + for (int i = 1; i <= kBpCountWarningWatermark; i++) { + createProxyOnce(kInvalidUid, kInvalidUid); + } + createProxyOnce(uid, kInvalidUid); // Warning callback should have been triggered. + for (int i = kBpCountWarningWatermark + 2; i <= kBpCountHighWatermark; i++) { + createProxyOnce(kInvalidUid, kInvalidUid); + } + createProxyOnce(kInvalidUid, uid); // Limit callback should have been triggered. + createProxyOnce(kInvalidUid, kInvalidUid); + for (int i = kBpCountHighWatermark + 2; i >= kBpCountHighWatermark; i--) { + removeProxyOnce(kInvalidUid, kInvalidUid); + } + createProxyOnce(kInvalidUid, kInvalidUid); + + // Go down below the low watermark. + for (int i = kBpCountHighWatermark; i >= kBpCountLowWatermark; i--) { + removeProxyOnce(kInvalidUid, kInvalidUid); + } + for (int i = kBpCountLowWatermark; i <= kBpCountWarningWatermark; i++) { + createProxyOnce(kInvalidUid, kInvalidUid); + } + createProxyOnce(uid, kInvalidUid); // Warning callback should have been triggered. + for (int i = kBpCountWarningWatermark + 2; i <= kBpCountHighWatermark; i++) { + createProxyOnce(kInvalidUid, kInvalidUid); + } + createProxyOnce(kInvalidUid, uid); // Limit callback should have been triggered. + createProxyOnce(kInvalidUid, kInvalidUid); + for (int i = kBpCountHighWatermark + 2; i >= kBpCountHighWatermark; i--) { + removeProxyOnce(kInvalidUid, kInvalidUid); + } + createProxyOnce(kInvalidUid, kInvalidUid); + } + EXPECT_EQ(BpBinder::getBinderProxyCount(), initialCount); +} + class BinderLibRpcTestBase : public BinderLibTest { public: void SetUp() override { @@ -1680,6 +1762,9 @@ public: case BINDER_LIB_TEST_GETPID: reply->writeInt32(getpid()); return NO_ERROR; + case BINDER_LIB_TEST_GETUID: + reply->writeInt32(getuid()); + return NO_ERROR; case BINDER_LIB_TEST_NOP_TRANSACTION_WAIT: usleep(5000); [[fallthrough]]; diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h index 62fe9e56f6..8832f1a6ba 100644 --- a/libs/binder/tests/binderRpcTestCommon.h +++ b/libs/binder/tests/binderRpcTestCommon.h @@ -74,6 +74,12 @@ static inline std::vector<RpcSecurity> RpcSecurityValues() { } static inline bool hasExperimentalRpc() { +#ifdef BINDER_RPC_TO_TRUSTY_TEST + // Trusty services do not support the experimental version, + // so that we can update the prebuilts separately. + // This covers the binderRpcToTrustyTest case on Android. + return false; +#endif #ifdef __ANDROID__ return base::GetProperty("ro.build.version.codename", "") != "REL"; #else diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp index 885bb45d82..f4807806ad 100644 --- a/libs/binder/tests/binderRpcUniversalTests.cpp +++ b/libs/binder/tests/binderRpcUniversalTests.cpp @@ -48,11 +48,13 @@ TEST(BinderRpc, CannotUseNextWireVersion) { EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15)); } +#ifndef BINDER_RPC_TO_TRUSTY_TEST TEST(BinderRpc, CanUseExperimentalWireVersion) { auto session = RpcSession::make(); EXPECT_EQ(hasExperimentalRpc(), session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL)); } +#endif TEST_P(BinderRpc, Ping) { auto proc = createRpcTestSocketServerProcess({}); diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp index 10912c7363..f912348689 100644 --- a/libs/binder/tests/binderThroughputTest.cpp +++ b/libs/binder/tests/binderThroughputTest.cpp @@ -7,9 +7,10 @@ #include <cstdlib> #include <cstdio> +#include <fstream> #include <iostream> -#include <vector> #include <tuple> +#include <vector> #include <unistd.h> #include <sys/wait.h> @@ -63,6 +64,18 @@ struct ProcResults { uint64_t worst() { return *max_element(data.begin(), data.end()); } + void dump_to_file(string filename) { + ofstream output; + output.open(filename); + if (!output.is_open()) { + cerr << "Failed to open '" << filename << "'." << endl; + exit(EXIT_FAILURE); + } + for (uint64_t value : data) { + output << value << "\n"; + } + output.close(); + } void dump() { if (data.size() == 0) { // This avoids index-out-of-bounds below. @@ -293,12 +306,8 @@ void signal_all(vector<Pipe>& v) } } -void run_main(int iterations, - int workers, - int payload_size, - int cs_pair, - bool training_round=false) -{ +void run_main(int iterations, int workers, int payload_size, int cs_pair, + bool training_round = false, bool dump_to_file = false, string dump_filename = "") { vector<Pipe> pipes; // Create all the workers and wait for them to spawn. for (int i = 0; i < workers; i++) { @@ -349,6 +358,9 @@ void run_main(int iterations, warn_latency = 2 * tot_results.worst(); cout << "Max latency during training: " << tot_results.worst() / 1.0E6 << "ms" << endl; } else { + if (dump_to_file) { + tot_results.dump_to_file(dump_filename); + } tot_results.dump(); } } @@ -361,6 +373,8 @@ int main(int argc, char *argv[]) bool cs_pair = false; bool training_round = false; int max_time_us; + bool dump_to_file = false; + string dump_filename; // Parse arguments. for (int i = 1; i < argc; i++) { @@ -372,6 +386,7 @@ int main(int argc, char *argv[]) cout << "\t-s N : Specify payload size." << endl; cout << "\t-t : Run training round." << endl; cout << "\t-w N : Specify total number of workers." << endl; + cout << "\t-d FILE : Dump raw data to file." << endl; return 0; } if (string(argv[i]) == "-w") { @@ -430,14 +445,24 @@ int main(int argc, char *argv[]) i++; continue; } + if (string(argv[i]) == "-d") { + if (i + 1 == argc) { + cout << "-d requires an argument\n" << endl; + exit(EXIT_FAILURE); + } + dump_to_file = true; + dump_filename = argv[i + 1]; + i++; + continue; + } } if (training_round) { cout << "Start training round" << endl; - run_main(iterations, workers, payload_size, cs_pair, training_round=true); + run_main(iterations, workers, payload_size, cs_pair, true); cout << "Completed training round" << endl << endl; } - run_main(iterations, workers, payload_size, cs_pair); + run_main(iterations, workers, payload_size, cs_pair, false, dump_to_file, dump_filename); return 0; } diff --git a/libs/binder/tests/unit_fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp index a88158299e..6871cca3c1 100644 --- a/libs/binder/tests/unit_fuzzers/Android.bp +++ b/libs/binder/tests/unit_fuzzers/Android.bp @@ -52,6 +52,18 @@ cc_defaults { enabled: false, }, }, + fuzz_config: { + cc: [ + "smoreland@google.com", + "waghpawan@google.com", + ], + componentid: 32456, + description: "The fuzzer targets the APIs of libbinder", + vector: "local_no_privileges_required", + service_privilege: "privileged", + users: "multi_user", + fuzzed_code_usage: "shipped", + }, } cc_fuzz { diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h index 0a584bfe56..83d0ca745f 100644 --- a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h +++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h @@ -95,14 +95,16 @@ static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BpBind }, [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder, const sp<IBinder::DeathRecipient>&) -> void { - binder_proxy_limit_callback cb = binder_proxy_limit_callback(); - bpbinder->setLimitCallback(cb); + binder_proxy_limit_callback cbl = binder_proxy_limit_callback(); + binder_proxy_warning_callback cbw = binder_proxy_warning_callback(); + bpbinder->setBinderProxyCountEventCallback(cbl, cbw); }, [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder, const sp<IBinder::DeathRecipient>&) -> void { int high = fdp->ConsumeIntegral<int>(); int low = fdp->ConsumeIntegral<int>(); - bpbinder->setBinderProxyCountWatermarks(high, low); + int warning = fdp->ConsumeIntegral<int>(); + bpbinder->setBinderProxyCountWatermarks(high, low, warning); }}; } // namespace android diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp index 1f857a0edb..17919c2a25 100644 --- a/libs/binder/trusty/RpcServerTrusty.cpp +++ b/libs/binder/trusty/RpcServerTrusty.cpp @@ -60,7 +60,7 @@ sp<RpcServerTrusty> RpcServerTrusty::make( RpcServerTrusty::RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize) - : mRpcServer(sp<RpcServer>::make(std::move(ctx))), + : mRpcServer(makeRpcServer(std::move(ctx))), mPortName(std::move(portName)), mPortAcl(std::move(portAcl)) { mTipcPort.name = mPortName.c_str(); @@ -68,10 +68,6 @@ RpcServerTrusty::RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::stri mTipcPort.msg_queue_len = 6; // Three each way mTipcPort.priv = this; - // TODO(b/266741352): follow-up to prevent needing this in the future - // Trusty needs to be set to the latest stable version that is in prebuilts there. - LOG_ALWAYS_FATAL_IF(!mRpcServer->setProtocolVersion(0)); - if (mPortAcl) { // Initialize the array of pointers to uuids. // The pointers in mUuidPtrs should stay valid across moves of @@ -101,8 +97,13 @@ RpcServerTrusty::RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::stri int RpcServerTrusty::handleConnect(const tipc_port* port, handle_t chan, const uuid* peer, void** ctx_p) { auto* server = reinterpret_cast<RpcServerTrusty*>(const_cast<void*>(port->priv)); - server->mRpcServer->mShutdownTrigger = FdTrigger::make(); - server->mRpcServer->mConnectingThreads[rpc_this_thread::get_id()] = RpcMaybeThread(); + return handleConnectInternal(server->mRpcServer.get(), chan, peer, ctx_p); +} + +int RpcServerTrusty::handleConnectInternal(RpcServer* rpcServer, handle_t chan, const uuid* peer, + void** ctx_p) { + rpcServer->mShutdownTrigger = FdTrigger::make(); + rpcServer->mConnectingThreads[rpc_this_thread::get_id()] = RpcMaybeThread(); int rc = NO_ERROR; auto joinFn = [&](sp<RpcSession>&& session, RpcSession::PreJoinSetupResult&& result) { @@ -138,13 +139,17 @@ int RpcServerTrusty::handleConnect(const tipc_port* port, handle_t chan, const u std::array<uint8_t, RpcServer::kRpcAddressSize> addr; constexpr size_t addrLen = sizeof(*peer); memcpy(addr.data(), peer, addrLen); - RpcServer::establishConnection(sp(server->mRpcServer), std::move(transportFd), addr, addrLen, - joinFn); + RpcServer::establishConnection(sp<RpcServer>::fromExisting(rpcServer), std::move(transportFd), + addr, addrLen, joinFn); return rc; } int RpcServerTrusty::handleMessage(const tipc_port* /*port*/, handle_t /*chan*/, void* ctx) { + return handleMessageInternal(ctx); +} + +int RpcServerTrusty::handleMessageInternal(void* ctx) { auto* channelContext = reinterpret_cast<ChannelContext*>(ctx); LOG_ALWAYS_FATAL_IF(channelContext == nullptr, "bad state: message received on uninitialized channel"); @@ -162,6 +167,10 @@ int RpcServerTrusty::handleMessage(const tipc_port* /*port*/, handle_t /*chan*/, } void RpcServerTrusty::handleDisconnect(const tipc_port* /*port*/, handle_t /*chan*/, void* ctx) { + return handleDisconnectInternal(ctx); +} + +void RpcServerTrusty::handleDisconnectInternal(void* ctx) { auto* channelContext = reinterpret_cast<ChannelContext*>(ctx); if (channelContext == nullptr) { // Connections marked "incoming" (outgoing from the server's side) diff --git a/libs/binder/trusty/build-config-usertests b/libs/binder/trusty/build-config-usertests index d0a1fbca49..72e5ff9b2a 100644 --- a/libs/binder/trusty/build-config-usertests +++ b/libs/binder/trusty/build-config-usertests @@ -16,4 +16,5 @@ [ porttest("com.android.trusty.binderRpcTest"), + porttest("com.android.trusty.rust.binder_rpc_test.test"), ] diff --git a/libs/binder/trusty/include/binder/ARpcServerTrusty.h b/libs/binder/trusty/include/binder/ARpcServerTrusty.h new file mode 100644 index 0000000000..c82268b47b --- /dev/null +++ b/libs/binder/trusty/include/binder/ARpcServerTrusty.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 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 <lib/tipc/tipc_srv.h> + +#if defined(__cplusplus) +extern "C" { +#endif + +struct AIBinder; +struct ARpcServerTrusty; + +struct ARpcServerTrusty* ARpcServerTrusty_newPerSession(struct AIBinder* (*)(const void*, size_t, + char*), + char*, void (*)(char*)); +void ARpcServerTrusty_delete(struct ARpcServerTrusty*); +int ARpcServerTrusty_handleConnect(struct ARpcServerTrusty*, handle_t, const struct uuid*, void**); +int ARpcServerTrusty_handleMessage(void*); +void ARpcServerTrusty_handleDisconnect(void*); +void ARpcServerTrusty_handleChannelCleanup(void*); + +#if defined(__cplusplus) +} +#endif diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h index f35d6c2252..fe44ea5e28 100644 --- a/libs/binder/trusty/include/binder/RpcServerTrusty.h +++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h @@ -16,6 +16,7 @@ #pragma once +#include <binder/ARpcServerTrusty.h> #include <binder/IBinder.h> #include <binder/RpcServer.h> #include <binder/RpcSession.h> @@ -88,6 +89,28 @@ private: explicit RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize); + // Internal helper that creates the RpcServer. + // This is used both from here and Rust. + static sp<RpcServer> makeRpcServer(std::unique_ptr<RpcTransportCtx> ctx) { + auto rpcServer = sp<RpcServer>::make(std::move(ctx)); + + // TODO(b/266741352): follow-up to prevent needing this in the future + // Trusty needs to be set to the latest stable version that is in prebuilts there. + LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(0)); + + return rpcServer; + } + + friend struct ::ARpcServerTrusty; + friend ::ARpcServerTrusty* ::ARpcServerTrusty_newPerSession(::AIBinder* (*)(const void*, size_t, + char*), + char*, void (*)(char*)); + friend void ::ARpcServerTrusty_delete(::ARpcServerTrusty*); + friend int ::ARpcServerTrusty_handleConnect(::ARpcServerTrusty*, handle_t, const uuid*, void**); + friend int ::ARpcServerTrusty_handleMessage(void*); + friend void ::ARpcServerTrusty_handleDisconnect(void*); + friend void ::ARpcServerTrusty_handleChannelCleanup(void*); + // The Rpc-specific context maintained for every open TIPC channel. struct ChannelContext { sp<RpcSession> session; @@ -99,6 +122,11 @@ private: static void handleDisconnect(const tipc_port* port, handle_t chan, void* ctx); static void handleChannelCleanup(void* ctx); + static int handleConnectInternal(RpcServer* rpcServer, handle_t chan, const uuid* peer, + void** ctx_p); + static int handleMessageInternal(void* ctx); + static void handleDisconnectInternal(void* ctx); + static constexpr tipc_srv_ops kTipcOps = { .on_connect = &handleConnect, .on_message = &handleMessage, diff --git a/libs/binder/trusty/ndk/Android.bp b/libs/binder/trusty/ndk/Android.bp new file mode 100644 index 0000000000..af9874a57a --- /dev/null +++ b/libs/binder/trusty/ndk/Android.bp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_library_headers { + name: "libbinder_trusty_ndk_headers", + export_include_dirs: ["include"], + host_supported: true, + vendor_available: true, +} diff --git a/libs/binder/trusty/ndk/include/android/llndk-versioning.h b/libs/binder/trusty/ndk/include/android/llndk-versioning.h new file mode 100644 index 0000000000..3ae3d8f577 --- /dev/null +++ b/libs/binder/trusty/ndk/include/android/llndk-versioning.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2024 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 + +#define __INTRODUCED_IN_LLNDK(x) /* nothing on Trusty */ diff --git a/libs/binder/trusty/ndk/include/sys/cdefs.h b/libs/binder/trusty/ndk/include/sys/cdefs.h index eabfe603bd..4e9b0e83c6 100644 --- a/libs/binder/trusty/ndk/include/sys/cdefs.h +++ b/libs/binder/trusty/ndk/include/sys/cdefs.h @@ -15,11 +15,15 @@ */ #pragma once +#if __has_include(<lk/compiler.h>) #include <lk/compiler.h> /* Alias the bionic macros to the ones from lk/compiler.h */ #define __BEGIN_DECLS __BEGIN_CDECLS #define __END_DECLS __END_CDECLS +#else // __has_include(<lk/compiler.h>) +#include_next <sys/cdefs.h> +#endif + #define __INTRODUCED_IN(x) /* nothing on Trusty */ -#define __INTRODUCED_IN_LLNDK(x) /* nothing on Trusty */ diff --git a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk index 672d9b75ec..2aaa061ddf 100644 --- a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk +++ b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk @@ -29,6 +29,10 @@ MODULE_LIBRARY_DEPS += \ $(LIBBINDER_DIR)/trusty/ndk \ trusty/user/base/lib/trusty-sys \ +MODULE_RUSTFLAGS += \ + --cfg 'android_vendor' \ + --cfg 'trusty' \ + MODULE_BINDGEN_SRC_HEADER := $(LIBBINDER_DIR)/rust/sys/BinderBindings.hpp # Add the flags from the flag file diff --git a/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp new file mode 100644 index 0000000000..451383a90a --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 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. + */ + +#include <android/binder_libbinder.h> +#include <binder/RpcServer.h> +#include <binder/RpcServerTrusty.h> +#include <binder/RpcSession.h> +#include <binder/RpcTransportTipcTrusty.h> + +using android::RpcServer; +using android::RpcServerTrusty; +using android::RpcSession; +using android::RpcTransportCtxFactoryTipcTrusty; +using android::sp; +using android::wp; + +struct ARpcServerTrusty { + sp<RpcServer> mRpcServer; + + ARpcServerTrusty() = delete; + ARpcServerTrusty(sp<RpcServer> rpcServer) : mRpcServer(std::move(rpcServer)) {} +}; + +ARpcServerTrusty* ARpcServerTrusty_newPerSession(AIBinder* (*cb)(const void*, size_t, char*), + char* cbArg, void (*cbArgDeleter)(char*)) { + std::shared_ptr<char> cbArgSp(cbArg, cbArgDeleter); + + auto rpcTransportCtxFactory = RpcTransportCtxFactoryTipcTrusty::make(); + if (rpcTransportCtxFactory == nullptr) { + return nullptr; + } + + auto ctx = rpcTransportCtxFactory->newServerCtx(); + if (ctx == nullptr) { + return nullptr; + } + + auto rpcServer = RpcServerTrusty::makeRpcServer(std::move(ctx)); + if (rpcServer == nullptr) { + return nullptr; + } + + rpcServer->setPerSessionRootObject( + [cb, cbArgSp](wp<RpcSession> /*session*/, const void* addrPtr, size_t len) { + auto* aib = (*cb)(addrPtr, len, cbArgSp.get()); + auto b = AIBinder_toPlatformBinder(aib); + + // We have a new sp<IBinder> backed by the same binder, so we can + // finally release the AIBinder* from the callback + AIBinder_decStrong(aib); + + return b; + }); + + return new (std::nothrow) ARpcServerTrusty(std::move(rpcServer)); +} + +void ARpcServerTrusty_delete(ARpcServerTrusty* rstr) { + delete rstr; +} + +int ARpcServerTrusty_handleConnect(ARpcServerTrusty* rstr, handle_t chan, const uuid* peer, + void** ctx_p) { + return RpcServerTrusty::handleConnectInternal(rstr->mRpcServer.get(), chan, peer, ctx_p); +} + +int ARpcServerTrusty_handleMessage(void* ctx) { + return RpcServerTrusty::handleMessageInternal(ctx); +} + +void ARpcServerTrusty_handleDisconnect(void* ctx) { + RpcServerTrusty::handleDisconnectInternal(ctx); +} + +void ARpcServerTrusty_handleChannelCleanup(void* ctx) { + RpcServerTrusty::handleChannelCleanup(ctx); +} diff --git a/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/rules.mk b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/rules.mk new file mode 100644 index 0000000000..6def6343fa --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/rules.mk @@ -0,0 +1,29 @@ +# Copyright (C) 2024 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../../../.. + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := \ + $(LOCAL_DIR)/ARpcServerTrusty.cpp \ + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty \ + $(LIBBINDER_DIR)/trusty/ndk \ + trusty/user/base/lib/libstdc++-trusty \ + +include make/library.mk diff --git a/libs/binder/trusty/rust/binder_rpc_server_bindgen/lib.rs b/libs/binder/trusty/rust/binder_rpc_server_bindgen/lib.rs new file mode 100644 index 0000000000..2e8b3ecb03 --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_server_bindgen/lib.rs @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 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. + */ + +//! Generated Rust bindings to binder_rpc_server + +#[allow(bad_style)] +mod sys { + include!(env!("BINDGEN_INC_FILE")); +} + +pub use sys::*; diff --git a/libs/binder/trusty/rust/binder_rpc_server_bindgen/rules.mk b/libs/binder/trusty/rust/binder_rpc_server_bindgen/rules.mk new file mode 100644 index 0000000000..4ee333f5be --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_server_bindgen/rules.mk @@ -0,0 +1,37 @@ +# Copyright (C) 2024 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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../../.. + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := $(LOCAL_DIR)/lib.rs + +MODULE_CRATE_NAME := binder_rpc_server_bindgen + +MODULE_LIBRARY_DEPS += \ + $(LOCAL_DIR)/cpp \ + trusty/user/base/lib/libstdc++-trusty \ + trusty/user/base/lib/trusty-sys \ + +MODULE_BINDGEN_SRC_HEADER := \ + $(LIBBINDER_DIR)/trusty/include/binder/ARpcServerTrusty.h + +MODULE_BINDGEN_FLAGS += \ + --allowlist-type="ARpcServerTrusty" \ + --allowlist-function="ARpcServerTrusty_.*" \ + +include make/library.mk diff --git a/libs/binder/trusty/rust/binder_rpc_test/aidl/rules.mk b/libs/binder/trusty/rust/binder_rpc_test/aidl/rules.mk new file mode 100644 index 0000000000..1b0dca09f4 --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_test/aidl/rules.mk @@ -0,0 +1,34 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_TESTS_DIR := $(LOCAL_DIR)/../../../../tests + +MODULE := $(LOCAL_DIR) + +MODULE_AIDL_LANGUAGE := rust + +MODULE_CRATE_NAME := binder_rpc_test_aidl + +MODULE_AIDLS := \ + $(LIBBINDER_TESTS_DIR)/BinderRpcTestClientInfo.aidl \ + $(LIBBINDER_TESTS_DIR)/BinderRpcTestServerConfig.aidl \ + $(LIBBINDER_TESTS_DIR)/BinderRpcTestServerInfo.aidl \ + $(LIBBINDER_TESTS_DIR)/IBinderRpcCallback.aidl \ + $(LIBBINDER_TESTS_DIR)/IBinderRpcSession.aidl \ + $(LIBBINDER_TESTS_DIR)/IBinderRpcTest.aidl \ + $(LIBBINDER_TESTS_DIR)/ParcelableCertificateData.aidl \ + +include make/aidl.mk diff --git a/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs new file mode 100644 index 0000000000..22cba44975 --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use binder::{Interface, ParcelFileDescriptor, SpIBinder, Status, StatusCode, Strong}; +use binder_rpc_test_aidl::aidl::IBinderRpcCallback::IBinderRpcCallback; +use binder_rpc_test_aidl::aidl::IBinderRpcSession::IBinderRpcSession; +use binder_rpc_test_aidl::aidl::IBinderRpcTest::IBinderRpcTest; +use std::sync::Mutex; + +static G_NUM: Mutex<i32> = Mutex::new(0); + +#[derive(Debug, Default)] +pub struct MyBinderRpcSession { + name: String, +} + +impl MyBinderRpcSession { + pub fn new(name: &str) -> Self { + Self::increment_instance_count(); + Self { name: name.to_string() } + } + + pub fn get_instance_count() -> i32 { + *G_NUM.lock().unwrap() + } + + fn increment_instance_count() { + *G_NUM.lock().unwrap() += 1; + } + + fn decrement_instance_count() { + *G_NUM.lock().unwrap() -= 1; + } +} + +impl Drop for MyBinderRpcSession { + fn drop(&mut self) { + MyBinderRpcSession::decrement_instance_count(); + } +} + +impl Interface for MyBinderRpcSession {} + +impl IBinderRpcSession for MyBinderRpcSession { + fn getName(&self) -> Result<String, Status> { + Ok(self.name.clone()) + } +} + +impl IBinderRpcTest for MyBinderRpcSession { + fn sendString(&self, _: &str) -> Result<(), Status> { + todo!() + } + fn doubleString(&self, _s: &str) -> Result<String, Status> { + todo!() + } + fn getClientPort(&self) -> Result<i32, Status> { + todo!() + } + fn countBinders(&self) -> Result<Vec<i32>, Status> { + todo!() + } + fn getNullBinder(&self) -> Result<SpIBinder, Status> { + todo!() + } + fn pingMe(&self, _binder: &SpIBinder) -> Result<i32, Status> { + todo!() + } + fn repeatBinder(&self, _binder: Option<&SpIBinder>) -> Result<Option<SpIBinder>, Status> { + todo!() + } + fn holdBinder(&self, _binder: Option<&SpIBinder>) -> Result<(), Status> { + todo!() + } + fn getHeldBinder(&self) -> Result<Option<SpIBinder>, Status> { + todo!() + } + fn nestMe( + &self, + binder: &Strong<(dyn IBinderRpcTest + 'static)>, + count: i32, + ) -> Result<(), Status> { + if count < 0 { + Ok(()) + } else { + binder.nestMe(binder, count - 1) + } + } + fn alwaysGiveMeTheSameBinder(&self) -> Result<SpIBinder, Status> { + todo!() + } + fn openSession( + &self, + _name: &str, + ) -> Result<Strong<(dyn IBinderRpcSession + 'static)>, Status> { + todo!() + } + fn getNumOpenSessions(&self) -> Result<i32, Status> { + todo!() + } + fn lock(&self) -> Result<(), Status> { + todo!() + } + fn unlockInMsAsync(&self, _: i32) -> Result<(), Status> { + todo!() + } + fn lockUnlock(&self) -> Result<(), Status> { + todo!() + } + fn sleepMs(&self, _: i32) -> Result<(), Status> { + todo!() + } + fn sleepMsAsync(&self, _: i32) -> Result<(), Status> { + todo!() + } + fn doCallback( + &self, + _: &Strong<(dyn IBinderRpcCallback + 'static)>, + _: bool, + _: bool, + _: &str, + ) -> Result<(), Status> { + todo!() + } + fn doCallbackAsync( + &self, + _: &Strong<(dyn IBinderRpcCallback + 'static)>, + _: bool, + _: bool, + _: &str, + ) -> Result<(), Status> { + todo!() + } + fn die(&self, _: bool) -> Result<(), Status> { + Err(Status::from(StatusCode::UNKNOWN_TRANSACTION)) + } + fn scheduleShutdown(&self) -> Result<(), Status> { + todo!() + } + fn useKernelBinderCallingId(&self) -> Result<(), Status> { + todo!() + } + fn echoAsFile(&self, _: &str) -> Result<ParcelFileDescriptor, Status> { + todo!() + } + fn concatFiles(&self, _: &[ParcelFileDescriptor]) -> Result<ParcelFileDescriptor, Status> { + todo!() + } + fn blockingSendFdOneway(&self, _: &ParcelFileDescriptor) -> Result<(), Status> { + todo!() + } + fn blockingRecvFd(&self) -> Result<ParcelFileDescriptor, Status> { + todo!() + } + fn blockingSendIntOneway(&self, _: i32) -> Result<(), Status> { + todo!() + } + fn blockingRecvInt(&self) -> Result<i32, Status> { + todo!() + } +} diff --git a/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/rules.mk b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/rules.mk new file mode 100644 index 0000000000..ae26355521 --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/rules.mk @@ -0,0 +1,32 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../../../.. + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := $(LOCAL_DIR)/lib.rs + +MODULE_CRATE_NAME := binder_rpc_test_session + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty/rust \ + $(LIBBINDER_DIR)/trusty/rust/rpcbinder \ + $(LOCAL_DIR)/../aidl \ + $(call FIND_CRATE,log) \ + trusty/user/base/lib/trusty-std \ + +include make/library.mk diff --git a/libs/binder/trusty/rust/binder_rpc_test/main.rs b/libs/binder/trusty/rust/binder_rpc_test/main.rs new file mode 100644 index 0000000000..baea5a827b --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_test/main.rs @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#![cfg(test)] + +use binder::{BinderFeatures, IBinder, Status, StatusCode, Strong}; +use binder_rpc_test_aidl::aidl::IBinderRpcSession::{BnBinderRpcSession, IBinderRpcSession}; +use binder_rpc_test_aidl::aidl::IBinderRpcTest::{BnBinderRpcTest, IBinderRpcTest}; +use binder_rpc_test_session::MyBinderRpcSession; +use libc::{clock_gettime, CLOCK_REALTIME}; +use rpcbinder::RpcSession; +use trusty_std::ffi::{CString, FallibleCString}; + +test::init!(); + +const SERVICE_PORT: &str = "com.android.trusty.binderRpcTestService.V1"; +const RUST_SERVICE_PORT: &str = "com.android.trusty.rust.binderRpcTestService.V1"; + +macro_rules! service_test { + ($c_name:ident, $rust_name:ident, $body:expr) => { + #[test] + fn $c_name() { + $body(get_service(SERVICE_PORT)) + } + #[test] + fn $rust_name() { + $body(get_service(RUST_SERVICE_PORT)) + } + }; +} + +fn get_service(port: &str) -> Strong<dyn IBinderRpcTest> { + let port = CString::try_new(port).expect("Failed to allocate port name"); + RpcSession::new().setup_trusty_client(port.as_c_str()).expect("Failed to create session") +} + +fn expect_sessions(expected: i32, srv: &Strong<dyn IBinderRpcTest>) { + let count = srv.getNumOpenSessions(); + assert!(count.is_ok()); + assert_eq!(expected, count.unwrap()); +} + +fn get_time_ns() -> u64 { + let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + + // Safety: Passing valid pointer to variable ts which lives past end of call + assert_eq!(unsafe { clock_gettime(CLOCK_REALTIME, &mut ts) }, 0); + + ts.tv_sec as u64 * 1_000_000_000u64 + ts.tv_nsec as u64 +} + +fn get_time_ms() -> u64 { + get_time_ns() / 1_000_000u64 +} + +// ---------- + +service_test! {ping, ping_rust, |srv: Strong<dyn IBinderRpcTest>| { + assert_eq!(srv.as_binder().ping_binder(), Ok(())); +}} + +service_test! {send_something_oneway, send_something_oneway_rust, |srv: Strong<dyn IBinderRpcTest>| { + assert_eq!(srv.sendString("Foo"), Ok(())); +}} + +service_test! {send_and_get_result_back, send_and_get_result_back_rust, |srv: Strong<dyn IBinderRpcTest>| { + assert_eq!(srv.doubleString("Foo"), Ok(String::from("FooFoo"))); +}} + +service_test! {send_and_get_result_back_big, send_and_get_result_back_big_rust, |srv: Strong<dyn IBinderRpcTest>| { + let single_len = 512; + let single = "a".repeat(single_len); + assert_eq!(srv.doubleString(&single), Ok(String::from(single.clone() + &single))); +}} + +service_test! {invalid_null_binder_return, invalid_null_binder_return_rust, |srv: Strong<dyn IBinderRpcTest>| { + let binder = srv.getNullBinder(); + assert!(binder == Err(Status::from(StatusCode::UNEXPECTED_NULL)) || binder == Err(Status::from(StatusCode::UNKNOWN_TRANSACTION))); +}} + +service_test! {call_me_back, call_me_back_rust, |srv: Strong<dyn IBinderRpcTest>| { + let binder = + BnBinderRpcSession::new_binder(MyBinderRpcSession::new("Foo"), BinderFeatures::default()) + .as_binder(); + let result = srv.pingMe(&binder); + assert_eq!(result, Ok(0)); +}} + +service_test! {repeat_binder, repeat_binder_rust, |srv: Strong<dyn IBinderRpcTest>| { + let in_binder = + BnBinderRpcSession::new_binder(MyBinderRpcSession::new("Foo"), BinderFeatures::default()) + .as_binder(); + let result = srv.repeatBinder(Some(&in_binder)); + assert_eq!(result.unwrap().unwrap(), in_binder); +}} + +service_test! {repeat_their_binder, repeat_their_binder_rust, |srv: Strong<dyn IBinderRpcTest>| { + let session = srv.openSession("Test"); + assert!(session.is_ok()); + + let in_binder = session.unwrap().as_binder(); + let out_binder = srv.repeatBinder(Some(&in_binder)); + assert_eq!(out_binder.unwrap().unwrap(), in_binder); +}} + +service_test! {hold_binder, hold_binder_rust, |srv: Strong<dyn IBinderRpcTest>| { + let name = "Foo"; + + let binder = + BnBinderRpcSession::new_binder(MyBinderRpcSession::new(name), BinderFeatures::default()) + .as_binder(); + assert!(srv.holdBinder(Some(&binder)).is_ok()); + + let held = srv.getHeldBinder(); + assert!(held.is_ok()); + let held = held.unwrap(); + assert!(held.is_some()); + let held = held.unwrap(); + assert_eq!(binder, held); + + let session = held.into_interface::<dyn IBinderRpcSession>(); + assert!(session.is_ok()); + + let session_name = session.unwrap().getName(); + assert!(session_name.is_ok()); + let session_name = session_name.unwrap(); + assert_eq!(session_name, name); + + assert!(srv.holdBinder(None).is_ok()); +}} + +service_test! {nested_transactions, nested_transactions_rust, |srv: Strong<dyn IBinderRpcTest>| { + let binder = + BnBinderRpcTest::new_binder(MyBinderRpcSession::new("Nest"), BinderFeatures::default()); + assert!(srv.nestMe(&binder, 10).is_ok()); +}} + +service_test! {same_binder_equality, same_binder_equality_rust, |srv: Strong<dyn IBinderRpcTest>| { + let a = srv.alwaysGiveMeTheSameBinder(); + assert!(a.is_ok()); + + let b = srv.alwaysGiveMeTheSameBinder(); + assert!(b.is_ok()); + + assert_eq!(a.unwrap(), b.unwrap()); +}} + +service_test! {single_session, single_session_rust, |srv: Strong<dyn IBinderRpcTest>| { + let session = srv.openSession("aoeu"); + assert!(session.is_ok()); + let session = session.unwrap(); + let name = session.getName(); + assert!(name.is_ok()); + assert_eq!(name.unwrap(), "aoeu"); + + let count = srv.getNumOpenSessions(); + assert!(count.is_ok()); + assert_eq!(count.unwrap(), 1); + + drop(session); + let count = srv.getNumOpenSessions(); + assert!(count.is_ok()); + assert_eq!(count.unwrap(), 0); +}} + +service_test! {many_session, many_session_rust, |srv: Strong<dyn IBinderRpcTest>| { + let mut sessions = Vec::new(); + + for i in 0..15 { + expect_sessions(i, &srv); + + let session = srv.openSession(&(i.to_string())); + assert!(session.is_ok()); + sessions.push(session.unwrap()); + } + + expect_sessions(sessions.len() as i32, &srv); + + for i in 0..sessions.len() { + let name = sessions[i].getName(); + assert!(name.is_ok()); + assert_eq!(name.unwrap(), i.to_string()); + } + + expect_sessions(sessions.len() as i32, &srv); + + while !sessions.is_empty() { + sessions.pop(); + + expect_sessions(sessions.len() as i32, &srv); + } + + expect_sessions(0, &srv); +}} + +service_test! {one_way_call_does_not_wait, one_way_call_does_not_wait_rust, |srv: Strong<dyn IBinderRpcTest>| { + let really_long_time_ms = 100; + let sleep_ms = really_long_time_ms * 5; + + let before = get_time_ms(); + let _ = srv.sleepMsAsync(sleep_ms); + let after = get_time_ms(); + + assert!(after < before + really_long_time_ms as u64); +}} diff --git a/libs/binder/trusty/rust/binder_rpc_test/manifest.json b/libs/binder/trusty/rust/binder_rpc_test/manifest.json new file mode 100644 index 0000000000..384ed441c6 --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_test/manifest.json @@ -0,0 +1,9 @@ +{ + "uuid": "91eed949-8a9e-4569-9c83-5935fb624025", + "app_name": "rust_binder_rpc_test", + "min_heap": 32768, + "min_stack": 16384, + "mgmt_flags": { + "non_critical_app": true + } +} diff --git a/libs/binder/trusty/rust/binder_rpc_test/rules.mk b/libs/binder/trusty/rust/binder_rpc_test/rules.mk new file mode 100644 index 0000000000..8347a359db --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_test/rules.mk @@ -0,0 +1,37 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../../.. + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := $(LOCAL_DIR)/main.rs + +MODULE_CRATE_NAME := binder_rpc_test + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty/rust \ + $(LIBBINDER_DIR)/trusty/rust/rpcbinder \ + $(LOCAL_DIR)/aidl \ + $(LOCAL_DIR)/binder_rpc_test_session \ + $(call FIND_CRATE,log) \ + trusty/user/base/lib/trusty-std \ + +MODULE_RUST_TESTS := true + +MANIFEST := $(LOCAL_DIR)/manifest.json + +include make/library.mk diff --git a/libs/binder/trusty/rust/binder_rpc_test/service/main.rs b/libs/binder/trusty/rust/binder_rpc_test/service/main.rs new file mode 100644 index 0000000000..c4a758a214 --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_test/service/main.rs @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use binder::{ + BinderFeatures, IBinder, Interface, ParcelFileDescriptor, SpIBinder, Status, StatusCode, Strong, +}; +use binder_rpc_test_aidl::aidl::IBinderRpcCallback::IBinderRpcCallback; +use binder_rpc_test_aidl::aidl::IBinderRpcSession::{BnBinderRpcSession, IBinderRpcSession}; +use binder_rpc_test_aidl::aidl::IBinderRpcTest::{BnBinderRpcTest, IBinderRpcTest}; +use binder_rpc_test_session::MyBinderRpcSession; +use libc::{c_long, nanosleep, timespec}; +use rpcbinder::RpcServer; +use std::rc::Rc; +use std::sync::Mutex; +use tipc::{service_dispatcher, wrap_service, Manager, PortCfg}; + +const RUST_SERVICE_PORT: &str = "com.android.trusty.rust.binderRpcTestService.V1"; + +// ----------------------------------------------------------------------------- + +static SESSION_COUNT: Mutex<i32> = Mutex::new(0); +static HOLD_BINDER: Mutex<Option<SpIBinder>> = Mutex::new(None); +static SAME_BINDER: Mutex<Option<SpIBinder>> = Mutex::new(None); + +#[derive(Debug, Default)] +struct TestService { + port: i32, + name: String, +} + +#[allow(dead_code)] +impl TestService { + fn new(name: &str) -> Self { + *SESSION_COUNT.lock().unwrap() += 1; + Self { name: name.to_string(), ..Default::default() } + } + + fn get_instance_count() -> i32 { + *SESSION_COUNT.lock().unwrap() + } +} + +impl Drop for TestService { + fn drop(&mut self) { + *SESSION_COUNT.lock().unwrap() -= 1; + } +} + +impl Interface for TestService {} + +impl IBinderRpcSession for TestService { + fn getName(&self) -> Result<String, Status> { + Ok(self.name.clone()) + } +} + +impl IBinderRpcTest for TestService { + fn sendString(&self, _: &str) -> Result<(), Status> { + // This is a oneway function, so caller returned immediately and gives back an Ok(()) regardless of what this returns + Ok(()) + } + fn doubleString(&self, s: &str) -> Result<String, Status> { + let ss = [s, s].concat(); + Ok(ss) + } + fn getClientPort(&self) -> Result<i32, Status> { + Ok(self.port) + } + fn countBinders(&self) -> Result<Vec<i32>, Status> { + todo!() + } + fn getNullBinder(&self) -> Result<SpIBinder, Status> { + Err(Status::from(StatusCode::UNKNOWN_TRANSACTION)) + } + fn pingMe(&self, binder: &SpIBinder) -> Result<i32, Status> { + match binder.clone().ping_binder() { + Ok(()) => Ok(StatusCode::OK as i32), + Err(e) => Err(Status::from(e)), + } + } + fn repeatBinder(&self, binder: Option<&SpIBinder>) -> Result<Option<SpIBinder>, Status> { + match binder { + Some(x) => Ok(Some(x.clone())), + None => Err(Status::from(StatusCode::BAD_VALUE)), + } + } + fn holdBinder(&self, binder: Option<&SpIBinder>) -> Result<(), Status> { + *HOLD_BINDER.lock().unwrap() = binder.cloned(); + Ok(()) + } + fn getHeldBinder(&self) -> Result<Option<SpIBinder>, Status> { + Ok((*HOLD_BINDER.lock().unwrap()).clone()) + } + fn nestMe( + &self, + binder: &Strong<(dyn IBinderRpcTest + 'static)>, + count: i32, + ) -> Result<(), Status> { + if count < 0 { + Ok(()) + } else { + binder.nestMe(binder, count - 1) + } + } + fn alwaysGiveMeTheSameBinder(&self) -> Result<SpIBinder, Status> { + let mut locked = SAME_BINDER.lock().unwrap(); + Ok((*locked) + .get_or_insert_with(|| { + BnBinderRpcTest::new_binder(TestService::default(), BinderFeatures::default()) + .as_binder() + }) + .clone()) + } + fn openSession(&self, name: &str) -> Result<Strong<(dyn IBinderRpcSession + 'static)>, Status> { + let s = BnBinderRpcSession::new_binder( + MyBinderRpcSession::new(name), + BinderFeatures::default(), + ); + Ok(s) + } + fn getNumOpenSessions(&self) -> Result<i32, Status> { + let count = MyBinderRpcSession::get_instance_count(); + Ok(count) + } + fn lock(&self) -> Result<(), Status> { + todo!() + } + fn unlockInMsAsync(&self, _: i32) -> Result<(), Status> { + todo!() + } + fn lockUnlock(&self) -> Result<(), Status> { + todo!() + } + fn sleepMs(&self, ms: i32) -> Result<(), Status> { + let ts = timespec { + tv_sec: (ms / 1000) as c_long, + tv_nsec: (ms % 1000) as c_long * 1_000_000 as c_long, + }; + + let mut rem = timespec { tv_sec: 0, tv_nsec: 0 }; + + // Safety: Passing valid pointers to variables ts & rem which live past end of call + assert_eq!(unsafe { nanosleep(&ts, &mut rem) }, 0); + + Ok(()) + } + fn sleepMsAsync(&self, ms: i32) -> Result<(), Status> { + self.sleepMs(ms) + } + fn doCallback( + &self, + _: &Strong<(dyn IBinderRpcCallback + 'static)>, + _: bool, + _: bool, + _: &str, + ) -> Result<(), Status> { + todo!() + } + fn doCallbackAsync( + &self, + _: &Strong<(dyn IBinderRpcCallback + 'static)>, + _: bool, + _: bool, + _: &str, + ) -> Result<(), Status> { + todo!() + } + fn die(&self, _: bool) -> Result<(), Status> { + Err(Status::from(StatusCode::UNKNOWN_TRANSACTION)) + } + fn scheduleShutdown(&self) -> Result<(), Status> { + todo!() + } + fn useKernelBinderCallingId(&self) -> Result<(), Status> { + todo!() + } + fn echoAsFile(&self, _: &str) -> Result<ParcelFileDescriptor, Status> { + todo!() + } + fn concatFiles(&self, _: &[ParcelFileDescriptor]) -> Result<ParcelFileDescriptor, Status> { + todo!() + } + fn blockingSendFdOneway(&self, _: &ParcelFileDescriptor) -> Result<(), Status> { + todo!() + } + fn blockingRecvFd(&self) -> Result<ParcelFileDescriptor, Status> { + todo!() + } + fn blockingSendIntOneway(&self, _: i32) -> Result<(), Status> { + todo!() + } + fn blockingRecvInt(&self) -> Result<i32, Status> { + todo!() + } +} + +wrap_service!(TestRpcServer(RpcServer: UnbufferedService)); + +service_dispatcher! { + enum TestDispatcher { + TestRpcServer, + } +} + +fn main() { + let mut dispatcher = TestDispatcher::<1>::new().expect("Could not create test dispatcher"); + + let service = BnBinderRpcTest::new_binder(TestService::default(), BinderFeatures::default()); + let rpc_server = + TestRpcServer::new(RpcServer::new_per_session(move |_uuid| Some(service.as_binder()))); + + let cfg = PortCfg::new(RUST_SERVICE_PORT) + .expect("Could not create port config") + .allow_ta_connect() + .allow_ns_connect(); + dispatcher.add_service(Rc::new(rpc_server), cfg).expect("Could not add service to dispatcher"); + + Manager::<_, _, 1, 4>::new_with_dispatcher(dispatcher, []) + .expect("Could not create service manager") + .run_event_loop() + .expect("Test event loop failed"); +} diff --git a/libs/binder/trusty/rust/binder_rpc_test/service/manifest.json b/libs/binder/trusty/rust/binder_rpc_test/service/manifest.json new file mode 100644 index 0000000000..121ba112a8 --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_test/service/manifest.json @@ -0,0 +1,10 @@ +{ + "uuid": "4741fc65-8b65-4893-ba55-b182c003c8b7", + "app_name": "rust_binder_rpc_test_service", + "min_heap": 16384, + "min_stack": 16384, + "mgmt_flags": { + "non_critical_app": true, + "restart_on_exit": true + } +} diff --git a/libs/binder/trusty/rust/binder_rpc_test/service/rules.mk b/libs/binder/trusty/rust/binder_rpc_test/service/rules.mk new file mode 100644 index 0000000000..f71ee9bcbd --- /dev/null +++ b/libs/binder/trusty/rust/binder_rpc_test/service/rules.mk @@ -0,0 +1,36 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) +LIBBINDER_DIR := $(LOCAL_DIR)/../../../.. + +MODULE := $(LOCAL_DIR) + +MODULE_SRCS := $(LOCAL_DIR)/main.rs + +MODULE_CRATE_NAME := binder_rpc_test_service + +MODULE_LIBRARY_DEPS += \ + $(LIBBINDER_DIR)/trusty/rust \ + $(LIBBINDER_DIR)/trusty/rust/rpcbinder \ + $(LIBBINDER_DIR)/trusty/rust/binder_rpc_server \ + $(LOCAL_DIR)/../aidl \ + $(LOCAL_DIR)/../binder_rpc_test_session \ + $(LOCAL_DIR)/.. \ + trusty/user/base/lib/tipc/rust \ + +MANIFEST := $(LOCAL_DIR)/manifest.json + +include make/trusted_app.mk diff --git a/libs/binder/trusty/rust/rpcbinder/rules.mk b/libs/binder/trusty/rust/rpcbinder/rules.mk index 76f3b9401f..97f5c03cba 100644 --- a/libs/binder/trusty/rust/rpcbinder/rules.mk +++ b/libs/binder/trusty/rust/rpcbinder/rules.mk @@ -28,6 +28,8 @@ MODULE_LIBRARY_DEPS += \ $(LIBBINDER_DIR)/trusty/rust \ $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \ $(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \ + $(LIBBINDER_DIR)/trusty/rust/binder_rpc_server_bindgen \ + external/rust/crates/cfg-if \ external/rust/crates/foreign-types \ trusty/user/base/lib/tipc/rust \ trusty/user/base/lib/trusty-sys \ diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk index d343f14240..36bd3a2e75 100644 --- a/libs/binder/trusty/rust/rules.mk +++ b/libs/binder/trusty/rust/rules.mk @@ -28,10 +28,12 @@ MODULE_LIBRARY_DEPS += \ $(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \ $(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \ external/rust/crates/downcast-rs \ + external/rust/crates/libc \ trusty/user/base/lib/trusty-sys \ MODULE_RUSTFLAGS += \ --cfg 'android_vendor' \ + --cfg 'trusty' \ # Trusty does not have `ProcessState`, so there are a few # doc links in `IBinder` that are still broken. diff --git a/libs/binder/trusty/usertests-inc.mk b/libs/binder/trusty/usertests-inc.mk index 13001219d4..833d209012 100644 --- a/libs/binder/trusty/usertests-inc.mk +++ b/libs/binder/trusty/usertests-inc.mk @@ -16,4 +16,8 @@ TRUSTY_USER_TESTS += \ frameworks/native/libs/binder/trusty/binderRpcTest \ frameworks/native/libs/binder/trusty/binderRpcTest/service \ + frameworks/native/libs/binder/trusty/rust/binder_rpc_test/service \ + +TRUSTY_RUST_USER_TESTS += \ + frameworks/native/libs/binder/trusty/rust/binder_rpc_test \ diff --git a/libs/binderdebug/BinderDebug.cpp b/libs/binderdebug/BinderDebug.cpp index a8f2cbfb5b..19f3aad04f 100644 --- a/libs/binderdebug/BinderDebug.cpp +++ b/libs/binderdebug/BinderDebug.cpp @@ -199,4 +199,31 @@ status_t getBinderClientPids(BinderDebugContext context, pid_t pid, pid_t servic return ret; } +status_t getBinderTransactions(pid_t pid, std::string& transactionsOutput) { + std::ifstream ifs("/dev/binderfs/binder_logs/transactions"); + if (!ifs.is_open()) { + ifs.open("/d/binder/transactions"); + if (!ifs.is_open()) { + LOG(ERROR) << "Could not open /dev/binderfs/binder_logs/transactions. " + << "Likely a permissions issue. errno: " << errno; + return -errno; + } + } + + std::string line; + while (getline(ifs, line)) { + // The section for this pid ends with another "proc <pid>" for another + // process. There is only one entry per pid so we can stop looking after + // we've grabbed the whole section + if (base::StartsWith(line, "proc " + std::to_string(pid))) { + do { + transactionsOutput += line + '\n'; + } while (getline(ifs, line) && !base::StartsWith(line, "proc ")); + return OK; + } + } + + return NAME_NOT_FOUND; +} + } // namespace android diff --git a/libs/binderdebug/include/binderdebug/BinderDebug.h b/libs/binderdebug/include/binderdebug/BinderDebug.h index 6ce8edfc7c..018393c128 100644 --- a/libs/binderdebug/include/binderdebug/BinderDebug.h +++ b/libs/binderdebug/include/binderdebug/BinderDebug.h @@ -44,4 +44,12 @@ status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* status_t getBinderClientPids(BinderDebugContext context, pid_t pid, pid_t servicePid, int32_t handle, std::vector<pid_t>* pids); +/** + * Get the transactions for a given process from /dev/binderfs/binder_logs/transactions + * Return: OK if the file was found and the pid was found in the file. + * -errno if there was an issue opening the file + * NAME_NOT_FOUND if the pid wasn't found in the file + */ +status_t getBinderTransactions(pid_t pid, std::string& transactionOutput); + } // namespace android diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp index b5c4010c7a..e888b0aea8 100644 --- a/libs/binderthreadstate/test.cpp +++ b/libs/binderthreadstate/test.cpp @@ -22,6 +22,7 @@ #include <binderthreadstateutilstest/1.0/IHidlStuff.h> #include <gtest/gtest.h> #include <hidl/HidlTransportSupport.h> +#include <hidl/ServiceManagement.h> #include <hwbinder/IPCThreadState.h> #include <thread> @@ -37,6 +38,7 @@ using android::OK; using android::sp; using android::String16; using android::binder::Status; +using android::hardware::isHidlSupported; using android::hardware::Return; using binderthreadstateutilstest::V1_0::IHidlStuff; @@ -67,6 +69,7 @@ std::string id2name(size_t id) { // complicated calls are possible, but this should do here. static void callHidl(size_t id, int32_t idx) { + CHECK_EQ(true, isHidlSupported()) << "We shouldn't be calling HIDL if it's not supported"; auto stuff = IHidlStuff::getService(id2name(id)); CHECK(stuff->call(idx).isOk()); } @@ -174,6 +177,7 @@ TEST(BinderThreadState, DoesntInitializeBinderDriver) { } TEST(BindThreadState, RemoteHidlCall) { + if (!isHidlSupported()) GTEST_SKIP() << "No HIDL support on device"; auto stuff = IHidlStuff::getService(id2name(kP1Id)); ASSERT_NE(nullptr, stuff); ASSERT_TRUE(stuff->call(0).isOk()); @@ -186,11 +190,14 @@ TEST(BindThreadState, RemoteAidlCall) { } TEST(BindThreadState, RemoteNestedStartHidlCall) { + if (!isHidlSupported()) GTEST_SKIP() << "No HIDL support on device"; auto stuff = IHidlStuff::getService(id2name(kP1Id)); ASSERT_NE(nullptr, stuff); ASSERT_TRUE(stuff->call(100).isOk()); } TEST(BindThreadState, RemoteNestedStartAidlCall) { + // this test case is trying ot nest a HIDL call which requires HIDL support + if (!isHidlSupported()) GTEST_SKIP() << "No HIDL support on device"; sp<IAidlStuff> stuff; ASSERT_EQ(OK, android::getService<IAidlStuff>(String16(id2name(kP1Id).c_str()), &stuff)); ASSERT_NE(nullptr, stuff); @@ -205,11 +212,15 @@ int server(size_t thisId, size_t otherId) { defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer)); android::ProcessState::self()->startThreadPool(); - // HIDL - android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/); - sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId); - CHECK_EQ(OK, hidlServer->registerAsService(id2name(thisId).c_str())); - android::hardware::joinRpcThreadpool(); + if (isHidlSupported()) { + // HIDL + android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/); + sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId); + CHECK_EQ(OK, hidlServer->registerAsService(id2name(thisId).c_str())); + android::hardware::joinRpcThreadpool(); + } else { + android::IPCThreadState::self()->joinThreadPool(true); + } return EXIT_FAILURE; } @@ -227,9 +238,15 @@ int main(int argc, char** argv) { } android::waitForService<IAidlStuff>(String16(id2name(kP1Id).c_str())); - android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP1Id).c_str()); + if (isHidlSupported()) { + android::hardware::details::waitForHwService(IHidlStuff::descriptor, + id2name(kP1Id).c_str()); + } android::waitForService<IAidlStuff>(String16(id2name(kP2Id).c_str())); - android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP2Id).c_str()); + if (isHidlSupported()) { + android::hardware::details::waitForHwService(IHidlStuff::descriptor, + id2name(kP2Id).c_str()); + } return RUN_ALL_TESTS(); } diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp index 3fe71cefce..2c25f746a8 100644 --- a/libs/bufferqueueconverter/Android.bp +++ b/libs/bufferqueueconverter/Android.bp @@ -16,9 +16,6 @@ cc_library_headers { cc_library { name: "libbufferqueueconverter", vendor_available: true, - vndk: { - enabled: true, - }, double_loadable: true, srcs: [ diff --git a/libs/bufferstreams/Android.bp b/libs/bufferstreams/Android.bp index 365fc457d1..6c2a980f71 100644 --- a/libs/bufferstreams/Android.bp +++ b/libs/bufferstreams/Android.bp @@ -19,6 +19,7 @@ package { aconfig_declarations { name: "bufferstreams_flags", package: "com.android.graphics.bufferstreams.flags", + container: "system", srcs: [ "aconfig/bufferstreams_flags.aconfig", ], diff --git a/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig b/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig index e258725e3d..d0f7812d21 100644 --- a/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig +++ b/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig @@ -1,4 +1,5 @@ package: "com.android.graphics.bufferstreams.flags" +container: "system" flag { name: "bufferstreams_steel_thread" diff --git a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs index 846105dacd..c5c1fd37c1 100644 --- a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs +++ b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! - use std::time::Instant; use crate::{ diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 6ccc6cafb6..81f6a585ab 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -40,6 +40,9 @@ namespace bpf { static constexpr uint64_t NSEC_PER_SEC = 1000000000; static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365; +// Declare busy loop variable globally to prevent removal during optimization +static long sum __attribute__((used)) = 0; + using std::vector; class TimeInStateTest : public testing::Test { @@ -576,7 +579,7 @@ uint64_t timeNanos() { // Keeps CPU busy with some number crunching void useCpu() { - long sum = 0; + sum = 0; for (int i = 0; i < 100000; i++) { sum *= i; } diff --git a/libs/debugstore/OWNERS b/libs/debugstore/OWNERS new file mode 100644 index 0000000000..428a1a2215 --- /dev/null +++ b/libs/debugstore/OWNERS @@ -0,0 +1,3 @@ +benmiles@google.com +gaillard@google.com +mohamadmahmoud@google.com diff --git a/libs/debugstore/rust/Android.bp b/libs/debugstore/rust/Android.bp new file mode 100644 index 0000000000..55ba3c32d1 --- /dev/null +++ b/libs/debugstore/rust/Android.bp @@ -0,0 +1,71 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_team: "trendy_team_android_telemetry_infra", + default_applicable_licenses: ["frameworks_native_license"], +} + +rust_defaults { + name: "libdebugstore_defaults", + srcs: ["src/lib.rs"], + rustlibs: [ + "libcrossbeam_queue", + "libparking_lot", + "libonce_cell", + "libcxx", + ], + shared_libs: ["libutils"], + edition: "2021", +} + +rust_ffi_static { + name: "libdebugstore_rust_ffi", + crate_name: "debugstore", + defaults: ["libdebugstore_defaults"], +} + +cc_library { + name: "libdebugstore_cxx", + generated_headers: ["libdebugstore_cxx_bridge_header"], + generated_sources: ["libdebugstore_cxx_bridge_code"], + export_generated_headers: ["libdebugstore_cxx_bridge_header"], + shared_libs: ["libutils"], + whole_static_libs: ["libdebugstore_rust_ffi"], +} + +rust_test { + name: "libdebugstore_tests", + defaults: ["libdebugstore_defaults"], + test_options: { + unit_test: true, + }, + shared_libs: ["libdebugstore_cxx"], +} + +genrule { + name: "libdebugstore_cxx_bridge_header", + tools: ["cxxbridge"], + cmd: "$(location cxxbridge) $(in) --header >> $(out)", + srcs: ["src/lib.rs"], + out: ["debugstore/debugstore_cxx_bridge.rs.h"], +} + +genrule { + name: "libdebugstore_cxx_bridge_code", + tools: ["cxxbridge"], + cmd: "$(location cxxbridge) $(in) >> $(out)", + srcs: ["src/lib.rs"], + out: ["debugstore/debugstore_cxx_bridge.rs.cpp"], +} diff --git a/libs/debugstore/rust/Cargo.toml b/libs/debugstore/rust/Cargo.toml new file mode 100644 index 0000000000..23a8d24647 --- /dev/null +++ b/libs/debugstore/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "debugstore" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies]
\ No newline at end of file diff --git a/libs/debugstore/rust/src/core.rs b/libs/debugstore/rust/src/core.rs new file mode 100644 index 0000000000..1dfa512151 --- /dev/null +++ b/libs/debugstore/rust/src/core.rs @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2024 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. + */ +use super::event::Event; +use super::event_type::EventType; +use super::storage::Storage; +use crate::cxxffi::uptimeMillis; +use once_cell::sync::Lazy; +use std::fmt; +use std::sync::atomic::{AtomicU64, Ordering}; + +// Lazily initialized static instance of DebugStore. +static INSTANCE: Lazy<DebugStore> = Lazy::new(DebugStore::new); + +/// The `DebugStore` struct is responsible for managing debug events and data. +pub struct DebugStore { + /// Atomic counter for generating unique event IDs. + id_generator: AtomicU64, + /// Non-blocking storage for debug events. + event_store: Storage<Event, { DebugStore::DEFAULT_EVENT_LIMIT }>, +} + +impl DebugStore { + /// The default limit for the number of events that can be stored. + /// + /// This limit is used to initialize the storage for debug events. + const DEFAULT_EVENT_LIMIT: usize = 16; + /// A designated identifier used for events that cannot be closed. + /// + /// This ID is used for point/instantaneous events, or events do not have + /// a distinct end. + const NON_CLOSABLE_ID: u64 = 0; + /// The version number for the encoding of debug store data. + /// + /// This constant is used as a part of the debug store's data format, + /// allowing for version tracking and compatibility checks. + const ENCODE_VERSION: u32 = 1; + + /// Creates a new instance of `DebugStore` with specified event limit and maximum delay. + fn new() -> Self { + Self { id_generator: AtomicU64::new(1), event_store: Storage::new() } + } + + /// Returns a shared instance of `DebugStore`. + /// + /// This method provides a singleton pattern access to `DebugStore`. + pub fn get_instance() -> &'static DebugStore { + &INSTANCE + } + + /// Begins a new debug event with the given name and data. + /// + /// This method logs the start of a debug event, assigning it a unique ID and marking its start time. + /// - `name`: The name of the debug event. + /// - `data`: Associated data as key-value pairs. + /// - Returns: A unique ID for the debug event. + pub fn begin(&self, name: String, data: Vec<(String, String)>) -> u64 { + let id = self.generate_id(); + self.event_store.insert(Event::new( + id, + Some(name), + uptimeMillis(), + EventType::DurationStart, + data, + )); + id + } + + /// Records a debug event without a specific duration, with the given name and data. + /// + /// This method logs an instantaneous debug event, useful for events that don't have a duration but are significant. + /// - `name`: The name of the debug event. + /// - `data`: Associated data as key-value pairs. + pub fn record(&self, name: String, data: Vec<(String, String)>) { + self.event_store.insert(Event::new( + Self::NON_CLOSABLE_ID, + Some(name), + uptimeMillis(), + EventType::Point, + data, + )); + } + + /// Ends a debug event that was previously started with the given ID. + /// + /// This method marks the end of a debug event, completing its lifecycle. + /// - `id`: The unique ID of the debug event to end. + /// - `data`: Additional data to log at the end of the event. + pub fn end(&self, id: u64, data: Vec<(String, String)>) { + if id != Self::NON_CLOSABLE_ID { + self.event_store.insert(Event::new( + id, + None, + uptimeMillis(), + EventType::DurationEnd, + data, + )); + } + } + + fn generate_id(&self) -> u64 { + let mut id = self.id_generator.fetch_add(1, Ordering::Relaxed); + while id == Self::NON_CLOSABLE_ID { + id = self.id_generator.fetch_add(1, Ordering::Relaxed); + } + id + } +} + +impl fmt::Display for DebugStore { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let uptime_now = uptimeMillis(); + write!(f, "{},{},{}::", Self::ENCODE_VERSION, self.event_store.len(), uptime_now)?; + + write!( + f, + "{}", + self.event_store.fold(String::new(), |mut acc, event| { + if !acc.is_empty() { + acc.push_str("||"); + } + acc.push_str(&event.to_string()); + acc + }) + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_begin_event() { + let debug_store = DebugStore::new(); + let _event_id = debug_store.begin("test_event".to_string(), vec![]); + let output = debug_store.to_string(); + assert!( + output.contains("test_event"), + "The output should contain the event name 'test_event'" + ); + } + + #[test] + fn test_unique_event_ids() { + let debug_store = DebugStore::new(); + let event_id1 = debug_store.begin("event1".to_string(), vec![]); + let event_id2 = debug_store.begin("event2".to_string(), vec![]); + assert_ne!(event_id1, event_id2, "Event IDs should be unique"); + } + + #[test] + fn test_end_event() { + let debug_store = DebugStore::new(); + let event_id = debug_store.begin("test_event".to_string(), vec![]); + debug_store.end(event_id, vec![]); + let output = debug_store.to_string(); + + let id_pattern = format!("ID:{},", event_id); + assert!( + output.contains("test_event"), + "The output should contain the event name 'test_event'" + ); + assert_eq!( + output.matches(&id_pattern).count(), + 2, + "The output should contain two events (start and end) associated with the given ID" + ); + } + + #[test] + fn test_event_data_handling() { + let debug_store = DebugStore::new(); + debug_store + .record("data_event".to_string(), vec![("key".to_string(), "value".to_string())]); + let output = debug_store.to_string(); + assert!( + output.contains("data_event"), + "The output should contain the event name 'data_event'" + ); + assert!( + output.contains("key=value"), + "The output should contain the event data 'key=value'" + ); + } +} diff --git a/libs/debugstore/rust/src/event.rs b/libs/debugstore/rust/src/event.rs new file mode 100644 index 0000000000..0c16665df4 --- /dev/null +++ b/libs/debugstore/rust/src/event.rs @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 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. + */ + +use super::event_type::EventType; +use std::fmt; + +/// Represents a single debug event within the Debug Store system. +/// +/// It contains all the necessary information for a debug event. +#[derive(Clone)] +pub struct Event { + /// The unique identifier for this event. + pub id: u64, + /// The optional name of the event. + pub name: Option<String>, + /// The system uptime when the event occurred. + pub timestamp: i64, + /// The type of the event. + pub event_type: EventType, + /// Additional data associated with the event, stored in the given order as key-value pairs. + data: Vec<(String, String)>, +} + +impl Event { + /// Constructs a new `Event`. + /// + /// - `id`: The unique identifier for the event. + /// - `name`: An optional name for the event. + /// - `timestamp`: The system uptime when the event occurred. + /// - `event_type`: The type of the event. + /// - `data`: Additional data for the event, represented as ordered key-value pairs. + pub fn new( + id: u64, + name: Option<String>, + timestamp: i64, + event_type: EventType, + data: Vec<(String, String)>, + ) -> Self { + Self { id, name, timestamp, event_type, data } + } +} + +impl fmt::Display for Event { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ID:{},C:{},T:{}", self.id, self.event_type, self.timestamp)?; + + if let Some(ref name) = self.name { + write!(f, ",N:{}", name)?; + } + + if !self.data.is_empty() { + let data_str = + self.data.iter().map(|(k, v)| format!("{}={}", k, v)).collect::<Vec<_>>().join(";"); + write!(f, ",D:{}", data_str)?; + } + + Ok(()) + } +} diff --git a/libs/debugstore/rust/src/event_type.rs b/libs/debugstore/rust/src/event_type.rs new file mode 100644 index 0000000000..6f4bafb58a --- /dev/null +++ b/libs/debugstore/rust/src/event_type.rs @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 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. + */ +use std::fmt; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum EventType { + /// Marks the an unknown or invalid event, for convenient mapping to a protobuf enum. + Invalid, + /// Marks the beginning of a duration-based event, indicating the start of a timed operation. + DurationStart, + /// Marks the end of a duration-based event, indicating the end of a timed operation. + DurationEnd, + /// Represents a single, instantaneous event with no duration. + Point, +} + +impl fmt::Display for EventType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + EventType::Invalid => "I", + EventType::DurationStart => "S", + EventType::DurationEnd => "E", + EventType::Point => "P", + } + ) + } +} diff --git a/libs/debugstore/rust/src/lib.rs b/libs/debugstore/rust/src/lib.rs new file mode 100644 index 0000000000..f2195c0529 --- /dev/null +++ b/libs/debugstore/rust/src/lib.rs @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 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. + */ + +//! # Debug Store Crate +/// The Debug Store Crate provides functionalities for storing debug events. +/// It allows logging and retrieval of debug events, and associated data. +mod core; +mod event; +mod event_type; +mod storage; + +pub use core::*; +pub use event::*; + +use cxx::{CxxString, CxxVector}; + +#[cxx::bridge(namespace = "android::debugstore")] +#[allow(unsafe_op_in_unsafe_fn)] +mod cxxffi { + extern "Rust" { + fn debug_store_to_string() -> String; + fn debug_store_record(name: &CxxString, data: &CxxVector<CxxString>); + fn debug_store_begin(name: &CxxString, data: &CxxVector<CxxString>) -> u64; + fn debug_store_end(id: u64, data: &CxxVector<CxxString>); + } + + #[namespace = "android"] + unsafe extern "C++" { + include!("utils/SystemClock.h"); + fn uptimeMillis() -> i64; + } +} + +fn debug_store_to_string() -> String { + DebugStore::get_instance().to_string() +} + +fn debug_store_record(name: &CxxString, data: &CxxVector<CxxString>) { + DebugStore::get_instance().record(name.to_string_lossy().into_owned(), cxx_vec_to_pairs(data)); +} + +fn debug_store_begin(name: &CxxString, data: &CxxVector<CxxString>) -> u64 { + DebugStore::get_instance().begin(name.to_string_lossy().into_owned(), cxx_vec_to_pairs(data)) +} + +fn debug_store_end(id: u64, data: &CxxVector<CxxString>) { + DebugStore::get_instance().end(id, cxx_vec_to_pairs(data)); +} + +fn cxx_vec_to_pairs(vec: &CxxVector<CxxString>) -> Vec<(String, String)> { + vec.iter() + .map(|s| s.to_string_lossy().into_owned()) + .collect::<Vec<_>>() + .chunks(2) + .filter_map(|chunk| match chunk { + [k, v] => Some((k.clone(), v.clone())), + _ => None, + }) + .collect() +} diff --git a/libs/debugstore/rust/src/storage.rs b/libs/debugstore/rust/src/storage.rs new file mode 100644 index 0000000000..2ad7f4e0b4 --- /dev/null +++ b/libs/debugstore/rust/src/storage.rs @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 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. + */ + +use crossbeam_queue::ArrayQueue; + +/// A thread-safe storage that allows non-blocking attempts to store and visit elements. +pub struct Storage<T, const N: usize> { + insertion_buffer: ArrayQueue<T>, +} + +impl<T, const N: usize> Storage<T, N> { + /// Creates a new Storage with the specified size. + pub fn new() -> Self { + Self { insertion_buffer: ArrayQueue::new(N) } + } + + /// Inserts a value into the storage, returning an error if the lock cannot be acquired. + pub fn insert(&self, value: T) { + self.insertion_buffer.force_push(value); + } + + /// Folds over the elements in the storage using the provided function. + pub fn fold<U, F>(&self, init: U, mut func: F) -> U + where + F: FnMut(U, &T) -> U, + { + let mut acc = init; + while let Some(value) = self.insertion_buffer.pop() { + acc = func(acc, &value); + } + acc + } + + /// Returns the number of elements that have been inserted into the storage. + pub fn len(&self) -> usize { + self.insertion_buffer.len() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_insert_and_retrieve() { + let storage = Storage::<i32, 10>::new(); + storage.insert(7); + + let sum = storage.fold(0, |acc, &x| acc + x); + assert_eq!(sum, 7, "The sum of the elements should be equal to the inserted value."); + } + + #[test] + fn test_fold_functionality() { + let storage = Storage::<i32, 5>::new(); + storage.insert(1); + storage.insert(2); + storage.insert(3); + + let sum = storage.fold(0, |acc, &x| acc + x); + assert_eq!( + sum, 6, + "The sum of the elements should be equal to the sum of inserted values." + ); + } + + #[test] + fn test_insert_and_retrieve_multiple_values() { + let storage = Storage::<i32, 10>::new(); + storage.insert(1); + storage.insert(2); + storage.insert(5); + + let first_sum = storage.fold(0, |acc, &x| acc + x); + assert_eq!(first_sum, 8, "The sum of the elements should be equal to the inserted values."); + + storage.insert(30); + storage.insert(22); + + let second_sum = storage.fold(0, |acc, &x| acc + x); + assert_eq!( + second_sum, 52, + "The sum of the elements should be equal to the inserted values." + ); + } + + #[test] + fn test_storage_limit() { + let storage = Storage::<i32, 1>::new(); + storage.insert(1); + // This value should overwrite the previously inserted value (1). + storage.insert(4); + let sum = storage.fold(0, |acc, &x| acc + x); + assert_eq!(sum, 4, "The sum of the elements should be equal to the inserted values."); + } + + #[test] + fn test_concurrent_insertions() { + use std::sync::Arc; + use std::thread; + + let storage = Arc::new(Storage::<i32, 100>::new()); + let threads: Vec<_> = (0..10) + .map(|_| { + let storage_clone = Arc::clone(&storage); + thread::spawn(move || { + for i in 0..10 { + storage_clone.insert(i); + } + }) + }) + .collect(); + + for thread in threads { + thread.join().expect("Thread should finish without panicking"); + } + + let count = storage.fold(0, |acc, _| acc + 1); + assert_eq!(count, 100, "Storage should be filled to its limit with concurrent insertions."); + } +} diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp index aab1276b47..6e2b803174 100644 --- a/libs/gralloc/types/Android.bp +++ b/libs/gralloc/types/Android.bp @@ -37,10 +37,7 @@ cc_library { }, vendor_available: true, - vndk: { - enabled: true, - support_system_process: true, - }, + double_loadable: true, apex_available: [ "//apex_available:platform", "com.android.media.swcodec", diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index eb4d3df21d..059e19ebb8 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -23,6 +23,7 @@ package { aconfig_declarations { name: "libgui_flags", package: "com.android.graphics.libgui.flags", + container: "system", srcs: ["libgui_flags.aconfig"], } @@ -281,10 +282,6 @@ cc_defaults { cc_library_shared { name: "libgui", vendor_available: true, - vndk: { - enabled: true, - private: true, - }, double_loadable: true, defaults: [ diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 8b6f2023dc..079ccda0fb 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -2445,7 +2445,6 @@ status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32 const sp<IBinder>& parentHandle, LayerMetadata metadata, uint32_t* outTransformHint) { - sp<SurfaceControl> sur; status_t err = mStatus; if (mStatus == NO_ERROR) { diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index b081030c9f..38646992da 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -1,4 +1,5 @@ package: "com.android.graphics.libgui.flags" +container: "system" flag { name: "bq_setframerate" diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 252040d1b0..20b35383bc 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -39,6 +39,7 @@ filegroup { aconfig_declarations { name: "com.android.input.flags-aconfig", package: "com.android.input.flags", + container: "system", srcs: ["input_flags.aconfig"], } diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 54eeb39935..dbc002ccb3 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -1,4 +1,5 @@ package: "com.android.input.flags" +container: "system" flag { name: "enable_outbound_event_verification" diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 13cfb491b5..9137a3468f 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -13,6 +13,7 @@ cc_test { cpp_std: "c++20", host_supported: true, srcs: [ + "BlockingQueue_test.cpp", "IdGenerator_test.cpp", "InputChannel_test.cpp", "InputDevice_test.cpp", diff --git a/services/inputflinger/tests/BlockingQueue_test.cpp b/libs/input/tests/BlockingQueue_test.cpp index 754a5c451e..924b937080 100644 --- a/services/inputflinger/tests/BlockingQueue_test.cpp +++ b/libs/input/tests/BlockingQueue_test.cpp @@ -14,8 +14,7 @@ * limitations under the License. */ -#include "../BlockingQueue.h" - +#include <input/BlockingQueue.h> #include <gtest/gtest.h> #include <thread> @@ -109,7 +108,7 @@ TEST(BlockingQueueTest, Queue_AllowsMultipleThreads) { BlockingQueue<int> queue(capacity); // Fill queue from a different thread - std::thread fillQueue([&queue](){ + std::thread fillQueue([&queue]() { for (size_t i = 0; i < capacity; i++) { ASSERT_TRUE(queue.push(static_cast<int>(i))); } @@ -136,7 +135,7 @@ TEST(BlockingQueueTest, Queue_BlocksWhileWaitingForElements) { std::atomic_bool hasReceivedElement = false; // fill queue from a different thread - std::thread waitUntilHasElements([&queue, &hasReceivedElement](){ + std::thread waitUntilHasElements([&queue, &hasReceivedElement]() { queue.pop(); // This should block until an element has been added hasReceivedElement = true; }); diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp index 90d0a8e400..798d804275 100644 --- a/libs/nativewindow/rust/Android.bp +++ b/libs/nativewindow/rust/Android.bp @@ -53,6 +53,10 @@ rust_bindgen { }, min_sdk_version: "VanillaIceCream", vendor_available: true, + apex_available: [ + "//apex_available:platform", + "com.android.virt", + ], } rust_library { @@ -77,6 +81,10 @@ rust_library { }, min_sdk_version: "VanillaIceCream", vendor_available: true, + apex_available: [ + "//apex_available:platform", + "com.android.virt", + ], } rust_test { @@ -115,6 +123,10 @@ rust_library { }, min_sdk_version: "VanillaIceCream", vendor_available: true, + apex_available: [ + "//apex_available:platform", + "com.android.virt", + ], } rust_test { diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs index 22ad83463c..dc3f51f7fd 100644 --- a/libs/nativewindow/rust/src/lib.rs +++ b/libs/nativewindow/rust/src/lib.rs @@ -16,7 +16,8 @@ extern crate nativewindow_bindgen as ffi; -pub mod surface; +mod surface; +pub use surface::Surface; pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags}; diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 818d0350c0..b41fa1dd69 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -33,7 +33,7 @@ #include <memory> /** - * Allows to set RenderEngine backend to GLES (default) or SkiaGL (NOT yet supported). + * Allows to override the RenderEngine backend. */ #define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend" diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp index 92fe4c0b47..ee95e59d90 100644 --- a/libs/renderengine/skia/AutoBackendTexture.cpp +++ b/libs/renderengine/skia/AutoBackendTexture.cpp @@ -77,7 +77,7 @@ AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer backendFormat, isOutputBuffer); } else { - LOG_ALWAYS_FATAL("Unexpected backend %d", backend); + LOG_ALWAYS_FATAL("Unexpected backend %u", static_cast<unsigned>(backend)); } mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format); @@ -145,8 +145,8 @@ void logFatalTexture(const char* msg, const GrBackendTexture& tex, ui::Dataspace "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i " "texType: %i\n\t\tVkImageInfo: success: %i fFormat: %i " "fSampleCount: %u fLevelCount: %u colorType %i", - msg, tex.isValid(), dataspace, tex.width(), tex.height(), - tex.hasMipmaps(), tex.isProtected(), + msg, tex.isValid(), static_cast<int32_t>(dataspace), tex.width(), + tex.height(), tex.hasMipmaps(), tex.isProtected(), static_cast<int>(tex.textureType()), retrievedImageInfo, imageInfo.fFormat, imageInfo.fSampleCount, imageInfo.fLevelCount, colorType); diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp index 5c9820cdc5..c528ae9f9d 100644 --- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp @@ -126,7 +126,7 @@ sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, surfaceTwo = surface->makeSurface(scaledInfo); LOG_ALWAYS_FATAL_IF(!surfaceTwo, "%s: Failed to create second blur surface!", __func__); - for (auto i = 1; i < numberOfPasses; i++) { + for (auto i = 2; i <= numberOfPasses; i++) { LOG_ALWAYS_FATAL_IF(tmpBlur == nullptr, "%s: tmpBlur is null for pass %d", __func__, i); blurBuilder.child("child") = tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear); diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 11d4fdebdc..c4c17c200e 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -1095,7 +1095,7 @@ void RenderEngineTest::fillBufferWithRoundedCorners() { template <typename SourceVariant> void RenderEngineTest::fillBufferAndBlurBackground() { - auto blurRadius = 50; + auto blurRadius = 30; auto center = DEFAULT_DISPLAY_WIDTH / 2; renderengine::DisplaySettings settings; diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index ec0ab4e464..f84d14548f 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -104,9 +104,6 @@ cc_library_static { cc_library_shared { name: "libui", vendor_available: true, - vndk: { - enabled: true, - }, double_loadable: true, cflags: [ diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp index 48d793a4d4..080e62b164 100644 --- a/services/gpuservice/GpuService.cpp +++ b/services/gpuservice/GpuService.cpp @@ -70,6 +70,9 @@ GpuService::GpuService() }; GpuService::~GpuService() { + mGpuMem->stop(); + mGpuWork->stop(); + mGpuWorkAsyncInitThread->join(); mGpuMemAsyncInitThread->join(); } diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp index 141fe021ee..d0783df109 100644 --- a/services/gpuservice/gpumem/GpuMem.cpp +++ b/services/gpuservice/gpumem/GpuMem.cpp @@ -61,6 +61,7 @@ void GpuMem::initialize() { return; } // Retry until GPU driver loaded or timeout. + if (mStop.load()) return; sleep(1); } diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h index 9aa74d6863..16b201f516 100644 --- a/services/gpuservice/gpumem/include/gpumem/GpuMem.h +++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h @@ -34,6 +34,7 @@ public: // dumpsys interface void dump(const Vector<String16>& args, std::string* result); bool isInitialized() { return mInitialized.load(); } + void stop() { mStop.store(true); } // Traverse the gpu memory total map to feed the callback function. void traverseGpuMemTotals(const std::function<void(int64_t ts, uint32_t gpuId, uint32_t pid, @@ -48,6 +49,10 @@ private: // indicate whether ebpf has been initialized std::atomic<bool> mInitialized = false; + + // whether initialization should be stopped + std::atomic<bool> mStop = false; + // bpf map for GPU memory total data android::bpf::BpfMapRO<uint64_t, uint64_t> mGpuMemTotalMap; diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp index fd703239e9..1a744abc6d 100644 --- a/services/gpuservice/gpuwork/GpuWork.cpp +++ b/services/gpuservice/gpuwork/GpuWork.cpp @@ -243,6 +243,7 @@ bool GpuWork::attachTracepoint(const char* programPath, const char* tracepointGr return false; } // Retry until GPU driver loaded or timeout. + if (mStop.load()) return false; sleep(1); errno = 0; } diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h index cece9999c6..e70da540b9 100644 --- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h +++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h @@ -40,6 +40,7 @@ public: ~GpuWork(); void initialize(); + void stop() { mStop.store(true); } // Dumps the GPU work information. void dump(const Vector<String16>& args, std::string* result); @@ -47,7 +48,7 @@ public: private: // Attaches tracepoint |tracepoint_group|/|tracepoint_name| to BPF program at path // |program_path|. The tracepoint is also enabled. - static bool attachTracepoint(const char* program_path, const char* tracepoint_group, + bool attachTracepoint(const char* program_path, const char* tracepoint_group, const char* tracepoint_name); // Native atom puller callback registered in statsd. @@ -80,6 +81,9 @@ private: // Indicates whether our eBPF components have been initialized. std::atomic<bool> mInitialized = false; + // Indicates whether eBPF initialization should be stopped. + std::atomic<bool> mStop = false; + // A thread that periodically checks whether |mGpuWorkMap| is nearly full // and, if so, clears it. std::thread mMapClearerThread; diff --git a/services/inputflinger/InputProcessor.h b/services/inputflinger/InputProcessor.h index dcbfebc62f..7a00a2dae8 100644 --- a/services/inputflinger/InputProcessor.h +++ b/services/inputflinger/InputProcessor.h @@ -22,7 +22,7 @@ #include <unordered_map> #include <aidl/android/hardware/input/processor/IInputProcessor.h> -#include "BlockingQueue.h" +#include <input/BlockingQueue.h> #include "InputListener.h" namespace android { diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 2a03ecc62b..9c9f643656 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -39,7 +39,6 @@ cc_test { ], srcs: [ "AnrTracker_test.cpp", - "BlockingQueue_test.cpp", "CapturedTouchpadEventConverter_test.cpp", "CursorInputMapper_test.cpp", "EventHub_test.cpp", diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 5002391f61..c2e67fa788 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -15,7 +15,6 @@ */ #include "../dispatcher/InputDispatcher.h" -#include "../BlockingQueue.h" #include "FakeApplicationHandle.h" #include "TestEventMatchers.h" @@ -31,6 +30,7 @@ #include <flag_macros.h> #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <input/BlockingQueue.h> #include <input/Input.h> #include <input/PrintTools.h> #include <linux/input.h> diff --git a/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp index 219b662ffb..863d0a165e 100644 --- a/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/BlockingQueueFuzzer.cpp @@ -15,8 +15,8 @@ */ #include <fuzzer/FuzzedDataProvider.h> +#include <input/BlockingQueue.h> #include <thread> -#include "BlockingQueue.h" // Chosen to be a number large enough for variation in fuzzer runs, but not consume too much memory. static constexpr size_t MAX_CAPACITY = 1024; diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 0989863b7d..dcef9a3775 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -10,6 +10,7 @@ package { aconfig_declarations { name: "surfaceflinger_flags", package: "com.android.graphics.surfaceflinger.flags", + container: "system", srcs: ["surfaceflinger_flags.aconfig"], } diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index ae2f2dbbf5..0a70d4299d 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -10,6 +10,7 @@ package { cc_defaults { name: "libcompositionengine_defaults", defaults: [ + "aconfig_lib_cc_static_link.defaults", "android.hardware.graphics.composer3-ndk_shared", "android.hardware.power-ndk_shared", "librenderengine_deps", diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS index 0aee7d497c..ffc1dd7979 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -2,7 +2,6 @@ adyabr@google.com alecmouri@google.com -chaviw@google.com domlaskowski@google.com jreck@google.com lpy@google.com @@ -10,5 +9,6 @@ pdwilliams@google.com racarr@google.com ramindani@google.com rnlee@google.com +sallyqi@google.com scroggo@google.com vishnun@google.com diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index c56dc83412..b980a65255 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -72,6 +72,7 @@ #include <gui/TraceUtils.h> #include <hidl/ServiceManagement.h> #include <layerproto/LayerProtoParser.h> +#include <linux/sched/types.h> #include <log/log.h> #include <private/android_filesystem_config.h> #include <private/gui/SyncFeatures.h> @@ -2251,7 +2252,7 @@ bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTi outTransactionsAreEmpty = !needsTraversal; const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal; if (shouldCommit) { - commitTransactions(); + commitTransactionsLegacy(); } bool mustComposite = latchBuffers() || shouldCommit; @@ -2379,8 +2380,14 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, } } + // Keep a copy of the drawing state (that is going to be overwritten + // by commitTransactionsLocked) outside of mStateLock so that the side + // effects of the State assignment don't happen with mStateLock held, + // which can cause deadlocks. + State drawingState(mDrawingState); + Mutex::Autolock lock(mStateLock); bool mustComposite = false; - mustComposite |= applyAndCommitDisplayTransactionStates(update.transactions); + mustComposite |= applyAndCommitDisplayTransactionStatesLocked(update.transactions); { ATRACE_NAME("LayerSnapshotBuilder:update"); @@ -2419,7 +2426,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool newDataLatched = false; if (!mLegacyFrontEndEnabled) { ATRACE_NAME("DisplayCallbackAndStatsUpdates"); - mustComposite |= applyTransactions(update.transactions, vsyncId); + mustComposite |= applyTransactionsLocked(update.transactions, vsyncId); traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); }); const nsecs_t latchTime = systemTime(); bool unused = false; @@ -3265,6 +3272,19 @@ void SurfaceFlinger::computeLayerBounds() { void SurfaceFlinger::commitTransactions() { ATRACE_CALL(); + mDebugInTransaction = systemTime(); + + // Here we're guaranteed that some transaction flags are set + // so we can call commitTransactionsLocked unconditionally. + // We clear the flags with mStateLock held to guarantee that + // mCurrentState won't change until the transaction is committed. + mScheduler->modulateVsync({}, &VsyncModulator::onTransactionCommit); + commitTransactionsLocked(clearTransactionFlags(eTransactionMask)); + mDebugInTransaction = 0; +} + +void SurfaceFlinger::commitTransactionsLegacy() { + ATRACE_CALL(); // Keep a copy of the drawing state (that is going to be overwritten // by commitTransactionsLocked) outside of mStateLock so that the side @@ -5069,9 +5089,8 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin return needsTraversal; } -bool SurfaceFlinger::applyAndCommitDisplayTransactionStates( +bool SurfaceFlinger::applyAndCommitDisplayTransactionStatesLocked( std::vector<TransactionState>& transactions) { - Mutex::Autolock lock(mStateLock); bool needsTraversal = false; uint32_t transactionFlags = 0; for (auto& transaction : transactions) { @@ -5863,7 +5882,8 @@ void SurfaceFlinger::initializeDisplays() { if (mLegacyFrontEndEnabled) { applyTransactions(transactions, VsyncId{0}); } else { - applyAndCommitDisplayTransactionStates(transactions); + Mutex::Autolock lock(mStateLock); + applyAndCommitDisplayTransactionStatesLocked(transactions); } { @@ -7565,20 +7585,6 @@ status_t SurfaceFlinger::setSchedAttr(bool enabled) { return NO_ERROR; } - // Currently, there is no wrapper in bionic: b/183240349. - struct sched_attr { - uint32_t size; - uint32_t sched_policy; - uint64_t sched_flags; - int32_t sched_nice; - uint32_t sched_priority; - uint64_t sched_runtime; - uint64_t sched_deadline; - uint64_t sched_period; - uint32_t sched_util_min; - uint32_t sched_util_max; - }; - sched_attr attr = {}; attr.size = sizeof(attr); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 6b4440193b..61360a70df 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -751,7 +751,8 @@ private: bool force = false) REQUIRES(mStateLock, kMainThreadContext); - void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext); + void commitTransactionsLegacy() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext); + void commitTransactions() REQUIRES(kMainThreadContext, mStateLock); void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock, kMainThreadContext); void doCommitTransactions() REQUIRES(mStateLock); @@ -804,8 +805,8 @@ private: bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext); bool applyTransactions(std::vector<TransactionState>&, VsyncId) REQUIRES(kMainThreadContext); - bool applyAndCommitDisplayTransactionStates(std::vector<TransactionState>& transactions) - REQUIRES(kMainThreadContext); + bool applyAndCommitDisplayTransactionStatesLocked(std::vector<TransactionState>& transactions) + REQUIRES(kMainThreadContext, mStateLock); // Returns true if there is at least one transaction that needs to be flushed bool transactionFlushNeeded(); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index 4fc39cc912..81fbc847a3 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -610,7 +610,7 @@ public: { ftl::FakeGuard guard(kMainThreadContext); - mFlinger->commitTransactions(); + mFlinger->commitTransactionsLegacy(); mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp)); scheduler::FrameTargeter frameTargeter(displayId, mFdp.ConsumeBool()); diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig index 1a28b81483..abe7ec708f 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -1,4 +1,5 @@ package: "com.android.graphics.surfaceflinger.flags" +container: "system" flag { name: "misc1" diff --git a/services/surfaceflinger/tests/OWNERS b/services/surfaceflinger/tests/OWNERS new file mode 100644 index 0000000000..56f2f1b07a --- /dev/null +++ b/services/surfaceflinger/tests/OWNERS @@ -0,0 +1,8 @@ +per-file HdrSdrRatioOverlay_test.cpp = alecmouri@google.com, sallyqi@google.com, jreck@google.com + +# Most layer-related files are owned by WM +per-file Layer* = set noparent +per-file Layer* = pdwilliams@google.com, vishnun@google.com, melodymhsu@google.com + +per-file LayerHistoryTest.cpp = file:/services/surfaceflinger/OWNERS +per-file LayerInfoTest.cpp = file:/services/surfaceflinger/OWNERS
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp index 22b72f98e5..f2e2c8a76f 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp @@ -33,24 +33,45 @@ using aidl::android::hardware::power::Boost; TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) { using namespace std::chrono_literals; + std::mutex timerMutex; + std::condition_variable cv; + injectDefaultInternalDisplay([](FakeDisplayDeviceInjector&) {}); - mFlinger.scheduler()->replaceTouchTimer(100); - std::this_thread::sleep_for(10ms); // wait for callback to be triggered + std::unique_lock lock(timerMutex); + bool didReset = false; // keeps track of what the most recent call was + + auto waitForTimerReset = [&] { cv.wait_for(lock, 100ms, [&] { return didReset; }); }; + auto waitForTimerExpired = [&] { cv.wait_for(lock, 100ms, [&] { return !didReset; }); }; + + // Add extra logic to unblock the test when the timer callbacks get called + mFlinger.scheduler()->replaceTouchTimer(10, [&](bool isReset) { + { + std::unique_lock lock(timerMutex); // guarantee we're waiting on the cv + didReset = isReset; + } + cv.notify_one(); // wake the cv + std::unique_lock lock(timerMutex); // guarantee we finished the cv logic + }); + + waitForTimerReset(); EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch - std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback - EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); + waitForTimerExpired(); + EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); // Stopping timer deactivates touch EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT))); - std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered - EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); - std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback + EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); + // Wait for the timer to start just in case + waitForTimerReset(); + EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); + // Wait for the timer to stop, again just in case + waitForTimerExpired(); EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION))); - std::this_thread::sleep_for(10ms); // wait for callback to be triggered. + waitForTimerReset(); EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); } diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 2a1b88e6fa..c0255d3e98 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -136,14 +136,25 @@ public: return mLayerHistory.mActiveLayerInfos.size(); } - void replaceTouchTimer(int64_t millis) { + void replaceTouchTimer(int64_t millis, + std::function<void(bool isReset)>&& testCallback = nullptr) { if (mTouchTimer) { mTouchTimer.reset(); } mTouchTimer.emplace( "Testable Touch timer", std::chrono::milliseconds(millis), - [this] { touchTimerCallback(TimerState::Reset); }, - [this] { touchTimerCallback(TimerState::Expired); }); + [this, testCallback] { + touchTimerCallback(TimerState::Reset); + if (testCallback != nullptr) { + testCallback(true); + } + }, + [this, testCallback] { + touchTimerCallback(TimerState::Expired); + if (testCallback != nullptr) { + testCallback(false); + } + }); mTouchTimer->start(); } diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp index 7eda9ef0c7..b2b1988d97 100644 --- a/services/vibratorservice/VibratorCallbackScheduler.cpp +++ b/services/vibratorservice/VibratorCallbackScheduler.cpp @@ -87,13 +87,13 @@ void CallbackScheduler::loop() { lock.lock(); } if (mQueue.empty()) { - // Wait until a new callback is scheduled. - mCondition.wait(mMutex); + // Wait until a new callback is scheduled or destructor was called. + mCondition.wait(lock, [this] { return mFinished || !mQueue.empty(); }); } else { - // Wait until next callback expires, or a new one is scheduled. + // Wait until next callback expires or a new one is scheduled. // Use the monotonic steady clock to wait for the measured delay interval via wait_for // instead of using a wall clock via wait_until. - mCondition.wait_for(mMutex, mQueue.top().getWaitForExpirationDuration()); + mCondition.wait_for(lock, mQueue.top().getWaitForExpirationDuration()); } } } diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp index 426cd426f7..881e32152c 100644 --- a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp +++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp @@ -14,20 +14,13 @@ * limitations under the License. */ -#define LOG_TAG "VibratorHalWrapperAidlTest" - -#include <android-base/thread_annotations.h> -#include <android/hardware/vibrator/IVibrator.h> -#include <condition_variable> - #include <gmock/gmock.h> #include <gtest/gtest.h> -#include <utils/Log.h> -#include <thread> - #include <vibratorservice/VibratorCallbackScheduler.h> +#include "test_utils.h" + using std::chrono::milliseconds; using std::chrono::steady_clock; using std::chrono::time_point; @@ -39,29 +32,25 @@ using namespace testing; // ------------------------------------------------------------------------------------------------- // Delay allowed for the scheduler to process callbacks during this test. -static const auto TEST_TIMEOUT = 50ms; +static const auto TEST_TIMEOUT = 100ms; class VibratorCallbackSchedulerTest : public Test { public: - void SetUp() override { - mScheduler = std::make_unique<vibrator::CallbackScheduler>(); - std::lock_guard<std::mutex> lock(mMutex); - mExpiredCallbacks.clear(); - } + void SetUp() override { mScheduler = std::make_unique<vibrator::CallbackScheduler>(); } protected: std::mutex mMutex; - std::condition_variable_any mCondition; std::unique_ptr<vibrator::CallbackScheduler> mScheduler = nullptr; + vibrator::TestCounter mCallbackCounter; std::vector<int32_t> mExpiredCallbacks GUARDED_BY(mMutex); std::function<void()> createCallback(int32_t id) { - return [=]() { + return [this, id]() { { std::lock_guard<std::mutex> lock(mMutex); mExpiredCallbacks.push_back(id); } - mCondition.notify_all(); + mCallbackCounter.increment(); }; } @@ -71,56 +60,42 @@ protected: } int32_t waitForCallbacks(int32_t callbackCount, milliseconds timeout) { - time_point<steady_clock> expirationTime = steady_clock::now() + timeout + TEST_TIMEOUT; - int32_t expiredCallbackCount = 0; - while (steady_clock::now() < expirationTime) { - std::lock_guard<std::mutex> lock(mMutex); - expiredCallbackCount = mExpiredCallbacks.size(); - if (callbackCount <= expiredCallbackCount) { - return expiredCallbackCount; - } - auto currentTimeout = std::chrono::duration_cast<std::chrono::milliseconds>( - expirationTime - steady_clock::now()); - if (currentTimeout > currentTimeout.zero()) { - // Use the monotonic steady clock to wait for the requested timeout via wait_for - // instead of using a wall clock via wait_until. - mCondition.wait_for(mMutex, currentTimeout); - } - } - return expiredCallbackCount; + mCallbackCounter.tryWaitUntilCountIsAtLeast(callbackCount, timeout); + return mCallbackCounter.get(); } }; // ------------------------------------------------------------------------------------------------- TEST_F(VibratorCallbackSchedulerTest, TestScheduleRunsOnlyAfterDelay) { + auto callbackDuration = 50ms; time_point<steady_clock> startTime = steady_clock::now(); - mScheduler->schedule(createCallback(1), 50ms); + mScheduler->schedule(createCallback(1), callbackDuration); - ASSERT_EQ(1, waitForCallbacks(1, 50ms)); + ASSERT_THAT(waitForCallbacks(1, callbackDuration + TEST_TIMEOUT), Eq(1)); time_point<steady_clock> callbackTime = steady_clock::now(); - // Callback happened at least 50ms after the beginning of the test. - ASSERT_TRUE(startTime + 50ms <= callbackTime); - ASSERT_THAT(getExpiredCallbacks(), ElementsAre(1)); + // Callback took at least the required duration to trigger. + ASSERT_THAT(callbackTime, Ge(startTime + callbackDuration)); } TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOrder) { // Schedule first callbacks long enough that all 3 will be scheduled together and run in order. - mScheduler->schedule(createCallback(1), 50ms); - mScheduler->schedule(createCallback(2), 40ms); - mScheduler->schedule(createCallback(3), 10ms); + mScheduler->schedule(createCallback(1), 50ms + 2 * TEST_TIMEOUT); + mScheduler->schedule(createCallback(2), 50ms + TEST_TIMEOUT); + mScheduler->schedule(createCallback(3), 50ms); - ASSERT_EQ(3, waitForCallbacks(3, 50ms)); + // Callbacks triggered in the expected order based on the requested durations. + ASSERT_THAT(waitForCallbacks(3, 50ms + 3 * TEST_TIMEOUT), Eq(3)); ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1)); } TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) { // Schedule callback long enough that scheduler will be destroyed while it's still scheduled. - mScheduler->schedule(createCallback(1), 50ms); + mScheduler->schedule(createCallback(1), 100ms); mScheduler.reset(nullptr); // Should timeout waiting for callback to run. - ASSERT_EQ(0, waitForCallbacks(1, 50ms)); - ASSERT_TRUE(getExpiredCallbacks().empty()); + ASSERT_THAT(waitForCallbacks(1, 100ms + TEST_TIMEOUT), Eq(0)); + ASSERT_THAT(getExpiredCallbacks(), IsEmpty()); } diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h index 1933a118ef..c08cfc6bfa 100644 --- a/services/vibratorservice/test/test_utils.h +++ b/services/vibratorservice/test/test_utils.h @@ -85,6 +85,34 @@ private: ~TestFactory() = delete; }; +class TestCounter { +public: + TestCounter(int32_t init = 0) : mMutex(), mCondVar(), mCount(init) {} + + int32_t get() { + std::lock_guard<std::mutex> lock(mMutex); + return mCount; + } + + void increment() { + { + std::lock_guard<std::mutex> lock(mMutex); + mCount += 1; + } + mCondVar.notify_all(); + } + + void tryWaitUntilCountIsAtLeast(int32_t count, std::chrono::milliseconds timeout) { + std::unique_lock<std::mutex> lock(mMutex); + mCondVar.wait_for(lock, timeout, [&] { return mCount >= count; }); + } + +private: + std::mutex mMutex; + std::condition_variable mCondVar; + int32_t mCount GUARDED_BY(mMutex); +}; + // ------------------------------------------------------------------------------------------------- } // namespace vibrator |