Merge "Activity manager: native callback for process state"
diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md
index d5b2953..6ac17d8 100644
--- a/cmds/dumpstate/README.md
+++ b/cmds/dumpstate/README.md
@@ -26,6 +26,18 @@
mmm -j frameworks/native/cmds/dumpstate && adb push ${OUT}/system/bin/dumpstate system/bin && adb shell am bug-report
```
+Make sure that the device is remounted before running the above command.
+* If you're working with `userdebug` variant, you may need to run the following to remount your device:
+
+ ```
+ adb root && adb remount -R && adb wait-for-device && adb root && adb remount
+ ```
+* If you're working with `eng` variant, you may need to run the following to remount your device:
+
+ ```
+ adb root && adb remount
+ ```
+
## To build, deploy, and run unit tests
First create `/data/nativetest64`:
@@ -37,13 +49,13 @@
Then run:
```
-mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_test* /data/nativetest64 && adb shell /data/nativetest/dumpstate_test/dumpstate_test
+mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_test* /data/nativetest64 && adb shell /data/nativetest64/dumpstate_test/dumpstate_test
```
And to run just one test (for example, `DumpstateTest.RunCommandNoArgs`):
```
-mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_test* /data/nativetest64 && adb shell /data/nativetest/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandNoArgs
+mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_test* /data/nativetest64 && adb shell /data/nativetest64/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandNoArgs
```
## To take quick bugreports
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index dc7288e..df80651 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1864,6 +1864,17 @@
do_text_file = true;
} else {
do_text_file = false;
+ // If the user has changed the suffix, we need to change the zip file name.
+ std::string new_path = ds.GetPath(".zip");
+ if (ds.path_ != new_path) {
+ MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str());
+ if (rename(ds.path_.c_str(), new_path.c_str())) {
+ MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(),
+ strerror(errno));
+ } else {
+ ds.path_ = new_path;
+ }
+ }
}
}
if (do_text_file) {
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index aad9939..25e5247 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -313,9 +313,14 @@
bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" ||
vold_decrypt == "1";
- const std::string resolve_startup_string_arg =
+ const std::string resolve_startup_string_arg =
MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings",
"--resolve-startup-const-strings=%s");
+
+ const std::string image_block_size_arg =
+ MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size",
+ "--max-image-block-size=%s");
+
const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false);
std::string image_format_arg;
@@ -430,6 +435,7 @@
AddRuntimeArg(dex2oat_Xmx_arg);
AddArg(resolve_startup_string_arg);
+ AddArg(image_block_size_arg);
AddArg(dex2oat_compiler_filter_arg);
AddArg(dex2oat_threads_arg);
AddArg(dex2oat_swap_fd);
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index c169b76..c70bc3e 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -45,6 +45,7 @@
OpaqueFlagChange opaque_flag = 13;
SecureFlagChange secure_flag = 14;
DeferredTransactionChange deferred_transaction = 15;
+ CornerRadiusChange corner_radius = 16;
}
}
@@ -62,6 +63,10 @@
required float alpha = 1;
}
+message CornerRadiusChange {
+ required float corner_radius = 1;
+}
+
message LayerChange {
required uint32 layer = 1;
}
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 6602546..384f21f 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -385,6 +385,9 @@
case SurfaceChange::SurfaceChangeCase::kCrop:
setCrop(transaction, change.id(), change.crop());
break;
+ case SurfaceChange::SurfaceChangeCase::kCornerRadius:
+ setCornerRadius(transaction, change.id(), change.corner_radius());
+ break;
case SurfaceChange::SurfaceChangeCase::kMatrix:
setMatrix(transaction, change.id(), change.matrix());
break;
@@ -489,6 +492,13 @@
t.setCrop_legacy(mLayers[id], r);
}
+void Replayer::setCornerRadius(SurfaceComposerClient::Transaction& t,
+ layer_id id, const CornerRadiusChange& cc) {
+ ALOGV("Layer %d: Setting Corner Radius -- cornerRadius=%d", id, cc.corner_radius());
+
+ t.setCornerRadius(mLayers[id], cc.corner_radius());
+}
+
void Replayer::setMatrix(SurfaceComposerClient::Transaction& t,
layer_id id, const MatrixChange& mc) {
ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(),
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index 68390d3..120dd9b 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -92,6 +92,8 @@
layer_id id, const LayerChange& lc);
void setCrop(SurfaceComposerClient::Transaction& t,
layer_id id, const CropChange& cc);
+ void setCornerRadius(SurfaceComposerClient::Transaction& t,
+ layer_id id, const CornerRadiusChange& cc);
void setMatrix(SurfaceComposerClient::Transaction& t,
layer_id id, const MatrixChange& mc);
void setOverrideScalingMode(SurfaceComposerClient::Transaction& t,
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 561f5ba..d6021c0 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -36,12 +36,15 @@
<feature name="android.hardware.type.automotive" />
<!-- basic system services -->
- <feature name="android.software.app_widgets" />
<feature name="android.software.connectionservice" />
<feature name="android.software.voice_recognizers" notLowRam="true" />
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
+ <feature name="android.software.input_methods" />
<feature name="android.software.print" />
+ <feature name="android.software.companion_device_setup" />
+ <feature name="android.software.autofill" />
+ <feature name="android.software.cant_save_state" />
<!-- Feature to specify if the device supports adding device admins. -->
<feature name="android.software.device_admin" />
diff --git a/include/android/keycodes.h b/include/android/keycodes.h
index 59d67f3..cfd2b40 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -769,7 +769,13 @@
/** all apps */
AKEYCODE_ALL_APPS = 284,
/** refresh key */
- AKEYCODE_REFRESH = 285
+ AKEYCODE_REFRESH = 285,
+ /** Thumbs up key. Apps can use this to let user upvote content. */
+ AKEYCODE_THUMBS_UP = 286,
+ /** Thumbs down key. Apps can use this to let user downvote content. */
+ AKEYCODE_THUMBS_DOWN = 287,
+ /** Consumed by system to switch current viewer profile. */
+ AKEYCODE_PROFILE_SWITCH = 288
// NOTE: If you add a new keycode here you must also add it to several other files.
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h
index c541511..38f036e 100644
--- a/include/android/system_fonts.h
+++ b/include/android/system_fonts.h
@@ -24,7 +24,7 @@
* The ASystemFontIterator_open method will give you an iterator which can iterate all system
* installed font files as shown in the following example.
*
- * <code>
+ * \code{.cpp}
* ASystemFontIterator* iterator = ASystemFontIterator_open();
* ASystemFont* font = NULL;
*
@@ -50,7 +50,7 @@
*
* // Use this font for your text rendering engine.
*
- * </code>
+ * \endcode
*
* Available since API level 29.
*/
@@ -142,7 +142,7 @@
/**
* Close an opened system font iterator, freeing any related resources.
*
- * \param a pointer of an iterator for the system fonts. Do nothing if NULL is passed.
+ * \param iterator a pointer of an iterator for the system fonts. Do nothing if NULL is passed.
*/
void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTRODUCED_IN(29);
@@ -174,7 +174,7 @@
* drawing Tofu character.
*
* Examples:
- * <code>
+ * \code{.cpp}
* // Simple font query for the ASCII character.
* std::vector<uint16_t> text = { 'A' };
* ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
@@ -202,15 +202,15 @@
* ASystemFont font = ASystemFont_matchFamilyStyleCharacter(
* "sans", 400, false, "en-US", text.data(), text.length(), &runLength);
* // runLength will be 1 and the font will points a Hebrew font.
- * </code>
+ * \endcode
*
* \param familyName a null character terminated font family name
* \param weight a font weight value. Only from 0 to 1000 value is valid
* \param italic true if italic, otherwise false.
* \param languageTags a null character terminated comma separated IETF BCP47 compliant language
* tags.
- * \param text a UTF-16 encoded text buffer to be rendered.
- * \param textLength a length of the given text buffer.
+ * \param text a UTF-16 encoded text buffer to be rendered. Do not pass empty string.
+ * \param textLength a length of the given text buffer. This must not be zero.
* \param runLengthOut if not null, the font run length will be filled.
* \return a font to be used for given text and params. You need to release the returned font by
* ASystemFont_close when it is no longer needed.
@@ -239,7 +239,7 @@
* The font file returned is guaranteed to be opend with O_RDONLY.
* Note that the returned pointer is valid until ASystemFont_close() is called for the given font.
*
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \param font a font object. Passing NULL is not allowed.
* \return a string of the font file path.
*/
const char* _Nonnull ASystemFont_getFontFilePath(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -251,14 +251,11 @@
* Here are pairs of the common names and their values.
* <p>
* <table>
- * <thead>
* <tr>
* <th align="center">Value</th>
* <th align="center">Name</th>
* <th align="center">NDK Definition</th>
* </tr>
- * </thead>
- * <tbody>
* <tr>
* <td align="center">100</td>
* <td align="center">Thin</td>
@@ -304,13 +301,13 @@
* <td align="center">Black (Heavy)</td>
* <td align="center">{@link ASYSTEM_FONT_WEIGHT_BLACK}</td>
* </tr>
- * </tbody>
+ * </table>
* </p>
* Note that the weight value may fall in between above values, e.g. 250 weight.
*
* For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass)
*
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \param font a font object. Passing NULL is not allowed.
* \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned.
*/
uint16_t ASystemFont_getWeight(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -318,7 +315,7 @@
/**
* Return true if the current font is italic, otherwise returns false.
*
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \param font a font object. Passing NULL is not allowed.
* \return true if italic, otherwise false.
*/
bool ASystemFont_isItalic(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -330,7 +327,7 @@
*
* Note that the returned pointer is valid until ASystemFont_close() is called.
*
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \param font a font object. Passing NULL is not allowed.
* \return a IETF BCP47 compliant langauge tag or nullptr if not available.
*/
const char* _Nullable ASystemFont_getLocale(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -342,7 +339,7 @@
* returns a non-negative value as an font offset in the collection. This
* always returns 0 if the target font file is a regular font.
*
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \param font a font object. Passing NULL is not allowed.
* \return a font collection index.
*/
size_t ASystemFont_getCollectionIndex(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -356,7 +353,7 @@
* 'wght' 700, 'slnt' -12
* In this case, ASystemFont_getAxisCount returns 2 and ASystemFont_getAxisTag
* and ASystemFont_getAxisValue will return following values.
- * <code>
+ * \code{.cpp}
* ASystemFont* font = ASystemFontIterator_next(ite);
*
* // Returns the number of axes
@@ -369,11 +366,11 @@
* // Returns the tag-value pair for the second axis.
* ASystemFont_getAxisTag(font, 1); // Returns 'slnt'(0x736c6e74)
* ASystemFont_getAxisValue(font, 1); // Returns -12.0
- * </code>
+ * \endcode
*
* For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar)
*
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
+ * \param font a font object. Passing NULL is not allowed.
* \return a number of font variation settings.
*/
size_t ASystemFont_getAxisCount(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29);
@@ -384,8 +381,8 @@
*
* See ASystemFont_getAxisCount for more details.
*
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
- * \param an index to the font variation settings. Passing value larger than or
+ * \param font a font object. Passing NULL is not allowed.
+ * \param axisIndex an index to the font variation settings. Passing value larger than or
* equal to {@link ASystemFont_getAxisCount} is not allowed.
* \return an OpenType axis tag value for the given font variation setting.
*/
@@ -397,8 +394,8 @@
*
* See ASystemFont_getAxisCount for more details.
*
- * \param iterator an iterator for the system fonts. Passing NULL is not allowed.
- * \param an index to the font variation settings. Passing value larger than or
+ * \param font a font object. Passing NULL is not allowed.
+ * \param axisIndex an index to the font variation settings. Passing value larger than or
* equal to {@link ASYstemFont_getAxisCount} is not allwed.
* \return a float value for the given font variation setting.
*/
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 0de3bb7..fa456bb 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -20,6 +20,8 @@
#include <android-base/stringprintf.h>
#include <ui/DisplayInfo.h>
#include <input/Input.h>
+#include <inttypes.h>
+#include <optional>
using android::base::StringPrintf;
@@ -66,13 +68,17 @@
int32_t deviceWidth;
int32_t deviceHeight;
std::string uniqueId;
+ // The actual (hardware) port that the associated display is connected to.
+ // Not all viewports will have this specified.
+ std::optional<uint8_t> physicalPort;
ViewportType type;
DisplayViewport() :
displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
- deviceWidth(0), deviceHeight(0), uniqueId(), type(ViewportType::VIEWPORT_INTERNAL) {
+ deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt),
+ type(ViewportType::VIEWPORT_INTERNAL) {
}
bool operator==(const DisplayViewport& other) const {
@@ -89,6 +95,7 @@
&& deviceWidth == other.deviceWidth
&& deviceHeight == other.deviceHeight
&& uniqueId == other.uniqueId
+ && physicalPort == other.physicalPort
&& type == other.type;
}
@@ -114,16 +121,19 @@
deviceWidth = width;
deviceHeight = height;
uniqueId.clear();
+ physicalPort = std::nullopt;
type = ViewportType::VIEWPORT_INTERNAL;
}
std::string toString() const {
- return StringPrintf("Viewport %s: displayId=%d, orientation=%d, "
+ return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, "
"logicalFrame=[%d, %d, %d, %d], "
"physicalFrame=[%d, %d, %d, %d], "
"deviceSize=[%d, %d]",
- viewportTypeToString(type),
- displayId, orientation,
+ viewportTypeToString(type), displayId,
+ uniqueId.c_str(),
+ physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>",
+ orientation,
logicalLeft, logicalTop,
logicalRight, logicalBottom,
physicalLeft, physicalTop,
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h
index 1ef8986..610834d 100644
--- a/include/input/IInputFlinger.h
+++ b/include/input/IInputFlinger.h
@@ -36,6 +36,9 @@
DECLARE_META_INTERFACE(InputFlinger)
virtual void setInputWindows(const Vector<InputWindowInfo>& inputHandles) = 0;
+
+ virtual void registerInputChannel(const sp<InputChannel>& channel) = 0;
+ virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0;
};
@@ -46,6 +49,8 @@
public:
enum {
SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+ REGISTER_INPUT_CHANNEL_TRANSACTION,
+ UNREGISTER_INPUT_CHANNEL_TRANSACTION
};
virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/include/input/Input.h b/include/input/Input.h
index cc45aef..ee22bc6 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -244,7 +244,12 @@
float getAxisValue(int32_t axis) const;
status_t setAxisValue(int32_t axis, float value);
- void scale(float scale);
+ void scale(float globalScale);
+
+ // Scale the pointer coordinates according to a global scale and a
+ // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR
+ // axes, however the window scaling will not.
+ void scale(float globalScale, float windowXScale, float windowYScale);
void applyOffset(float xOffset, float yOffset);
inline float getX() const {
@@ -595,7 +600,7 @@
void offsetLocation(float xOffset, float yOffset);
- void scale(float scaleFactor);
+ void scale(float globalScaleFactor);
// Apply 3x3 perspective matrix transformation.
// Matrix is in row-major form and compatible with SkMatrix.
diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h
index 9b365b9..71a8f20 100644
--- a/include/input/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -19,6 +19,9 @@
#include <string>
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+
#include <input/Input.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
@@ -29,8 +32,12 @@
* Describes the properties of an application that can receive input.
*/
struct InputApplicationInfo {
+ sp<IBinder> token;
std::string name;
nsecs_t dispatchingTimeout;
+
+ status_t write(Parcel& output) const;
+ static InputApplicationInfo read(const Parcel& from);
};
@@ -54,6 +61,10 @@
return mInfo ? mInfo->dispatchingTimeout : defaultValue;
}
+ inline sp<IBinder> getApplicationToken() const {
+ return mInfo ? mInfo->token : nullptr;
+ }
+
/**
* Requests that the state of this object be updated to reflect
* the most current available information about the application.
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 6d072a3..59d16d1 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -325,6 +325,9 @@
DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT),
DEFINE_KEYCODE(ALL_APPS),
DEFINE_KEYCODE(REFRESH),
+ DEFINE_KEYCODE(THUMBS_UP),
+ DEFINE_KEYCODE(THUMBS_DOWN),
+ DEFINE_KEYCODE(PROFILE_SWITCH),
{ nullptr, 0 }
};
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 9e3d334..8dd95cf 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -116,17 +116,42 @@
INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002,
INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004,
};
-
- sp<InputChannel> inputChannel;
+
+ /* These values are filled in by the WM and passed through SurfaceFlinger
+ * unless specified otherwise.
+ */
+ sp<IBinder> token;
std::string name;
int32_t layoutParamsFlags;
int32_t layoutParamsType;
nsecs_t dispatchingTimeout;
+
+ /* These values are filled in by SurfaceFlinger. */
int32_t frameLeft;
int32_t frameTop;
int32_t frameRight;
int32_t frameBottom;
- float scaleFactor;
+
+ /*
+ * SurfaceFlinger consumes this value to shrink the computed frame. This is
+ * different from shrinking the touchable region in that it DOES shift the coordinate
+ * space where-as the touchable region does not and is more like "cropping". This
+ * is used for window shadows.
+ */
+ int32_t surfaceInset = 0;
+
+ // A global scaling factor for all windows. Unlike windowScaleX/Y this results
+ // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis.
+ float globalScaleFactor;
+
+ // Scaling factors applied to individual windows.
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
+
+ /*
+ * This is filled in by the WM relative to the frame and then translated
+ * to absolute coordinates by SurfaceFlinger once the frame is computed.
+ */
Region touchableRegion;
bool visible;
bool canReceiveKeys;
@@ -138,6 +163,7 @@
int32_t ownerUid;
int32_t inputFeatures;
int32_t displayId;
+ InputApplicationInfo applicationInfo;
void addTouchableRegion(const Rect& region);
@@ -168,20 +194,23 @@
*/
class InputWindowHandle : public RefBase {
public:
- const sp<InputApplicationHandle> inputApplicationHandle;
inline const InputWindowInfo* getInfo() const {
return &mInfo;
}
- sp<InputChannel> getInputChannel() const;
+ sp<IBinder> getToken() const;
+
+ sp<IBinder> getApplicationToken() {
+ return mInfo.applicationInfo.token;
+ }
inline std::string getName() const {
- return mInfo.inputChannel ? mInfo.name : "<invalid>";
+ return mInfo.token ? mInfo.name : "<invalid>";
}
inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo.inputChannel? mInfo.dispatchingTimeout : defaultValue;
+ return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
}
/**
@@ -202,7 +231,7 @@
void releaseChannel();
protected:
- explicit InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle);
+ explicit InputWindowHandle();
virtual ~InputWindowHandle();
InputWindowInfo mInfo;
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index f0c21f5..8df83f1 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -109,9 +109,7 @@
"BC_DEAD_BINDER_DONE"
};
-// The work source represents the UID of the process we should attribute the transaction to.
-// We use -1 to specify that the work source was not set using #setWorkSource.
-static const int kUnsetWorkSource = -1;
+static const int64_t kWorkSourcePropagatedBitIndex = 32;
static const char* getReturnString(uint32_t cmd)
{
@@ -389,12 +387,29 @@
int64_t IPCThreadState::setCallingWorkSourceUid(uid_t uid)
{
- // Note: we currently only use half of the int64. We return an int64 for extensibility.
- int64_t token = mWorkSource;
+ int64_t token = setCallingWorkSourceUidWithoutPropagation(uid);
+ mPropagateWorkSource = true;
+ return token;
+}
+
+int64_t IPCThreadState::setCallingWorkSourceUidWithoutPropagation(uid_t uid)
+{
+ const int64_t propagatedBit = ((int64_t)mPropagateWorkSource) << kWorkSourcePropagatedBitIndex;
+ int64_t token = propagatedBit | mWorkSource;
mWorkSource = uid;
return token;
}
+void IPCThreadState::clearPropagateWorkSource()
+{
+ mPropagateWorkSource = false;
+}
+
+bool IPCThreadState::shouldPropagateWorkSource() const
+{
+ return mPropagateWorkSource;
+}
+
uid_t IPCThreadState::getCallingWorkSourceUid() const
{
return mWorkSource;
@@ -408,7 +423,8 @@
void IPCThreadState::restoreCallingWorkSource(int64_t token)
{
uid_t uid = (int)token;
- setCallingWorkSourceUid(uid);
+ setCallingWorkSourceUidWithoutPropagation(uid);
+ mPropagateWorkSource = ((token >> kWorkSourcePropagatedBitIndex) & 1) == 1;
}
void IPCThreadState::setLastTransactionBinderFlags(int32_t flags)
@@ -765,6 +781,7 @@
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mWorkSource(kUnsetWorkSource),
+ mPropagateWorkSource(false),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
@@ -1127,6 +1144,13 @@
const uid_t origUid = mCallingUid;
const int32_t origStrictModePolicy = mStrictModePolicy;
const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
+ const int32_t origWorkSource = mWorkSource;
+ const bool origPropagateWorkSet = mPropagateWorkSource;
+ // Calling work source will be set by Parcel#enforceInterface. Parcel#enforceInterface
+ // is only guaranteed to be called for AIDL-generated stubs so we reset the work source
+ // here to never propagate it.
+ clearCallingWorkSource();
+ clearPropagateWorkSource();
mCallingPid = tr.sender_pid;
mCallingUid = tr.sender_euid;
@@ -1179,6 +1203,8 @@
mCallingUid = origUid;
mStrictModePolicy = origStrictModePolicy;
mLastTransactionBinderFlags = origTransactionBinderFlags;
+ mWorkSource = origWorkSource;
+ mPropagateWorkSource = origPropagateWorkSet;
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 643f428..a0a634a 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -599,9 +599,10 @@
// Write RPC headers. (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
{
- writeInt32(IPCThreadState::self()->getStrictModePolicy() |
- STRICT_MODE_PENALTY_GATHER);
- writeInt32(IPCThreadState::self()->getCallingWorkSourceUid());
+ const IPCThreadState* threadState = IPCThreadState::self();
+ writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER);
+ writeInt32(threadState->shouldPropagateWorkSource() ?
+ threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource);
// currently the interface identification token is just its name as a string
return writeString16(interface);
}
@@ -631,7 +632,7 @@
}
// WorkSource.
int32_t workSource = readInt32();
- threadState->setCallingWorkSourceUid(workSource);
+ threadState->setCallingWorkSourceUidWithoutPropagation(workSource);
// Interface descriptor.
const String16 str(readString16());
if (str == interface) {
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index de126d5..26e8c0b 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -49,12 +49,16 @@
// See Binder#setCallingWorkSourceUid in Binder.java.
int64_t setCallingWorkSourceUid(uid_t uid);
+ // Internal only. Use setCallingWorkSourceUid(uid) instead.
+ int64_t setCallingWorkSourceUidWithoutPropagation(uid_t uid);
// See Binder#getCallingWorkSourceUid in Binder.java.
uid_t getCallingWorkSourceUid() const;
// See Binder#clearCallingWorkSource in Binder.java.
int64_t clearCallingWorkSource();
// See Binder#restoreCallingWorkSource in Binder.java.
void restoreCallingWorkSource(int64_t token);
+ void clearPropagateWorkSource();
+ bool shouldPropagateWorkSource() const;
void setLastTransactionBinderFlags(int32_t flags);
int32_t getLastTransactionBinderFlags() const;
@@ -127,6 +131,13 @@
// infer information about thread state.
bool isServingCall() const;
+ // The work source represents the UID of the process we should attribute the transaction
+ // to. We use -1 to specify that the work source was not set using #setWorkSource.
+ //
+ // This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java
+ // side.
+ static const int32_t kUnsetWorkSource = -1;
+
private:
IPCThreadState();
~IPCThreadState();
@@ -167,6 +178,8 @@
// The UID of the process who is responsible for this transaction.
// This is used for resource attribution.
int32_t mWorkSource;
+ // Whether the work source should be propagated.
+ bool mPropagateWorkSource;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
IPCThreadStateBase *mIPCThreadStateBase;
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 14ce4cb..d799c5f 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -39,8 +39,6 @@
"libutils",
],
- cpp_std: "c++17",
-
version_script: "libbinder_ndk.map.txt",
}
@@ -52,10 +50,12 @@
"include_ndk/android/*.h",
],
license: "NOTICE",
+ draft: true,
}
ndk_library {
name: "libbinder_ndk",
symbol_file: "libbinder_ndk.map.txt",
first_version: "29",
+ draft: true,
}
diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
index e2c0cfa..c6fcaa4 100644
--- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
@@ -163,7 +163,9 @@
ScopedAResource& operator=(ScopedAResource&&) = delete;
// move-constructing is okay
- ScopedAResource(ScopedAResource&&) = default;
+ ScopedAResource(ScopedAResource&& other) : mT(std::move(other.mT)) {
+ other.mT = DEFAULT;
+ }
private:
T mT;
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index 3594349..866af70 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -54,6 +54,31 @@
void AParcel_delete(AParcel* parcel) __INTRODUCED_IN(29);
/**
+ * Sets the position within the parcel.
+ *
+ * \param parcel The parcel of which to set the position.
+ * \param position Position of the parcel to set. This must be a value returned by
+ * AParcel_getDataPosition. Positions are constant for a given parcel between processes.
+ *
+ * \return STATUS_OK on success. If position is negative, then STATUS_BAD_VALUE will be returned.
+ */
+binder_status_t AParcel_setDataPosition(const AParcel* parcel, int32_t position)
+ __INTRODUCED_IN(29);
+
+/**
+ * Gets the current position within the parcel.
+ *
+ * \param parcel The parcel of which to get the position.
+ *
+ * \return The size of the parcel. This will always be greater than 0. The values returned by this
+ * function before and after calling various reads and writes are not defined. Only the delta
+ * between two positions between a specific sequence of calls is defined. For instance, if position
+ * is X, writeBool is called, and then position is Y, readBool can be called from position X will
+ * return the same value, and then position will be Y.
+ */
+int32_t AParcel_getDataPosition(const AParcel* parcel) __INTRODUCED_IN(29);
+
+/**
* This is called to allocate a buffer for a C-style string (null-terminated). The returned buffer
* should be at least length bytes. This includes space for a null terminator. For a string, length
* will always be strictly less than or equal to the maximum size that can be held in a size_t and
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 41df90b..4328b6e 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -25,6 +25,7 @@
AIBinder_Weak_new;
AIBinder_Weak_promote;
AParcel_delete;
+ AParcel_getDataPosition;
AParcel_readBool;
AParcel_readBoolArray;
AParcel_readByte;
@@ -48,6 +49,7 @@
AParcel_readUint32Array;
AParcel_readUint64;
AParcel_readUint64Array;
+ AParcel_setDataPosition;
AParcel_writeBool;
AParcel_writeBoolArray;
AParcel_writeByte;
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 3c32100..2d68559 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -212,6 +212,19 @@
delete parcel;
}
+binder_status_t AParcel_setDataPosition(const AParcel* parcel, int32_t position) {
+ if (position < 0) {
+ return STATUS_BAD_VALUE;
+ }
+
+ parcel->get()->setDataPosition(position);
+ return STATUS_OK;
+}
+
+int32_t AParcel_getDataPosition(const AParcel* parcel) {
+ return parcel->get()->dataPosition();
+}
+
binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) {
sp<IBinder> writeBinder = binder != nullptr ? binder->getBinder() : nullptr;
return parcel->get()->writeStrongBinder(writeBinder);
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index 67481cf..b29b6e7 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -22,7 +22,6 @@
strip: {
none: true,
},
- cpp_std: "c++17",
cflags: [
"-O0",
"-g",
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 22c1bad..cd37d49 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -180,6 +180,7 @@
public:
virtual void SetUp() {
m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer();
+ IPCThreadState::self()->restoreCallingWorkSource(0);
}
virtual void TearDown() {
}
@@ -953,11 +954,28 @@
{
status_t ret;
Parcel data, reply;
+ IPCThreadState::self()->clearCallingWorkSource();
int64_t previousWorkSource = IPCThreadState::self()->setCallingWorkSourceUid(100);
data.writeInterfaceToken(binderLibTestServiceName);
ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
EXPECT_EQ(100, reply.readInt32());
EXPECT_EQ(-1, previousWorkSource);
+ EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
+ EXPECT_EQ(NO_ERROR, ret);
+}
+
+TEST_F(BinderLibTest, WorkSourceSetWithoutPropagation)
+{
+ status_t ret;
+ Parcel data, reply;
+
+ IPCThreadState::self()->setCallingWorkSourceUidWithoutPropagation(100);
+ EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+
+ data.writeInterfaceToken(binderLibTestServiceName);
+ ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+ EXPECT_EQ(-1, reply.readInt32());
+ EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
EXPECT_EQ(NO_ERROR, ret);
}
@@ -967,7 +985,8 @@
Parcel data, reply;
IPCThreadState::self()->setCallingWorkSourceUid(100);
- int64_t previousWorkSource = IPCThreadState::self()->clearCallingWorkSource();
+ int64_t token = IPCThreadState::self()->clearCallingWorkSource();
+ int32_t previousWorkSource = (int32_t)token;
data.writeInterfaceToken(binderLibTestServiceName);
ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
@@ -989,9 +1008,58 @@
ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
EXPECT_EQ(100, reply.readInt32());
+ EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
EXPECT_EQ(NO_ERROR, ret);
}
+TEST_F(BinderLibTest, PropagateFlagSet)
+{
+ status_t ret;
+ Parcel data, reply;
+
+ IPCThreadState::self()->clearPropagateWorkSource();
+ IPCThreadState::self()->setCallingWorkSourceUid(100);
+ EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, PropagateFlagCleared)
+{
+ status_t ret;
+ Parcel data, reply;
+
+ IPCThreadState::self()->setCallingWorkSourceUid(100);
+ IPCThreadState::self()->clearPropagateWorkSource();
+ EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, PropagateFlagRestored)
+{
+ status_t ret;
+ Parcel data, reply;
+
+ int token = IPCThreadState::self()->setCallingWorkSourceUid(100);
+ IPCThreadState::self()->restoreCallingWorkSource(token);
+
+ EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource());
+}
+
+TEST_F(BinderLibTest, WorkSourcePropagatedForAllFollowingBinderCalls)
+{
+ IPCThreadState::self()->setCallingWorkSourceUid(100);
+
+ Parcel data, reply;
+ status_t ret;
+ data.writeInterfaceToken(binderLibTestServiceName);
+ ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply);
+
+ Parcel data2, reply2;
+ status_t ret2;
+ data2.writeInterfaceToken(binderLibTestServiceName);
+ ret2 = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data2, &reply2);
+ EXPECT_EQ(100, reply2.readInt32());
+ EXPECT_EQ(NO_ERROR, ret2);
+}
+
class BinderLibTestService : public BBinder
{
public:
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 0d224d8..04884bb 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -48,6 +48,7 @@
"android.hardware.drm@1.0::IDrmFactory",
"android.hardware.graphics.allocator@2.0::IAllocator",
"android.hardware.graphics.composer@2.1::IComposer",
+ "android.hardware.health@2.0::IHealth",
"android.hardware.media.omx@1.0::IOmx",
"android.hardware.media.omx@1.0::IOmxStore",
"android.hardware.sensors@1.0::ISensors",
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index bab87ac..280c14a 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -22,8 +22,9 @@
cflags: ["-Wall", "-Werror"],
shared_libs: [
- "liblog",
+ "libbase",
"libcutils",
+ "liblog",
],
export_include_dirs: ["include"],
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index afa32b6..c2a6764 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -18,32 +18,99 @@
#define LOG_TAG "GraphicsEnv"
#include <graphicsenv/GraphicsEnv.h>
+#include <dlfcn.h>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android/dlext.h>
+#include <cutils/properties.h>
+#include <log/log.h>
#include <sys/prctl.h>
#include <mutex>
-#include <android/dlext.h>
-#include <cutils/properties.h>
-#include <log/log.h>
-
// TODO(b/37049319) Get this from a header once one exists
extern "C" {
- android_namespace_t* android_get_exported_namespace(const char*);
- android_namespace_t* android_create_namespace(const char* name,
- const char* ld_library_path,
- const char* default_library_path,
- uint64_t type,
- const char* permitted_when_isolated_path,
- android_namespace_t* parent);
+android_namespace_t* android_get_exported_namespace(const char*);
+android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path,
+ const char* default_library_path, uint64_t type,
+ const char* permitted_when_isolated_path,
+ android_namespace_t* parent);
+bool android_link_namespaces(android_namespace_t* from, android_namespace_t* to,
+ const char* shared_libs_sonames);
- enum {
- ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
- ANDROID_NAMESPACE_TYPE_SHARED = 2,
- };
+enum {
+ ANDROID_NAMESPACE_TYPE_ISOLATED = 1,
+ ANDROID_NAMESPACE_TYPE_SHARED = 2,
+};
}
namespace android {
+enum NativeLibrary {
+ LLNDK = 0,
+ VNDKSP = 1,
+};
+
+static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/etc/llndk.libraries.txt",
+ "/etc/vndksp.libraries.txt"};
+
+static std::string vndkVersionStr() {
+#ifdef __BIONIC__
+ std::string version = android::base::GetProperty("ro.vndk.version", "");
+ if (version != "" && version != "current") {
+ return "." + version;
+ }
+#endif
+ return "";
+}
+
+static void insertVndkVersionStr(std::string* fileName) {
+ LOG_ALWAYS_FATAL_IF(!fileName, "fileName should never be nullptr");
+ size_t insertPos = fileName->find_last_of(".");
+ if (insertPos == std::string::npos) {
+ insertPos = fileName->length();
+ }
+ fileName->insert(insertPos, vndkVersionStr());
+}
+
+static bool readConfig(const std::string& configFile, std::vector<std::string>* soNames) {
+ // Read list of public native libraries from the config file.
+ std::string fileContent;
+ if (!base::ReadFileToString(configFile, &fileContent)) {
+ return false;
+ }
+
+ std::vector<std::string> lines = base::Split(fileContent, "\n");
+
+ for (auto& line : lines) {
+ auto trimmedLine = base::Trim(line);
+ if (!trimmedLine.empty()) {
+ soNames->push_back(trimmedLine);
+ }
+ }
+
+ return true;
+}
+
+static const std::string getSystemNativeLibraries(NativeLibrary type) {
+ static const char* androidRootEnv = getenv("ANDROID_ROOT");
+ static const std::string rootDir = androidRootEnv != nullptr ? androidRootEnv : "/system";
+
+ std::string nativeLibrariesSystemConfig = rootDir + kNativeLibrariesSystemConfigPath[type];
+
+ insertVndkVersionStr(&nativeLibrariesSystemConfig);
+
+ std::vector<std::string> soNames;
+ if (!readConfig(nativeLibrariesSystemConfig, &soNames)) {
+ ALOGE("Failed to retrieve library names from %s", nativeLibrariesSystemConfig.c_str());
+ return "";
+ }
+
+ return base::Join(soNames, ':');
+}
+
/*static*/ GraphicsEnv& GraphicsEnv::getInstance() {
static GraphicsEnv env;
return env;
@@ -59,8 +126,8 @@
void GraphicsEnv::setDriverPath(const std::string path) {
if (!mDriverPath.empty()) {
- ALOGV("ignoring attempt to change driver path from '%s' to '%s'",
- mDriverPath.c_str(), path.c_str());
+ ALOGV("ignoring attempt to change driver path from '%s' to '%s'", mDriverPath.c_str(),
+ path.c_str());
return;
}
ALOGV("setting driver path to '%s'", path.c_str());
@@ -102,7 +169,7 @@
mAppNamespace = appNamespace;
} else {
ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'",
- layerPaths.c_str(), appNamespace);
+ layerPaths.c_str(), appNamespace);
}
}
@@ -154,20 +221,41 @@
android_namespace_t* GraphicsEnv::getDriverNamespace() {
static std::once_flag once;
std::call_once(once, [this]() {
- if (mDriverPath.empty())
- return;
- // If the sphal namespace isn't configured for a device, don't support updatable drivers.
- // We need a parent namespace to inherit the default search path from.
- auto sphalNamespace = android_get_exported_namespace("sphal");
- if (!sphalNamespace) return;
+ if (mDriverPath.empty()) return;
+
+ auto vndkNamespace = android_get_exported_namespace("vndk");
+ if (!vndkNamespace) return;
+
mDriverNamespace = android_create_namespace("gfx driver",
mDriverPath.c_str(), // ld_library_path
mDriverPath.c_str(), // default_library_path
- ANDROID_NAMESPACE_TYPE_SHARED |
- ANDROID_NAMESPACE_TYPE_ISOLATED,
+ ANDROID_NAMESPACE_TYPE_ISOLATED,
nullptr, // permitted_when_isolated_path
- sphalNamespace);
+ nullptr);
+
+ const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK);
+ if (llndkLibraries.empty()) {
+ mDriverNamespace = nullptr;
+ return;
+ }
+ if (!android_link_namespaces(mDriverNamespace, nullptr, llndkLibraries.c_str())) {
+ ALOGE("Failed to link default namespace[%s]", dlerror());
+ mDriverNamespace = nullptr;
+ return;
+ }
+
+ const std::string vndkspLibraries = getSystemNativeLibraries(NativeLibrary::VNDKSP);
+ if (vndkspLibraries.empty()) {
+ mDriverNamespace = nullptr;
+ return;
+ }
+ if (!android_link_namespaces(mDriverNamespace, vndkNamespace, vndkspLibraries.c_str())) {
+ ALOGE("Failed to link vndk namespace[%s]", dlerror());
+ mDriverNamespace = nullptr;
+ return;
+ }
});
+
return mDriverNamespace;
}
diff --git a/libs/gui/HdrMetadata.cpp b/libs/gui/HdrMetadata.cpp
index b715e43..add3ef0 100644
--- a/libs/gui/HdrMetadata.cpp
+++ b/libs/gui/HdrMetadata.cpp
@@ -15,6 +15,7 @@
*/
#include <gui/HdrMetadata.h>
+#include <limits>
namespace android {
@@ -26,6 +27,10 @@
if (validTypes & CTA861_3) {
size += sizeof(cta8613);
}
+ if (validTypes & HDR10PLUS) {
+ size += sizeof(size_t);
+ size += hdr10plus.size();
+ }
return size;
}
@@ -41,6 +46,12 @@
if (validTypes & CTA861_3) {
FlattenableUtils::write(buffer, size, cta8613);
}
+ if (validTypes & HDR10PLUS) {
+ size_t metadataSize = hdr10plus.size();
+ FlattenableUtils::write(buffer, size, metadataSize);
+ memcpy(buffer, hdr10plus.data(), metadataSize);
+ FlattenableUtils::advance(buffer, size, metadataSize);
+ }
return NO_ERROR;
}
@@ -62,6 +73,22 @@
}
FlattenableUtils::read(buffer, size, cta8613);
}
+ if (validTypes & HDR10PLUS) {
+ if (size < sizeof(size_t)) {
+ return NO_MEMORY;
+ }
+
+ size_t metadataSize;
+ FlattenableUtils::read(buffer, size, metadataSize);
+
+ if (size < metadataSize) {
+ return NO_MEMORY;
+ }
+
+ hdr10plus.resize(metadataSize);
+ memcpy(hdr10plus.data(), buffer, metadataSize);
+ FlattenableUtils::advance(buffer, size, metadataSize);
+ }
return NO_ERROR;
}
@@ -91,6 +118,10 @@
}
}
+ if ((validTypes & HDR10PLUS) == HDR10PLUS) {
+ if (hdr10plus != rhs.hdr10plus) return false;
+ }
+
return true;
}
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 69e5379..80d435f 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -599,6 +599,43 @@
}
return err;
}
+
+ virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const {
+ if (!outFormat || !outDataspace || !outComponentMask) return BAD_VALUE;
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ data.writeStrongBinder(display);
+
+ status_t error =
+ remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES,
+ data, &reply);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ uint32_t value = 0;
+ error = reply.readUint32(&value);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ *outFormat = static_cast<ui::PixelFormat>(value);
+
+ error = reply.readUint32(&value);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ *outDataspace = static_cast<ui::Dataspace>(value);
+
+ error = reply.readUint32(&value);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ *outComponentMask = static_cast<uint8_t>(value);
+ return error;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -951,6 +988,22 @@
}
return result;
}
+ case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+ sp<IBinder> display = data.readStrongBinder();
+ ui::PixelFormat format;
+ ui::Dataspace dataspace;
+ uint8_t component = 0;
+ auto result =
+ getDisplayedContentSamplingAttributes(display, &format, &dataspace, &component);
+ if (result == NO_ERROR) {
+ reply->writeUint32(static_cast<uint32_t>(format));
+ reply->writeUint32(static_cast<uint32_t>(dataspace));
+ reply->writeUint32(static_cast<uint32_t>(component));
+ }
+ return result;
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 9f30060..407eecb 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -82,6 +82,7 @@
memcpy(output.writeInplace(16 * sizeof(float)),
colorTransform.asArray(), 16 * sizeof(float));
+ output.writeFloat(cornerRadius);
if (output.writeVectorSize(listenerCallbacks) == NO_ERROR) {
for (const auto& [listener, callbackIds] : listenerCallbacks) {
@@ -149,6 +150,7 @@
}
colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
+ cornerRadius = input.readFloat();
int32_t listenersSize = input.readInt32();
for (int32_t i = 0; i < listenersSize; i++) {
@@ -270,6 +272,10 @@
what |= eCropChanged_legacy;
crop_legacy = other.crop_legacy;
}
+ if (other.what & eCornerRadiusChanged) {
+ what |= eCornerRadiusChanged;
+ cornerRadius = other.cornerRadius;
+ }
if (other.what & eDeferTransaction_legacy) {
what |= eDeferTransaction_legacy;
barrierHandle_legacy = other.barrierHandle_legacy;
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index b505c6f..00e23f0 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -965,6 +965,9 @@
case NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA:
res = dispatchSetBuffersCta8613Metadata(args);
break;
+ case NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA:
+ res = dispatchSetBuffersHdr10PlusMetadata(args);
+ break;
case NATIVE_WINDOW_SET_SURFACE_DAMAGE:
res = dispatchSetSurfaceDamage(args);
break;
@@ -1120,6 +1123,12 @@
return setBuffersCta8613Metadata(metadata);
}
+int Surface::dispatchSetBuffersHdr10PlusMetadata(va_list args) {
+ const size_t size = va_arg(args, size_t);
+ const uint8_t* metadata = va_arg(args, const uint8_t*);
+ return setBuffersHdr10PlusMetadata(size, metadata);
+}
+
int Surface::dispatchSetSurfaceDamage(va_list args) {
android_native_rect_t* rects = va_arg(args, android_native_rect_t*);
size_t numRects = va_arg(args, size_t);
@@ -1568,6 +1577,19 @@
return NO_ERROR;
}
+int Surface::setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata) {
+ ALOGV("Surface::setBuffersBlobMetadata");
+ Mutex::Autolock lock(mMutex);
+ if (size > 0) {
+ mHdrMetadata.hdr10plus.assign(metadata, metadata + size);
+ mHdrMetadata.validTypes |= HdrMetadata::HDR10PLUS;
+ } else {
+ mHdrMetadata.validTypes &= ~HdrMetadata::HDR10PLUS;
+ mHdrMetadata.hdr10plus.clear();
+ }
+ return NO_ERROR;
+}
+
Dataspace Surface::getBuffersDataSpace() {
ALOGV("Surface::getBuffersDataSpace");
Mutex::Autolock lock(mMutex);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 87c6f27..9dfccc7 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -475,6 +475,18 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCornerRadius(
+ const sp<SurfaceControl>& sc, float cornerRadius) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eCornerRadiusChanged;
+ s->cornerRadius = cornerRadius;
+ return *this;
+}
+
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
const sp<IBinder>& handle,
@@ -1077,6 +1089,15 @@
outCapabilities);
}
+status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) {
+ return ComposerService::getComposerService()
+ ->getDisplayedContentSamplingAttributes(display, outFormat, outDataspace,
+ outComponentMask);
+}
+
// ----------------------------------------------------------------------------
status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
diff --git a/libs/gui/include/gui/HdrMetadata.h b/libs/gui/include/gui/HdrMetadata.h
index 9800602..1e9c3e7 100644
--- a/libs/gui/include/gui/HdrMetadata.h
+++ b/libs/gui/include/gui/HdrMetadata.h
@@ -17,6 +17,7 @@
#pragma once
#include <stdint.h>
+#include <vector>
#include <system/graphics.h>
#include <utils/Flattenable.h>
@@ -26,12 +27,15 @@
struct HdrMetadata : public LightFlattenable<HdrMetadata> {
enum Type : uint32_t {
SMPTE2086 = 1 << 0,
- CTA861_3 = 1 << 1,
+ CTA861_3 = 1 << 1,
+ HDR10PLUS = 1 << 2,
};
+
uint32_t validTypes{0};
android_smpte2086_metadata smpte2086{};
android_cta861_3_metadata cta8613{};
+ std::vector<uint8_t> hdr10plus{};
// LightFlattenable
bool isFixedSize() const { return false; }
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 761f31a..3b6c6e4 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -293,6 +293,13 @@
ui::PixelFormat* defaultPixelFormat,
ui::Dataspace* wideColorGamutDataspace,
ui::PixelFormat* wideColorGamutPixelFormat) const = 0;
+ /*
+ * Requires the ACCESS_SURFACE_FLINGER permission.
+ */
+ virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const = 0;
};
// ----------------------------------------------------------------------------
@@ -332,6 +339,7 @@
CREATE_SCOPED_CONNECTION,
GET_COMPOSITION_PREFERENCE,
GET_COLOR_MANAGEMENT,
+ GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES,
};
virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index cdb2309..3cfee9e 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -82,6 +82,7 @@
eColorTransformChanged = 0x10000000,
eListenerCallbacksChanged = 0x20000000,
eInputInfoChanged = 0x40000000,
+ eCornerRadiusChanged = 0x80000000,
};
layer_state_t()
@@ -97,6 +98,7 @@
mask(0),
reserved(0),
crop_legacy(Rect::INVALID_RECT),
+ cornerRadius(0.0f),
frameNumber_legacy(0),
overrideScalingMode(-1),
transform(0),
@@ -135,6 +137,7 @@
uint8_t reserved;
matrix22_t matrix;
Rect crop_legacy;
+ float cornerRadius;
sp<IBinder> barrierHandle_legacy;
sp<IBinder> reparentHandle;
uint64_t frameNumber_legacy;
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 32ee595..248e105 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -218,6 +218,7 @@
int dispatchSetBuffersDataSpace(va_list args);
int dispatchSetBuffersSmpte2086Metadata(va_list args);
int dispatchSetBuffersCta8613Metadata(va_list args);
+ int dispatchSetBuffersHdr10PlusMetadata(va_list args);
int dispatchSetSurfaceDamage(va_list args);
int dispatchSetSharedBufferMode(va_list args);
int dispatchSetAutoRefresh(va_list args);
@@ -249,6 +250,7 @@
virtual int setBuffersDataSpace(ui::Dataspace dataSpace);
virtual int setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata);
virtual int setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata);
+ virtual int setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata);
virtual int setCrop(Rect const* rect);
virtual int setUsage(uint64_t reqUsage);
virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects);
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 10c27b1..7d05512 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -265,6 +265,7 @@
Transaction& setMatrix(const sp<SurfaceControl>& sc,
float dsdx, float dtdx, float dtdy, float dsdy);
Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
+ Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
// Defers applying any changes made in this transaction until the Layer
// identified by handle reaches the given frameNumber. If the Layer identified
@@ -381,6 +382,11 @@
inline sp<ISurfaceComposerClient> getClient() { return mClient; }
+ static status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask);
+
private:
virtual void onFirstRef();
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 7ecadf8..6de641d 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -16,6 +16,8 @@
"BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
"CpuConsumer_test.cpp",
+ "EndToEndNativeInputTest.cpp",
+ "DisplayedContentSampling_test.cpp",
"FillBuffer.cpp",
"GLTest.cpp",
"IGraphicBufferProducer_test.cpp",
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
new file mode 100644
index 0000000..f2c0e0c
--- /dev/null
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <binder/ProcessState.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <inttypes.h>
+
+namespace android {
+
+using Transaction = SurfaceComposerClient::Transaction;
+
+static constexpr uint32_t INVALID_MASK = 0x10;
+class DisplayedContentSamplingTest : public ::testing::Test {
+protected:
+ void SetUp() {
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(OK, mComposerClient->initCheck());
+ mDisplayToken = mComposerClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
+ ASSERT_TRUE(mDisplayToken);
+ }
+
+ bool shouldSkipTest(status_t status) {
+ if (status == PERMISSION_DENIED) {
+ SUCCEED() << "permissions denial, skipping test";
+ return true;
+ }
+ if (status == INVALID_OPERATION) {
+ SUCCEED() << "optional function not supported, skipping test";
+ return true;
+ }
+ return false;
+ }
+
+ sp<SurfaceComposerClient> mComposerClient;
+ sp<IBinder> mDisplayToken;
+};
+
+TEST_F(DisplayedContentSamplingTest, GetDisplayedContentSamplingAttributesAreSane) {
+ ui::PixelFormat format;
+ ui::Dataspace dataspace;
+ uint8_t componentMask = 0;
+ status_t status =
+ mComposerClient->getDisplayedContentSamplingAttributes(mDisplayToken, &format,
+ &dataspace, &componentMask);
+ if (shouldSkipTest(status)) {
+ return;
+ }
+ EXPECT_EQ(OK, status);
+ EXPECT_LE(componentMask, INVALID_MASK);
+}
+} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
new file mode 100644
index 0000000..86e9c23
--- /dev/null
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <poll.h>
+
+#include <memory>
+
+#include <binder/Binder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+
+#include <input/InputWindow.h>
+#include <input/IInputFlinger.h>
+#include <input/InputTransport.h>
+#include <input/Input.h>
+
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+
+namespace android {
+namespace test {
+
+sp<IInputFlinger> getInputFlinger() {
+ sp<IBinder> input(defaultServiceManager()->getService(
+ String16("inputflinger")));
+ if (input == nullptr) {
+ ALOGE("Failed to link to input service");
+ } else { ALOGE("Linked to input"); }
+ return interface_cast<IInputFlinger>(input);
+}
+
+// We use the top 10 layers as a way to haphazardly place ourselves above anything else.
+static const int LAYER_BASE = INT32_MAX - 10;
+
+class InputSurface {
+public:
+ InputSurface(const sp<SurfaceComposerClient>& scc, int width, int height) {
+ mSurfaceControl = scc->createSurface(String8("Test Surface"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor);
+
+ InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
+ mServerChannel->setToken(new BBinder());
+
+ mInputFlinger = getInputFlinger();
+ mInputFlinger->registerInputChannel(mServerChannel);
+
+ populateInputInfo(width, height);
+
+ mInputConsumer = new InputConsumer(mClientChannel);
+ }
+
+ InputEvent* consumeEvent() {
+ waitForEventAvailable();
+
+ InputEvent *ev;
+ uint32_t seqId;
+ status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev);
+ if (consumed != OK) {
+ return nullptr;
+ }
+ mInputConsumer->sendFinishedSignal(seqId, true);
+ return ev;
+ }
+
+ void expectTap(int x, int y) {
+ InputEvent* ev = consumeEvent();
+ EXPECT_TRUE(ev != nullptr);
+ EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+ MotionEvent* mev = static_cast<MotionEvent*>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
+ EXPECT_EQ(x, mev->getX(0));
+ EXPECT_EQ(y, mev->getY(0));
+
+ ev = consumeEvent();
+ EXPECT_TRUE(ev != nullptr);
+ EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+ mev = static_cast<MotionEvent*>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+ }
+
+ ~InputSurface() {
+ mInputFlinger->unregisterInputChannel(mServerChannel);
+ }
+
+ void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
+ const sp<SurfaceControl>&)> transactionBody) {
+ SurfaceComposerClient::Transaction t;
+ transactionBody(t, mSurfaceControl);
+ t.apply(true);
+ }
+
+ void showAt(int x, int y) {
+ SurfaceComposerClient::Transaction t;
+ t.show(mSurfaceControl);
+ t.setInputWindowInfo(mSurfaceControl, mInputInfo);
+ t.setLayer(mSurfaceControl, LAYER_BASE);
+ t.setPosition(mSurfaceControl, x, y);
+ t.setCrop_legacy(mSurfaceControl, Rect(0, 0, 100, 100));
+ t.setAlpha(mSurfaceControl, 1);
+ t.apply(true);
+ }
+
+private:
+ void waitForEventAvailable() {
+ struct pollfd fd;
+
+ fd.fd = mClientChannel->getFd();
+ fd.events = POLLIN;
+ poll(&fd, 1, 3000);
+ }
+
+ void populateInputInfo(int width, int height) {
+ mInputInfo.token = mServerChannel->getToken();
+ mInputInfo.name = "Test info";
+ mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
+ mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
+ mInputInfo.dispatchingTimeout = 100000;
+ mInputInfo.globalScaleFactor = 1.0;
+ mInputInfo.canReceiveKeys = true;
+ mInputInfo.hasFocus = true;
+ mInputInfo.hasWallpaper = false;
+ mInputInfo.paused = false;
+
+ mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height));
+
+ // TODO: Fill in from SF?
+ mInputInfo.ownerPid = 11111;
+ mInputInfo.ownerUid = 11111;
+ mInputInfo.inputFeatures = 0;
+ mInputInfo.displayId = 0;
+
+ InputApplicationInfo aInfo;
+ aInfo.token = new BBinder();
+ aInfo.name = "Test app info";
+ aInfo.dispatchingTimeout = 100000;
+
+ mInputInfo.applicationInfo = aInfo;
+ }
+public:
+ sp<SurfaceControl> mSurfaceControl;
+ sp<InputChannel> mServerChannel, mClientChannel;
+ sp<IInputFlinger> mInputFlinger;
+
+ InputWindowInfo mInputInfo;
+
+ PreallocatedInputEventFactory mInputEventFactory;
+ InputConsumer* mInputConsumer;
+};
+
+class InputSurfacesTest : public ::testing::Test {
+public:
+ InputSurfacesTest() {
+ ProcessState::self()->startThreadPool();
+ }
+
+ void SetUp() {
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+ }
+
+ void TearDown() {
+ mComposerClient->dispose();
+ }
+
+ std::unique_ptr<InputSurface> makeSurface(int width, int height) {
+ return std::make_unique<InputSurface>(mComposerClient, width, height);
+ }
+
+ sp<SurfaceComposerClient> mComposerClient;
+};
+
+void injectTap(int x, int y) {
+ char *buf1, *buf2;
+ asprintf(&buf1, "%d", x);
+ asprintf(&buf2, "%d", y);
+ if (fork() == 0) {
+ execlp("input", "input", "tap", buf1, buf2, NULL);
+ }
+}
+
+TEST_F(InputSurfacesTest, can_receive_input) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ injectTap(101, 101);
+
+ EXPECT_TRUE(surface->consumeEvent() != nullptr);
+}
+
+TEST_F(InputSurfacesTest, input_respects_positioning) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
+ surface2->showAt(200, 200);
+
+ injectTap(201, 201);
+ surface2->expectTap(1, 1);
+
+ injectTap(101, 101);
+ surface->expectTap(1, 1);
+
+ surface2->doTransaction([](auto &t, auto &sc) {
+ t.setPosition(sc, 100, 100);
+ });
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setPosition(sc, 200, 200);
+ });
+
+ injectTap(101, 101);
+ surface2->expectTap(1, 1);
+
+ injectTap(201, 201);
+ surface->expectTap(1, 1);
+}
+
+TEST_F(InputSurfacesTest, input_respects_layering) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
+
+ surface->showAt(10, 10);
+ surface2->showAt(10, 10);
+
+ surface->doTransaction([](auto &t, auto &sc) {
+ t.setLayer(sc, LAYER_BASE + 1);
+ });
+
+ injectTap(11, 11);
+ surface->expectTap(1, 1);
+
+ surface2->doTransaction([](auto &t, auto &sc) {
+ t.setLayer(sc, LAYER_BASE + 1);
+ });
+
+ injectTap(11, 11);
+ surface2->expectTap(1, 1);
+
+ surface2->doTransaction([](auto &t, auto &sc) {
+ t.hide(sc);
+ });
+
+ injectTap(11, 11);
+ surface->expectTap(1, 1);
+}
+
+}
+}
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index d0600da..3950bb6 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -366,10 +366,17 @@
78.0,
62.0,
};
+
+ std::vector<uint8_t> hdr10plus;
+ hdr10plus.push_back(0xff);
+
int error = native_window_set_buffers_smpte2086_metadata(window.get(), &smpte2086);
ASSERT_EQ(error, NO_ERROR);
error = native_window_set_buffers_cta861_3_metadata(window.get(), &cta861_3);
ASSERT_EQ(error, NO_ERROR);
+ error = native_window_set_buffers_hdr10_plus_metadata(window.get(), hdr10plus.size(),
+ hdr10plus.data());
+ ASSERT_EQ(error, NO_ERROR);
}
TEST_F(SurfaceTest, DynamicSetBufferCount) {
@@ -634,6 +641,12 @@
ui::PixelFormat* /*outWideColorGamutPixelFormat*/) const override {
return NO_ERROR;
}
+ status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& /*display*/,
+ ui::PixelFormat* /*outFormat*/,
+ ui::Dataspace* /*outDataspace*/,
+ uint8_t* /*outComponentMask*/) const override {
+ return NO_ERROR;
+ }
virtual status_t getColorManagement(bool* /*outGetColorManagement*/) const { return NO_ERROR; }
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 8cb8649..fc676f1 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -16,7 +16,6 @@
cc_library {
name: "libinput",
- cpp_std: "c++17",
host_supported: true,
cflags: [
"-Wall",
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
index 47a2c0c..139570a 100644
--- a/libs/input/IInputFlinger.cpp
+++ b/libs/input/IInputFlinger.cpp
@@ -38,7 +38,22 @@
for (const auto& info : inputInfo) {
info.write(data);
}
- remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply);
+ remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply,
+ IBinder::FLAG_ONEWAY);
+ }
+
+ virtual void registerInputChannel(const sp<InputChannel>& channel) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
+ channel->write(data);
+ remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
+ }
+
+ virtual void unregisterInputChannel(const sp<InputChannel>& channel) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor());
+ channel->write(data);
+ remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply);
}
};
@@ -61,6 +76,20 @@
setInputWindows(handles);
break;
}
+ case REGISTER_INPUT_CHANNEL_TRANSACTION: {
+ CHECK_INTERFACE(IInputFlinger, data, reply);
+ sp<InputChannel> channel = new InputChannel();
+ channel->read(data);
+ registerInputChannel(channel);
+ break;
+ }
+ case UNREGISTER_INPUT_CHANNEL_TRANSACTION: {
+ CHECK_INTERFACE(IInputFlinger, data, reply);
+ sp<InputChannel> channel = new InputChannel();
+ channel->read(data);
+ unregisterInputChannel(channel);
+ break;
+ }
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 8a15e2f..a558970 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -131,15 +131,24 @@
}
}
-void PointerCoords::scale(float scaleFactor) {
+void PointerCoords::scale(float globalScaleFactor, float windowXScale, float windowYScale) {
// No need to scale pressure or size since they are normalized.
// No need to scale orientation since it is meaningless to do so.
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor);
- scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor);
+
+ // If there is a global scale factor, it is included in the windowX/YScale
+ // so we don't need to apply it twice to the X/Y axes.
+ // However we don't want to apply any windowXYScale not included in the global scale
+ // to the TOUCH_MAJOR/MINOR coordinates.
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, windowXScale);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, windowYScale);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, globalScaleFactor);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, globalScaleFactor);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, globalScaleFactor);
+ scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, globalScaleFactor);
+}
+
+void PointerCoords::scale(float globalScaleFactor) {
+ scale(globalScaleFactor, globalScaleFactor, globalScaleFactor);
}
void PointerCoords::applyOffset(float xOffset, float yOffset) {
@@ -345,15 +354,15 @@
mYOffset += yOffset;
}
-void MotionEvent::scale(float scaleFactor) {
- mXOffset *= scaleFactor;
- mYOffset *= scaleFactor;
- mXPrecision *= scaleFactor;
- mYPrecision *= scaleFactor;
+void MotionEvent::scale(float globalScaleFactor) {
+ mXOffset *= globalScaleFactor;
+ mYOffset *= globalScaleFactor;
+ mXPrecision *= globalScaleFactor;
+ mYPrecision *= globalScaleFactor;
size_t numSamples = mSamplePointerCoords.size();
for (size_t i = 0; i < numSamples; i++) {
- mSamplePointerCoords.editItemAt(i).scale(scaleFactor);
+ mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor);
}
}
diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp
index a0d1668..7936f50 100644
--- a/libs/input/InputApplication.cpp
+++ b/libs/input/InputApplication.cpp
@@ -39,4 +39,21 @@
}
}
+InputApplicationInfo InputApplicationInfo::read(const Parcel& from) {
+ InputApplicationInfo ret;
+ ret.token = from.readStrongBinder();
+ ret.name = from.readString8().c_str();
+ ret.dispatchingTimeout = from.readInt64();
+
+ return ret;
+}
+
+status_t InputApplicationInfo::write(Parcel& output) const {
+ output.writeStrongBinder(token);
+ output.writeString8(String8(name.c_str()));
+ output.writeInt64(dispatchingTimeout);
+
+ return OK;
+}
+
} // namespace android
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index f82437e..556a005 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -65,12 +65,12 @@
}
status_t InputWindowInfo::write(Parcel& output) const {
- if (inputChannel == nullptr) {
+ if (token == nullptr) {
output.writeInt32(0);
return OK;
}
output.writeInt32(1);
- status_t s = inputChannel->write(output);
+ status_t s = output.writeStrongBinder(token);
if (s != OK) return s;
output.writeString8(String8(name.c_str()));
@@ -81,7 +81,10 @@
output.writeInt32(frameTop);
output.writeInt32(frameRight);
output.writeInt32(frameBottom);
- output.writeFloat(scaleFactor);
+ output.writeInt32(surfaceInset);
+ output.writeFloat(globalScaleFactor);
+ output.writeFloat(windowXScale);
+ output.writeFloat(windowYScale);
output.writeBool(visible);
output.writeBool(canReceiveKeys);
output.writeBool(hasFocus);
@@ -92,6 +95,7 @@
output.writeInt32(ownerUid);
output.writeInt32(inputFeatures);
output.writeInt32(displayId);
+ applicationInfo.write(output);
output.write(touchableRegion);
return OK;
@@ -102,15 +106,14 @@
if (from.readInt32() == 0) {
return ret;
-
}
- sp<InputChannel> inputChannel = new InputChannel();
- status_t s = inputChannel->read(from);
- if (s != OK) {
+
+ sp<IBinder> token = from.readStrongBinder();
+ if (token == nullptr) {
return ret;
}
- ret.inputChannel = inputChannel;
+ ret.token = token;
ret.name = from.readString8().c_str();
ret.layoutParamsFlags = from.readInt32();
ret.layoutParamsType = from.readInt32();
@@ -119,7 +122,10 @@
ret.frameTop = from.readInt32();
ret.frameRight = from.readInt32();
ret.frameBottom = from.readInt32();
- ret.scaleFactor = from.readFloat();
+ ret.surfaceInset = from.readInt32();
+ ret.globalScaleFactor = from.readFloat();
+ ret.windowXScale = from.readFloat();
+ ret.windowYScale = from.readFloat();
ret.visible = from.readBool();
ret.canReceiveKeys = from.readBool();
ret.hasFocus = from.readBool();
@@ -130,6 +136,7 @@
ret.ownerUid = from.readInt32();
ret.inputFeatures = from.readInt32();
ret.displayId = from.readInt32();
+ ret.applicationInfo = InputApplicationInfo::read(from);
from.read(ret.touchableRegion);
return ret;
@@ -141,19 +148,18 @@
// --- InputWindowHandle ---
-InputWindowHandle::InputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle) :
- inputApplicationHandle(inputApplicationHandle) {
+InputWindowHandle::InputWindowHandle() {
}
InputWindowHandle::~InputWindowHandle() {
}
void InputWindowHandle::releaseChannel() {
- mInfo.inputChannel.clear();
+ mInfo.token.clear();
}
-sp<InputChannel> InputWindowHandle::getInputChannel() const {
- return mInfo.inputChannel;
+sp<IBinder> InputWindowHandle::getToken() const {
+ return mInfo.token;
}
} // namespace android
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 39ad26e..5e5893f 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -16,6 +16,7 @@
#include <gtest/gtest.h>
+#include <binder/Binder.h>
#include <binder/Parcel.h>
#include <input/InputWindow.h>
@@ -24,24 +25,20 @@
namespace android {
namespace test {
-TEST(InputWindowInfo, ParcellingWithoutChannel) {
+TEST(InputWindowInfo, ParcellingWithoutToken) {
InputWindowInfo i;
- i.inputChannel = nullptr;
+ i.token = nullptr;
Parcel p;
ASSERT_EQ(OK, i.write(p));
p.setDataPosition(0);
InputWindowInfo i2 = InputWindowInfo::read(p);
- ASSERT_TRUE(i2.inputChannel == nullptr);
+ ASSERT_TRUE(i2.token == nullptr);
}
TEST(InputWindowInfo, Parcelling) {
- sp<InputChannel> channel, junkChannel;
- status_t result = InputChannel::openInputChannelPair("name", channel, junkChannel);
- ASSERT_EQ(OK, result) << "openInputChannelPair should have returned valid channels";
-
InputWindowInfo i;
- i.inputChannel = channel;
+ i.token = new BBinder();
i.name = "Foobar";
i.layoutParamsFlags = 7;
i.layoutParamsType = 39;
@@ -50,7 +47,10 @@
i.frameTop = 34;
i.frameRight = 16;
i.frameBottom = 19;
- i.scaleFactor = 0.3;
+ i.surfaceInset = 17;
+ i.globalScaleFactor = 0.3;
+ i.windowXScale = 0.4;
+ i.windowYScale = 0.5;
i.visible = false;
i.canReceiveKeys = false;
i.hasFocus = false;
@@ -67,7 +67,7 @@
p.setDataPosition(0);
InputWindowInfo i2 = InputWindowInfo::read(p);
- ASSERT_EQ(i.inputChannel->getName(), i2.inputChannel->getName());
+ ASSERT_EQ(i.token, i2.token);
ASSERT_EQ(i.name, i2.name);
ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
@@ -76,7 +76,10 @@
ASSERT_EQ(i.frameTop, i2.frameTop);
ASSERT_EQ(i.frameRight, i2.frameRight);
ASSERT_EQ(i.frameBottom, i2.frameBottom);
- ASSERT_EQ(i.scaleFactor, i2.scaleFactor);
+ ASSERT_EQ(i.surfaceInset, i2.surfaceInset);
+ ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor);
+ ASSERT_EQ(i.windowXScale, i2.windowXScale);
+ ASSERT_EQ(i.windowYScale, i2.windowYScale);
ASSERT_EQ(i.visible, i2.visible);
ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys);
ASSERT_EQ(i.hasFocus, i2.hasFocus);
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 7e26b0b..a19fe17 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -41,33 +41,11 @@
// ----------------------------------------------------------------------------
int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) {
- if (!outBuffer || !desc)
- return BAD_VALUE;
-
- if (!AHardwareBuffer_isValidPixelFormat(desc->format)) {
- ALOGE("Invalid AHardwareBuffer pixel format %u (%#x))", desc->format, desc->format);
- return BAD_VALUE;
- }
+ if (!outBuffer || !desc) return BAD_VALUE;
+ if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) return BAD_VALUE;
int format = AHardwareBuffer_convertToPixelFormat(desc->format);
- if (desc->rfu0 != 0 || desc->rfu1 != 0) {
- ALOGE("AHardwareBuffer_Desc::rfu fields must be 0");
- return BAD_VALUE;
- }
-
- if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) {
- ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB format");
- return BAD_VALUE;
- }
-
- if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) &&
- (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) {
- ALOGE("AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER "
- "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER");
- return BAD_VALUE;
- }
-
- uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
+ uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
sp<GraphicBuffer> gbuffer(new GraphicBuffer(
desc->width, desc->height, format, desc->layers, usage,
std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]"));
@@ -122,19 +100,26 @@
if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only "
- " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
+ "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed");
return BAD_VALUE;
}
usage = AHardwareBuffer_convertToGrallocUsageBits(usage);
- GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+ GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer);
+
+ if (gbuffer->getLayerCount() > 1) {
+ ALOGE("Buffer with multiple layers passed to AHardwareBuffer_lock; "
+ "only buffers with one layer are allowed");
+ return INVALID_OPERATION;
+ }
+
Rect bounds;
if (!rect) {
- bounds.set(Rect(gBuffer->getWidth(), gBuffer->getHeight()));
+ bounds.set(Rect(gbuffer->getWidth(), gbuffer->getHeight()));
} else {
bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
}
- return gBuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence);
+ return gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence);
}
int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) {
@@ -274,6 +259,25 @@
return NO_ERROR;
}
+int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) {
+ if (!desc) return 0;
+ if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0;
+
+ // Make a trial allocation.
+ // TODO(b/115660272): add implementation that uses a HAL query.
+ AHardwareBuffer_Desc trialDesc = *desc;
+ trialDesc.width = 4;
+ trialDesc.height = desc->format == AHARDWAREBUFFER_FORMAT_BLOB ? 1 : 4;
+ trialDesc.layers = desc->layers == 1 ? 1 : 2;
+ AHardwareBuffer* trialBuffer = nullptr;
+ int result = AHardwareBuffer_allocate(&trialDesc, &trialBuffer);
+ if (result == NO_ERROR) {
+ AHardwareBuffer_release(trialBuffer);
+ return 1;
+ }
+ return 0;
+}
+
// ----------------------------------------------------------------------------
// VNDK functions
@@ -322,14 +326,71 @@
namespace android {
-// A 1:1 mapping of AHardwaqreBuffer bitmasks to gralloc1 bitmasks.
-struct UsageMaskMapping {
- uint64_t hardwareBufferMask;
- uint64_t grallocMask;
-};
+bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log) {
+ if (desc->width == 0 || desc->height == 0 || desc->layers == 0) {
+ ALOGE_IF(log, "Width, height and layers must all be nonzero");
+ return false;
+ }
-static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) {
- return (mask & bitsToCheck) == bitsToCheck && bitsToCheck;
+ if (!AHardwareBuffer_isValidPixelFormat(desc->format)) {
+ ALOGE_IF(log, "Invalid AHardwareBuffer pixel format %u (%#x))",
+ desc->format, desc->format);
+ return false;
+ }
+
+ if (desc->rfu0 != 0 || desc->rfu1 != 0) {
+ ALOGE_IF(log, "AHardwareBuffer_Desc::rfu fields must be 0");
+ return false;
+ }
+
+ if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB) {
+ if (desc->height != 1 || desc->layers != 1) {
+ ALOGE_IF(log, "Height and layers must be 1 for AHARDWAREBUFFER_FORMAT_BLOB");
+ return false;
+ }
+ const uint64_t blobInvalidGpuMask =
+ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+ AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER |
+ AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE |
+ AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP;
+ if (desc->usage & blobInvalidGpuMask) {
+ ALOGE_IF(log, "Invalid GPU usage flag for AHARDWAREBUFFER_FORMAT_BLOB; "
+ "only AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER is allowed");
+ return false;
+ }
+ if (desc->usage & AHARDWAREBUFFER_USAGE_VIDEO_ENCODE) {
+ ALOGE_IF(log, "AHARDWAREBUFFER_FORMAT_BLOB cannot be encoded as video");
+ return false;
+ }
+ } else {
+ if (desc->usage & AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA) {
+ ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA requires AHARDWAREBUFFER_FORMAT_BLOB");
+ return false;
+ }
+ if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) {
+ ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER requires AHARDWAREBUFFER_FORMAT_BLOB");
+ return false;
+ }
+ }
+
+ if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) &&
+ (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) {
+ ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER "
+ "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER");
+ return false;
+ }
+
+ if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) {
+ if (desc->width != desc->height) {
+ ALOGE_IF(log, "Cube maps must be square");
+ return false;
+ }
+ if (desc->layers % 6 != 0) {
+ ALOGE_IF(log, "Cube map layers must be a multiple of 6");
+ return false;
+ }
+ }
+ return true;
}
bool AHardwareBuffer_isValidPixelFormat(uint32_t format) {
@@ -445,7 +506,7 @@
"gralloc and AHardwareBuffer flags don't match");
static_assert(AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE == (uint64_t)BufferUsage::GPU_TEXTURE,
"gralloc and AHardwareBuffer flags don't match");
- static_assert(AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT == (uint64_t)BufferUsage::GPU_RENDER_TARGET,
+ static_assert(AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER == (uint64_t)BufferUsage::GPU_RENDER_TARGET,
"gralloc and AHardwareBuffer flags don't match");
static_assert(AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT == (uint64_t)BufferUsage::PROTECTED,
"gralloc and AHardwareBuffer flags don't match");
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index fba319d..d847884 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -51,10 +51,6 @@
"-Wno-unused-function",
],
- cppflags: [
- "-std=c++1z"
- ],
-
version_script: "libnativewindow.map.txt",
srcs: [
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
index 71f5634..bf688f8 100644
--- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
+++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -28,10 +28,15 @@
#include <stdint.h>
struct AHardwareBuffer;
+struct AHardwareBuffer_Desc;
struct ANativeWindowBuffer;
namespace android {
+// Validates whether the passed description does not have conflicting
+// parameters. Note: this does not verify any platform-specific contraints.
+bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log);
+
// whether this AHardwareBuffer format is valid
bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format);
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index 23ac60b..03545a6 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -102,8 +102,10 @@
AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM = 0x2b,
/**
- * An opaque binary blob format that must have height 1, with width equal to
- * the buffer size in bytes.
+ * Opaque binary blob format.
+ * Must have height 1 and one layer, with width equal to the buffer
+ * size in bytes. Corresponds to Vulkan buffers and OpenGL buffer
+ * objects. Can be bound to the latter using GL_EXT_external_buffer.
*/
AHARDWAREBUFFER_FORMAT_BLOB = 0x21,
@@ -195,20 +197,25 @@
/// The buffer will be written to by the GPU as a framebuffer attachment.
AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = 1UL << 9,
/**
- * The buffer will be written to by the GPU as a framebuffer attachment.
- * Note that the name of this flag is somewhat misleading: it does not imply
- * that the buffer contains a color format. A buffer with depth or stencil
- * format that will be used as a framebuffer attachment should also have
- * this flag.
+ * The buffer will be written to by the GPU as a framebuffer
+ * attachment.
+ *
+ * Note that the name of this flag is somewhat misleading: it does
+ * not imply that the buffer contains a color format. A buffer with
+ * depth or stencil format that will be used as a framebuffer
+ * attachment should also have this flag. Use the equivalent flag
+ * AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER to avoid this confusion.
*/
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER,
/**
- * The buffer is protected from direct CPU access or being read by non-secure
- * hardware, such as video encoders. This flag is incompatible with CPU
- * read and write flags. It is mainly used when handling DRM video.
- * Refer to the EGL extension EGL_EXT_protected_content and GL extension
- * EXT_protected_textures for more information on how these buffers are expected
- * to behave.
+ * The buffer is protected from direct CPU access or being read by
+ * non-secure hardware, such as video encoders.
+ *
+ * This flag is incompatible with CPU read and write flags. It is
+ * mainly used when handling DRM video. Refer to the EGL extension
+ * EGL_EXT_protected_content and GL extension
+ * GL_EXT_protected_textures for more information on how these
+ * buffers are expected to behave.
*/
AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14,
/// The buffer will be read by a hardware video encoder.
@@ -225,11 +232,17 @@
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24,
/**
* The buffer will be used as a cube map texture.
- * When this flag is present, the buffer must have a layer count that is
- * a multiple of 6.
+ * When this flag is present, the buffer must have a layer count
+ * that is a multiple of 6. Note that buffers with this flag must be
+ * bound to OpenGL textures using the extension
+ * GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image.
*/
AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP = 1UL << 25,
- /// The buffer contains a complete mipmap hierarchy.
+ /**
+ * The buffer contains a complete mipmap hierarchy.
+ * Note that buffers with this flag must be bound to OpenGL textures using
+ * the extension GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image.
+ */
AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE = 1UL << 26,
AHARDWAREBUFFER_USAGE_VENDOR_0 = 1ULL << 28,
@@ -255,13 +268,21 @@
};
/**
- * Buffer description. Used for allocating new buffers and querying parameters
- * of existing ones.
+ * Buffer description. Used for allocating new buffers and querying
+ * parameters of existing ones.
*/
typedef struct AHardwareBuffer_Desc {
uint32_t width; ///< Width in pixels.
uint32_t height; ///< Height in pixels.
- uint32_t layers; ///< Number of images in an image array.
+ /**
+ * Number of images in an image array. AHardwareBuffers with one
+ * layer correspond to regular 2D textures. AHardwareBuffers with
+ * more than layer correspond to texture arrays. If the layer count
+ * is a multiple of 6 and the usage flag
+ * AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP is present, the buffer is
+ * a cube map or a cube map array.
+ */
+ uint32_t layers;
uint32_t format; ///< One of AHardwareBuffer_Format.
uint64_t usage; ///< Combination of AHardwareBuffer_UsageFlags.
uint32_t stride; ///< Row stride in pixels, ignored for AHardwareBuffer_allocate()
@@ -290,8 +311,10 @@
int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc,
AHardwareBuffer** outBuffer) __INTRODUCED_IN(26);
/**
- * Acquire a reference on the given AHardwareBuffer object. This prevents the
- * object from being deleted until the last reference is removed.
+ * Acquire a reference on the given AHardwareBuffer object.
+ *
+ * This prevents the object from being deleted until the last reference
+ * is removed.
*/
void AHardwareBuffer_acquire(AHardwareBuffer* buffer) __INTRODUCED_IN(26);
@@ -309,50 +332,73 @@
AHardwareBuffer_Desc* outDesc) __INTRODUCED_IN(26);
/**
- * Lock the AHardwareBuffer for reading or writing, depending on the usage flags
- * passed. This call may block if the hardware needs to finish rendering or if
- * CPU caches need to be synchronized, or possibly for other implementation-
- * specific reasons. If fence is not negative, then it specifies a fence file
- * descriptor that will be signaled when the buffer is locked, otherwise the
- * caller will block until the buffer is available.
+ * Lock the AHardwareBuffer for direct CPU access.
*
- * If \a rect is not NULL, the caller promises to modify only data in the area
- * specified by rect. If rect is NULL, the caller may modify the contents of the
- * entire buffer.
+ * This function can lock the buffer for either reading or writing.
+ * It may block if the hardware needs to finish rendering, if CPU caches
+ * need to be synchronized, or possibly for other implementation-
+ * specific reasons.
*
- * The content of the buffer outside of the specified rect is NOT modified
- * by this call.
+ * The passed AHardwareBuffer must have one layer, otherwise the call
+ * will fail.
*
- * The \a usage parameter may only specify AHARDWAREBUFFER_USAGE_CPU_*. If set,
- * then outVirtualAddress is filled with the address of the buffer in virtual
+ * If \a fence is not negative, it specifies a fence file descriptor on
+ * which to wait before locking the buffer. If it's negative, the caller
+ * is responsible for ensuring that writes to the buffer have completed
+ * before calling this function. Using this parameter is more efficient
+ * than waiting on the fence and then calling this function.
+ *
+ * The \a usage parameter may only specify AHARDWAREBUFFER_USAGE_CPU_*.
+ * If set, then outVirtualAddress is filled with the address of the
+ * buffer in virtual memory. The flags must also be compatible with
+ * usage flags specified at buffer creation: if a read flag is passed,
+ * the buffer must have been created with
+ * AHARDWAREBUFFER_USAGE_CPU_READ_RARELY or
+ * AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN. If a write flag is passed, it
+ * must have been created with AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY or
+ * AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN.
+ *
+ * If \a rect is not NULL, the caller promises to modify only data in
+ * the area specified by rect. If rect is NULL, the caller may modify
+ * the contents of the entire buffer. The content of the buffer outside
+ * of the specified rect is NOT modified by this call.
+ *
+ * It is legal for several different threads to lock a buffer for read
+ * access; none of the threads are blocked.
+ *
+ * Locking a buffer simultaneously for write or read/write is undefined,
+ * but will neither terminate the process nor block the caller.
+ * AHardwareBuffer_lock may return an error or leave the buffer's
+ * content in an indeterminate state.
+ *
+ * If the buffer has AHARDWAREBUFFER_FORMAT_BLOB, it is legal lock it
+ * for reading and writing in multiple threads and/or processes
+ * simultaneously, and the contents of the buffer behave like shared
* memory.
*
- * THREADING CONSIDERATIONS:
- *
- * It is legal for several different threads to lock a buffer for read access;
- * none of the threads are blocked.
- *
- * Locking a buffer simultaneously for write or read/write is undefined, but
- * will neither terminate the process nor block the caller; AHardwareBuffer_lock
- * may return an error or leave the buffer's content into an indeterminate
- * state.
- *
- * \return 0 on success, -EINVAL if \a buffer is NULL or if the usage
- * flags are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or an error
- * number of the lock fails for any reason.
+ * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags
+ * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer
+ * has more than one layer. Error number if the lock fails for any other
+ * reason.
*/
int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage,
int32_t fence, const ARect* rect, void** outVirtualAddress) __INTRODUCED_IN(26);
/**
- * Unlock the AHardwareBuffer; must be called after all changes to the buffer
- * are completed by the caller. If fence is not NULL then it will be set to a
- * file descriptor that is signaled when all pending work on the buffer is
- * completed. The caller is responsible for closing the fence when it is no
- * longer needed.
+ * Unlock the AHardwareBuffer from direct CPU access.
*
- * \return 0 on success, -EINVAL if \a buffer is NULL, or an error
- * number if the unlock fails for any reason.
+ * Must be called after all changes to the buffer are completed by the
+ * caller. If \a fence is NULL, the function will block until all work
+ * is completed. Otherwise, \a fence will be set either to a valid file
+ * descriptor or to -1. The file descriptor will become signaled once
+ * the unlocking is complete and buffer contents are updated.
+ * The caller is responsible for closing the file descriptor once it's
+ * no longer needed. The value -1 indicates that unlocking has already
+ * completed before the function returned and no further operations are
+ * necessary.
+ *
+ * \return 0 on success. -EINVAL if \a buffer is NULL. Error number if
+ * the unlock fails for any reason.
*/
int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) __INTRODUCED_IN(26);
@@ -365,7 +411,7 @@
int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd) __INTRODUCED_IN(26);
/**
- * Receive the AHardwareBuffer from an AF_UNIX socket.
+ * Receive an AHardwareBuffer from an AF_UNIX socket.
*
* \return 0 on success, -EINVAL if \a outBuffer is NULL, or an error
* number if the operation fails for any reason.
@@ -374,6 +420,29 @@
#endif // __ANDROID_API__ >= 26
+#if __ANDROID_API__ >= 29
+
+/**
+ * Test whether the given format and usage flag combination is
+ * allocatable.
+ *
+ * If this function returns true, it means that a buffer with the given
+ * description can be allocated on this implementation, unless resource
+ * exhaustion occurs. If this function returns false, it means that the
+ * allocation of the given description will never succeed.
+ *
+ * The return value of this function may depend on all fields in the
+ * description, except stride, which is always ignored. For example,
+ * some implementations have implementation-defined limits on texture
+ * size and layer count.
+ *
+ * \return 1 if the format and usage flag combination is allocatable,
+ * 0 otherwise.
+ */
+int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_IN(29);
+
+#endif // __ANDROID_API__ >= 29
+
__END_DECLS
#endif // ANDROID_HARDWARE_BUFFER_H
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 197f73f..61590e0 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -202,7 +202,7 @@
* ANativeWindow.
*/
enum {
-// clang-format off
+ // clang-format off
NATIVE_WINDOW_SET_USAGE = 0, /* deprecated */
NATIVE_WINDOW_CONNECT = 1, /* deprecated */
NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */
@@ -237,7 +237,8 @@
NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31,
NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32,
NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33,
-// clang-format on
+ NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34,
+ // clang-format on
};
/* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
@@ -748,6 +749,27 @@
}
/*
+ * native_window_set_buffers_hdr10_plus_metadata(..., metadata)
+ * All buffers queued after this call will be associated with the
+ * HDR10+ dynamic metadata specified.
+ *
+ * metadata specifies additional dynamic information about the
+ * contents of the buffer that may affect how it is displayed. When
+ * it is nullptr, it means no such information is available. No
+ * HDR10+ dynamic emtadata is associated with the buffers by default.
+ *
+ * Parameter "size" refers to the length of the metadata blob pointed to
+ * by parameter "data". The metadata blob will adhere to the HDR10+ SEI
+ * message standard.
+ */
+static inline int native_window_set_buffers_hdr10_plus_metadata(struct ANativeWindow* window,
+ const size_t size,
+ const uint8_t* metadata) {
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA, size,
+ metadata);
+}
+
+/*
* native_window_set_buffers_transform(..., int transform)
* All buffers queued after this call will be displayed transformed according
* to the transform parameter specified.
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 753954d..a796e97 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -5,6 +5,7 @@
AHardwareBuffer_createFromHandle; # vndk
AHardwareBuffer_describe;
AHardwareBuffer_getNativeHandle; # vndk
+ AHardwareBuffer_isSupported; # introduced=29
AHardwareBuffer_lock;
AHardwareBuffer_recvHandleFromUnixSocket;
AHardwareBuffer_release;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 7efc8bd..beaf9ee 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -50,7 +50,6 @@
"gl/GLExtensions.cpp",
"gl/GLFramebuffer.cpp",
"gl/GLImage.cpp",
- "gl/GLSurface.cpp",
"gl/Program.cpp",
"gl/ProgramCache.cpp",
],
diff --git a/libs/renderengine/Mesh.cpp b/libs/renderengine/Mesh.cpp
index 6a40c6c..f5387f2 100644
--- a/libs/renderengine/Mesh.cpp
+++ b/libs/renderengine/Mesh.cpp
@@ -33,13 +33,15 @@
return;
}
- size_t stride = vertexSize + texCoordSize;
+ const size_t CROP_COORD_SIZE = 2;
+ size_t stride = vertexSize + texCoordSize + CROP_COORD_SIZE;
size_t remainder = (stride * vertexCount) / vertexCount;
// Since all of the input parameters are unsigned, if stride is less than
// either vertexSize or texCoordSize, it must have overflowed. remainder
// will be equal to stride as long as stride * vertexCount doesn't overflow.
if ((stride < vertexSize) || (remainder != stride)) {
- ALOGE("Overflow in Mesh(..., %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize);
+ ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize,
+ CROP_COORD_SIZE);
mVertices.resize(1);
mVertices[0] = 0.0f;
mVertexCount = 0;
@@ -71,6 +73,13 @@
return mVertices.data() + mVertexSize;
}
+float const* Mesh::getCropCoords() const {
+ return mVertices.data() + mVertexSize + mTexCoordsSize;
+}
+float* Mesh::getCropCoords() {
+ return mVertices.data() + mVertexSize + mTexCoordsSize;
+}
+
size_t Mesh::getVertexCount() const {
return mVertexCount;
}
diff --git a/libs/renderengine/gl/GLES20RenderEngine.cpp b/libs/renderengine/gl/GLES20RenderEngine.cpp
index e244a83..7adda83 100644
--- a/libs/renderengine/gl/GLES20RenderEngine.cpp
+++ b/libs/renderengine/gl/GLES20RenderEngine.cpp
@@ -41,7 +41,6 @@
#include "GLExtensions.h"
#include "GLFramebuffer.h"
#include "GLImage.h"
-#include "GLSurface.h"
#include "Program.h"
#include "ProgramCache.h"
@@ -241,60 +240,29 @@
config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
}
- EGLint renderableType = 0;
- if (config == EGL_NO_CONFIG) {
- renderableType = EGL_OPENGL_ES2_BIT;
- } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
- LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
- }
- EGLint contextClientVersion = 0;
- if (renderableType & EGL_OPENGL_ES2_BIT) {
- contextClientVersion = 2;
- } else if (renderableType & EGL_OPENGL_ES_BIT) {
- contextClientVersion = 1;
- } else {
- LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
- }
-
- std::vector<EGLint> contextAttributes;
- contextAttributes.reserve(6);
- contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
- contextAttributes.push_back(contextClientVersion);
bool useContextPriority = extensions.hasContextPriority() &&
(featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT);
- if (useContextPriority) {
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
- contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
- }
- contextAttributes.push_back(EGL_NONE);
-
- EGLContext ctxt = eglCreateContext(display, config, nullptr, contextAttributes.data());
+ EGLContext ctxt = createEglContext(display, config, EGL_NO_CONTEXT, useContextPriority);
// if can't create a GL context, we can only abort.
LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
- // now figure out what version of GL did we actually get
- // NOTE: a dummy surface is not needed if KHR_create_context is supported
- // TODO(alecmouri): don't create this surface if EGL_KHR_surfaceless_context
- // is supported.
-
- EGLConfig dummyConfig = config;
- if (dummyConfig == EGL_NO_CONFIG) {
- dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+ EGLSurface dummy = EGL_NO_SURFACE;
+ if (!extensions.hasSurfacelessContext()) {
+ dummy = createDummyEglPbufferSurface(display, config, hwcFormat);
+ LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
}
- EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
- EGLSurface dummy = eglCreatePbufferSurface(display, dummyConfig, attribs);
- LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
+
EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
+ // now figure out what version of GL did we actually get
GlesVersion version = parseGlesVersion(extensions.getVersion());
// initialize the renderer while GL is current
-
std::unique_ptr<GLES20RenderEngine> engine;
switch (version) {
case GLES_VERSION_1_0:
@@ -423,10 +391,6 @@
return std::make_unique<GLFramebuffer>(*this);
}
-std::unique_ptr<Surface> GLES20RenderEngine::createSurface() {
- return std::make_unique<GLSurface>(*this);
-}
-
std::unique_ptr<Image> GLES20RenderEngine::createImage() {
return std::make_unique<GLImage>(*this);
}
@@ -439,31 +403,6 @@
return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext();
}
-bool GLES20RenderEngine::setCurrentSurface(const Surface& surface) {
- // Surface is an abstract interface. GLES20RenderEngine only ever
- // creates GLSurface's, so it is safe to just cast to the actual
- // type.
- bool success = true;
- const GLSurface& glSurface = static_cast<const GLSurface&>(surface);
- EGLSurface eglSurface = glSurface.getEGLSurface();
- if (eglSurface != eglGetCurrentSurface(EGL_DRAW)) {
- success = eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext) == EGL_TRUE;
- if (success && glSurface.getAsync()) {
- eglSwapInterval(mEGLDisplay, 0);
- }
- if (success) {
- mSurfaceHeight = glSurface.getHeight();
- }
- }
-
- return success;
-}
-
-void GLES20RenderEngine::resetCurrentSurface() {
- eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- mSurfaceHeight = 0;
-}
-
base::unique_fd GLES20RenderEngine::flush() {
if (!GLExtensions::getInstance().hasNativeFenceSync()) {
return base::unique_fd();
@@ -577,7 +516,7 @@
void GLES20RenderEngine::setScissor(const Rect& region) {
// Invert y-coordinate to map to GL-space.
- int32_t canvasHeight = mRenderToFbo ? mFboHeight : mSurfaceHeight;
+ int32_t canvasHeight = mFboHeight;
int32_t glBottom = canvasHeight - region.bottom;
glScissor(region.left, glBottom, region.getWidth(), region.getHeight());
@@ -620,7 +559,6 @@
glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
- mRenderToFbo = true;
mFboHeight = glFramebuffer->getBufferHeight();
uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
@@ -632,7 +570,6 @@
}
void GLES20RenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
- mRenderToFbo = false;
mFboHeight = 0;
// back to main framebuffer
@@ -661,9 +598,7 @@
int32_t r = sourceCrop.right;
int32_t b = sourceCrop.bottom;
int32_t t = sourceCrop.top;
- if (mRenderToFbo) {
- std::swap(t, b);
- }
+ std::swap(t, b);
mat4 m = mat4::ortho(l, r, b, t, 0, 1);
// Apply custom rotation to the projection.
@@ -691,16 +626,18 @@
}
void GLES20RenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque,
- bool disableTexture, const half4& color) {
+ bool disableTexture, const half4& color,
+ float cornerRadius) {
mState.isPremultipliedAlpha = premultipliedAlpha;
mState.isOpaque = opaque;
mState.color = color;
+ mState.cornerRadius = cornerRadius;
if (disableTexture) {
mState.textureEnabled = false;
}
- if (color.a < 1.0f || !opaque) {
+ if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) {
glEnable(GL_BLEND);
glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} else {
@@ -768,6 +705,10 @@
glDisable(GL_BLEND);
}
+void GLES20RenderEngine::setupCornerRadiusCropSize(float width, float height) {
+ mState.cropSize = half2(width, height);
+}
+
void GLES20RenderEngine::drawMesh(const Mesh& mesh) {
ATRACE_CALL();
if (mesh.getTexCoordsSize()) {
@@ -779,6 +720,12 @@
glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
mesh.getByteStride(), mesh.getPositions());
+ if (mState.cornerRadius > 0.0f) {
+ glEnableVertexAttribArray(Program::cropCoords);
+ glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getCropCoords());
+ }
+
// By default, DISPLAY_P3 is the only supported wide color output. However,
// when HDR content is present, hardware composer may be able to handle
// BT2020 data space, in that case, the output data space is set to be
@@ -896,6 +843,10 @@
if (mesh.getTexCoordsSize()) {
glDisableVertexAttribArray(Program::texCoords);
}
+
+ if (mState.cornerRadius > 0.0f) {
+ glDisableVertexAttribArray(Program::cropCoords);
+ }
}
size_t GLES20RenderEngine::getMaxTextureSize() const {
@@ -942,6 +893,53 @@
return GLES_VERSION_1_0;
}
+EGLContext GLES20RenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
+ EGLContext shareContext, bool useContextPriority) {
+ EGLint renderableType = 0;
+ if (config == EGL_NO_CONFIG) {
+ renderableType = EGL_OPENGL_ES2_BIT;
+ } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
+ LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
+ }
+ EGLint contextClientVersion = 0;
+ if (renderableType & EGL_OPENGL_ES2_BIT) {
+ contextClientVersion = 2;
+ } else if (renderableType & EGL_OPENGL_ES_BIT) {
+ contextClientVersion = 1;
+ } else {
+ LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
+ }
+
+ std::vector<EGLint> contextAttributes;
+ contextAttributes.reserve(5);
+ contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
+ contextAttributes.push_back(contextClientVersion);
+ if (useContextPriority) {
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+ }
+ contextAttributes.push_back(EGL_NONE);
+
+ return eglCreateContext(display, config, shareContext, contextAttributes.data());
+}
+
+EGLSurface GLES20RenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
+ int hwcFormat) {
+ EGLConfig dummyConfig = config;
+ if (dummyConfig == EGL_NO_CONFIG) {
+ dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+ }
+ std::vector<EGLint> attributes;
+ attributes.reserve(5);
+ attributes.push_back(EGL_WIDTH);
+ attributes.push_back(1);
+ attributes.push_back(EGL_HEIGHT);
+ attributes.push_back(1);
+ attributes.push_back(EGL_NONE);
+
+ return eglCreatePbufferSurface(display, dummyConfig, attributes.data());
+}
+
bool GLES20RenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK);
const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK);
diff --git a/libs/renderengine/gl/GLES20RenderEngine.h b/libs/renderengine/gl/GLES20RenderEngine.h
index aebb319..a9f8cad 100644
--- a/libs/renderengine/gl/GLES20RenderEngine.h
+++ b/libs/renderengine/gl/GLES20RenderEngine.h
@@ -40,7 +40,6 @@
namespace gl {
class GLImage;
-class GLSurface;
class GLES20RenderEngine : public impl::RenderEngine {
public:
@@ -52,13 +51,10 @@
~GLES20RenderEngine() override;
std::unique_ptr<Framebuffer> createFramebuffer() override;
- std::unique_ptr<Surface> createSurface() override;
std::unique_ptr<Image> createImage() override;
void primeCache() const override;
bool isCurrent() const override;
- bool setCurrentSurface(const Surface& surface) override;
- void resetCurrentSurface() override;
base::unique_fd flush() override;
bool finish() override;
bool waitFence(base::unique_fd fenceFd) override;
@@ -87,13 +83,14 @@
void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
ui::Transform::orientation_flags rotation) override;
void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
- const half4& color) override;
+ const half4& color, float cornerRadius) override;
void setupLayerTexturing(const Texture& texture) override;
void setupLayerBlackedOut() override;
void setupFillWithColor(float r, float g, float b, float a) override;
void setColorTransform(const mat4& colorTransform) override;
void disableTexturing() override;
void disableBlending() override;
+ void setupCornerRadiusCropSize(float width, float height) override;
// HDR and color management related functions and state
void setSourceY410BT2020(bool enable) override;
@@ -116,6 +113,10 @@
};
static GlesVersion parseGlesVersion(const char* str);
+ static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
+ EGLContext shareContext, bool useContextPriority);
+ static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
+ int hwcFormat);
// A data space is considered HDR data space if it has BT2020 color space
// with PQ or HLG transfer function.
@@ -147,8 +148,6 @@
mat4 mBt2020ToSrgb;
mat4 mBt2020ToDisplayP3;
- bool mRenderToFbo = false;
- int32_t mSurfaceHeight = 0;
int32_t mFboHeight = 0;
// Current dataspace of layer being rendered
diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp
index 784693b..ce83dd5 100644
--- a/libs/renderengine/gl/GLExtensions.cpp
+++ b/libs/renderengine/gl/GLExtensions.cpp
@@ -112,6 +112,9 @@
if (extensionSet.hasExtension("EGL_IMG_context_priority")) {
mHasContextPriority = true;
}
+ if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) {
+ mHasSurfacelessContext = true;
+ }
}
char const* GLExtensions::getEGLVersion() const {
diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h
index 382c23a..2a654d5 100644
--- a/libs/renderengine/gl/GLExtensions.h
+++ b/libs/renderengine/gl/GLExtensions.h
@@ -32,29 +32,6 @@
namespace gl {
class GLExtensions : public Singleton<GLExtensions> {
- friend class Singleton<GLExtensions>;
-
- bool mHasNoConfigContext = false;
- bool mHasNativeFenceSync = false;
- bool mHasFenceSync = false;
- bool mHasWaitSync = false;
- bool mHasProtectedContent = false;
- bool mHasContextPriority = false;
-
- String8 mVendor;
- String8 mRenderer;
- String8 mVersion;
- String8 mExtensions;
-
- String8 mEGLVersion;
- String8 mEGLExtensions;
-
- GLExtensions(const GLExtensions&);
- GLExtensions& operator=(const GLExtensions&);
-
-protected:
- GLExtensions() = default;
-
public:
bool hasNoConfigContext() const { return mHasNoConfigContext; }
bool hasNativeFenceSync() const { return mHasNativeFenceSync; }
@@ -62,6 +39,7 @@
bool hasWaitSync() const { return mHasWaitSync; }
bool hasProtectedContent() const { return mHasProtectedContent; }
bool hasContextPriority() const { return mHasContextPriority; }
+ bool hasSurfacelessContext() const { return mHasSurfacelessContext; }
void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
GLubyte const* extensions);
@@ -73,6 +51,31 @@
void initWithEGLStrings(char const* eglVersion, char const* eglExtensions);
char const* getEGLVersion() const;
char const* getEGLExtensions() const;
+
+protected:
+ GLExtensions() = default;
+
+private:
+ friend class Singleton<GLExtensions>;
+
+ bool mHasNoConfigContext = false;
+ bool mHasNativeFenceSync = false;
+ bool mHasFenceSync = false;
+ bool mHasWaitSync = false;
+ bool mHasProtectedContent = false;
+ bool mHasContextPriority = false;
+ bool mHasSurfacelessContext = false;
+
+ String8 mVendor;
+ String8 mRenderer;
+ String8 mVersion;
+ String8 mExtensions;
+
+ String8 mEGLVersion;
+ String8 mEGLExtensions;
+
+ GLExtensions(const GLExtensions&);
+ GLExtensions& operator=(const GLExtensions&);
};
} // namespace gl
diff --git a/libs/renderengine/gl/GLSurface.cpp b/libs/renderengine/gl/GLSurface.cpp
deleted file mode 100644
index 2d694e9..0000000
--- a/libs/renderengine/gl/GLSurface.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GLSurface.h"
-
-#include <android/native_window.h>
-#include <log/log.h>
-#include <ui/PixelFormat.h>
-#include "GLES20RenderEngine.h"
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GLSurface::GLSurface(const GLES20RenderEngine& engine)
- : mEGLDisplay(engine.getEGLDisplay()), mEGLConfig(engine.getEGLConfig()) {
- // RE does not assume any config when EGL_KHR_no_config_context is supported
- if (mEGLConfig == EGL_NO_CONFIG_KHR) {
- mEGLConfig =
- GLES20RenderEngine::chooseEglConfig(mEGLDisplay, PIXEL_FORMAT_RGBA_8888, false);
- }
-}
-
-GLSurface::~GLSurface() {
- setNativeWindow(nullptr);
-}
-
-void GLSurface::setNativeWindow(ANativeWindow* window) {
- if (mEGLSurface != EGL_NO_SURFACE) {
- eglDestroySurface(mEGLDisplay, mEGLSurface);
- mEGLSurface = EGL_NO_SURFACE;
- mSurfaceWidth = 0;
- mSurfaceHeight = 0;
- }
-
- mWindow = window;
- if (mWindow) {
- mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mWindow, nullptr);
- mSurfaceWidth = ANativeWindow_getWidth(window);
- mSurfaceHeight = ANativeWindow_getHeight(window);
- }
-}
-
-void GLSurface::swapBuffers() const {
- if (!eglSwapBuffers(mEGLDisplay, mEGLSurface)) {
- EGLint error = eglGetError();
-
- const char format[] = "eglSwapBuffers(%p, %p) failed with 0x%08x";
- if (mCritical || error == EGL_CONTEXT_LOST) {
- LOG_ALWAYS_FATAL(format, mEGLDisplay, mEGLSurface, error);
- } else {
- ALOGE(format, mEGLDisplay, mEGLSurface, error);
- }
- }
-}
-
-EGLint GLSurface::queryConfig(EGLint attrib) const {
- EGLint value;
- if (!eglGetConfigAttrib(mEGLDisplay, mEGLConfig, attrib, &value)) {
- value = 0;
- }
-
- return value;
-}
-
-int32_t GLSurface::queryRedSize() const {
- return queryConfig(EGL_RED_SIZE);
-}
-
-int32_t GLSurface::queryGreenSize() const {
- return queryConfig(EGL_GREEN_SIZE);
-}
-
-int32_t GLSurface::queryBlueSize() const {
- return queryConfig(EGL_BLUE_SIZE);
-}
-
-int32_t GLSurface::queryAlphaSize() const {
- return queryConfig(EGL_ALPHA_SIZE);
-}
-
-int32_t GLSurface::getWidth() const {
- return mSurfaceWidth;
-}
-
-int32_t GLSurface::getHeight() const {
- return mSurfaceHeight;
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/GLSurface.h b/libs/renderengine/gl/GLSurface.h
deleted file mode 100644
index 092d371..0000000
--- a/libs/renderengine/gl/GLSurface.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-
-#include <EGL/egl.h>
-#include <android-base/macros.h>
-#include <renderengine/Surface.h>
-
-struct ANativeWindow;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class GLES20RenderEngine;
-
-class GLSurface final : public renderengine::Surface {
-public:
- GLSurface(const GLES20RenderEngine& engine);
- ~GLSurface() override;
-
- // renderengine::Surface implementation
- void setCritical(bool enable) override { mCritical = enable; }
- void setAsync(bool enable) override { mAsync = enable; }
-
- void setNativeWindow(ANativeWindow* window) override;
- void swapBuffers() const override;
-
- int32_t queryRedSize() const override;
- int32_t queryGreenSize() const override;
- int32_t queryBlueSize() const override;
- int32_t queryAlphaSize() const override;
-
- bool getAsync() const { return mAsync; }
- EGLSurface getEGLSurface() const { return mEGLSurface; }
-
- int32_t getWidth() const override;
- int32_t getHeight() const override;
-
-private:
- EGLint queryConfig(EGLint attrib) const;
-
- EGLDisplay mEGLDisplay;
- EGLConfig mEGLConfig;
-
- bool mCritical = false;
- bool mAsync = false;
-
- int32_t mSurfaceWidth = 0;
- int32_t mSurfaceHeight = 0;
-
- ANativeWindow* mWindow = nullptr;
- EGLSurface mEGLSurface = EGL_NO_SURFACE;
-
- DISALLOW_COPY_AND_ASSIGN(GLSurface);
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index 7d2ea90..fe9d909 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -36,6 +36,7 @@
glAttachShader(programId, fragmentId);
glBindAttribLocation(programId, position, "position");
glBindAttribLocation(programId, texCoords, "texCoords");
+ glBindAttribLocation(programId, cropCoords, "cropCoords");
glLinkProgram(programId);
GLint status;
@@ -66,6 +67,8 @@
mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
+ mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius");
+ mCropCenterLoc = glGetUniformLocation(programId, "cropCenter");
// set-up the default values for our uniforms
glUseProgram(programId);
@@ -135,6 +138,12 @@
if (mDisplayMaxLuminanceLoc >= 0) {
glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
}
+ if (mCornerRadiusLoc >= 0) {
+ glUniform1f(mCornerRadiusLoc, desc.cornerRadius);
+ }
+ if (mCropCenterLoc >= 0) {
+ glUniform2f(mCropCenterLoc, desc.cropSize.x / 2.0f, desc.cropSize.y / 2.0f);
+ }
// these uniforms are always present
glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray());
}
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index 99bf0f0..bc9cf08 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -36,7 +36,16 @@
class Program {
public:
// known locations for position and texture coordinates
- enum { position = 0, texCoords = 1 };
+ enum {
+ /* position of each vertex for vertex shader */
+ position = 0,
+
+ /* UV coordinates for texture mapping */
+ texCoords = 1,
+
+ /* Crop coordinates, in pixels */
+ cropCoords = 2
+ };
Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
~Program() = default;
@@ -85,6 +94,12 @@
/* location of transform matrix */
GLint mInputTransformMatrixLoc;
GLint mOutputTransformMatrixLoc;
+
+ /* location of corner radius uniform */
+ GLint mCornerRadiusLoc;
+
+ /* location of surface crop origin uniform, for rounded corner clipping */
+ GLint mCropCenterLoc;
};
} // namespace gl
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 464fc15..d0916ad 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -79,7 +79,8 @@
void ProgramCache::primeCache(bool useColorManagement) {
uint32_t shaderCount = 0;
- uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK;
+ uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK
+ | Key::ROUNDED_CORNERS_MASK;
// Prime the cache for all combinations of the above masks,
// leaving off the experimental color matrix mask options.
@@ -136,12 +137,15 @@
.set(Key::OPACITY_MASK,
description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
.set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK,
- description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON
- : Key::INPUT_TRANSFORM_MATRIX_OFF)
+ description.hasInputTransformMatrix()
+ ? Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF)
.set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
description.hasOutputTransformMatrix() || description.hasColorMatrix()
? Key::OUTPUT_TRANSFORM_MATRIX_ON
- : Key::OUTPUT_TRANSFORM_MATRIX_OFF);
+ : Key::OUTPUT_TRANSFORM_MATRIX_OFF)
+ .set(Key::ROUNDED_CORNERS_MASK,
+ description.cornerRadius > 0
+ ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
needs.set(Key::Y410_BT2020_MASK,
description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
@@ -513,6 +517,10 @@
vs << "attribute vec4 texCoords;"
<< "varying vec2 outTexCoords;";
}
+ if (needs.hasRoundedCorners()) {
+ vs << "attribute lowp vec4 cropCoords;";
+ vs << "varying lowp vec2 outCropCoords;";
+ }
vs << "attribute vec4 position;"
<< "uniform mat4 projection;"
<< "uniform mat4 texture;"
@@ -520,6 +528,9 @@
if (needs.isTexturing()) {
vs << "outTexCoords = (texture * texCoords).st;";
}
+ if (needs.hasRoundedCorners()) {
+ vs << "outCropCoords = cropCoords.st;";
+ }
vs << dedent << "}";
return vs.getString();
}
@@ -541,6 +552,27 @@
<< "varying vec2 outTexCoords;";
}
+ if (needs.hasRoundedCorners()) {
+ // Rounded corners implementation using a signed distance function.
+ fs << R"__SHADER__(
+ uniform float cornerRadius;
+ uniform vec2 cropCenter;
+ varying vec2 outCropCoords;
+
+ /**
+ * This function takes the current crop coordinates and calculates an alpha value based
+ * on the corner radius and distance from the crop center.
+ */
+ float applyCornerRadius(vec2 cropCoords)
+ {
+ vec2 position = cropCoords - cropCenter;
+ vec2 dist = abs(position) + vec2(cornerRadius) - cropCenter;
+ float plane = length(max(dist, vec2(0.0)));
+ return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0);
+ }
+ )__SHADER__";
+ }
+
if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
fs << "uniform vec4 color;";
}
@@ -639,6 +671,14 @@
}
}
+ if (needs.hasRoundedCorners()) {
+ if (needs.isPremultiplied()) {
+ fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));";
+ } else {
+ fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);";
+ }
+ }
+
fs << dedent << "}";
return fs.getString();
}
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index d60fee6..653aaf0 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -78,31 +78,36 @@
TEXTURE_EXT = 1 << TEXTURE_SHIFT,
TEXTURE_2D = 2 << TEXTURE_SHIFT,
- INPUT_TRANSFORM_MATRIX_SHIFT = 5,
+ ROUNDED_CORNERS_SHIFT = 5,
+ ROUNDED_CORNERS_MASK = 1 << ROUNDED_CORNERS_SHIFT,
+ ROUNDED_CORNERS_OFF = 0 << ROUNDED_CORNERS_SHIFT,
+ ROUNDED_CORNERS_ON = 1 << ROUNDED_CORNERS_SHIFT,
+
+ INPUT_TRANSFORM_MATRIX_SHIFT = 6,
INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT,
INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
- OUTPUT_TRANSFORM_MATRIX_SHIFT = 6,
+ OUTPUT_TRANSFORM_MATRIX_SHIFT = 7,
OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
- INPUT_TF_SHIFT = 7,
+ INPUT_TF_SHIFT = 8,
INPUT_TF_MASK = 3 << INPUT_TF_SHIFT,
INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT,
INPUT_TF_HLG = 3 << INPUT_TF_SHIFT,
- OUTPUT_TF_SHIFT = 9,
+ OUTPUT_TF_SHIFT = 10,
OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT,
OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT,
OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT,
OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT,
OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT,
- Y410_BT2020_SHIFT = 11,
+ Y410_BT2020_SHIFT = 12,
Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
@@ -121,6 +126,9 @@
inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; }
+ inline bool hasRoundedCorners() const {
+ return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON;
+ }
inline bool hasInputTransformMatrix() const {
return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
}
diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h
index fe9022d..7618424 100644
--- a/libs/renderengine/include/renderengine/Mesh.h
+++ b/libs/renderengine/include/renderengine/Mesh.h
@@ -62,14 +62,22 @@
return VertexArray<TYPE>(getTexCoords(), mStride);
}
+ template <typename TYPE>
+ VertexArray<TYPE> getCropCoordArray() {
+ return VertexArray<TYPE>(getCropCoords(), mStride);
+ }
+
Primitive getPrimitive() const;
// returns a pointer to the vertices positions
float const* getPositions() const;
- // returns a pointer to the vertices texture coordinates
+ // returns a pointer to the vertices texture coordinates
float const* getTexCoords() const;
+ // returns a pointer to the vertices crop coordinates
+ float const* getCropCoords() const;
+
// number of vertices in this mesh
size_t getVertexCount() const;
@@ -92,6 +100,7 @@
float* getPositions();
float* getTexCoords();
+ float* getCropCoords();
std::vector<float> mVertices;
size_t mVertexCount;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index becb3c3..bb7f4df 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -48,7 +48,6 @@
class BindNativeBufferAsFramebuffer;
class Image;
class Mesh;
-class Surface;
class Texture;
namespace impl {
@@ -72,7 +71,6 @@
// used to support legacy behavior.
virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0;
- virtual std::unique_ptr<Surface> createSurface() = 0;
virtual std::unique_ptr<Image> createImage() = 0;
virtual void primeCache() const = 0;
@@ -84,8 +82,6 @@
virtual bool useWaitSync() const = 0;
virtual bool isCurrent() const = 0;
- virtual bool setCurrentSurface(const Surface& surface) = 0;
- virtual void resetCurrentSurface() = 0;
// helpers
// flush submits RenderEngine command stream for execution and returns a
@@ -118,10 +114,20 @@
virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
ui::Transform::orientation_flags rotation) = 0;
virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
- const half4& color) = 0;
+ const half4& color, float cornerRadius) = 0;
virtual void setupLayerTexturing(const Texture& texture) = 0;
virtual void setupLayerBlackedOut() = 0;
virtual void setupFillWithColor(float r, float g, float b, float a) = 0;
+ // Sets up the crop size for corner radius clipping.
+ //
+ // Having corner radius will force GPU composition on the layer and its children, drawing it
+ // with a special shader. The shader will receive the radius and the crop rectangle as input,
+ // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1.
+ // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop
+ // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be
+ // in local layer coordinate space, so we have to take the layer transform into account when
+ // walking up the tree.
+ virtual void setupCornerRadiusCropSize(float width, float height) = 0;
// Set a color transform matrix that is applied in linear space right before OETF.
virtual void setColorTransform(const mat4& /* colorTransform */) = 0;
diff --git a/libs/renderengine/include/renderengine/Surface.h b/libs/renderengine/include/renderengine/Surface.h
deleted file mode 100644
index ba7331d..0000000
--- a/libs/renderengine/include/renderengine/Surface.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2017 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 <cstdint>
-
-struct ANativeWindow;
-
-namespace android {
-namespace renderengine {
-
-class Surface {
-public:
- virtual ~Surface() = default;
-
- virtual void setCritical(bool enable) = 0;
- virtual void setAsync(bool enable) = 0;
-
- virtual void setNativeWindow(ANativeWindow* window) = 0;
- virtual void swapBuffers() const = 0;
-
- virtual int32_t queryRedSize() const = 0;
- virtual int32_t queryGreenSize() const = 0;
- virtual int32_t queryBlueSize() const = 0;
- virtual int32_t queryAlphaSize() const = 0;
-
- virtual int32_t getWidth() const = 0;
- virtual int32_t getHeight() const = 0;
-};
-
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index eadd656..bd2055f 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -50,6 +50,12 @@
// whether this layer is marked as opaque
bool isOpaque = true;
+ // corner radius of the layer
+ float cornerRadius = 0;
+
+ // Size of the rounded rectangle we are cropping to
+ half2 cropSize;
+
// Texture this layer uses
Texture texture;
bool textureEnabled = false;
diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS
index d4393d6..81099e8 100644
--- a/libs/sensor/OWNERS
+++ b/libs/sensor/OWNERS
@@ -1,2 +1,3 @@
arthuri@google.com
bduddie@google.com
+bstack@google.com
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 0a0c8ca..f770975 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -109,6 +109,7 @@
// bufferhub is not used when building libgui for vendors
target: {
vendor: {
+ cflags: ["-DLIBUI_IN_VNDK"],
exclude_srcs: [
"BufferHubBuffer.cpp",
"BufferHubMetadata.cpp",
@@ -116,6 +117,7 @@
exclude_header_libs: [
"libbufferhub_headers",
"libdvr_headers",
+ "libnativewindow_headers",
],
exclude_shared_libs: [
"libpdx_default_transport",
@@ -128,6 +130,7 @@
"libbufferhub_headers",
"libdvr_headers",
"libnativebase_headers",
+ "libnativewindow_headers",
"libhardware_headers",
"libui_headers",
"libpdx_headers",
@@ -155,6 +158,7 @@
vendor_available: true,
target: {
vendor: {
+ cflags: ["-DLIBUI_IN_VNDK"],
override_export_include_dirs: ["include_vndk"],
},
},
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index 8cc1a4e..e747ee1 100644
--- a/libs/ui/BufferHubBuffer.cpp
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -36,10 +36,12 @@
#include <private/dvr/bufferhub_rpc.h>
#pragma clang diagnostic pop
-#include <ui/BufferHubBuffer.h>
-
#include <poll.h>
+#include <android-base/unique_fd.h>
+#include <ui/BufferHubBuffer.h>
+
+using android::base::unique_fd;
using android::dvr::BufferTraits;
using android::dvr::DetachedBufferRPC;
using android::dvr::NativeHandleWrapper;
@@ -132,7 +134,9 @@
const int bufferId = bufferTraits.id();
// Import the metadata.
- mMetadata = BufferHubMetadata::Import(bufferTraits.take_metadata_handle());
+ LocalHandle metadataHandle = bufferTraits.take_metadata_handle();
+ unique_fd metadataFd(metadataHandle.Release());
+ mMetadata = BufferHubMetadata::Import(std::move(metadataFd));
if (!mMetadata.IsValid()) {
ALOGE("BufferHubBuffer::ImportGraphicBuffer: invalid metadata.");
@@ -156,6 +160,16 @@
// GraphicBuffer instance can be created in future.
mBufferHandle = bufferTraits.take_buffer_handle();
+ // Populate buffer desc based on buffer traits.
+ mBufferDesc.width = bufferTraits.width();
+ mBufferDesc.height = bufferTraits.height();
+ mBufferDesc.layers = bufferTraits.layer_count();
+ mBufferDesc.format = bufferTraits.format();
+ mBufferDesc.usage = bufferTraits.usage();
+ mBufferDesc.stride = bufferTraits.stride();
+ mBufferDesc.rfu0 = 0U;
+ mBufferDesc.rfu1 = 0U;
+
// If all imports succeed, replace the previous buffer and id.
mId = bufferId;
mClientStateMask = bufferTraits.client_state_mask();
diff --git a/libs/ui/BufferHubMetadata.cpp b/libs/ui/BufferHubMetadata.cpp
index 1e08ed1..18d9a2c 100644
--- a/libs/ui/BufferHubMetadata.cpp
+++ b/libs/ui/BufferHubMetadata.cpp
@@ -48,47 +48,47 @@
return {};
}
- // Hand over the ownership of the fd to a pdx::LocalHandle immediately after the successful
- // return of ashmem_create_region. The ashmemHandle is going to own the fd and to prevent fd
+ // Hand over the ownership of the fd to a unique_fd immediately after the successful
+ // return of ashmem_create_region. The ashmemFd is going to own the fd and to prevent fd
// leaks during error handling.
- pdx::LocalHandle ashmemHandle{fd};
+ unique_fd ashmemFd{fd};
- if (ashmem_set_prot_region(ashmemHandle.Get(), kAshmemProt) != 0) {
+ if (ashmem_set_prot_region(ashmemFd.get(), kAshmemProt) != 0) {
ALOGE("BufferHubMetadata::Create: failed to set protect region.");
return {};
}
- return BufferHubMetadata::Import(std::move(ashmemHandle));
+ return BufferHubMetadata::Import(std::move(ashmemFd));
}
/* static */
-BufferHubMetadata BufferHubMetadata::Import(pdx::LocalHandle ashmemHandle) {
- if (!ashmem_valid(ashmemHandle.Get())) {
+BufferHubMetadata BufferHubMetadata::Import(unique_fd ashmemFd) {
+ if (!ashmem_valid(ashmemFd.get())) {
ALOGE("BufferHubMetadata::Import: invalid ashmem fd.");
return {};
}
- size_t metadataSize = static_cast<size_t>(ashmem_get_size_region(ashmemHandle.Get()));
+ size_t metadataSize = static_cast<size_t>(ashmem_get_size_region(ashmemFd.get()));
size_t userMetadataSize = metadataSize - kMetadataHeaderSize;
// Note that here the buffer state is mapped from shared memory as an atomic object. The
// std::atomic's constructor will not be called so that the original value stored in the memory
// region can be preserved.
auto metadataHeader = static_cast<MetadataHeader*>(mmap(nullptr, metadataSize, kAshmemProt,
- MAP_SHARED, ashmemHandle.Get(),
+ MAP_SHARED, ashmemFd.get(),
/*offset=*/0));
if (metadataHeader == nullptr) {
ALOGE("BufferHubMetadata::Import: failed to map region.");
return {};
}
- return BufferHubMetadata(userMetadataSize, std::move(ashmemHandle), metadataHeader);
+ return BufferHubMetadata(userMetadataSize, std::move(ashmemFd), metadataHeader);
}
-BufferHubMetadata::BufferHubMetadata(size_t userMetadataSize, pdx::LocalHandle ashmemHandle,
+BufferHubMetadata::BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd,
MetadataHeader* metadataHeader)
: mUserMetadataSize(userMetadataSize),
- mAshmemHandle(std::move(ashmemHandle)),
+ mAshmemFd(std::move(ashmemFd)),
mMetadataHeader(metadataHeader) {}
BufferHubMetadata::~BufferHubMetadata() {
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 29deb01..e606e26 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -22,6 +22,10 @@
#include <grallocusage/GrallocUsageConversion.h>
+#ifndef LIBUI_IN_VNDK
+#include <ui/BufferHubBuffer.h>
+#endif // LIBUI_IN_VNDK
+
#include <ui/Gralloc2.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
@@ -89,6 +93,21 @@
inUsage, inStride);
}
+#ifndef LIBUI_IN_VNDK
+GraphicBuffer::GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer) : GraphicBuffer() {
+ if (buffer == nullptr) {
+ mInitCheck = BAD_VALUE;
+ return;
+ }
+
+ mInitCheck = initWithHandle(buffer->DuplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE,
+ buffer->desc().width, buffer->desc().height,
+ static_cast<PixelFormat>(buffer->desc().format),
+ buffer->desc().layers, buffer->desc().usage, buffer->desc().stride);
+ mBufferHubBuffer = std::move(buffer);
+}
+#endif // LIBUI_IN_VNDK
+
GraphicBuffer::~GraphicBuffer()
{
if (handle) {
@@ -483,6 +502,12 @@
return NO_ERROR;
}
+#ifndef LIBUI_IN_VNDK
+bool GraphicBuffer::isBufferHubBuffer() const {
+ return mBufferHubBuffer != nullptr;
+}
+#endif // LIBUI_IN_VNDK
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index fe4ae6c..618c7d6 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -325,6 +325,20 @@
return *this;
}
+Region& Region::scaleSelf(float sx, float sy) {
+ size_t count = mStorage.size();
+ Rect* rects = mStorage.editArray();
+ while (count) {
+ rects->left = static_cast<int32_t>(rects->left * sx + 0.5f);
+ rects->right = static_cast<int32_t>(rects->right * sx + 0.5f);
+ rects->top = static_cast<int32_t>(rects->top * sy + 0.5f);
+ rects->bottom = static_cast<int32_t>(rects->bottom * sy + 0.5f);
+ rects++;
+ count--;
+ }
+ return *this;
+}
+
// ----------------------------------------------------------------------------
const Region Region::merge(const Rect& rhs) const {
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 8e949ec..25128ef 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -95,6 +95,14 @@
return mMatrix[2][1];
}
+float Transform::sx() const {
+ return mMatrix[0][0];
+}
+
+float Transform::sy() const {
+ return mMatrix[1][1];
+}
+
void Transform::reset() {
mType = IDENTITY;
for(size_t i = 0; i < 3; i++) {
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h
index daf6192..6850b43 100644
--- a/libs/ui/include/ui/BufferHubBuffer.h
+++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -38,6 +38,7 @@
#include <private/dvr/native_handle_wrapper.h>
#pragma clang diagnostic pop
+#include <android/hardware_buffer.h>
#include <ui/BufferHubMetadata.h>
namespace android {
@@ -62,9 +63,9 @@
// Allocates a standalone BufferHubBuffer not associated with any producer consumer set.
static std::unique_ptr<BufferHubBuffer> Create(uint32_t width, uint32_t height,
uint32_t layerCount, uint32_t format,
- uint64_t usage, size_t mUserMetadataSize) {
+ uint64_t usage, size_t userMetadataSize) {
return std::unique_ptr<BufferHubBuffer>(
- new BufferHubBuffer(width, height, layerCount, format, usage, mUserMetadataSize));
+ new BufferHubBuffer(width, height, layerCount, format, usage, userMetadataSize));
}
// Imports the given channel handle to a BufferHubBuffer, taking ownership.
@@ -79,6 +80,9 @@
// bufferhubd share the same buffer id.
int id() const { return mId; }
+ // Returns the buffer description, which is guaranteed to be faithful values from bufferhubd.
+ const AHardwareBuffer_Desc& desc() const { return mBufferDesc; }
+
const native_handle_t* DuplicateHandle() { return mBufferHandle.DuplicateHandle(); }
// Returns the current value of MetadataHeader::buffer_state.
@@ -118,7 +122,7 @@
private:
BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
- uint64_t usage, size_t mUserMetadataSize);
+ uint64_t usage, size_t userMetadataSize);
BufferHubBuffer(pdx::LocalChannelHandle mChannelHandle);
@@ -128,6 +132,9 @@
int mId = -1;
uint64_t mClientStateMask = 0;
+ // Stores ground truth of the buffer.
+ AHardwareBuffer_Desc mBufferDesc;
+
// Wrapps the gralloc buffer handle of this buffer.
dvr::NativeHandleWrapper<pdx::LocalHandle> mBufferHandle;
diff --git a/libs/ui/include/ui/BufferHubMetadata.h b/libs/ui/include/ui/BufferHubMetadata.h
index 94a9000..4261971 100644
--- a/libs/ui/include/ui/BufferHubMetadata.h
+++ b/libs/ui/include/ui/BufferHubMetadata.h
@@ -33,12 +33,17 @@
#pragma clang diagnostic ignored "-Wundefined-func-template"
#pragma clang diagnostic ignored "-Wunused-template"
#pragma clang diagnostic ignored "-Wweak-vtables"
-#include <pdx/file_handle.h>
#include <private/dvr/buffer_hub_defs.h>
#pragma clang diagnostic pop
+#include <android-base/unique_fd.h>
+
namespace android {
+namespace {
+using base::unique_fd;
+} // namespace
+
class BufferHubMetadata {
public:
// Creates a new BufferHubMetadata backed by an ashmem region.
@@ -50,11 +55,8 @@
// Imports an existing BufferHubMetadata from an ashmem FD.
//
- // TODO(b/112338294): Refactor BufferHub to use Binder as its internal IPC backend instead of
- // UDS.
- //
- // @param ashmemHandle Ashmem file handle representing an ashmem region.
- static BufferHubMetadata Import(pdx::LocalHandle ashmemHandle);
+ // @param ashmemFd Ashmem file descriptor representing an ashmem region.
+ static BufferHubMetadata Import(unique_fd ashmemFd);
BufferHubMetadata() = default;
@@ -67,7 +69,7 @@
mUserMetadataSize = other.mUserMetadataSize;
other.mUserMetadataSize = 0;
- mAshmemHandle = std::move(other.mAshmemHandle);
+ mAshmemFd = std::move(other.mAshmemFd);
// The old raw mMetadataHeader pointer must be cleared, otherwise the destructor will
// automatically mummap() the shared memory.
@@ -79,25 +81,25 @@
// Returns true if the metadata is valid, i.e. the metadata has a valid ashmem fd and the ashmem
// has been mapped into virtual address space.
- bool IsValid() const { return mAshmemHandle.IsValid() && mMetadataHeader != nullptr; }
+ bool IsValid() const { return mAshmemFd.get() != -1 && mMetadataHeader != nullptr; }
size_t user_metadata_size() const { return mUserMetadataSize; }
size_t metadata_size() const {
return mUserMetadataSize + dvr::BufferHubDefs::kMetadataHeaderSize;
}
- const pdx::LocalHandle& ashmem_handle() const { return mAshmemHandle; }
+ const unique_fd& ashmem_fd() const { return mAshmemFd; }
dvr::BufferHubDefs::MetadataHeader* metadata_header() { return mMetadataHeader; }
private:
- BufferHubMetadata(size_t userMetadataSize, pdx::LocalHandle ashmemHandle,
+ BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd,
dvr::BufferHubDefs::MetadataHeader* metadataHeader);
BufferHubMetadata(const BufferHubMetadata&) = delete;
void operator=(const BufferHubMetadata&) = delete;
size_t mUserMetadataSize = 0;
- pdx::LocalHandle mAshmemHandle;
+ unique_fd mAshmemFd;
dvr::BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr;
};
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index fe6229a..81f6cd9 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -34,6 +34,10 @@
namespace android {
+#ifndef LIBUI_IN_VNDK
+class BufferHubBuffer;
+#endif // LIBUI_IN_VNDK
+
class GraphicBufferMapper;
// ===========================================================================
@@ -133,6 +137,11 @@
GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat,
uint32_t inUsage, std::string requestorName = "<Unknown>");
+#ifndef LIBUI_IN_VNDK
+ // Create a GraphicBuffer from an existing BufferHubBuffer.
+ GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer);
+#endif // LIBUI_IN_VNDK
+
// return status
status_t initCheck() const;
@@ -188,6 +197,11 @@
status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+#ifndef LIBUI_IN_VNDK
+ // Returns whether this GraphicBuffer is backed by BufferHubBuffer.
+ bool isBufferHubBuffer() const;
+#endif // LIBUI_IN_VNDK
+
private:
~GraphicBuffer();
@@ -237,6 +251,11 @@
// match the BufferQueue's internal generation number (set through
// IGBP::setGenerationNumber), attempts to attach the buffer will fail.
uint32_t mGenerationNumber;
+
+#ifndef LIBUI_IN_VNDK
+ // Stores a BufferHubBuffer that handles buffer signaling, identification.
+ std::unique_ptr<BufferHubBuffer> mBufferHubBuffer;
+#endif // LIBUI_IN_VNDK
};
}; // namespace android
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 68b60fc..0a09960 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -89,11 +89,13 @@
// these translate rhs first
Region& translateSelf(int dx, int dy);
+ Region& scaleSelf(float sx, float sy);
Region& orSelf(const Region& rhs, int dx, int dy);
Region& xorSelf(const Region& rhs, int dx, int dy);
Region& andSelf(const Region& rhs, int dx, int dy);
Region& subtractSelf(const Region& rhs, int dx, int dy);
+
// these translate rhs first
const Region translate(int dx, int dy) const WARN_UNUSED;
const Region merge(const Region& rhs, int dx, int dy) const WARN_UNUSED;
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index 42dca75..900a5c4 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -65,6 +65,8 @@
const vec3& operator [] (size_t i) const; // returns column i
float tx() const;
float ty() const;
+ float sx() const;
+ float sy() const;
// modify the transform
void reset();
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 4c9c176..00f30a6 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,6 +29,22 @@
}
cc_test {
+ name: "GraphicBuffer_test",
+ header_libs: [
+ "libbufferhub_headers",
+ "libdvr_headers",
+ "libnativewindow_headers",
+ ],
+ shared_libs: [
+ "libpdx_default_transport",
+ "libui",
+ "libutils",
+ ],
+ srcs: ["GraphicBuffer_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
name: "BufferHubBuffer_test",
header_libs: [
"libbufferhub_headers",
@@ -37,6 +53,7 @@
],
shared_libs: [
"android.frameworks.bufferhub@1.0",
+ "libcutils",
"libhidlbase",
"libhwbinder",
"libpdx_default_transport",
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp
index 6af8033..7c85e66 100644
--- a/libs/ui/tests/BufferHubBuffer_test.cpp
+++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -19,6 +19,7 @@
#include <android/frameworks/bufferhub/1.0/IBufferClient.h>
#include <android/frameworks/bufferhub/1.0/IBufferHub.h>
#include <android/hardware_buffer.h>
+#include <cutils/native_handle.h>
#include <gtest/gtest.h>
#include <hidl/ServiceManagement.h>
#include <hwbinder/IPCThreadState.h>
@@ -35,7 +36,7 @@
const int kUsage = 0;
const size_t kUserMetadataSize = 0;
-using dvr::BufferHubDefs::IsBufferGained;
+using dvr::BufferHubDefs::IsBufferReleased;
using dvr::BufferHubDefs::kFirstClientBitMask;
using dvr::BufferHubDefs::kMetadataHeaderSize;
using frameworks::bufferhub::V1_0::BufferHubStatus;
@@ -118,9 +119,9 @@
// We use client_state_mask() to tell those two instances apart.
EXPECT_NE(bufferStateMask1, bufferStateMask2);
- // Both buffer instances should be in gained state.
- EXPECT_TRUE(IsBufferGained(b1->buffer_state()));
- EXPECT_TRUE(IsBufferGained(b2->buffer_state()));
+ // Both buffer instances should be in released state currently.
+ EXPECT_TRUE(IsBufferReleased(b1->buffer_state()));
+ EXPECT_TRUE(IsBufferReleased(b2->buffer_state()));
// TODO(b/112338294): rewrite test after migration
return;
@@ -144,7 +145,7 @@
EXPECT_TRUE(bufferHub->allocateBuffer(desc, kUserMetadataSize, callback).isOk());
}
-TEST_F(BufferHubBufferTest, DuplicateBuffer) {
+TEST_F(BufferHubBufferTest, DuplicateAndImportBuffer) {
// TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
sp<IBufferHub> bufferhub = IBufferHub::getService();
ASSERT_NE(nullptr, bufferhub.get());
@@ -156,20 +157,77 @@
memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription));
sp<IBufferClient> client;
+ BufferHubStatus ret;
IBufferHub::allocateBuffer_cb alloc_cb = [&](const auto& outClient, const auto& status) {
- ASSERT_EQ(status, BufferHubStatus::NO_ERROR);
- ASSERT_NE(nullptr, outClient.get());
client = outClient;
+ ret = status;
};
ASSERT_TRUE(bufferhub->allocateBuffer(desc, kUserMetadataSize, alloc_cb).isOk());
+ EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
+ ASSERT_NE(nullptr, client.get());
- IBufferClient::duplicate_cb dup_cb = [](const auto& token, const auto& status) {
- ASSERT_EQ(status, BufferHubStatus::NO_ERROR);
- ASSERT_NE(token.getNativeHandle(), nullptr);
- EXPECT_EQ(token->numInts, 1);
- EXPECT_EQ(token->numFds, 0);
+ hidl_handle token;
+ IBufferClient::duplicate_cb dup_cb = [&](const auto& outToken, const auto& status) {
+ token = outToken;
+ ret = status;
};
- EXPECT_TRUE(client->duplicate(dup_cb).isOk());
+ ASSERT_TRUE(client->duplicate(dup_cb).isOk());
+ EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
+ ASSERT_NE(token.getNativeHandle(), nullptr);
+ EXPECT_EQ(token->numInts, 1);
+ EXPECT_EQ(token->numFds, 0);
+
+ sp<IBufferClient> client2;
+ IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
+ ret = status;
+ client2 = outClient;
+ };
+ ASSERT_TRUE(bufferhub->importBuffer(token, import_cb).isOk());
+ EXPECT_EQ(ret, BufferHubStatus::NO_ERROR);
+ EXPECT_NE(nullptr, client2.get());
+ // TODO(b/116681016): once BufferNode.id() is exposed via BufferHubBuffer, check origin.id =
+ // improted.id here.
+}
+
+// nullptr must not crash the service
+TEST_F(BufferHubBufferTest, ImportNullToken) {
+ // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
+ sp<IBufferHub> bufferhub = IBufferHub::getService();
+ ASSERT_NE(nullptr, bufferhub.get());
+
+ hidl_handle nullToken;
+ sp<IBufferClient> client;
+ BufferHubStatus ret;
+ IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
+ client = outClient;
+ ret = status;
+ };
+ ASSERT_TRUE(bufferhub->importBuffer(nullToken, import_cb).isOk());
+ EXPECT_EQ(ret, BufferHubStatus::INVALID_TOKEN);
+ EXPECT_EQ(nullptr, client.get());
+}
+
+// This test has a very little chance to fail (number of existing tokens / 2 ^ 32)
+TEST_F(BufferHubBufferTest, ImportInvalidToken) {
+ // TODO(b/116681016): directly test on BufferHubBuffer instead of the service.
+ sp<IBufferHub> bufferhub = IBufferHub::getService();
+ ASSERT_NE(nullptr, bufferhub.get());
+
+ native_handle_t* tokenHandle = native_handle_create(/*numFds=*/0, /*numInts=*/1);
+ tokenHandle->data[0] = 0;
+
+ hidl_handle invalidToken(tokenHandle);
+ sp<IBufferClient> client;
+ BufferHubStatus ret;
+ IBufferHub::importBuffer_cb import_cb = [&](const auto& outClient, const auto& status) {
+ client = outClient;
+ ret = status;
+ };
+ ASSERT_TRUE(bufferhub->importBuffer(invalidToken, import_cb).isOk());
+ EXPECT_EQ(ret, BufferHubStatus::INVALID_TOKEN);
+ EXPECT_EQ(nullptr, client.get());
+
+ native_handle_delete(tokenHandle);
}
} // namespace
diff --git a/libs/ui/tests/BufferHubMetadata_test.cpp b/libs/ui/tests/BufferHubMetadata_test.cpp
index 4209392..14422bf 100644
--- a/libs/ui/tests/BufferHubMetadata_test.cpp
+++ b/libs/ui/tests/BufferHubMetadata_test.cpp
@@ -17,7 +17,7 @@
#include <gtest/gtest.h>
#include <ui/BufferHubMetadata.h>
-using android::dvr::BufferHubDefs::IsBufferGained;
+using android::dvr::BufferHubDefs::IsBufferReleased;
namespace android {
namespace dvr {
@@ -43,59 +43,51 @@
EXPECT_TRUE(m1.IsValid());
EXPECT_NE(m1.metadata_header(), nullptr);
- pdx::LocalHandle h2 = m1.ashmem_handle().Duplicate();
- EXPECT_TRUE(h2.IsValid());
+ unique_fd h2 = unique_fd(dup(m1.ashmem_fd().get()));
+ EXPECT_NE(h2.get(), -1);
BufferHubMetadata m2 = BufferHubMetadata::Import(std::move(h2));
- EXPECT_FALSE(h2.IsValid());
+ EXPECT_EQ(h2.get(), -1);
EXPECT_TRUE(m1.IsValid());
BufferHubDefs::MetadataHeader* mh1 = m1.metadata_header();
EXPECT_NE(mh1, nullptr);
- // TODO(b/111976433): Update this test once BufferHub state machine gets
- // updated. In the old model, buffer starts in the gained state (i.e.
- // valued 0). In the new model, buffer states in the released state.
- EXPECT_TRUE(IsBufferGained(mh1->fence_state.load()));
+ EXPECT_TRUE(IsBufferReleased(mh1->buffer_state.load()));
EXPECT_TRUE(m2.IsValid());
BufferHubDefs::MetadataHeader* mh2 = m2.metadata_header();
EXPECT_NE(mh2, nullptr);
- // TODO(b/111976433): Update this test once BufferHub state machine gets
- // updated. In the old model, buffer starts in the gained state (i.e.
- // valued 0). In the new model, buffer states in the released state.
- EXPECT_TRUE(IsBufferGained(mh2->fence_state.load()));
+ EXPECT_TRUE(IsBufferReleased(mh2->buffer_state.load()));
}
TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) {
BufferHubMetadata m1 = BufferHubMetadata::Create(sizeof(int));
EXPECT_TRUE(m1.IsValid());
EXPECT_NE(m1.metadata_header(), nullptr);
- EXPECT_TRUE(m1.ashmem_handle().IsValid());
+ EXPECT_NE(m1.ashmem_fd().get(), -1);
EXPECT_EQ(m1.user_metadata_size(), sizeof(int));
BufferHubMetadata m2 = std::move(m1);
- // After the move, the metadata header (a raw pointer) should be reset in the
- // older buffer.
+ // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
EXPECT_EQ(m1.metadata_header(), nullptr);
EXPECT_NE(m2.metadata_header(), nullptr);
- EXPECT_FALSE(m1.ashmem_handle().IsValid());
- EXPECT_TRUE(m2.ashmem_handle().IsValid());
+ EXPECT_EQ(m1.ashmem_fd().get(), -1);
+ EXPECT_NE(m2.ashmem_fd().get(), -1);
EXPECT_EQ(m1.user_metadata_size(), 0U);
EXPECT_EQ(m2.user_metadata_size(), sizeof(int));
BufferHubMetadata m3{std::move(m2)};
- // After the move, the metadata header (a raw pointer) should be reset in the
- // older buffer.
+ // After the move, the metadata header (a raw pointer) should be reset in the older buffer.
EXPECT_EQ(m2.metadata_header(), nullptr);
EXPECT_NE(m3.metadata_header(), nullptr);
- EXPECT_FALSE(m2.ashmem_handle().IsValid());
- EXPECT_TRUE(m3.ashmem_handle().IsValid());
+ EXPECT_EQ(m2.ashmem_fd().get(), -1);
+ EXPECT_NE(m3.ashmem_fd().get(), -1);
EXPECT_EQ(m2.user_metadata_size(), 0U);
EXPECT_EQ(m3.user_metadata_size(), sizeof(int));
diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp
new file mode 100644
index 0000000..95ca2c1
--- /dev/null
+++ b/libs/ui/tests/GraphicBuffer_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GraphicBufferTest"
+
+#include <ui/BufferHubBuffer.h>
+#include <ui/GraphicBuffer.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+namespace {
+
+constexpr uint32_t kTestWidth = 1024;
+constexpr uint32_t kTestHeight = 1;
+constexpr uint32_t kTestFormat = HAL_PIXEL_FORMAT_BLOB;
+constexpr uint32_t kTestLayerCount = 1;
+constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN;
+
+} // namespace
+
+class GraphicBufferTest : public testing::Test {};
+
+TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) {
+ std::unique_ptr<BufferHubBuffer> b1 =
+ BufferHubBuffer::Create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
+ kTestUsage, /*userMetadataSize=*/0);
+ EXPECT_TRUE(b1->IsValid());
+
+ sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1)));
+ EXPECT_TRUE(gb->isBufferHubBuffer());
+
+ EXPECT_EQ(gb->getWidth(), kTestWidth);
+ EXPECT_EQ(gb->getHeight(), kTestHeight);
+ EXPECT_EQ(static_cast<uint32_t>(gb->getPixelFormat()), kTestFormat);
+ EXPECT_EQ(gb->getUsage(), kTestUsage);
+ EXPECT_EQ(gb->getLayerCount(), kTestLayerCount);
+}
+
+} // namespace android
diff --git a/libs/vr/libbufferhub/buffer_client_impl.cpp b/libs/vr/libbufferhub/buffer_client_impl.cpp
index 30cbb9f..efa9c28 100644
--- a/libs/vr/libbufferhub/buffer_client_impl.cpp
+++ b/libs/vr/libbufferhub/buffer_client_impl.cpp
@@ -26,14 +26,14 @@
Parcel data, reply;
status_t ret =
data.writeInterfaceToken(IBufferClient::getInterfaceDescriptor());
- if (ret != NO_ERROR) {
+ if (ret != OK) {
ALOGE("BpBufferClient::isValid: failed to write into parcel; errno=%d",
ret);
return false;
}
ret = remote()->transact(IS_VALID, data, &reply);
- if (ret == NO_ERROR) {
+ if (ret == OK) {
return reply.readBool();
} else {
ALOGE("BpBufferClient::isValid: failed to transact; errno=%d", ret);
@@ -45,16 +45,16 @@
Parcel data, reply;
status_t ret =
data.writeInterfaceToken(IBufferClient::getInterfaceDescriptor());
- if (ret != NO_ERROR) {
+ if (ret != OK) {
ALOGE("BpBufferClient::duplicate: failed to write into parcel; errno=%d",
ret);
return ret;
}
ret = remote()->transact(DUPLICATE, data, &reply);
- if (ret == NO_ERROR) {
+ if (ret == OK) {
*outToken = reply.readUint64();
- return NO_ERROR;
+ return OK;
} else {
ALOGE("BpBufferClient::duplicate: failed to transact; errno=%d", ret);
return ret;
@@ -72,7 +72,7 @@
CHECK_INTERFACE(IBufferClient, data, reply);
uint64_t token = 0;
status_t ret = duplicate(&token);
- if (ret != NO_ERROR) {
+ if (ret != OK) {
return ret;
}
return reply->writeUint64(token);
diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
index 73ca69b..9bcfaa1 100644
--- a/libs/vr/libbufferhub/buffer_hub-test.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -23,11 +23,13 @@
using android::sp;
using android::dvr::ConsumerBuffer;
using android::dvr::ProducerBuffer;
-using android::dvr::BufferHubDefs::IsBufferAcquired;
-using android::dvr::BufferHubDefs::IsBufferGained;
-using android::dvr::BufferHubDefs::IsBufferPosted;
+using android::dvr::BufferHubDefs::AnyClientAcquired;
+using android::dvr::BufferHubDefs::AnyClientGained;
+using android::dvr::BufferHubDefs::AnyClientPosted;
using android::dvr::BufferHubDefs::IsBufferReleased;
-using android::dvr::BufferHubDefs::kConsumerStateMask;
+using android::dvr::BufferHubDefs::IsClientAcquired;
+using android::dvr::BufferHubDefs::IsClientPosted;
+using android::dvr::BufferHubDefs::IsClientReleased;
using android::dvr::BufferHubDefs::kFirstClientBitMask;
using android::dvr::BufferHubDefs::kMetadataHeaderSize;
using android::pdx::LocalChannelHandle;
@@ -52,58 +54,49 @@
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- std::unique_ptr<ConsumerBuffer> c =
+ std::unique_ptr<ConsumerBuffer> c1 =
ConsumerBuffer::Import(p->CreateConsumer());
- ASSERT_TRUE(c.get() != nullptr);
+ ASSERT_TRUE(c1.get() != nullptr);
// Check that consumers can spawn other consumers.
std::unique_ptr<ConsumerBuffer> c2 =
- ConsumerBuffer::Import(c->CreateConsumer());
+ ConsumerBuffer::Import(c1->CreateConsumer());
ASSERT_TRUE(c2.get() != nullptr);
- // Producer state mask is unique, i.e. 1.
+ // Checks the state masks of client p, c1 and c2.
EXPECT_EQ(p->client_state_mask(), kFirstClientBitMask);
- // Consumer state mask cannot have producer bit on.
- EXPECT_EQ(c->client_state_mask() & kFirstClientBitMask, 0U);
- // Consumer state mask must be a single, i.e. power of 2.
- EXPECT_NE(c->client_state_mask(), 0U);
- EXPECT_EQ(c->client_state_mask() & (c->client_state_mask() - 1), 0U);
- // Consumer state mask cannot have producer bit on.
- EXPECT_EQ(c2->client_state_mask() & kFirstClientBitMask, 0U);
- // Consumer state mask must be a single, i.e. power of 2.
- EXPECT_NE(c2->client_state_mask(), 0U);
- EXPECT_EQ(c2->client_state_mask() & (c2->client_state_mask() - 1), 0U);
- // Each consumer should have unique bit.
- EXPECT_EQ(c->client_state_mask() & c2->client_state_mask(), 0U);
+ EXPECT_EQ(c1->client_state_mask(), kFirstClientBitMask << 1);
+ EXPECT_EQ(c2->client_state_mask(), kFirstClientBitMask << 2);
// Initial state: producer not available, consumers not available.
EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, p->GainAsync());
EXPECT_EQ(0, p->Post(LocalHandle()));
// New state: producer not available, consumers available.
EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_EQ(1, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(1, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
LocalHandle fence;
- EXPECT_EQ(0, c->Acquire(&fence));
- EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, c1->Acquire(&fence));
+ EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, c2->Acquire(&fence));
EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, c->Release(LocalHandle()));
+ EXPECT_EQ(0, c1->Release(LocalHandle()));
EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, c2->Discard());
-
EXPECT_EQ(1, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+
EXPECT_EQ(0, p->Gain(&fence));
EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
}
@@ -144,7 +137,8 @@
// No events should be signaled initially.
ASSERT_EQ(0, epoll_wait(epoll_fd.Get(), events.data(), events.size(), 0));
- // Post the producer and check for consumer signal.
+ // Gain and post the producer and check for consumer signal.
+ EXPECT_EQ(0, p->GainAsync());
EXPECT_EQ(0, p->Post({}));
ASSERT_EQ(1, epoll_wait(epoll_fd.Get(), events.data(), events.size(),
kPollTimeoutMs));
@@ -189,7 +183,7 @@
EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U);
client_state_masks |= cs[i]->client_state_mask();
}
- EXPECT_EQ(client_state_masks, kFirstClientBitMask | kConsumerStateMask);
+ EXPECT_EQ(client_state_masks, ~0ULL);
// The 64th creation will fail with out-of-memory error.
auto state = p->CreateConsumer();
@@ -204,7 +198,6 @@
// The released state mask will be reused.
EXPECT_EQ(client_state_masks & cs[i]->client_state_mask(), 0U);
client_state_masks |= cs[i]->client_state_mask();
- EXPECT_EQ(client_state_masks, kFirstClientBitMask | kConsumerStateMask);
}
}
@@ -217,24 +210,20 @@
ASSERT_TRUE(c.get() != nullptr);
LocalHandle fence;
+ EXPECT_EQ(0, p->GainAsync());
- // The producer buffer starts in gained state.
-
- // Acquire, release, and gain in gained state should fail.
+ // Acquire in gained state should fail.
EXPECT_EQ(-EBUSY, c->Acquire(&fence));
- EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
- EXPECT_EQ(-EALREADY, p->Gain(&fence));
// Post in gained state should succeed.
EXPECT_EQ(0, p->Post(LocalHandle()));
- // Post, release, and gain in posted state should fail.
+ // Post and gain in posted state should fail.
EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
- EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
EXPECT_EQ(-EBUSY, p->Gain(&fence));
// Acquire in posted state should succeed.
- EXPECT_LE(0, c->Acquire(&fence));
+ EXPECT_EQ(0, c->Acquire(&fence));
// Acquire, post, and gain in acquired state should fail.
EXPECT_EQ(-EBUSY, c->Acquire(&fence));
@@ -245,18 +234,15 @@
EXPECT_EQ(0, c->Release(LocalHandle()));
EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- // Release, acquire, and post in released state should fail.
- EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
+ // Acquire and post in released state should fail.
EXPECT_EQ(-EBUSY, c->Acquire(&fence));
EXPECT_EQ(-EBUSY, p->Post(LocalHandle()));
// Gain in released state should succeed.
EXPECT_EQ(0, p->Gain(&fence));
- // Acquire, release, and gain in gained state should fail.
+ // Acquire in gained state should fail.
EXPECT_EQ(-EBUSY, c->Acquire(&fence));
- EXPECT_EQ(-EBUSY, c->Release(LocalHandle()));
- EXPECT_EQ(-EALREADY, p->Gain(&fence));
}
TEST_F(LibBufferHubTest, TestAsyncStateTransitions) {
@@ -269,24 +255,20 @@
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
+ EXPECT_EQ(0, p->GainAsync());
- // The producer buffer starts in gained state.
-
- // Acquire, release, and gain in gained state should fail.
+ // Acquire in gained state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
- EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
- EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
// Post in gained state should succeed.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+ EXPECT_TRUE(AnyClientPosted(p->buffer_state()));
- // Post, release, and gain in posted state should fail.
+ // Post and gain in posted state should fail.
EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
- EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
@@ -295,7 +277,7 @@
EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+ EXPECT_TRUE(AnyClientAcquired(p->buffer_state()));
// Acquire, post, and gain in acquired state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
@@ -310,8 +292,7 @@
EXPECT_EQ(p->buffer_state(), c->buffer_state());
EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
- // Release, acquire, and post in released state should fail.
- EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+ // Acquire and post in released state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
@@ -320,23 +301,32 @@
EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
EXPECT_EQ(p->buffer_state(), c->buffer_state());
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_TRUE(AnyClientGained(p->buffer_state()));
- // Acquire, release, and gain in gained state should fail.
+ // Acquire and gain in gained state should fail.
EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
EXPECT_FALSE(invalid_fence.IsValid());
- EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
- EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
- EXPECT_FALSE(invalid_fence.IsValid());
+}
+
+TEST_F(LibBufferHubTest, TestGainTwiceByTheSameProducer) {
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+
+ ASSERT_EQ(0, p->GainAsync());
+ ASSERT_EQ(0, p->GainAsync());
}
TEST_F(LibBufferHubTest, TestGainPostedBuffer) {
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
-
- // The producer buffer starts in gained state. Post the buffer.
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ ASSERT_EQ(0, p->GainAsync());
ASSERT_EQ(0, p->Post(LocalHandle()));
+ ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
// Gain in posted state should only succeed with gain_posted_buffer = true.
LocalHandle invalid_fence;
@@ -348,9 +338,12 @@
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
-
- // The producer buffer starts in gained state. Post the buffer.
+ std::unique_ptr<ConsumerBuffer> c =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c.get() != nullptr);
+ ASSERT_EQ(0, p->GainAsync());
ASSERT_EQ(0, p->Post(LocalHandle()));
+ ASSERT_TRUE(AnyClientPosted(p->buffer_state()));
// GainAsync in posted state should only succeed with gain_posted_buffer
// equals true.
@@ -360,54 +353,49 @@
EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence, true));
}
-TEST_F(LibBufferHubTest, TestZeroConsumer) {
+TEST_F(LibBufferHubTest, TestGainPostedBuffer_noConsumer) {
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
+ ASSERT_EQ(0, p->GainAsync());
+ ASSERT_EQ(0, p->Post(LocalHandle()));
+ // Producer state bit is in released state after post. The overall state of
+ // the buffer is also released because there is no consumer of this buffer.
+ ASSERT_TRUE(IsBufferReleased(p->buffer_state()));
- DvrNativeBufferMetadata metadata;
+ // Gain in released state should succeed.
LocalHandle invalid_fence;
-
- // Newly created.
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
- EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
-
- // The buffer should stay in posted stay until a consumer picks it up.
- EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
-
- // A new consumer should still be able to acquire the buffer immediately.
- std::unique_ptr<ConsumerBuffer> c =
- ConsumerBuffer::Import(p->CreateConsumer());
- ASSERT_TRUE(c.get() != nullptr);
- EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+ EXPECT_EQ(0, p->Gain(&invalid_fence, false));
}
TEST_F(LibBufferHubTest, TestMaxConsumers) {
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
+ uint64_t producer_state_mask = p->client_state_mask();
std::array<std::unique_ptr<ConsumerBuffer>, kMaxConsumerCount> cs;
- for (size_t i = 0; i < kMaxConsumerCount; i++) {
+ for (size_t i = 0; i < kMaxConsumerCount; ++i) {
cs[i] = ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(cs[i].get() != nullptr);
- EXPECT_TRUE(IsBufferGained(cs[i]->buffer_state()));
+ EXPECT_TRUE(IsBufferReleased(cs[i]->buffer_state()));
+ EXPECT_NE(producer_state_mask, cs[i]->client_state_mask());
}
+ EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
// Post the producer should trigger all consumers to be available.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
- for (size_t i = 0; i < kMaxConsumerCount; i++) {
+ EXPECT_TRUE(IsClientReleased(p->buffer_state(), p->client_state_mask()));
+ for (size_t i = 0; i < kMaxConsumerCount; ++i) {
EXPECT_TRUE(
- IsBufferPosted(cs[i]->buffer_state(), cs[i]->client_state_mask()));
+ IsClientPosted(cs[i]->buffer_state(), cs[i]->client_state_mask()));
EXPECT_LT(0, RETRY_EINTR(cs[i]->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, cs[i]->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+ EXPECT_TRUE(
+ IsClientAcquired(p->buffer_state(), cs[i]->client_state_mask()));
}
// All consumers have to release before the buffer is considered to be
@@ -430,44 +418,57 @@
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_EQ(0, p->GainAsync());
+ EXPECT_TRUE(AnyClientGained(p->buffer_state()));
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
- EXPECT_TRUE(IsBufferGained(c->buffer_state()));
+ EXPECT_TRUE(AnyClientGained(c->buffer_state()));
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
// Post the gained buffer should signal already created consumer.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+ EXPECT_TRUE(AnyClientPosted(p->buffer_state()));
EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+ EXPECT_TRUE(AnyClientAcquired(c->buffer_state()));
}
-TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferPosted) {
+TEST_F(LibBufferHubTest, TestCreateTheFirstConsumerAfterPostingBuffer) {
std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
ASSERT_TRUE(p.get() != nullptr);
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_EQ(0, p->GainAsync());
+ EXPECT_TRUE(AnyClientGained(p->buffer_state()));
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
// Post the gained buffer before any consumer gets created.
+ // The buffer should be in released state because it is not expected to be
+ // read by any clients.
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
- EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+ EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+ EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
- // Newly created consumer should be automatically sigalled.
+ // Newly created consumer will not be signalled for the posted buffer before
+ // its creation. It cannot acquire the buffer immediately.
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
- EXPECT_TRUE(IsBufferPosted(c->buffer_state()));
+ EXPECT_FALSE(IsClientPosted(c->buffer_state(), c->client_state_mask()));
+ EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+
+ // Producer should be able to gain back and post the buffer
+ EXPECT_EQ(0, p->GainAsync());
+ EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+
+ // Consumer should be able to pick up the buffer this time.
EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+ EXPECT_TRUE(IsClientAcquired(c->buffer_state(), c->client_state_mask()));
}
TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) {
@@ -479,6 +480,7 @@
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c1.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata metadata;
LocalHandle invalid_fence;
@@ -503,7 +505,7 @@
EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
- EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+ EXPECT_TRUE(AnyClientGained(p->buffer_state()));
}
TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
@@ -517,6 +519,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
Metadata m = {1, 3};
EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(Metadata)));
EXPECT_LE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs)));
@@ -545,6 +548,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
// It is illegal to post metadata larger than originally requested during
// buffer allocation.
@@ -573,6 +577,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
Metadata m = {1, 3};
EXPECT_EQ(0, p->Post(LocalHandle(), &m, sizeof(m)));
@@ -598,6 +603,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
int64_t sequence = 3;
EXPECT_EQ(0, p->Post(LocalHandle(), &sequence, sizeof(sequence)));
@@ -613,6 +619,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
LocalHandle fence;
@@ -627,6 +634,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
int64_t sequence = 3;
EXPECT_NE(0, p->Post(LocalHandle(), &sequence, sizeof(sequence)));
@@ -648,6 +656,7 @@
std::unique_ptr<ConsumerBuffer> c =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c.get() != nullptr);
+ EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata meta;
LocalHandle f1(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
@@ -711,44 +720,94 @@
ASSERT_TRUE(c1.get() != nullptr);
const uint64_t client_state_mask1 = c1->client_state_mask();
+ EXPECT_EQ(0, p->GainAsync());
DvrNativeBufferMetadata meta;
EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
LocalHandle fence;
EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
- EXPECT_LE(0, c1->AcquireAsync(&meta, &fence));
- // Destroy the consumer now will make it orphaned and the buffer is still
- // acquired.
- c1 = nullptr;
- EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, c1->AcquireAsync(&meta, &fence));
+ // Destroy the consumer who has acquired but not released the buffer.
+ c1 = nullptr;
+
+ // The buffer is now available for the producer to gain.
+ EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+
+ // Newly added consumer is not able to acquire the buffer.
std::unique_ptr<ConsumerBuffer> c2 =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c2.get() != nullptr);
const uint64_t client_state_mask2 = c2->client_state_mask();
EXPECT_NE(client_state_mask1, client_state_mask2);
+ EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(-EBUSY, c2->AcquireAsync(&meta, &fence));
- // The new consumer is available for acquire.
+ // Producer should be able to gain.
+ EXPECT_EQ(0, p->GainAsync(&meta, &fence, false));
+}
+
+TEST_F(LibBufferHubTest, TestAcquireLastPosted) {
+ std::unique_ptr<ProducerBuffer> p = ProducerBuffer::Create(
+ kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+ ASSERT_TRUE(p.get() != nullptr);
+ std::unique_ptr<ConsumerBuffer> c1 =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c1.get() != nullptr);
+ const uint64_t client_state_mask1 = c1->client_state_mask();
+
+ EXPECT_EQ(0, p->GainAsync());
+ DvrNativeBufferMetadata meta;
+ EXPECT_EQ(0, p->PostAsync(&meta, LocalHandle()));
+ EXPECT_LT(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs)));
+
+ // c2 is created when the buffer is in posted state. buffer state for c1 is
+ // posted. Thus, c2 should be automatically set to posted and able to acquire.
+ std::unique_ptr<ConsumerBuffer> c2 =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c2.get() != nullptr);
+ const uint64_t client_state_mask2 = c2->client_state_mask();
+ EXPECT_NE(client_state_mask1, client_state_mask2);
EXPECT_LT(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs)));
- EXPECT_LE(0, c2->AcquireAsync(&meta, &fence));
- // Releasing the consumer makes the buffer gainable.
- EXPECT_EQ(0, c2->ReleaseAsync(&meta, LocalHandle()));
+ LocalHandle invalid_fence;
+ EXPECT_EQ(0, c2->AcquireAsync(&meta, &invalid_fence));
- // The buffer is now available for the producer to gain.
- EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, c1->AcquireAsync(&meta, &invalid_fence));
- // But if another consumer is created in released state.
+ // c3 is created when the buffer is in acquired state. buffer state for c1 and
+ // c2 are acquired. Thus, c3 should be automatically set to posted and able to
+ // acquire.
std::unique_ptr<ConsumerBuffer> c3 =
ConsumerBuffer::Import(p->CreateConsumer());
ASSERT_TRUE(c3.get() != nullptr);
const uint64_t client_state_mask3 = c3->client_state_mask();
+ EXPECT_NE(client_state_mask1, client_state_mask3);
EXPECT_NE(client_state_mask2, client_state_mask3);
- // The consumer buffer is not acquirable.
- EXPECT_GE(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
- EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &fence));
+ EXPECT_LT(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(0, c3->AcquireAsync(&meta, &invalid_fence));
- // Producer should be able to gain no matter what.
- EXPECT_EQ(0, p->GainAsync(&meta, &fence));
+ // Releasing c2 and c3 in normal ways.
+ EXPECT_EQ(0, c2->Release(LocalHandle()));
+ EXPECT_EQ(0, c3->ReleaseAsync(&meta, LocalHandle()));
+
+ // Destroy the c1 who has not released the buffer.
+ c1 = nullptr;
+
+ // The buffer is now available for the producer to gain.
+ EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs)));
+
+ // C4 is created in released state. Thus, it cannot gain the just posted
+ // buffer.
+ std::unique_ptr<ConsumerBuffer> c4 =
+ ConsumerBuffer::Import(p->CreateConsumer());
+ ASSERT_TRUE(c4.get() != nullptr);
+ const uint64_t client_state_mask4 = c4->client_state_mask();
+ EXPECT_NE(client_state_mask3, client_state_mask4);
+ EXPECT_GE(0, RETRY_EINTR(c3->Poll(kPollTimeoutMs)));
+ EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &invalid_fence));
+
+ // Producer should be able to gain.
+ EXPECT_EQ(0, p->GainAsync(&meta, &invalid_fence));
}
TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) {
@@ -767,6 +826,7 @@
int p_id = p->id();
// Detach in posted state should fail.
+ EXPECT_EQ(0, p->GainAsync());
EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
EXPECT_GT(RETRY_EINTR(c->Poll(kPollTimeoutMs)), 0);
auto s1 = p->Detach();
@@ -869,7 +929,8 @@
ASSERT_TRUE(p1.get() != nullptr);
int p1_id = p1->id();
- // Detached the producer.
+ // Detached the producer from gained state.
+ EXPECT_EQ(0, p1->GainAsync());
auto status_or_handle = p1->Detach();
EXPECT_TRUE(status_or_handle.ok());
LocalChannelHandle h1 = status_or_handle.take();
@@ -919,8 +980,8 @@
EXPECT_NE(b1->client_state_mask(), b2->client_state_mask());
// Both buffer instances should be in gained state.
- EXPECT_TRUE(IsBufferGained(b1->buffer_state()));
- EXPECT_TRUE(IsBufferGained(b2->buffer_state()));
+ EXPECT_TRUE(IsBufferReleased(b1->buffer_state()));
+ EXPECT_TRUE(IsBufferReleased(b2->buffer_state()));
// TODO(b/112338294) rewrite test after migration
return;
diff --git a/libs/vr/libbufferhub/buffer_hub_base.cpp b/libs/vr/libbufferhub/buffer_hub_base.cpp
index 68cc766..2dc427a 100644
--- a/libs/vr/libbufferhub/buffer_hub_base.cpp
+++ b/libs/vr/libbufferhub/buffer_hub_base.cpp
@@ -26,6 +26,8 @@
cid_(-1) {}
BufferHubBase::~BufferHubBase() {
+ // buffer_state and fence_state are not reset here. They will be used to
+ // clean up epoll fd if necessary in ProducerChannel::RemoveConsumer method.
if (metadata_header_ != nullptr) {
metadata_buffer_.Unlock();
}
diff --git a/libs/vr/libbufferhub/consumer_buffer.cpp b/libs/vr/libbufferhub/consumer_buffer.cpp
index 8e630ec..62fb5fd 100644
--- a/libs/vr/libbufferhub/consumer_buffer.cpp
+++ b/libs/vr/libbufferhub/consumer_buffer.cpp
@@ -35,17 +35,41 @@
if (!out_meta)
return -EINVAL;
- // Only check producer bit and this consumer buffer's particular consumer bit.
- // The buffer is can be acquired iff: 1) producer bit is set; 2) consumer bit
- // is not set.
- uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsBufferPosted(buffer_state, client_state_mask())) {
- ALOGE("ConsumerBuffer::LocalAcquire: not posted, id=%d state=%" PRIx64
- " client_state_mask=%" PRIx64 ".",
- id(), buffer_state, client_state_mask());
+ // The buffer can be acquired iff the buffer state for this client is posted.
+ uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+ client_state_mask())) {
+ ALOGE(
+ "%s: Failed to acquire the buffer. The buffer is not posted, id=%d "
+ "state=%" PRIx64 " client_state_mask=%" PRIx64 ".",
+ __FUNCTION__, id(), current_buffer_state, client_state_mask());
return -EBUSY;
}
+ // Change the buffer state for this consumer from posted to acquired.
+ uint64_t updated_buffer_state = current_buffer_state ^ client_state_mask();
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGD(
+ "%s Failed to acquire the buffer. Current buffer state was changed to "
+ "%" PRIx64
+ " when trying to acquire the buffer and modify the buffer state to "
+ "%" PRIx64 ". About to try again if the buffer is still posted.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+ if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+ client_state_mask())) {
+ ALOGE(
+ "%s: Failed to acquire the buffer. The buffer is no longer posted, "
+ "id=%d state=%" PRIx64 " client_state_mask=%" PRIx64 ".",
+ __FUNCTION__, id(), current_buffer_state, client_state_mask());
+ return -EBUSY;
+ }
+ // The failure of compare_exchange_weak updates current_buffer_state.
+ updated_buffer_state = current_buffer_state ^ client_state_mask();
+ }
+
// Copy the canonical metadata.
void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
memcpy(out_meta, metadata_ptr, sizeof(DvrNativeBufferMetadata));
@@ -64,8 +88,6 @@
*out_fence = shared_acquire_fence_.Duplicate();
}
- // Set the consumer bit unique to this consumer.
- BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL, client_state_mask());
return 0;
}
@@ -118,12 +140,26 @@
if (const int error = CheckMetadata(meta->user_metadata_size))
return error;
- // Check invalid state transition.
- uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsBufferAcquired(buffer_state)) {
- ALOGE("ConsumerBuffer::LocalRelease: not acquired id=%d state=%" PRIx64 ".",
- id(), buffer_state);
- return -EBUSY;
+ // Set the buffer state of this client to released if it is not already in
+ // released state.
+ uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (BufferHubDefs::IsClientReleased(current_buffer_state,
+ client_state_mask())) {
+ return 0;
+ }
+ uint64_t updated_buffer_state = current_buffer_state & (~client_state_mask());
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGD(
+ "%s: Failed to release the buffer. Current buffer state was changed to "
+ "%" PRIx64
+ " when trying to release the buffer and modify the buffer state to "
+ "%" PRIx64 ". About to try again.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+ // The failure of compare_exchange_weak updates current_buffer_state.
+ updated_buffer_state = current_buffer_state & (~client_state_mask());
}
// On release, only the user requested metadata is copied back into the shared
@@ -141,8 +177,6 @@
if (const int error = UpdateSharedFence(release_fence, shared_release_fence_))
return error;
- // For release operation, the client don't need to change the state as it's
- // bufferhubd's job to flip the produer bit once all consumers are released.
return 0;
}
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
index 650da97..400def7 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h
@@ -20,52 +20,104 @@
static constexpr uint32_t kMetadataUsage =
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
-// Single producuer multiple (up to 63) consumers ownership signal.
+// Single buffer clients (up to 32) ownership signal.
// 64-bit atomic unsigned int.
+// Each client takes 2 bits. The first bit locates in the first 32 bits of
+// buffer_state; the second bit locates in the last 32 bits of buffer_state.
+// Client states:
+// Gained state 11. Exclusive write state.
+// Posted state 10.
+// Acquired state 01. Shared read state.
+// Released state 00.
//
-// MSB LSB
-// | |
-// v v
-// [C62|...|C1|C0|P]
-// Gain'ed state: [..|0|0|0] -> Exclusively Writable.
-// Post'ed state: [..|0|0|1]
-// Acquired'ed state: [..|X|X|1] -> At least one bit is set in higher 63 bits
-// Released'ed state: [..|X|X|0] -> At least one bit is set in higher 63 bits
-static constexpr int kMaxNumberOfClients = 64;
-static constexpr uint64_t kFirstClientBitMask = 1ULL;
-static constexpr uint64_t kConsumerStateMask = ~kFirstClientBitMask;
+// MSB LSB
+// | |
+// v v
+// [C31|...|C1|C0|C31| ... |C1|C0]
-static inline void ModifyBufferState(std::atomic<uint64_t>* buffer_state,
- uint64_t clear_mask, uint64_t set_mask) {
- uint64_t old_state;
- uint64_t new_state;
- do {
- old_state = buffer_state->load(std::memory_order_acquire);
- new_state = (old_state & ~clear_mask) | set_mask;
- } while (!buffer_state->compare_exchange_weak(old_state, new_state));
+// Maximum number of clients a buffer can have.
+static constexpr int kMaxNumberOfClients = 32;
+
+// Definition of bit masks.
+// MSB LSB
+// | kHighBitsMask | kLowbitsMask |
+// v v v
+// [b63| ... |b32|b31| ... |b0]
+
+// The location of lower 32 bits in the 64-bit buffer state.
+static constexpr uint64_t kLowbitsMask = (1ULL << kMaxNumberOfClients) - 1ULL;
+
+// The location of higher 32 bits in the 64-bit buffer state.
+static constexpr uint64_t kHighBitsMask = ~kLowbitsMask;
+
+// The client bit mask of the first client.
+static constexpr uint64_t kFirstClientBitMask =
+ (1ULL << kMaxNumberOfClients) + 1ULL;
+
+// Returns true if any of the client is in gained state.
+static inline bool AnyClientGained(uint64_t state) {
+ uint64_t high_bits = state >> kMaxNumberOfClients;
+ uint64_t low_bits = state & kLowbitsMask;
+ return high_bits == low_bits && low_bits != 0ULL;
}
-static inline bool IsBufferGained(uint64_t state) { return state == 0; }
-
-static inline bool IsBufferPosted(uint64_t state,
- uint64_t consumer_bit = kConsumerStateMask) {
- return (state & kFirstClientBitMask) && !(state & consumer_bit);
+// Returns true if the input client is in gained state.
+static inline bool IsClientGained(uint64_t state, uint64_t client_bit_mask) {
+ return state == client_bit_mask;
}
-static inline bool IsBufferAcquired(uint64_t state) {
- return (state & kFirstClientBitMask) && (state & kConsumerStateMask);
+// Returns true if any of the client is in posted state.
+static inline bool AnyClientPosted(uint64_t state) {
+ uint64_t high_bits = state >> kMaxNumberOfClients;
+ uint64_t low_bits = state & kLowbitsMask;
+ uint64_t posted_or_acquired = high_bits ^ low_bits;
+ return posted_or_acquired & high_bits;
}
-static inline bool IsBufferReleased(uint64_t state) {
- return !(state & kFirstClientBitMask) && (state & kConsumerStateMask);
+// Returns true if the input client is in posted state.
+static inline bool IsClientPosted(uint64_t state, uint64_t client_bit_mask) {
+ uint64_t client_bits = state & client_bit_mask;
+ if (client_bits == 0ULL)
+ return false;
+ uint64_t low_bits = client_bits & kLowbitsMask;
+ return low_bits == 0ULL;
}
-static inline uint64_t FindNextAvailableClientStateMask(uint64_t bits) {
- return ~bits - (~bits & (~bits - 1));
+// Return true if any of the client is in acquired state.
+static inline bool AnyClientAcquired(uint64_t state) {
+ uint64_t high_bits = state >> kMaxNumberOfClients;
+ uint64_t low_bits = state & kLowbitsMask;
+ uint64_t posted_or_acquired = high_bits ^ low_bits;
+ return posted_or_acquired & low_bits;
}
-static inline uint64_t FindFirstClearedBit() {
- return FindNextAvailableClientStateMask(kFirstClientBitMask);
+// Return true if the input client is in acquired state.
+static inline bool IsClientAcquired(uint64_t state, uint64_t client_bit_mask) {
+ uint64_t client_bits = state & client_bit_mask;
+ if (client_bits == 0ULL)
+ return false;
+ uint64_t high_bits = client_bits & kHighBitsMask;
+ return high_bits == 0ULL;
+}
+
+// Returns true if all clients are in released state.
+static inline bool IsBufferReleased(uint64_t state) { return state == 0ULL; }
+
+// Returns true if the input client is in released state.
+static inline bool IsClientReleased(uint64_t state, uint64_t client_bit_mask) {
+ return (state & client_bit_mask) == 0ULL;
+}
+
+// Returns the next available buffer client's client_state_masks.
+// @params union_bits. Union of all existing clients' client_state_masks.
+static inline uint64_t FindNextAvailableClientStateMask(uint64_t union_bits) {
+ uint64_t low_union = union_bits & kLowbitsMask;
+ if (low_union == kLowbitsMask)
+ return 0ULL;
+ uint64_t incremented = low_union + 1ULL;
+ uint64_t difference = incremented ^ low_union;
+ uint64_t new_low_bit = (difference + 1ULL) >> 1;
+ return new_low_bit + (new_low_bit << kMaxNumberOfClients);
}
struct __attribute__((packed, aligned(8))) MetadataHeader {
@@ -74,9 +126,21 @@
// part is subject for future updates, it's not stable cross Android version,
// so don't have it visible from outside of the Android platform (include Apps
// and vendor HAL).
+
+ // Every client takes up one bit from the higher 32 bits and one bit from the
+ // lower 32 bits in buffer_state.
std::atomic<uint64_t> buffer_state;
+
+ // Every client takes up one bit in fence_state. Only the lower 32 bits are
+ // valid. The upper 32 bits are there for easier manipulation, but the value
+ // should be ignored.
std::atomic<uint64_t> fence_state;
+
+ // Every client takes up one bit from the higher 32 bits and one bit from the
+ // lower 32 bits in active_clients_bit_mask.
std::atomic<uint64_t> active_clients_bit_mask;
+
+ // The index of the buffer queue where the buffer belongs to.
uint64_t queue_index;
// Public data format, which should be updated with caution. See more details
diff --git a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
index 7349779..7aa50b1 100644
--- a/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
+++ b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h
@@ -48,9 +48,8 @@
// Asynchronously acquires a bufer.
int AcquireAsync(DvrNativeBufferMetadata* out_meta, LocalHandle* out_fence);
- // This should be called after a successful Acquire call. If the fence is
- // valid the fence determines the buffer usage, otherwise the buffer is
- // released immediately.
+ // Releases the buffer from any buffer state. If the fence is valid the fence
+ // determines the buffer usage, otherwise the buffer is released immediately.
// This returns zero or a negative unix error code.
int Release(const LocalHandle& release_fence);
int ReleaseAsync();
diff --git a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
index 860f08a..ed38e7f 100644
--- a/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
+++ b/libs/vr/libbufferhub/include/private/dvr/ion_buffer.h
@@ -24,7 +24,7 @@
IonBuffer& operator=(IonBuffer&& other) noexcept;
// Returns check this IonBuffer holds a valid Gralloc buffer.
- bool IsValid() const { return buffer_ && buffer_->initCheck() == NO_ERROR; }
+ bool IsValid() const { return buffer_ && buffer_->initCheck() == OK; }
// Frees the underlying native handle and leaves the instance initialized to
// empty.
diff --git a/libs/vr/libbufferhub/ion_buffer.cpp b/libs/vr/libbufferhub/ion_buffer.cpp
index 1295531..1965410 100644
--- a/libs/vr/libbufferhub/ion_buffer.cpp
+++ b/libs/vr/libbufferhub/ion_buffer.cpp
@@ -205,7 +205,7 @@
status_t err =
buffer_->lock(usage, Rect(x, y, x + width, y + height), address);
- if (err != NO_ERROR)
+ if (err != OK)
return -EINVAL;
else
return 0;
@@ -220,7 +220,7 @@
status_t err =
buffer_->lockYCbCr(usage, Rect(x, y, x + width, y + height), yuv);
- if (err != NO_ERROR)
+ if (err != OK)
return -EINVAL;
else
return 0;
@@ -231,7 +231,7 @@
ALOGD_IF(TRACE, "IonBuffer::Unlock: handle=%p", handle());
status_t err = buffer_->unlock();
- if (err != NO_ERROR)
+ if (err != OK)
return -EINVAL;
else
return 0;
diff --git a/libs/vr/libbufferhub/producer_buffer.cpp b/libs/vr/libbufferhub/producer_buffer.cpp
index f36e169..c177970 100644
--- a/libs/vr/libbufferhub/producer_buffer.cpp
+++ b/libs/vr/libbufferhub/producer_buffer.cpp
@@ -79,14 +79,43 @@
if (const int error = CheckMetadata(meta->user_metadata_size))
return error;
- // Check invalid state transition.
- uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsBufferGained(buffer_state)) {
- ALOGE("ProducerBuffer::LocalPost: not gained, id=%d state=%" PRIx64 ".",
- id(), buffer_state);
+ // The buffer can be posted iff the buffer state for this client is gained.
+ uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (!BufferHubDefs::IsClientGained(current_buffer_state,
+ client_state_mask())) {
+ ALOGE("%s: not gained, id=%d state=%" PRIx64 ".", __FUNCTION__, id(),
+ current_buffer_state);
return -EBUSY;
}
+ // Set the producer client buffer state to released, other clients' buffer
+ // state to posted.
+ uint64_t current_active_clients_bit_mask =
+ active_clients_bit_mask_->load(std::memory_order_acquire);
+ uint64_t updated_buffer_state = current_active_clients_bit_mask &
+ (~client_state_mask()) &
+ BufferHubDefs::kHighBitsMask;
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGD(
+ "%s: Failed to post the buffer. Current buffer state was changed to "
+ "%" PRIx64
+ " when trying to post the buffer and modify the buffer state to "
+ "%" PRIx64
+ ". About to try again if the buffer is still gained by this client.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+ if (!BufferHubDefs::IsClientGained(current_buffer_state,
+ client_state_mask())) {
+ ALOGE(
+ "%s: Failed to post the buffer. The buffer is no longer gained, "
+ "id=%d state=%" PRIx64 ".",
+ __FUNCTION__, id(), current_buffer_state);
+ return -EBUSY;
+ }
+ }
+
// Copy the canonical metadata.
void* metadata_ptr = reinterpret_cast<void*>(&metadata_header_->metadata);
memcpy(metadata_ptr, meta, sizeof(DvrNativeBufferMetadata));
@@ -101,10 +130,6 @@
if (const int error = UpdateSharedFence(ready_fence, shared_acquire_fence_))
return error;
- // Set the producer bit atomically to transit into posted state.
- // The producer state bit mask is kFirstClientBitMask for now.
- BufferHubDefs::ModifyBufferState(buffer_state_, 0ULL,
- BufferHubDefs::kFirstClientBitMask);
return 0;
}
@@ -136,25 +161,52 @@
int ProducerBuffer::LocalGain(DvrNativeBufferMetadata* out_meta,
LocalHandle* out_fence, bool gain_posted_buffer) {
- uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- ALOGD_IF(TRACE, "ProducerBuffer::LocalGain: buffer=%d, state=%" PRIx64 ".",
- id(), buffer_state);
-
if (!out_meta)
return -EINVAL;
- if (BufferHubDefs::IsBufferGained(buffer_state)) {
- // We don't want to log error when gaining a newly allocated
- // buffer.
- ALOGI("ProducerBuffer::LocalGain: already gained id=%d.", id());
- return -EALREADY;
+ uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ ALOGD_IF(TRACE, "%s: buffer=%d, state=%" PRIx64 ".", __FUNCTION__, id(),
+ current_buffer_state);
+
+ if (BufferHubDefs::IsClientGained(current_buffer_state,
+ client_state_mask())) {
+ ALOGI("%s: already gained id=%d.", __FUNCTION__, id());
+ return 0;
}
- if (BufferHubDefs::IsBufferAcquired(buffer_state) ||
- (BufferHubDefs::IsBufferPosted(buffer_state) && !gain_posted_buffer)) {
- ALOGE("ProducerBuffer::LocalGain: not released id=%d state=%" PRIx64 ".",
- id(), buffer_state);
+ if (BufferHubDefs::AnyClientAcquired(current_buffer_state) ||
+ BufferHubDefs::AnyClientGained(current_buffer_state) ||
+ (BufferHubDefs::AnyClientPosted(current_buffer_state) &&
+ !gain_posted_buffer)) {
+ ALOGE("%s: not released id=%d state=%" PRIx64 ".", __FUNCTION__, id(),
+ current_buffer_state);
return -EBUSY;
}
+ // Change the buffer state to gained state.
+ uint64_t updated_buffer_state = client_state_mask();
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGD(
+ "%s: Failed to gain the buffer. Current buffer state was changed to "
+ "%" PRIx64
+ " when trying to gain the buffer and modify the buffer state to "
+ "%" PRIx64
+ ". About to try again if the buffer is still not read by other "
+ "clients.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+
+ if (BufferHubDefs::AnyClientAcquired(current_buffer_state) ||
+ BufferHubDefs::AnyClientGained(current_buffer_state) ||
+ (BufferHubDefs::AnyClientPosted(current_buffer_state) &&
+ !gain_posted_buffer)) {
+ ALOGE(
+ "%s: Failed to gain the buffer. The buffer is no longer released. "
+ "id=%d state=%" PRIx64 ".",
+ __FUNCTION__, id(), current_buffer_state);
+ return -EBUSY;
+ }
+ }
// Canonical metadata is undefined on Gain. Except for user_metadata and
// release_fence_mask. Fill in the user_metadata_ptr in address space of the
@@ -169,16 +221,20 @@
out_meta->user_metadata_ptr = 0;
}
- uint64_t fence_state = fence_state_->load(std::memory_order_acquire);
+ uint64_t current_fence_state = fence_state_->load(std::memory_order_acquire);
+ uint64_t current_active_clients_bit_mask =
+ active_clients_bit_mask_->load(std::memory_order_acquire);
// If there is an release fence from consumer, we need to return it.
- if (fence_state & BufferHubDefs::kConsumerStateMask) {
+ // TODO(b/112007999) add an atomic variable in metadata header in shared
+ // memory to indicate which client is the last producer of the buffer.
+ // Currently, assume the first client is the only producer to the buffer.
+ if (current_fence_state & current_active_clients_bit_mask &
+ (~BufferHubDefs::kFirstClientBitMask)) {
*out_fence = shared_release_fence_.Duplicate();
out_meta->release_fence_mask =
- fence_state & BufferHubDefs::kConsumerStateMask;
+ current_fence_state & current_active_clients_bit_mask;
}
- // Clear out all bits and the buffer is now back to gained state.
- buffer_state_->store(0ULL);
return 0;
}
@@ -232,7 +288,8 @@
// TODO(b/112338294) Keep here for reference. Remove it after new logic is
// written.
/* uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+ if (!BufferHubDefs::IsClientGained(
+ buffer_state, BufferHubDefs::kFirstClientStateMask)) {
// Can only detach a ProducerBuffer when it's in gained state.
ALOGW("ProducerBuffer::Detach: The buffer (id=%d, state=0x%" PRIx64
") is not in gained state.",
diff --git a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
index b2b4d7a..b6813eb 100644
--- a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
+++ b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp
@@ -66,7 +66,7 @@
reply->writeStrongBinder(
IGraphicBufferProducer::asBinder(new_queue->producer));
buffer_queues_.push_back(new_queue);
- return NO_ERROR;
+ return OK;
}
default:
return UNKNOWN_TRANSACTION;
@@ -89,7 +89,7 @@
/*waitForFence=*/false);
}
- if (ret != NO_ERROR) {
+ if (ret != OK) {
LOG(ERROR) << "Failed to acquire next buffer.";
return;
}
@@ -99,7 +99,7 @@
ret = buffer_item_consumer_->releaseBuffer(buffer);
}
- if (ret != NO_ERROR) {
+ if (ret != OK) {
LOG(ERROR) << "Failed to release buffer.";
return;
}
@@ -171,14 +171,14 @@
Parcel data;
Parcel reply;
int error = service_->transact(CREATE_BUFFER_QUEUE, data, &reply);
- if (error != NO_ERROR) {
+ if (error != OK) {
LOG(ERROR) << "Failed to get buffer queue over binder.";
return nullptr;
}
sp<IBinder> binder;
error = reply.readNullableStrongBinder(&binder);
- if (error != NO_ERROR) {
+ if (error != OK) {
LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder.";
return nullptr;
}
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index f7942d0..9c4f73f 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -532,7 +532,8 @@
Status<size_t> ProducerQueue::InsertBuffer(
const std::shared_ptr<BufferProducer>& buffer) {
if (buffer == nullptr ||
- !BufferHubDefs::IsBufferGained(buffer->buffer_state())) {
+ !BufferHubDefs::IsClientGained(buffer->buffer_state(),
+ buffer->client_state_mask())) {
ALOGE(
"ProducerQueue::InsertBuffer: Can only insert a buffer when it's in "
"gained state.");
@@ -637,7 +638,7 @@
static_cast<int>(*slot));
return ErrorStatus(EIO);
}
- if (!BufferHubDefs::IsBufferAcquired(buffer->buffer_state())) {
+ if (!BufferHubDefs::AnyClientAcquired(buffer->buffer_state())) {
*slot = *iter;
unavailable_buffers_slot_.erase(iter);
unavailable_buffers_slot_.push_back(*slot);
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
index 2cd7c45..f705749 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
@@ -35,7 +35,7 @@
}
status_t res = parcel->writeUint32(Magic);
- if (res != NO_ERROR) {
+ if (res != OK) {
ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic.");
return res;
}
@@ -53,10 +53,10 @@
}
uint32_t out_magic = 0;
- status_t res = NO_ERROR;
+ status_t res = OK;
res = parcel->readUint32(&out_magic);
- if (res != NO_ERROR)
+ if (res != OK)
return res;
if (out_magic != Magic) {
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
index c58f55f..fd6ca43 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -112,7 +112,7 @@
// Consumer acquires a buffer.
auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(c1_status.ok());
+ EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
auto c1 = c1_status.take();
ASSERT_NE(c1, nullptr);
EXPECT_EQ(mi.index, i);
@@ -334,6 +334,7 @@
std::shared_ptr<BufferProducer> p1 = BufferProducer::Create(
kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage, 0);
ASSERT_TRUE(p1 != nullptr);
+ ASSERT_EQ(p1->GainAsync(), 0);
// Inserting a posted buffer will fail.
DvrNativeBufferMetadata meta;
@@ -345,9 +346,10 @@
// Inserting a gained buffer will succeed.
std::shared_ptr<BufferProducer> p2 = BufferProducer::Create(
kBufferWidth, kBufferHeight, kBufferFormat, kBufferUsage);
+ ASSERT_EQ(p2->GainAsync(), 0);
ASSERT_TRUE(p2 != nullptr);
status_or_slot = producer_queue_->InsertBuffer(p2);
- EXPECT_TRUE(status_or_slot.ok());
+ EXPECT_TRUE(status_or_slot.ok()) << status_or_slot.GetErrorMessage();
// This is the first buffer inserted, should take slot 0.
size_t slot = status_or_slot.get();
EXPECT_EQ(slot, 0);
@@ -585,7 +587,7 @@
mi.user_metadata_ptr = reinterpret_cast<uint64_t>(&user_metadata);
EXPECT_EQ(p1->PostAsync(&mi, {}), 0);
auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- EXPECT_TRUE(c1_status.ok());
+ EXPECT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
auto c1 = c1_status.take();
ASSERT_NE(c1, nullptr);
@@ -689,7 +691,7 @@
size_t cs1, cs2;
auto c1_status = consumer_queue_->Dequeue(kTimeoutMs, &cs1, &mo, &fence);
- ASSERT_TRUE(c1_status.ok());
+ ASSERT_TRUE(c1_status.ok()) << c1_status.GetErrorMessage();
auto c1 = c1_status.take();
ASSERT_NE(c1, nullptr);
ASSERT_EQ(consumer_queue_->count(), 0U);
@@ -905,7 +907,7 @@
ASSERT_NE(producer_buffer, nullptr);
ASSERT_EQ(producer_buffer->PostAsync(&mi, fence), 0);
consumer_status = consumer_queue_->Dequeue(kTimeoutMs, &slot, &mo, &fence);
- ASSERT_TRUE(consumer_status.ok());
+ ASSERT_TRUE(consumer_status.ok()) << consumer_status.GetErrorMessage();
}
status = producer_queue_->FreeAllBuffers();
@@ -948,7 +950,7 @@
Parcel parcel;
status_t res;
res = output_parcelable.writeToParcel(&parcel);
- EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(res, OK);
// After written into parcelable, the output_parcelable is still valid has
// keeps the producer channel alive.
@@ -970,7 +972,7 @@
EXPECT_FALSE(input_parcelable.IsValid());
res = input_parcelable.readFromParcel(&parcel);
- EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(res, OK);
EXPECT_TRUE(input_parcelable.IsValid());
EXPECT_EQ(producer_queue_, nullptr);
@@ -999,7 +1001,7 @@
// Make sure the buffer can be dequeued from consumer side.
auto s4 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
- EXPECT_TRUE(s4.ok());
+ EXPECT_TRUE(s4.ok()) << s4.GetErrorMessage();
EXPECT_EQ(consumer_queue_->capacity(), 1U);
auto consumer = s4.take();
@@ -1040,7 +1042,7 @@
EXPECT_FALSE(input_parcelable.IsValid());
res = input_parcelable.readFromParcel(&parcel);
- EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(res, OK);
EXPECT_TRUE(input_parcelable.IsValid());
consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle());
@@ -1066,7 +1068,7 @@
// Make sure the buffer can be dequeued from consumer side.
auto s3 = consumer_queue_->Dequeue(kTimeoutMs, &slot, &consumer_meta, &fence);
- EXPECT_TRUE(s3.ok());
+ EXPECT_TRUE(s3.ok()) << s3.GetErrorMessage();
EXPECT_EQ(consumer_queue_->capacity(), 1U);
auto consumer = s3.take();
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
index 4f10f83..8cc7081 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp
@@ -108,8 +108,8 @@
void ConnectProducer() {
IGraphicBufferProducer::QueueBufferOutput output;
// Can connect the first time.
- ASSERT_EQ(NO_ERROR, mProducer->connect(kDummyListener, kTestApi,
- kTestControlledByApp, &output));
+ ASSERT_EQ(OK, mProducer->connect(kDummyListener, kTestApi,
+ kTestControlledByApp, &output));
}
// Dequeue a buffer in a 'correct' fashion.
@@ -170,7 +170,7 @@
TEST_F(BufferHubQueueProducerTest, Disconnect_Succeeds) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
- ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
}
TEST_F(BufferHubQueueProducerTest, Disconnect_ReturnsError) {
@@ -186,26 +186,24 @@
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
int32_t value = -1;
- EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
EXPECT_EQ(kDefaultWidth, static_cast<uint32_t>(value));
- EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_HEIGHT, &value));
EXPECT_EQ(kDefaultHeight, static_cast<uint32_t>(value));
- EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_FORMAT, &value));
EXPECT_EQ(kDefaultFormat, value);
- EXPECT_EQ(NO_ERROR,
- mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value));
EXPECT_LE(0, value);
EXPECT_GE(BufferQueueDefs::NUM_BUFFER_SLOTS, value);
- EXPECT_EQ(NO_ERROR,
+ EXPECT_EQ(OK,
mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value));
EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue
- EXPECT_EQ(NO_ERROR,
- mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value));
EXPECT_EQ(kDefaultConsumerUsageBits, value);
}
@@ -243,14 +241,14 @@
// Request the buffer (pre-requisite for queueing)
sp<GraphicBuffer> buffer;
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
// A generic "valid" input
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
IGraphicBufferProducer::QueueBufferOutput output;
// Queue the buffer back into the BQ
- ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
EXPECT_EQ(kDefaultWidth, output.width);
EXPECT_EQ(kDefaultHeight, output.height);
@@ -313,7 +311,7 @@
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
sp<GraphicBuffer> buffer;
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
sp<Fence> nullFence = NULL;
@@ -332,7 +330,7 @@
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
sp<GraphicBuffer> buffer;
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input =
QueueBufferInputBuilder().setScalingMode(-1).build();
@@ -353,7 +351,7 @@
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
sp<GraphicBuffer> buffer;
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
IGraphicBufferProducer::QueueBufferInput input =
QueueBufferInputBuilder()
@@ -372,7 +370,7 @@
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot, &fence));
// Should be able to cancel buffer after a dequeue.
- EXPECT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
+ EXPECT_EQ(OK, mProducer->cancelBuffer(slot, fence));
}
TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) {
@@ -380,16 +378,15 @@
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
int minUndequeuedBuffers;
- ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers));
+ ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers));
const int minBuffers = 1;
const int maxBuffers =
BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
- ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
- << "async mode: " << false;
- ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(minBuffers))
+ ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false;
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(minBuffers))
<< "bufferCount: " << minBuffers;
// Should now be able to dequeue up to minBuffers times
@@ -399,14 +396,14 @@
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
}
- ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers));
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers));
// queue the first buffer to enable max dequeued buffer count checking
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
IGraphicBufferProducer::QueueBufferOutput output;
sp<GraphicBuffer> buffer;
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
- ASSERT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
sp<Fence> fence;
for (int i = 0; i < maxBuffers; ++i) {
@@ -414,25 +411,24 @@
}
// Cancel a buffer, so we can decrease the buffer count
- ASSERT_EQ(NO_ERROR, mProducer->cancelBuffer(slot, fence));
+ ASSERT_EQ(OK, mProducer->cancelBuffer(slot, fence));
// Should now be able to decrease the max dequeued count by 1
- ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxBuffers - 1));
}
TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) {
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
int minUndequeuedBuffers;
- ASSERT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers));
+ ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers));
const int minBuffers = 1;
const int maxBuffers =
BufferQueueDefs::NUM_BUFFER_SLOTS - minUndequeuedBuffers;
- ASSERT_EQ(NO_ERROR, mProducer->setAsyncMode(false))
- << "async mode: " << false;
+ ASSERT_EQ(OK, mProducer->setAsyncMode(false)) << "async mode: " << false;
// Buffer count was out of range
EXPECT_EQ(BAD_VALUE, mProducer->setMaxDequeuedBufferCount(0))
<< "bufferCount: " << 0;
@@ -440,7 +436,7 @@
<< "bufferCount: " << maxBuffers + 1;
// Set max dequeue count to 2
- ASSERT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(2));
+ ASSERT_EQ(OK, mProducer->setMaxDequeuedBufferCount(2));
// Dequeue 2 buffers
int slot = -1;
sp<Fence> fence;
@@ -478,7 +474,7 @@
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
// Shouldn't be able to request buffer after disconnect.
- ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
ASSERT_EQ(NO_INIT, mProducer->requestBuffer(slot, &buffer));
}
@@ -489,14 +485,14 @@
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
// A generic "valid" input
IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput();
IGraphicBufferProducer::QueueBufferOutput output;
// Shouldn't be able to queue buffer after disconnect.
- ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
ASSERT_EQ(NO_INIT, mProducer->queueBuffer(slot, input, &output));
}
@@ -507,10 +503,10 @@
ASSERT_NO_FATAL_FAILURE(ConnectProducer());
ASSERT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- ASSERT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
+ ASSERT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
// Shouldn't be able to cancel buffer after disconnect.
- ASSERT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ ASSERT_EQ(OK, mProducer->disconnect(kTestApi));
ASSERT_EQ(NO_INIT, mProducer->cancelBuffer(slot, Fence::NO_FENCE));
}
@@ -524,32 +520,32 @@
constexpr int maxDequeuedBuffers = 1;
int minUndequeuedBuffers;
- EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &minUndequeuedBuffers));
- EXPECT_EQ(NO_ERROR, mProducer->setAsyncMode(false));
- EXPECT_EQ(NO_ERROR, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &minUndequeuedBuffers));
+ EXPECT_EQ(OK, mProducer->setAsyncMode(false));
+ EXPECT_EQ(OK, mProducer->setMaxDequeuedBufferCount(maxDequeuedBuffers));
int maxCapacity = maxDequeuedBuffers + minUndequeuedBuffers;
// Dequeue, request, and queue all buffers.
for (int i = 0; i < maxCapacity; i++) {
EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
- EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
}
// Disconnect then reconnect.
- EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ EXPECT_EQ(OK, mProducer->disconnect(kTestApi));
EXPECT_NO_FATAL_FAILURE(ConnectProducer());
// Dequeue, request, and queue all buffers.
for (int i = 0; i < maxCapacity; i++) {
EXPECT_NO_FATAL_FAILURE(DequeueBuffer(&slot));
- EXPECT_EQ(NO_ERROR, mProducer->requestBuffer(slot, &buffer));
- EXPECT_EQ(NO_ERROR, mProducer->queueBuffer(slot, input, &output));
+ EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buffer));
+ EXPECT_EQ(OK, mProducer->queueBuffer(slot, input, &output));
}
- EXPECT_EQ(NO_ERROR, mProducer->disconnect(kTestApi));
+ EXPECT_EQ(OK, mProducer->disconnect(kTestApi));
}
TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) {
@@ -568,21 +564,21 @@
EXPECT_TRUE(dummy_producer_parcelable.IsValid());
// Disconnect producer can be taken out, but only to an invalid parcelable.
- ASSERT_EQ(mProducer->disconnect(kTestApi), NO_ERROR);
+ ASSERT_EQ(mProducer->disconnect(kTestApi), OK);
EXPECT_EQ(mProducer->TakeAsParcelable(&dummy_producer_parcelable), BAD_VALUE);
EXPECT_FALSE(producer_parcelable.IsValid());
- EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), NO_ERROR);
+ EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), OK);
EXPECT_TRUE(producer_parcelable.IsValid());
// Should still be able to query buffer dimension after disconnect.
int32_t value = -1;
- EXPECT_EQ(NO_ERROR, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
+ EXPECT_EQ(OK, mProducer->query(NATIVE_WINDOW_WIDTH, &value));
EXPECT_EQ(static_cast<uint32_t>(value), kDefaultWidth);
- EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), NO_ERROR);
+ EXPECT_EQ(mProducer->query(NATIVE_WINDOW_HEIGHT, &value), OK);
EXPECT_EQ(static_cast<uint32_t>(value), kDefaultHeight);
- EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), NO_ERROR);
+ EXPECT_EQ(mProducer->query(NATIVE_WINDOW_FORMAT, &value), OK);
EXPECT_EQ(value, kDefaultFormat);
// But connect to API will fail.
@@ -598,7 +594,7 @@
ASSERT_TRUE(new_producer != nullptr);
EXPECT_EQ(new_producer->connect(kDummyListener, kTestApi,
kTestControlledByApp, &output),
- NO_ERROR);
+ OK);
}
} // namespace
diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp
index 43b1196..4668b98 100644
--- a/libs/vr/libdisplay/vsync_service.cpp
+++ b/libs/vr/libdisplay/vsync_service.cpp
@@ -13,12 +13,12 @@
CHECK_INTERFACE(IVsyncCallback, data, reply);
int64_t vsync_timestamp = 0;
status_t result = data.readInt64(&vsync_timestamp);
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("onVsync failed to readInt64: %d", result);
return result;
}
onVsync(vsync_timestamp);
- return NO_ERROR;
+ return OK;
}
default: {
return BBinder::onTransact(code, data, reply, flags);
@@ -36,18 +36,18 @@
Parcel data, reply;
status_t result = data.writeInterfaceToken(
IVsyncCallback::getInterfaceDescriptor());
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("onVsync failed to writeInterfaceToken: %d", result);
return result;
}
result = data.writeInt64(vsync_timestamp);
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("onVsync failed to writeInt64: %d", result);
return result;
}
result = remote()->transact(
BnVsyncCallback::ON_VSYNC, data, &reply, TF_ONE_WAY);
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("onVsync failed to transact: %d", result);
return result;
}
@@ -65,23 +65,23 @@
CHECK_INTERFACE(IVsyncService, data, reply);
sp<IBinder> callback;
status_t result = data.readStrongBinder(&callback);
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("registerCallback failed to readStrongBinder: %d", result);
return result;
}
registerCallback(interface_cast<IVsyncCallback>(callback));
- return NO_ERROR;
+ return OK;
}
case UNREGISTER_CALLBACK: {
CHECK_INTERFACE(IVsyncService, data, reply);
sp<IBinder> callback;
status_t result = data.readStrongBinder(&callback);
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("unregisterCallback failed to readStrongBinder: %d", result);
return result;
}
unregisterCallback(interface_cast<IVsyncCallback>(callback));
- return NO_ERROR;
+ return OK;
}
default: {
return BBinder::onTransact(code, data, reply, flags);
@@ -99,18 +99,18 @@
Parcel data, reply;
status_t result = data.writeInterfaceToken(
IVsyncService::getInterfaceDescriptor());
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("registerCallback failed to writeInterfaceToken: %d", result);
return result;
}
result = data.writeStrongBinder(IInterface::asBinder(callback));
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("registerCallback failed to writeStrongBinder: %d", result);
return result;
}
result = remote()->transact(
BnVsyncService::REGISTER_CALLBACK, data, &reply);
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("registerCallback failed to transact: %d", result);
return result;
}
@@ -121,18 +121,18 @@
Parcel data, reply;
status_t result = data.writeInterfaceToken(
IVsyncService::getInterfaceDescriptor());
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result);
return result;
}
result = data.writeStrongBinder(IInterface::asBinder(callback));
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("unregisterCallback failed to writeStrongBinder: %d", result);
return result;
}
result = remote()->transact(
BnVsyncService::UNREGISTER_CALLBACK, data, &reply);
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("unregisterCallback failed to transact: %d", result);
return result;
}
diff --git a/libs/vr/libpdx_uds/channel_parcelable.cpp b/libs/vr/libpdx_uds/channel_parcelable.cpp
index e7bce27..5156846 100644
--- a/libs/vr/libpdx_uds/channel_parcelable.cpp
+++ b/libs/vr/libpdx_uds/channel_parcelable.cpp
@@ -36,7 +36,7 @@
}
status_t ChannelParcelable::writeToParcel(Parcel* parcel) const {
- status_t res = NO_ERROR;
+ status_t res = OK;
if (!IsValid()) {
ALOGE("ChannelParcelable::writeToParcel: Invalid channel parcel.");
@@ -44,20 +44,20 @@
}
res = parcel->writeUint32(kUdsMagicParcelHeader);
- if (res != NO_ERROR) {
+ if (res != OK) {
ALOGE("ChannelParcelable::writeToParcel: Cannot write magic: res=%d.", res);
return res;
}
res = parcel->writeFileDescriptor(data_fd_.Get());
- if (res != NO_ERROR) {
+ if (res != OK) {
ALOGE("ChannelParcelable::writeToParcel: Cannot write data fd: res=%d.",
res);
return res;
}
res = parcel->writeFileDescriptor(pollin_event_fd_.Get());
- if (res != NO_ERROR) {
+ if (res != OK) {
ALOGE(
"ChannelParcelable::writeToParcel: Cannot write pollin event fd: "
"res=%d.",
@@ -66,7 +66,7 @@
}
res = parcel->writeFileDescriptor(pollhup_event_fd_.Get());
- if (res != NO_ERROR) {
+ if (res != OK) {
ALOGE(
"ChannelParcelable::writeToParcel: Cannot write pollhup event fd: "
"res=%d.",
@@ -79,7 +79,7 @@
status_t ChannelParcelable::readFromParcel(const Parcel* parcel) {
uint32_t magic = 0;
- status_t res = NO_ERROR;
+ status_t res = OK;
if (IsValid()) {
ALOGE(
@@ -89,7 +89,7 @@
}
res = parcel->readUint32(&magic);
- if (res != NO_ERROR) {
+ if (res != OK) {
ALOGE("ChannelParcelable::readFromParcel: Failed to read magic: res=%d.",
res);
return res;
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 07904fb..4f8bdbf 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -85,9 +85,6 @@
"-Wno-error=sign-compare", // to fix later
"-Wno-unused-variable",
],
- cppflags: [
- "-std=c++1z"
- ],
shared_libs: sharedLibraries,
whole_static_libs: staticLibraries,
header_libs: headerLibraries,
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 5f6455c..6d259bd 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -1098,7 +1098,7 @@
if (FindCallback(callback) == callbacks_.cend()) {
callbacks_.push_back(callback);
}
- return NO_ERROR;
+ return OK;
}
status_t HardwareComposer::VsyncService::unregisterCallback(
@@ -1108,7 +1108,7 @@
if (iter != callbacks_.cend()) {
callbacks_.erase(iter);
}
- return NO_ERROR;
+ return OK;
}
void HardwareComposer::VsyncService::OnVsync(int64_t vsync_timestamp) {
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
index 1d5740f..0eb7fec 100644
--- a/libs/vr/libvrflinger/tests/vrflinger_test.cpp
+++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
@@ -72,17 +72,17 @@
Parcel data, reply;
status_t result =
data.writeInterfaceToken(surface_flinger_->getInterfaceDescriptor());
- if (result != NO_ERROR) {
+ if (result != OK) {
return std::nullopt;
}
result = IInterface::asBinder(surface_flinger_)
->transact(kIsVrFlingerActiveTransactionCode, data, &reply);
- if (result != NO_ERROR) {
+ if (result != OK) {
return std::nullopt;
}
bool vr_flinger_active;
result = reply.readBool(&vr_flinger_active);
- if (result != NO_ERROR) {
+ if (result != OK) {
return std::nullopt;
}
return vr_flinger_active;
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index e27f233..6edadcd 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -41,13 +41,27 @@
extern "C" {
android_namespace_t* android_get_exported_namespace(const char*);
- // TODO(ianelliott@): Get this from an ANGLE header:
+ // TODO(ianelliott@): Get the following from an ANGLE header:
+ // Version-1 API:
typedef bool (*fpANGLEGetUtilityAPI)(unsigned int* versionToUse);
-
- // TODO(ianelliott@): Get this from an ANGLE header:
typedef bool (*fpAndroidUseANGLEForApplication)(int fd, long offset, long length,
const char* appName, const char* deviceMfr,
const char* deviceModel);
+ // Version-2 API:
+ typedef bool (*fpANGLEGetFeatureSupportUtilAPIVersion)(unsigned int* versionToUse);
+ typedef bool (*fpANGLEAndroidParseRulesString)(const char *rulesString,
+ void** rulesHandle, int* rulesVersion);
+ typedef bool (*fpANGLEGetSystemInfo)(void** handle);
+ typedef bool (*fpANGLEAddDeviceInfoToSystemInfo)(const char* deviceMfr,
+ const char* deviceModel,
+ void* handle);
+ typedef bool (*fpANGLEShouldBeUsedForApplication)(void* rules_handle,
+ int rules_version,
+ void* system_info_handle,
+ const char *appName);
+ typedef bool (*fpANGLEFreeRulesHandle)(void* handle);
+ typedef bool (*fpANGLEFreeSystemInfoHandle)(void* handle);
+
}
// ----------------------------------------------------------------------------
@@ -523,19 +537,21 @@
property_get("ro.product.manufacturer", manufacturer, "UNSET");
property_get("ro.product.model", model, "UNSET");
+ // TODO: Replace this with the new function name once the version-1 API is removed:
fpANGLEGetUtilityAPI ANGLEGetUtilityAPI =
(fpANGLEGetUtilityAPI)dlsym(so, "ANGLEGetUtilityAPI");
if (ANGLEGetUtilityAPI) {
// Negotiate the interface version by requesting most recent known to the platform
- unsigned int versionToUse = 1;
+ unsigned int versionToUse = 2;
+ // TODO: Replace this with the new function name once the version-1 API is removed:
if ((ANGLEGetUtilityAPI)(&versionToUse)) {
// Add and remove versions below as needed
switch(versionToUse) {
case 1: {
- ALOGV("Using version 1 of ANGLE opt-in/out logic interface");
+ ALOGV("Using version 1 of ANGLE feature-support library");
fpAndroidUseANGLEForApplication AndroidUseANGLEForApplication =
(fpAndroidUseANGLEForApplication)dlsym(so, "AndroidUseANGLEForApplication");
@@ -548,6 +564,65 @@
}
}
break;
+ case 2: {
+ ALOGV("Using version 2 of ANGLE feature-support library");
+ void* rules_handle = nullptr;
+ int rules_version = 0;
+ void* system_info_handle = nullptr;
+
+ // Get the symbols for the feature-support-utility library:
+#define GET_SYMBOL(symbol) \
+ fp##symbol symbol = (fp##symbol)dlsym(so, #symbol); \
+ if (!symbol) { \
+ ALOGW("Cannot find "#symbol" in ANGLE feature-support library"); \
+ break; \
+ }
+ GET_SYMBOL(ANGLEAndroidParseRulesString);
+ GET_SYMBOL(ANGLEGetSystemInfo);
+ GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo);
+ GET_SYMBOL(ANGLEShouldBeUsedForApplication);
+ GET_SYMBOL(ANGLEFreeRulesHandle);
+ GET_SYMBOL(ANGLEFreeSystemInfoHandle);
+
+ // Read the contents of the file into a string:
+ off_t fileSize = rules_length;
+ off_t startOfContent = rules_offset;
+ lseek(rules_fd, startOfContent, SEEK_SET);
+ char *buffer = new char[fileSize + 1];
+ ssize_t numBytesRead = read(rules_fd, buffer, fileSize);
+ if (numBytesRead < 0) {
+ ALOGW("Cannot read rules file");
+ break;
+ }
+ if (numBytesRead == 0) {
+ ALOGW("Empty rules file");
+ break;
+ }
+ buffer[numBytesRead] = '\0';
+ std::string rule_file_contents = std::string(buffer);
+ delete[] buffer;
+
+ // Parse the rules, obtain the SystemInfo, and evaluate the
+ // application against the rules:
+ if (!(ANGLEAndroidParseRulesString)(rule_file_contents.c_str(),
+ &rules_handle, &rules_version)) {
+ ALOGW("ANGLE feature-support library cannot parse rules file");
+ break;
+ }
+ if (!(ANGLEGetSystemInfo)(&system_info_handle)) {
+ ALOGW("ANGLE feature-support library cannot obtain SystemInfo");
+ break;
+ }
+ if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, system_info_handle)) {
+ ALOGW("ANGLE feature-support library cannot add device info to SystemInfo");
+ break;
+ }
+ use_angle = (ANGLEShouldBeUsedForApplication)(rules_handle, rules_version,
+ system_info_handle, app_name_str.c_str());
+ (ANGLEFreeRulesHandle)(rules_handle);
+ (ANGLEFreeSystemInfoHandle)(system_info_handle);
+ }
+ break;
default:
ALOGW("Cannot find supported version of ANGLE feature-support library, found version %u", versionToUse);
}
diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp
index 28a7501..b747dbc 100644
--- a/services/bufferhub/Android.bp
+++ b/services/bufferhub/Android.bp
@@ -17,6 +17,7 @@
cc_library_shared {
name: "libbufferhubservice",
cflags: [
+ "-DLOG_TAG=\"libbufferhubservice\"",
"-Wall",
"-Werror",
"-Wextra",
@@ -71,6 +72,7 @@
"libutils",
],
cflags: [
+ "-DLOG_TAG=\"bufferhub\"",
"-Wall",
"-Werror",
"-Wextra",
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index fc5ad1d..6f97f0d 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -51,14 +51,48 @@
return Void();
}
-Return<void> BufferHubService::importBuffer(const hidl_handle& /*nativeHandle*/,
+Return<void> BufferHubService::importBuffer(const hidl_handle& tokenHandle,
importBuffer_cb _hidl_cb) {
- // TODO(b/118614157): implement buffer import
- _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::NO_ERROR);
+ if (!tokenHandle.getNativeHandle() || tokenHandle->numFds != 0 || tokenHandle->numInts != 1) {
+ // nullptr handle or wrong format
+ _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::INVALID_TOKEN);
+ return Void();
+ }
+
+ uint32_t token = tokenHandle->data[0];
+
+ wp<BufferClient> originClientWp;
+ {
+ std::lock_guard<std::mutex> lock(mTokenMapMutex);
+ auto iter = mTokenMap.find(token);
+ if (iter == mTokenMap.end()) {
+ // Invalid token
+ _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::INVALID_TOKEN);
+ return Void();
+ }
+
+ originClientWp = iter->second;
+ mTokenMap.erase(iter);
+ }
+
+ // Check if original client is dead
+ sp<BufferClient> originClient = originClientWp.promote();
+ if (!originClient) {
+ // Should not happen since token should be removed if already gone
+ ALOGE("%s: original client %p gone!", __FUNCTION__, originClientWp.unsafe_get());
+ _hidl_cb(/*bufferClient=*/nullptr, /*status=*/BufferHubStatus::BUFFER_FREED);
+ return Void();
+ }
+
+ sp<BufferClient> client = new BufferClient(*originClient);
+
+ std::lock_guard<std::mutex> lock(mClientListMutex);
+ mClientList.push_back(client);
+ _hidl_cb(/*bufferClient=*/client, /*status=*/BufferHubStatus::NO_ERROR);
return Void();
}
-hidl_handle BufferHubService::registerToken(const BufferClient* client) {
+hidl_handle BufferHubService::registerToken(const wp<BufferClient>& client) {
uint32_t token;
std::lock_guard<std::mutex> lock(mTokenMapMutex);
do {
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
index 5456ec3..769ec86 100644
--- a/services/bufferhub/include/bufferhub/BufferClient.h
+++ b/services/bufferhub/include/bufferhub/BufferClient.h
@@ -37,15 +37,19 @@
class BufferClient : public IBufferClient {
public:
// Creates a server-side buffer client from an existing BufferNode. Note that
- // this funciton takes ownership of the shared_ptr.
+ // this function takes ownership of the shared_ptr.
// Returns a raw pointer to the BufferClient on success, nullptr on failure.
static BufferClient* create(BufferHubService* service, const std::shared_ptr<BufferNode>& node);
+ // Creates a BufferClient from an existing BufferClient. Will share the same BufferNode.
+ explicit BufferClient(const BufferClient& other)
+ : mService(other.mService), mBufferNode(other.mBufferNode) {}
+
Return<void> duplicate(duplicate_cb _hidl_cb) override;
private:
BufferClient(wp<BufferHubService> service, const std::shared_ptr<BufferNode>& node)
- : mService(service), mBufferNode(node){};
+ : mService(service), mBufferNode(node) {}
wp<BufferHubService> mService;
std::shared_ptr<BufferNode> mBufferNode;
diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h
index 5441750..6535659 100644
--- a/services/bufferhub/include/bufferhub/BufferHubService.h
+++ b/services/bufferhub/include/bufferhub/BufferHubService.h
@@ -42,22 +42,22 @@
Return<void> allocateBuffer(const HardwareBufferDescription& description,
const uint32_t userMetadataSize,
allocateBuffer_cb _hidl_cb) override;
- Return<void> importBuffer(const hidl_handle& nativeHandle, importBuffer_cb _hidl_cb) override;
+ Return<void> importBuffer(const hidl_handle& tokenHandle, importBuffer_cb _hidl_cb) override;
// Non-binder functions
// Internal help function for IBufferClient::duplicate.
- hidl_handle registerToken(const BufferClient* client);
+ hidl_handle registerToken(const wp<BufferClient>& client);
private:
// List of active BufferClient for bookkeeping.
std::mutex mClientListMutex;
- std::vector<sp<BufferClient>> mClientList GUARDED_BY(mClientListMutex);
+ std::vector<wp<BufferClient>> mClientList GUARDED_BY(mClientListMutex);
// TODO(b/118180214): use a more secure implementation
std::mt19937 mTokenEngine;
// The mapping from token to the client creates it.
std::mutex mTokenMapMutex;
- std::map<uint32_t, const BufferClient*> mTokenMap GUARDED_BY(mTokenMapMutex);
+ std::map<uint32_t, const wp<BufferClient>> mTokenMap GUARDED_BY(mTokenMapMutex);
};
} // namespace implementation
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 250bbee..47bed65 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -21,7 +21,6 @@
"-Wunused",
"-Wunreachable-code",
],
- cppflags: ["-std=c++1z"],
srcs: [
":gpuservice_sources",
],
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 0c9e04b..1dca1b6 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -489,7 +489,7 @@
if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
&& (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
&& mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
- && mInputTargetWaitApplicationHandle != nullptr) {
+ && mInputTargetWaitApplicationToken != nullptr) {
int32_t displayId = motionEntry->displayId;
int32_t x = int32_t(motionEntry->pointerCoords[0].
getAxisValue(AMOTION_EVENT_AXIS_X));
@@ -497,8 +497,8 @@
getAxisValue(AMOTION_EVENT_AXIS_Y));
sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
if (touchedWindowHandle != nullptr
- && touchedWindowHandle->inputApplicationHandle
- != mInputTargetWaitApplicationHandle) {
+ && touchedWindowHandle->getApplicationToken()
+ != mInputTargetWaitApplicationToken) {
// User touched a different application than the one we are waiting on.
// Flag the event, and start pruning the input queue.
mNextUnblockedEvent = motionEntry;
@@ -819,7 +819,8 @@
sp<InputWindowHandle> focusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));
if (focusedWindowHandle != nullptr) {
- commandEntry->inputChannel = focusedWindowHandle->getInputChannel();
+ commandEntry->inputChannel =
+ getInputChannelLocked(focusedWindowHandle->getToken());
}
commandEntry->keyEntry = entry;
entry->refCount += 1;
@@ -1010,7 +1011,7 @@
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
mInputTargetWaitTimeoutExpired = false;
- mInputTargetWaitApplicationHandle.clear();
+ mInputTargetWaitApplicationToken.clear();
}
} else {
if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
@@ -1033,13 +1034,13 @@
mInputTargetWaitStartTime = currentTime;
mInputTargetWaitTimeoutTime = currentTime + timeout;
mInputTargetWaitTimeoutExpired = false;
- mInputTargetWaitApplicationHandle.clear();
+ mInputTargetWaitApplicationToken.clear();
if (windowHandle != nullptr) {
- mInputTargetWaitApplicationHandle = windowHandle->inputApplicationHandle;
+ mInputTargetWaitApplicationToken = windowHandle->getApplicationToken();
}
- if (mInputTargetWaitApplicationHandle == nullptr && applicationHandle != nullptr) {
- mInputTargetWaitApplicationHandle = applicationHandle;
+ if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) {
+ mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken();
}
}
}
@@ -1117,7 +1118,7 @@
// Reset input target wait timeout.
mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
- mInputTargetWaitApplicationHandle.clear();
+ mInputTargetWaitApplicationToken.clear();
}
/**
@@ -1666,11 +1667,13 @@
const InputWindowInfo* windowInfo = windowHandle->getInfo();
InputTarget& target = inputTargets.editTop();
- target.inputChannel = windowInfo->inputChannel;
+ target.inputChannel = getInputChannelLocked(windowHandle->getToken());
target.flags = targetFlags;
target.xOffset = - windowInfo->frameLeft;
target.yOffset = - windowInfo->frameTop;
- target.scaleFactor = windowInfo->scaleFactor;
+ target.globalScaleFactor = windowInfo->globalScaleFactor;
+ target.windowXScale = windowInfo->windowXScale;
+ target.windowYScale = windowInfo->windowYScale;
target.pointerIds = pointerIds;
}
@@ -1691,7 +1694,7 @@
target.xOffset = 0;
target.yOffset = 0;
target.pointerIds.clear();
- target.scaleFactor = 1.0f;
+ target.globalScaleFactor = 1.0f;
}
} else {
// If there is no monitor channel registered or all monitor channel unregistered,
@@ -1773,7 +1776,8 @@
}
// If the window's connection is not registered then keep waiting.
- ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
+ ssize_t connectionIndex = getConnectionIndexLocked(
+ getInputChannelLocked(windowHandle->getToken()));
if (connectionIndex < 0) {
return StringPrintf("Waiting because the %s window's input channel is not "
"registered with the input dispatcher. The window may be in the process "
@@ -1910,11 +1914,13 @@
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
- "xOffset=%f, yOffset=%f, scaleFactor=%f, "
- "pointerIds=0x%x",
+ "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
+ "windowScaleFactor=(%f, %f), pointerIds=0x%x",
connection->getInputChannelName().c_str(), inputTarget->flags,
inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->scaleFactor, inputTarget->pointerIds.value);
+ inputTarget->globalScaleFactor,
+ inputTarget->windowXScale, inputTarget->windowYScale,
+ inputTarget->pointerIds.value);
#endif
// Skip this event if the connection status is not normal.
@@ -1991,7 +1997,8 @@
// Enqueue a new dispatch entry onto the outbound queue for this connection.
DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->scaleFactor);
+ inputTarget->globalScaleFactor, inputTarget->windowXScale,
+ inputTarget->windowYScale);
// Apply target flags and update the connection's input state.
switch (eventEntry->type) {
@@ -2107,13 +2114,15 @@
float xOffset, yOffset;
if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
&& !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
- float scaleFactor = dispatchEntry->scaleFactor;
- xOffset = dispatchEntry->xOffset * scaleFactor;
- yOffset = dispatchEntry->yOffset * scaleFactor;
- if (scaleFactor != 1.0f) {
+ float globalScaleFactor = dispatchEntry->globalScaleFactor;
+ float wxs = dispatchEntry->windowXScale;
+ float wys = dispatchEntry->windowYScale;
+ xOffset = dispatchEntry->xOffset * wxs;
+ yOffset = dispatchEntry->yOffset * wys;
+ if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
scaledCoords[i] = motionEntry->pointerCoords[i];
- scaledCoords[i].scale(scaleFactor);
+ scaledCoords[i].scale(globalScaleFactor, wxs, wys);
}
usingCoords = scaledCoords;
}
@@ -2371,11 +2380,13 @@
const InputWindowInfo* windowInfo = windowHandle->getInfo();
target.xOffset = -windowInfo->frameLeft;
target.yOffset = -windowInfo->frameTop;
- target.scaleFactor = windowInfo->scaleFactor;
+ target.globalScaleFactor = windowInfo->globalScaleFactor;
+ target.windowXScale = windowInfo->windowXScale;
+ target.windowYScale = windowInfo->windowYScale;
} else {
target.xOffset = 0;
target.yOffset = 0;
- target.scaleFactor = 1.0f;
+ target.globalScaleFactor = 1.0f;
}
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
@@ -3004,7 +3015,7 @@
size_t numWindows = windowHandles.size();
for (size_t i = 0; i < numWindows; i++) {
const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i);
- if (windowHandle->getInputChannel() == inputChannel) {
+ if (windowHandle->getToken() == inputChannel->getToken()) {
return windowHandle;
}
}
@@ -3018,8 +3029,8 @@
const Vector<sp<InputWindowHandle>> windowHandles = it.second;
size_t numWindows = windowHandles.size();
for (size_t i = 0; i < numWindows; i++) {
- if (windowHandles.itemAt(i)->getInputChannel()->getToken()
- == windowHandle->getInputChannel()->getToken()) {
+ if (windowHandles.itemAt(i)->getToken()
+ == windowHandle->getToken()) {
if (windowHandle->getInfo()->displayId != it.first) {
ALOGE("Found window %s in display %" PRId32
", but it should belong to display %" PRId32,
@@ -3033,6 +3044,14 @@
return false;
}
+sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token) const {
+ size_t count = mInputChannelsByToken.count(token);
+ if (count == 0) {
+ return nullptr;
+ }
+ return mInputChannelsByToken.at(token);
+}
+
/**
* Called from InputManagerService, update window handle list by displayId that can receive input.
* A window handle contains information about InputChannel, Touch Region, Types, Focused,...
@@ -3061,7 +3080,9 @@
size_t numWindows = inputWindowHandles.size();
for (size_t i = 0; i < numWindows; i++) {
const sp<InputWindowHandle>& windowHandle = inputWindowHandles.itemAt(i);
- if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == nullptr) {
+ if (!windowHandle->updateInfo() || getInputChannelLocked(windowHandle->getToken()) == nullptr) {
+ ALOGE("Window handle %s has no registered input channel",
+ windowHandle->getName().c_str());
continue;
}
@@ -3072,7 +3093,7 @@
continue;
}
- if (windowHandle->getInfo()->hasFocus) {
+ if (windowHandle->getInfo()->hasFocus && windowHandle->getInfo()->visible) {
newFocusedWindowHandle = windowHandle;
}
if (windowHandle == mLastHoverWindowHandle) {
@@ -3097,7 +3118,8 @@
ALOGD("Focus left window: %s in display %" PRId32,
oldFocusedWindowHandle->getName().c_str(), displayId);
#endif
- sp<InputChannel> focusedInputChannel = oldFocusedWindowHandle->getInputChannel();
+ sp<InputChannel> focusedInputChannel = getInputChannelLocked(
+ oldFocusedWindowHandle->getToken());
if (focusedInputChannel != nullptr) {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
"focus left window");
@@ -3113,6 +3135,11 @@
#endif
mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
}
+
+ if (mFocusedDisplayId == displayId) {
+ onFocusChangedLocked(newFocusedWindowHandle);
+ }
+
}
ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
@@ -3126,7 +3153,7 @@
touchedWindow.windowHandle->getName().c_str(), displayId);
#endif
sp<InputChannel> touchedInputChannel =
- touchedWindow.windowHandle->getInputChannel();
+ getInputChannelLocked(touchedWindow.windowHandle->getToken());
if (touchedInputChannel != nullptr) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
"touched window was removed");
@@ -3214,7 +3241,8 @@
sp<InputWindowHandle> oldFocusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
if (oldFocusedWindowHandle != nullptr) {
- sp<InputChannel> inputChannel = oldFocusedWindowHandle->getInputChannel();
+ sp<InputChannel> inputChannel =
+ getInputChannelLocked(oldFocusedWindowHandle->getToken());
if (inputChannel != nullptr) {
CancelationOptions options(
CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS,
@@ -3227,6 +3255,8 @@
// Sanity check
sp<InputWindowHandle> newFocusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+ onFocusChangedLocked(newFocusedWindowHandle);
+
if (newFocusedWindowHandle == nullptr) {
ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
if (!mFocusedWindowHandlesByDisplay.empty()) {
@@ -3488,7 +3518,7 @@
dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
"paused=%s, hasFocus=%s, hasWallpaper=%s, "
"visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, "
- "frame=[%d,%d][%d,%d], scale=%f, "
+ "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=(%f,%f), "
"touchableRegion=",
i, windowInfo->name.c_str(), windowInfo->displayId,
toString(windowInfo->paused),
@@ -3500,7 +3530,8 @@
windowInfo->layer,
windowInfo->frameLeft, windowInfo->frameTop,
windowInfo->frameRight, windowInfo->frameBottom,
- windowInfo->scaleFactor);
+ windowInfo->globalScaleFactor,
+ windowInfo->windowXScale, windowInfo->windowYScale);
dumpRegion(dump, windowInfo->touchableRegion);
dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
@@ -3667,6 +3698,7 @@
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
+ mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
// Store monitor channel by displayId.
if (monitor) {
@@ -3715,6 +3747,8 @@
sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
mConnectionsByFd.removeItemsAt(connectionIndex);
+ mInputChannelsByToken.erase(inputChannel->getToken());
+
if (connection->monitor) {
removeMonitorChannelLocked(inputChannel);
}
@@ -3782,6 +3816,13 @@
commandEntry->connection = connection;
}
+void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& newFocus) {
+ sp<IBinder> token = newFocus != nullptr ? newFocus->getToken() : nullptr;
+ CommandEntry* commandEntry = postCommandLocked(
+ & InputDispatcher::doNotifyFocusChangedLockedInterruptible);
+ commandEntry->token = token;
+}
+
void InputDispatcher::onANRLocked(
nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
@@ -3812,7 +3853,8 @@
CommandEntry* commandEntry = postCommandLocked(
& InputDispatcher::doNotifyANRLockedInterruptible);
commandEntry->inputApplicationHandle = applicationHandle;
- commandEntry->inputChannel = windowHandle != nullptr ? windowHandle->getInputChannel() : nullptr;
+ commandEntry->inputChannel = windowHandle != nullptr ?
+ getInputChannelLocked(windowHandle->getToken()) : nullptr;
commandEntry->reason = reason;
}
@@ -3838,6 +3880,14 @@
}
}
+void InputDispatcher::doNotifyFocusChangedLockedInterruptible(
+ CommandEntry* commandEntry) {
+ sp<IBinder> token = commandEntry->token;
+ mLock.unlock();
+ mPolicy->notifyFocusChanged(token);
+ mLock.lock();
+}
+
void InputDispatcher::doNotifyANRLockedInterruptible(
CommandEntry* commandEntry) {
mLock.unlock();
@@ -4343,10 +4393,12 @@
volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic;
InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) :
+ int32_t targetFlags, float xOffset, float yOffset, float globalScaleFactor,
+ float windowXScale, float windowYScale) :
seq(nextSeq()),
eventEntry(eventEntry), targetFlags(targetFlags),
- xOffset(xOffset), yOffset(yOffset), scaleFactor(scaleFactor),
+ xOffset(xOffset), yOffset(yOffset), globalScaleFactor(globalScaleFactor),
+ windowXScale(windowXScale), windowYScale(windowYScale),
deliveryTime(0), resolvedAction(0), resolvedFlags(0) {
eventEntry->refCount += 1;
}
@@ -4847,7 +4899,7 @@
void InputDispatcher::TouchState::removeWindowByToken(const sp<IBinder>& token) {
for (size_t i = 0; i < windows.size(); i++) {
- if (windows.itemAt(i).windowHandle->getInputChannel()->getToken() == token) {
+ if (windows.itemAt(i).windowHandle->getToken() == token) {
windows.removeAt(i);
return;
}
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 5016082..73bcc25 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -29,6 +29,7 @@
#include <utils/Looper.h>
#include <utils/BitSet.h>
#include <cutils/atomic.h>
+#include <unordered_map>
#include <stddef.h>
#include <unistd.h>
@@ -159,7 +160,9 @@
// Scaling factor to apply to MotionEvent as it is delivered.
// (ignored for KeyEvents)
- float scaleFactor;
+ float globalScaleFactor;
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
@@ -213,6 +216,7 @@
/* Notifies the system that an input channel is unrecoverably broken. */
virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0;
+ virtual void notifyFocusChanged(const sp<IBinder>& token) = 0;
/* Gets the input dispatcher configuration. */
virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0;
@@ -561,7 +565,9 @@
int32_t targetFlags;
float xOffset;
float yOffset;
- float scaleFactor;
+ float globalScaleFactor;
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
nsecs_t deliveryTime; // time when the event was actually delivered
// Set to the resolved action and flags when the event is enqueued.
@@ -569,7 +575,8 @@
int32_t resolvedFlags;
DispatchEntry(EventEntry* eventEntry,
- int32_t targetFlags, float xOffset, float yOffset, float scaleFactor);
+ int32_t targetFlags, float xOffset, float yOffset,
+ float globalScaleFactor, float windowXScale, float windowYScale);
~DispatchEntry();
inline bool hasForegroundTarget() const {
@@ -622,6 +629,7 @@
uint32_t seq;
bool handled;
sp<InputChannel> inputChannel;
+ sp<IBinder> token;
};
// Generic queue implementation.
@@ -916,6 +924,13 @@
// All registered connections mapped by channel file descriptor.
KeyedVector<int, sp<Connection> > mConnectionsByFd;
+ struct IBinderHash {
+ std::size_t operator()(const sp<IBinder>& b) const {
+ return std::hash<IBinder *>{}(b.get());
+ }
+ };
+ std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken;
+
ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel);
// Input channels that will receive a copy of all input events sent to the provided display.
@@ -979,6 +994,7 @@
// Get window handles by display, return an empty vector if not found.
Vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const;
sp<InputWindowHandle> getWindowHandleLocked(const sp<InputChannel>& inputChannel) const;
+ sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const;
bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const;
// Focus tracking for keys, trackball, etc.
@@ -1051,7 +1067,7 @@
nsecs_t mInputTargetWaitStartTime;
nsecs_t mInputTargetWaitTimeoutTime;
bool mInputTargetWaitTimeoutExpired;
- sp<InputApplicationHandle> mInputTargetWaitApplicationHandle;
+ sp<IBinder> mInputTargetWaitApplicationToken;
// Contains the last window which received a hover event.
sp<InputWindowHandle> mLastHoverWindowHandle;
@@ -1143,6 +1159,7 @@
nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled);
void onDispatchCycleBrokenLocked(
nsecs_t currentTime, const sp<Connection>& connection);
+ void onFocusChangedLocked(const sp<InputWindowHandle>& newFocus);
void onANRLocked(
nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
@@ -1151,6 +1168,7 @@
// Outbound policy interactions.
void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry);
void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry);
+ void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry);
void doNotifyANRLockedInterruptible(CommandEntry* commandEntry);
void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry);
void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry);
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 388423c..15d8070 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -21,9 +21,13 @@
#include "InputManager.h"
#include "InputReaderFactory.h"
+#include <binder/IPCThreadState.h>
+
#include <log/log.h>
#include <unordered_map>
+#include <private/android_filesystem_config.h>
+
namespace android {
InputManager::InputManager(
@@ -83,20 +87,9 @@
return mDispatcher;
}
-class BinderApplicationHandle : public InputApplicationHandle {
-public:
- BinderApplicationHandle() = default;
-
- bool updateInfo() override {
- return true;
- }
-};
-
class BinderWindowHandle : public InputWindowHandle {
public:
- BinderWindowHandle(const InputWindowInfo& info) :
- InputWindowHandle(new BinderApplicationHandle()) {
-
+ BinderWindowHandle(const InputWindowInfo& info) {
mInfo = info;
}
@@ -118,4 +111,20 @@
}
}
+// Used by tests only.
+void InputManager::registerInputChannel(const sp<InputChannel>& channel) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int uid = ipc->getCallingUid();
+ if (uid != AID_SHELL && uid != AID_ROOT) {
+ ALOGE("Invalid attempt to register input channel over IPC"
+ "from non shell/root entity (PID: %d)", ipc->getCallingPid());
+ return;
+ }
+ mDispatcher->registerInputChannel(channel, false);
+}
+
+void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
+ mDispatcher->unregisterInputChannel(channel);
+}
+
} // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 1173fa1..8f7551e 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -35,6 +35,7 @@
#include <utils/RefBase.h>
namespace android {
+class InputChannel;
/*
* The input manager is the core of the system event processing.
@@ -91,6 +92,9 @@
virtual void setInputWindows(const Vector<InputWindowInfo>& handles);
+ virtual void registerInputChannel(const sp<InputChannel>& channel);
+ virtual void unregisterInputChannel(const sp<InputChannel>& channel);
+
private:
sp<InputReaderInterface> mReader;
sp<InputReaderThread> mReaderThread;
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 9dd14dc..9e748d8 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -991,6 +991,12 @@
deviceInfo.getDisplayName().c_str());
dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration);
dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal));
+ dump += StringPrintf(INDENT2 "AssociatedDisplayPort: ");
+ if (mAssociatedDisplayPort) {
+ dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort);
+ } else {
+ dump += "<none>\n";
+ }
dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic));
dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources());
dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType());
@@ -1060,6 +1066,20 @@
setEnabled(enabled, when);
}
+ if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+ // In most situations, no port will be specified.
+ mAssociatedDisplayPort = std::nullopt;
+ // Find the display port that corresponds to the current input port.
+ const std::string& inputPort = mIdentifier.location;
+ if (!inputPort.empty()) {
+ const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations;
+ const auto& displayPort = ports.find(inputPort);
+ if (displayPort != ports.end()) {
+ mAssociatedDisplayPort = std::make_optional(displayPort->second);
+ }
+ }
+ }
+
size_t numMappers = mMappers.size();
for (size_t i = 0; i < numMappers; i++) {
InputMapper* mapper = mMappers[i];
@@ -2203,7 +2223,7 @@
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
if (mParameters.orientationAware) {
- mViewport = config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+ mViewport = config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
}
}
}
@@ -2613,7 +2633,7 @@
mOrientation = DISPLAY_ORIENTATION_0;
if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {
std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+ config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
if (internalViewport) {
mOrientation = internalViewport->orientation;
}
@@ -2929,7 +2949,7 @@
}
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+ config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
if (internalViewport) {
mOrientation = internalViewport->orientation;
} else {
@@ -3310,6 +3330,9 @@
mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
}
}
+ if (getDevice()->getAssociatedDisplayPort()) {
+ mParameters.hasAssociatedDisplay = true;
+ }
// Initial downs on external touch devices should wake the device.
// Normally we don't do this for internal touch screens to prevent them from waking
@@ -3384,6 +3407,49 @@
return mExternalStylusConnected;
}
+/**
+ * Determine which DisplayViewport to use.
+ * 1. If display port is specified, return the matching viewport. If matching viewport not
+ * found, then return.
+ * 2. If a device has associated display, get the matching viewport by either unique id or by
+ * the display type (internal or external).
+ * 3. Otherwise, use a non-display viewport.
+ */
+std::optional<DisplayViewport> TouchInputMapper::findViewport() {
+ if (mParameters.hasAssociatedDisplay) {
+ const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+ if (displayPort) {
+ // Find the viewport that contains the same port
+ std::optional<DisplayViewport> v = mConfig.getDisplayViewportByPort(*displayPort);
+ if (!v) {
+ ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
+ "but the corresponding viewport is not found.",
+ getDeviceName().c_str(), *displayPort);
+ }
+ return v;
+ }
+
+ if (!mParameters.uniqueDisplayId.empty()) {
+ return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
+ }
+
+ ViewportType viewportTypeToUse;
+ if (mParameters.associatedDisplayIsExternal) {
+ viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
+ } else {
+ viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
+ }
+ return mConfig.getDisplayViewportByType(viewportTypeToUse);
+ }
+
+ DisplayViewport newViewport;
+ // Raw width and height in the natural orientation.
+ int32_t rawWidth = mRawPointerAxes.getRawWidth();
+ int32_t rawHeight = mRawPointerAxes.getRawHeight();
+ newViewport.setNonDisplayViewport(rawWidth, rawHeight);
+ return std::make_optional(newViewport);
+}
+
void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
int32_t oldDeviceMode = mDeviceMode;
@@ -3417,50 +3483,30 @@
// Ensure we have valid X and Y axes.
if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) {
- ALOGW(INDENT "Touch device '%s' did not report support for X or Y axis! "
+ ALOGW("Touch device '%s' did not report support for X or Y axis! "
"The device will be inoperable.", getDeviceName().c_str());
mDeviceMode = DEVICE_MODE_DISABLED;
return;
}
+ // Get associated display dimensions.
+ std::optional<DisplayViewport> newViewport = findViewport();
+ if (!newViewport) {
+ ALOGI("Touch device '%s' could not query the properties of its associated "
+ "display. The device will be inoperable until the display size "
+ "becomes available.",
+ getDeviceName().c_str());
+ mDeviceMode = DEVICE_MODE_DISABLED;
+ return;
+ }
+
// Raw width and height in the natural orientation.
int32_t rawWidth = mRawPointerAxes.getRawWidth();
int32_t rawHeight = mRawPointerAxes.getRawHeight();
- // Get associated display dimensions.
- DisplayViewport newViewport;
- if (mParameters.hasAssociatedDisplay) {
- std::string uniqueDisplayId;
- ViewportType viewportTypeToUse;
-
- if (mParameters.associatedDisplayIsExternal) {
- viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL;
- } else if (!mParameters.uniqueDisplayId.empty()) {
- // If the IDC file specified a unique display Id, then it expects to be linked to a
- // virtual display with the same unique ID.
- uniqueDisplayId = mParameters.uniqueDisplayId;
- viewportTypeToUse = ViewportType::VIEWPORT_VIRTUAL;
- } else {
- viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL;
- }
-
- std::optional<DisplayViewport> viewportToUse =
- mConfig.getDisplayViewport(viewportTypeToUse, uniqueDisplayId);
- if (!viewportToUse) {
- ALOGI(INDENT "Touch device '%s' could not query the properties of its associated "
- "display. The device will be inoperable until the display size "
- "becomes available.",
- getDeviceName().c_str());
- mDeviceMode = DEVICE_MODE_DISABLED;
- return;
- }
- newViewport = *viewportToUse;
- } else {
- newViewport.setNonDisplayViewport(rawWidth, rawHeight);
- }
- bool viewportChanged = mViewport != newViewport;
+ bool viewportChanged = mViewport != *newViewport;
if (viewportChanged) {
- mViewport = newViewport;
+ mViewport = *newViewport;
if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) {
// Convert rotated viewport to natural surface coordinates.
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index 1786fe8..13f1bed 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -252,6 +252,9 @@
inline bool isExternal() { return mIsExternal; }
inline void setExternal(bool external) { mIsExternal = external; }
+ inline std::optional<uint8_t> getAssociatedDisplayPort() const {
+ return mAssociatedDisplayPort;
+ }
inline void setMic(bool hasMic) { mHasMic = hasMic; }
inline bool hasMic() const { return mHasMic; }
@@ -324,6 +327,7 @@
uint32_t mSources;
bool mIsExternal;
+ std::optional<uint8_t> mAssociatedDisplayPort;
bool mHasMic;
bool mDropUntilNextSync;
@@ -1501,6 +1505,8 @@
VelocityControl mWheelXVelocityControl;
VelocityControl mWheelYVelocityControl;
+ std::optional<DisplayViewport> findViewport();
+
void resetExternalStylus();
void clearStylusDataPendingFlags();
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 17a116e..f48a645 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -49,14 +49,51 @@
// --- InputReaderConfiguration ---
-std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewport(
- ViewportType viewportType, const std::string& uniqueDisplayId) const {
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByUniqueId(
+ const std::string& uniqueDisplayId) const {
+ if (uniqueDisplayId.empty()) {
+ ALOGE("Empty string provided to %s", __func__);
+ return std::nullopt;
+ }
+ size_t count = 0;
+ std::optional<DisplayViewport> result = std::nullopt;
for (const DisplayViewport& currentViewport : mDisplays) {
- if (currentViewport.type == viewportType) {
- if (uniqueDisplayId.empty() ||
- (!uniqueDisplayId.empty() && uniqueDisplayId == currentViewport.uniqueId)) {
- return std::make_optional(currentViewport);
- }
+ if (uniqueDisplayId == currentViewport.uniqueId) {
+ result = std::make_optional(currentViewport);
+ count++;
+ }
+ }
+ if (count > 1) {
+ ALOGE("Found %zu viewports with uniqueId %s, but expected 1 at most",
+ count, uniqueDisplayId.c_str());
+ }
+ return result;
+}
+
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByType(ViewportType type)
+ const {
+ size_t count = 0;
+ std::optional<DisplayViewport> result = std::nullopt;
+ for (const DisplayViewport& currentViewport : mDisplays) {
+ // Return the first match
+ if (currentViewport.type == type && !result) {
+ result = std::make_optional(currentViewport);
+ count++;
+ }
+ }
+ if (count > 1) {
+ ALOGE("Found %zu viewports with type %s, but expected 1 at most",
+ count, viewportTypeToString(type));
+ }
+ return result;
+}
+
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByPort(
+ uint8_t displayPort) const {
+ for (const DisplayViewport& currentViewport : mDisplays) {
+ const std::optional<uint8_t>& physicalPort = currentViewport.physicalPort;
+ if (physicalPort && (*physicalPort == displayPort)) {
+ return std::make_optional(currentViewport);
}
}
return std::nullopt;
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index 0e48f24..cbe0190 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -62,6 +62,4 @@
static_libs: [
"libarect",
],
-
- init_rc: ["inputflinger.rc"],
}
diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h
index 15ca7b3..82ff089 100644
--- a/services/inputflinger/host/InputFlinger.h
+++ b/services/inputflinger/host/InputFlinger.h
@@ -40,6 +40,8 @@
virtual status_t dump(int fd, const Vector<String16>& args);
void setInputWindows(const Vector<InputWindowInfo>&) {}
+ void registerInputChannel(const sp<InputChannel>&) {}
+ void unregisterInputChannel(const sp<InputChannel>&) {}
private:
virtual ~InputFlinger();
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index fff8480..fe1c50b 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -32,6 +32,7 @@
#include <optional>
#include <stddef.h>
#include <unistd.h>
+#include <unordered_map>
#include <vector>
// Maximum supported size of a vibration pattern.
@@ -164,6 +165,10 @@
// Devices with these names will be ignored.
std::vector<std::string> excludedDeviceNames;
+ // The associations between input ports and display ports.
+ // Used to determine which DisplayViewport should be tied to which InputDevice.
+ std::unordered_map<std::string, uint8_t> portAssociations;
+
// Velocity control parameters for mouse pointer movements.
VelocityControlParameters pointerVelocityControlParameters;
@@ -262,8 +267,10 @@
pointerGestureZoomSpeedRatio(0.3f),
showTouches(false) { }
- std::optional<DisplayViewport> getDisplayViewport(ViewportType viewportType,
- const std::string& uniqueDisplayId) const;
+ std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
+ std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
+ const;
+ std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t physicalPort) const;
void setDisplayViewports(const std::vector<DisplayViewport>& viewports);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index e860db5..26f01b7 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -63,6 +63,9 @@
virtual void notifyInputChannelBroken(const sp<IBinder>&) {
}
+ virtual void notifyFocusChanged(const sp<IBinder>&) {
+ }
+
virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
*outConfig = mConfig;
}
@@ -338,7 +341,6 @@
const std::string name, int32_t displayId) :
mDispatcher(dispatcher), mName(name), mDisplayId(displayId) {
InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
-
mConsumer = new InputConsumer(mClientChannel);
}
@@ -352,6 +354,7 @@
sp<InputDispatcher> mDispatcher;
sp<InputChannel> mServerChannel, mClientChannel;
+ sp<IBinder> mToken;
InputConsumer *mConsumer;
PreallocatedInputEventFactory mEventFactory;
@@ -366,15 +369,17 @@
FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
- InputWindowHandle(inputApplicationHandle),
FakeInputReceiver(dispatcher, name, displayId),
mFocused(false) {
mServerChannel->setToken(new BBinder());
mDispatcher->registerInputChannel(mServerChannel, displayId);
+
+ inputApplicationHandle->updateInfo();
+ mInfo.applicationInfo = *inputApplicationHandle->getInfo();
}
virtual bool updateInfo() {
- mInfo.inputChannel = mServerChannel;
+ mInfo.token = mServerChannel->getToken();
mInfo.name = mName;
mInfo.layoutParamsFlags = 0;
mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
@@ -383,7 +388,7 @@
mInfo.frameTop = 0;
mInfo.frameRight = WIDTH;
mInfo.frameBottom = HEIGHT;
- mInfo.scaleFactor = 1.0;
+ mInfo.globalScaleFactor = 1.0;
mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
mInfo.visible = true;
mInfo.canReceiveKeys = true;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 929424b..04b87d5 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -28,12 +28,14 @@
// Arbitrary display properties.
static const int32_t DISPLAY_ID = 0;
+static const int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
static const int32_t DISPLAY_WIDTH = 480;
static const int32_t DISPLAY_HEIGHT = 800;
static const int32_t VIRTUAL_DISPLAY_ID = 1;
static const int32_t VIRTUAL_DISPLAY_WIDTH = 400;
static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1";
+static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
// Error tolerance for floating point assertions.
static const float EPSILON = 0.001f;
@@ -147,15 +149,22 @@
mConfig.setDisplayViewports(mViewports);
}
- std::optional<DisplayViewport> getDisplayViewport(ViewportType viewportType,
- const std::string& uniqueId) {
- return mConfig.getDisplayViewport(viewportType, uniqueId);
+ std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const {
+ return mConfig.getDisplayViewportByUniqueId(uniqueId);
+ }
+ std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const {
+ return mConfig.getDisplayViewportByType(type);
+ }
+
+ std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const {
+ return mConfig.getDisplayViewportByPort(displayPort);
}
void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
- const std::string& uniqueId, ViewportType viewportType) {
+ const std::string& uniqueId, std::optional<uint8_t> physicalPort,
+ ViewportType viewportType) {
const DisplayViewport viewport = createDisplayViewport(displayId, width, height,
- orientation, uniqueId, viewportType);
+ orientation, uniqueId, physicalPort, viewportType);
mViewports.push_back(viewport);
mConfig.setDisplayViewports(mViewports);
}
@@ -164,6 +173,10 @@
mConfig.excludedDeviceNames.push_back(deviceName);
}
+ void addInputPortAssociation(const std::string& inputPort, uint8_t displayPort) {
+ mConfig.portAssociations.insert({inputPort, displayPort});
+ }
+
void addDisabledDevice(int32_t deviceId) {
ssize_t index = mConfig.disabledDevices.indexOf(deviceId);
bool currentlyEnabled = index < 0;
@@ -207,7 +220,8 @@
private:
DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, const std::string& uniqueId, ViewportType type) {
+ int32_t orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort,
+ ViewportType type) {
bool isRotated = (orientation == DISPLAY_ORIENTATION_90
|| orientation == DISPLAY_ORIENTATION_270);
DisplayViewport v;
@@ -224,6 +238,7 @@
v.deviceWidth = isRotated ? height : width;
v.deviceHeight = isRotated ? width : height;
v.uniqueId = uniqueId;
+ v.physicalPort = physicalPort;
v.type = type;
return v;
}
@@ -1105,26 +1120,28 @@
// We didn't add any viewports yet, so there shouldn't be any.
std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId);
+ mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
ASSERT_FALSE(internalViewport);
// Add an internal viewport, then clear it
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, uniqueId, ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, uniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
// Check matching by uniqueId
- internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId);
+ internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
ASSERT_TRUE(internalViewport);
+ ASSERT_EQ(ViewportType::VIEWPORT_INTERNAL, internalViewport->type);
// Check matching by viewport type
- internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+ internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
ASSERT_TRUE(internalViewport);
+ ASSERT_EQ(uniqueId, internalViewport->uniqueId);
mFakePolicy->clearViewports();
// Make sure nothing is found after clear
- internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, uniqueId);
+ internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId);
ASSERT_FALSE(internalViewport);
- internalViewport = mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+ internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
ASSERT_FALSE(internalViewport);
}
@@ -1138,40 +1155,42 @@
// Add an internal viewport
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, internalUniqueId, ViewportType::VIEWPORT_INTERNAL);
+ DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
// Add an external viewport
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, externalUniqueId, ViewportType::VIEWPORT_EXTERNAL);
+ DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT, ViewportType::VIEWPORT_EXTERNAL);
// Add an virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, virtualUniqueId1, ViewportType::VIEWPORT_VIRTUAL);
+ DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
// Add another virtual viewport
mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, virtualUniqueId2, ViewportType::VIEWPORT_VIRTUAL);
+ DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
// Check matching by type for internal
std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_INTERNAL, "");
+ mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
ASSERT_TRUE(internalViewport);
ASSERT_EQ(internalUniqueId, internalViewport->uniqueId);
// Check matching by type for external
std::optional<DisplayViewport> externalViewport =
- mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_EXTERNAL, "");
+ mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_EXTERNAL);
ASSERT_TRUE(externalViewport);
ASSERT_EQ(externalUniqueId, externalViewport->uniqueId);
// Check matching by uniqueId for virtual viewport #1
std::optional<DisplayViewport> virtualViewport1 =
- mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_VIRTUAL, virtualUniqueId1);
+ mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId1);
ASSERT_TRUE(virtualViewport1);
+ ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport1->type);
ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId);
ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId);
// Check matching by uniqueId for virtual viewport #2
std::optional<DisplayViewport> virtualViewport2 =
- mFakePolicy->getDisplayViewport(ViewportType::VIEWPORT_VIRTUAL, virtualUniqueId2);
+ mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId2);
ASSERT_TRUE(virtualViewport2);
+ ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport2->type);
ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId);
ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId);
}
@@ -1194,18 +1213,20 @@
mFakePolicy->clearViewports();
// Add a viewport
mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, uniqueId1, type);
+ DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT, type);
// Add another viewport
mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_0, uniqueId2, type);
+ DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT, type);
// Check that correct display viewport was returned by comparing the display IDs.
- std::optional<DisplayViewport> viewport1 = mFakePolicy->getDisplayViewport(type, uniqueId1);
+ std::optional<DisplayViewport> viewport1 =
+ mFakePolicy->getDisplayViewportByUniqueId(uniqueId1);
ASSERT_TRUE(viewport1);
ASSERT_EQ(displayId1, viewport1->displayId);
ASSERT_EQ(type, viewport1->type);
- std::optional<DisplayViewport> viewport2 = mFakePolicy->getDisplayViewport(type, uniqueId2);
+ std::optional<DisplayViewport> viewport2 =
+ mFakePolicy->getDisplayViewportByUniqueId(uniqueId2);
ASSERT_TRUE(viewport2);
ASSERT_EQ(displayId2, viewport2->displayId);
ASSERT_EQ(type, viewport2->type);
@@ -1216,11 +1237,50 @@
// is just implementation detail.
// However, we can check that it still returns *a* viewport, we just cannot assert
// which one specifically is returned.
- std::optional<DisplayViewport> someViewport = mFakePolicy->getDisplayViewport(type, "");
+ std::optional<DisplayViewport> someViewport = mFakePolicy->getDisplayViewportByType(type);
ASSERT_TRUE(someViewport);
}
}
+/**
+ * Check getDisplayViewportByPort
+ */
+TEST_F(InputReaderPolicyTest, Viewports_GetByPort) {
+ constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+ const std::string uniqueId1 = "uniqueId1";
+ const std::string uniqueId2 = "uniqueId2";
+ constexpr int32_t displayId1 = 1;
+ constexpr int32_t displayId2 = 2;
+ const uint8_t hdmi1 = 0;
+ const uint8_t hdmi2 = 1;
+ const uint8_t hdmi3 = 2;
+
+ mFakePolicy->clearViewports();
+ // Add a viewport that's associated with some display port that's not of interest.
+ mFakePolicy->addDisplayViewport(displayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId1, hdmi3, type);
+ // Add another viewport, connected to HDMI1 port
+ mFakePolicy->addDisplayViewport(displayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId2, hdmi1, type);
+
+ // Check that correct display viewport was returned by comparing the display ports.
+ std::optional<DisplayViewport> hdmi1Viewport = mFakePolicy->getDisplayViewportByPort(hdmi1);
+ ASSERT_TRUE(hdmi1Viewport);
+ ASSERT_EQ(displayId2, hdmi1Viewport->displayId);
+ ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId);
+
+ // Check that we can still get the same viewport using the uniqueId
+ hdmi1Viewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId2);
+ ASSERT_TRUE(hdmi1Viewport);
+ ASSERT_EQ(displayId2, hdmi1Viewport->displayId);
+ ASSERT_EQ(uniqueId2, hdmi1Viewport->uniqueId);
+ ASSERT_EQ(type, hdmi1Viewport->type);
+
+ // Check that we cannot find a port with "HDMI2", because we never added one
+ std::optional<DisplayViewport> hdmi2Viewport = mFakePolicy->getDisplayViewportByPort(hdmi2);
+ ASSERT_FALSE(hdmi2Viewport);
+}
+
// --- InputReaderTest ---
class InputReaderTest : public testing::Test {
@@ -1696,6 +1756,7 @@
class InputMapperTest : public testing::Test {
protected:
static const char* DEVICE_NAME;
+ static const char* DEVICE_LOCATION;
static const int32_t DEVICE_ID;
static const int32_t DEVICE_GENERATION;
static const int32_t DEVICE_CONTROLLER_NUMBER;
@@ -1714,10 +1775,11 @@
mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
InputDeviceIdentifier identifier;
identifier.name = DEVICE_NAME;
+ identifier.location = DEVICE_LOCATION;
mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
- mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0);
+ mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
}
virtual void TearDown() {
@@ -1729,7 +1791,7 @@
}
void addConfigurationProperty(const char* key, const char* value) {
- mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8(key), String8(value));
+ mFakeEventHub->addConfigurationProperty(mDevice->getId(), String8(key), String8(value));
}
void configureDevice(uint32_t changes) {
@@ -1743,9 +1805,10 @@
}
void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, const std::string& uniqueId, ViewportType viewportType) {
+ int32_t orientation, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort, ViewportType viewportType) {
mFakePolicy->addDisplayViewport(
- displayId, width, height, orientation, uniqueId, viewportType);
+ displayId, width, height, orientation, uniqueId, physicalPort, viewportType);
configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
@@ -1801,6 +1864,7 @@
};
const char* InputMapperTest::DEVICE_NAME = "device";
+const char* InputMapperTest::DEVICE_LOCATION = "USB1";
const int32_t InputMapperTest::DEVICE_ID = 1;
const int32_t InputMapperTest::DEVICE_GENERATION = 2;
const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
@@ -1867,7 +1931,7 @@
*/
void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) {
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- orientation, UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+ orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
}
void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
@@ -2170,7 +2234,7 @@
// ^--- already checked by the previous test
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+ UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
@@ -2180,7 +2244,7 @@
constexpr int32_t newDisplayId = 2;
clearViewports();
setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
- UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+ UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
@@ -2305,11 +2369,18 @@
InputMapperTest::SetUp();
mFakePointerController = new FakePointerController();
- mFakePolicy->setPointerController(DEVICE_ID, mFakePointerController);
+ mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController);
}
void testMotionRotation(CursorInputMapper* mapper,
int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY);
+
+ void prepareDisplay(int32_t orientation) {
+ const std::string uniqueId = "local:0";
+ const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ orientation, uniqueId, NO_PORT, viewportType);
+ }
};
const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
@@ -2599,12 +2670,7 @@
addConfigurationProperty("cursor.mode", "navigation");
addMapperAndConfigure(mapper);
- const std::string uniqueId = "local:0";
- const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT,
- DISPLAY_ORIENTATION_90, uniqueId, viewportType);
+ prepareDisplay(DISPLAY_ORIENTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
@@ -2621,11 +2687,7 @@
addConfigurationProperty("cursor.orientationAware", "1");
addMapperAndConfigure(mapper);
- const std::string uniqueId = "local:0";
- const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL;
-
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, uniqueId, viewportType);
+ prepareDisplay(DISPLAY_ORIENTATION_0);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
@@ -2635,8 +2697,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_90, uniqueId, viewportType);
+ prepareDisplay(DISPLAY_ORIENTATION_90);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1));
@@ -2646,8 +2707,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1));
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_180, uniqueId, viewportType);
+ prepareDisplay(DISPLAY_ORIENTATION_180);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0));
@@ -2657,8 +2717,7 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1));
- setDisplayInfoAndReconfigure(DISPLAY_ID,
- DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_270, uniqueId, viewportType);
+ prepareDisplay(DISPLAY_ORIENTATION_270);
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1));
ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1));
@@ -3104,6 +3163,7 @@
static const VirtualKeyDefinition VIRTUAL_KEYS[2];
const std::string UNIQUE_ID = "local:0";
+ const std::string SECONDARY_UNIQUE_ID = "local:1";
enum Axes {
POSITION = 1 << 0,
@@ -3119,7 +3179,8 @@
TOOL_TYPE = 1 << 10,
};
- void prepareDisplay(int32_t orientation);
+ void prepareDisplay(int32_t orientation, std::optional<uint8_t> port = NO_PORT);
+ void prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port = NO_PORT);
void prepareVirtualDisplay(int32_t orientation);
void prepareVirtualKeys();
void prepareLocationCalibration();
@@ -3172,15 +3233,20 @@
{ KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 },
};
-void TouchInputMapperTest::prepareDisplay(int32_t orientation) {
+void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) {
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
- UNIQUE_ID, ViewportType::VIEWPORT_INTERNAL);
+ UNIQUE_ID, port, ViewportType::VIEWPORT_INTERNAL);
+}
+
+void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) {
+ setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, SECONDARY_UNIQUE_ID, port, type);
}
void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) {
setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH,
VIRTUAL_DISPLAY_HEIGHT, orientation,
- VIRTUAL_DISPLAY_UNIQUE_ID, ViewportType::VIEWPORT_VIRTUAL);
+ VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_VIRTUAL);
}
void TouchInputMapperTest::prepareVirtualKeys() {
@@ -6138,5 +6204,46 @@
ASSERT_EQ(0U, args.deviceTimestamp);
}
+/**
+ * Set the input device port <--> display port associations, and check that the
+ * events are routed to the display that matches the display port.
+ * This can be checked by looking at the displayId of the resulting NotifyMotionArgs.
+ */
+TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) {
+ MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+ const std::string usb2 = "USB2";
+ const uint8_t hdmi1 = 0;
+ const uint8_t hdmi2 = 1;
+ const std::string secondaryUniqueId = "uniqueId2";
+ constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareAxes(POSITION);
+ addMapperAndConfigure(mapper);
+
+ mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+ mFakePolicy->addInputPortAssociation(usb2, hdmi2);
+
+ // We are intentionally not adding the viewport for display 1 yet. Since the port association
+ // for this input device is specified, and the matching viewport is not present,
+ // the input device should be disabled (at the mapper level).
+
+ // Add viewport for display 2 on hdmi2
+ prepareSecondaryDisplay(type, hdmi2);
+ // Send a touch event
+ processPosition(mapper, 100, 100);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Add viewport for display 1 on hdmi1
+ prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1);
+ // Send a touch event again
+ processPosition(mapper, 100, 100);
+ processSync(mapper);
+
+ NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(DISPLAY_ID, args.displayId);
+}
} // namespace android
diff --git a/services/sensorservice/OWNERS b/services/sensorservice/OWNERS
index d4393d6..81099e8 100644
--- a/services/sensorservice/OWNERS
+++ b/services/sensorservice/OWNERS
@@ -1,2 +1,3 @@
arthuri@google.com
bduddie@google.com
+bstack@google.com
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 0fb4ac6..8dc80cc 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -315,32 +315,7 @@
if (mCacheSize != 0) {
// There are some events in the cache which need to be sent first. Copy this buffer to
// the end of cache.
- if (mCacheSize + count <= mMaxCacheSize) {
- memcpy(&mEventCache[mCacheSize], scratch, count * sizeof(sensors_event_t));
- mCacheSize += count;
- } else {
- // Check if any new sensors have registered on this connection which may have increased
- // the max cache size that is desired.
- if (mCacheSize + count < computeMaxCacheSizeLocked()) {
- reAllocateCacheLocked(scratch, count);
- return status_t(NO_ERROR);
- }
- // Some events need to be dropped.
- int remaningCacheSize = mMaxCacheSize - mCacheSize;
- if (remaningCacheSize != 0) {
- memcpy(&mEventCache[mCacheSize], scratch,
- remaningCacheSize * sizeof(sensors_event_t));
- }
- int numEventsDropped = count - remaningCacheSize;
- countFlushCompleteEventsLocked(mEventCache, numEventsDropped);
- // Drop the first "numEventsDropped" in the cache.
- memmove(mEventCache, &mEventCache[numEventsDropped],
- (mCacheSize - numEventsDropped) * sizeof(sensors_event_t));
-
- // Copy the remainingEvents in scratch buffer to the end of cache.
- memcpy(&mEventCache[mCacheSize - numEventsDropped], scratch + remaningCacheSize,
- numEventsDropped * sizeof(sensors_event_t));
- }
+ appendEventsToCacheLocked(scratch, count);
return status_t(NO_ERROR);
}
@@ -376,8 +351,8 @@
mEventCache = new sensors_event_t[mMaxCacheSize];
mCacheSize = 0;
}
- memcpy(&mEventCache[mCacheSize], scratch, count * sizeof(sensors_event_t));
- mCacheSize += count;
+ // Save the events so that they can be written later
+ appendEventsToCacheLocked(scratch, count);
// Add this file descriptor to the looper to get a callback when this fd is available for
// writing.
@@ -417,6 +392,51 @@
mMaxCacheSize = new_cache_size;
}
+void SensorService::SensorEventConnection::appendEventsToCacheLocked(sensors_event_t const* events,
+ int count) {
+ if (count <= 0) {
+ return;
+ } else if (mCacheSize + count <= mMaxCacheSize) {
+ // The events fit within the current cache: add them
+ memcpy(&mEventCache[mCacheSize], events, count * sizeof(sensors_event_t));
+ mCacheSize += count;
+ } else if (mCacheSize + count <= computeMaxCacheSizeLocked()) {
+ // The events fit within a resized cache: resize the cache and add the events
+ reAllocateCacheLocked(events, count);
+ } else {
+ // The events do not fit within the cache: drop the oldest events.
+ ALOGW("Dropping events from cache (%d / %d) to save %d newer events", mCacheSize,
+ mMaxCacheSize, count);
+
+ int freeSpace = mMaxCacheSize - mCacheSize;
+
+ // Drop up to the currently cached number of events to make room for new events
+ int cachedEventsToDrop = std::min(mCacheSize, count - freeSpace);
+
+ // New events need to be dropped if there are more new events than the size of the cache
+ int newEventsToDrop = std::max(0, count - mMaxCacheSize);
+
+ // Determine the number of new events to copy into the cache
+ int eventsToCopy = std::min(mMaxCacheSize, count);
+
+ // Check for any flush complete events in the events that will be dropped
+ countFlushCompleteEventsLocked(mEventCache, cachedEventsToDrop);
+ countFlushCompleteEventsLocked(events, newEventsToDrop);
+
+ // Only shift the events if they will not all be overwritten
+ if (eventsToCopy != mMaxCacheSize) {
+ memmove(mEventCache, &mEventCache[cachedEventsToDrop],
+ (mCacheSize - cachedEventsToDrop) * sizeof(sensors_event_t));
+ }
+ mCacheSize -= cachedEventsToDrop;
+
+ // Copy the events into the cache
+ memcpy(&mEventCache[mCacheSize], &events[newEventsToDrop],
+ eventsToCopy * sizeof(sensors_event_t));
+ mCacheSize += eventsToCopy;
+ }
+}
+
void SensorService::SensorEventConnection::sendPendingFlushEventsLocked() {
ASensorEvent flushCompleteEvent;
memset(&flushCompleteEvent, 0, sizeof(flushCompleteEvent));
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 40c21ff..eefd81a 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -108,6 +108,10 @@
// size, reallocate memory and copy over events from the older cache.
void reAllocateCacheLocked(sensors_event_t const* scratch, int count);
+ // Add the events to the cache. If the cache would be exceeded, drop events at the beginning of
+ // the cache.
+ void appendEventsToCacheLocked(sensors_event_t const* events, int count);
+
// LooperCallback method. If there is data to read on this fd, it is an ack from the app that it
// has read events from a wake up sensor, decrement mWakeLockRefCount. If this fd is available
// for writing send the data from the cache.
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 088c256..22e4d1e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -9,7 +9,6 @@
"-Wunused",
"-Wunreachable-code",
],
- cppflags: ["-std=c++1z"],
}
cc_defaults {
@@ -54,6 +53,7 @@
"libsync",
"libtimestats_proto",
"libui",
+ "libinput",
"libutils",
],
static_libs: [
@@ -142,6 +142,7 @@
"Scheduler/LayerHistory.cpp",
"Scheduler/MessageQueue.cpp",
"Scheduler/Scheduler.cpp",
+ "Scheduler/SchedulerUtils.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceInterceptor.cpp",
@@ -183,6 +184,7 @@
"libdisplayservicehidl",
"libhidlbase",
"libhidltransport",
+ "libinput",
"liblayers_proto",
"liblog",
"libsync",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index ade62bf..9b1c0db 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -24,6 +24,8 @@
#include "DisplayDevice.h"
#include "LayerRejecter.h"
+#include "TimeStats/TimeStats.h"
+
#include <renderengine/RenderEngine.h>
#include <gui/BufferItem.h>
@@ -68,10 +70,10 @@
ALOGE("Found stale hardware composer layers when destroying "
"surface flinger layer %s",
mName.string());
- destroyAllHwcLayers();
+ destroyAllHwcLayersPlusChildren();
}
- mTimeStats.onDestroy(getSequence());
+ mFlinger->mTimeStats->onDestroy(getSequence());
}
void BufferLayer::useSurfaceDamage() {
@@ -334,7 +336,7 @@
mFrameTracker.setDesiredPresentTime(desiredPresentTime);
const int32_t layerID = getSequence();
- mTimeStats.setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
+ mFlinger->mTimeStats->setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
std::shared_ptr<FenceTime> frameReadyFence = getCurrentFenceTime();
if (frameReadyFence->isValid()) {
@@ -346,13 +348,13 @@
}
if (presentFence->isValid()) {
- mTimeStats.setPresentFence(layerID, mCurrentFrameNumber, presentFence);
+ mFlinger->mTimeStats->setPresentFence(layerID, mCurrentFrameNumber, presentFence);
mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
} else if (displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(*displayId);
- mTimeStats.setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
+ mFlinger->mTimeStats->setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
mFrameTracker.setActualPresentTime(actualPresentTime);
}
@@ -501,7 +503,7 @@
// FIXME: postedRegion should be dirty & bounds
// transform the dirty region to window-manager space
- return getTransform().transform(Region(Rect(getActiveWidth(s), getActiveHeight(s))));
+ return getTransform().transform(Region(getBufferSize(s)));
}
// transaction
@@ -624,11 +626,13 @@
ui::Transform t = getTransform();
Rect win = bounds;
+ const int bufferWidth = getBufferSize(s).getWidth();
+ const int bufferHeight = getBufferSize(s).getHeight();
- float left = float(win.left) / float(getActiveWidth(s));
- float top = float(win.top) / float(getActiveHeight(s));
- float right = float(win.right) / float(getActiveWidth(s));
- float bottom = float(win.bottom) / float(getActiveHeight(s));
+ const float left = float(win.left) / float(bufferWidth);
+ const float top = float(win.top) / float(bufferHeight);
+ const float right = float(win.right) / float(bufferWidth);
+ const float bottom = float(win.bottom) / float(bufferHeight);
// TODO: we probably want to generate the texture coords with the mesh
// here we assume that we only have 4 vertices
@@ -639,15 +643,21 @@
texCoords[2] = vec2(right, 1.0f - bottom);
texCoords[3] = vec2(right, 1.0f - top);
+ const auto roundedCornerState = getRoundedCornerState();
+ const auto cropRect = roundedCornerState.cropRect;
+ setupRoundedCornersCropCoordinates(win, cropRect);
+
auto& engine(mFlinger->getRenderEngine());
engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), false /* disableTexture */,
- getColor());
+ getColor(), roundedCornerState.radius);
engine.setSourceDataSpace(mCurrentDataSpace);
if (isHdrY410()) {
engine.setSourceY410BT2020(true);
}
+ engine.setupCornerRadiusCropSize(cropRect.getWidth(), cropRect.getHeight());
+
engine.drawMesh(getBE().mMesh);
engine.disableBlending();
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 78ab23a..b784d11 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -17,6 +17,8 @@
#include "BufferQueueLayer.h"
#include "LayerRejecter.h"
+#include "TimeStats/TimeStats.h"
+
#include <system/window.h>
namespace android {
@@ -246,7 +248,7 @@
// and return early
if (queuedBuffer) {
Mutex::Autolock lock(mQueueItemLock);
- mTimeStats.removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+ mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
mQueueItems.removeAt(0);
mQueuedFrames--;
}
@@ -260,7 +262,7 @@
Mutex::Autolock lock(mQueueItemLock);
mQueueItems.clear();
mQueuedFrames = 0;
- mTimeStats.clearLayerRecord(layerID);
+ mFlinger->mTimeStats->onDestroy(layerID);
}
// Once we have hit this state, the shadow queue may no longer
@@ -281,13 +283,14 @@
// Remove any stale buffers that have been dropped during
// updateTexImage
while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
- mTimeStats.removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+ mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
mQueueItems.removeAt(0);
mQueuedFrames--;
}
- mTimeStats.setAcquireFence(layerID, currentFrameNumber, mQueueItems[0].mFenceTime);
- mTimeStats.setLatchTime(layerID, currentFrameNumber, latchTime);
+ mFlinger->mTimeStats->setAcquireFence(layerID, currentFrameNumber,
+ mQueueItems[0].mFenceTime);
+ mFlinger->mTimeStats->setLatchTime(layerID, currentFrameNumber, latchTime);
mQueueItems.removeAt(0);
}
@@ -352,9 +355,10 @@
void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
// Add this buffer from our internal queue tracker
{ // Autolock scope
- // Report the timestamp to the Scheduler.
+ // Report the requested present time to the Scheduler.
if (mFlinger->mUseScheduler) {
- mFlinger->mScheduler->addNewFrameTimestamp(item.mTimestamp, item.mIsAutoTimestamp);
+ mFlinger->mScheduler->addFramePresentTimeForLayer(item.mTimestamp,
+ item.mIsAutoTimestamp, mName.c_str());
}
Mutex::Autolock lock(mQueueItemLock);
@@ -382,7 +386,7 @@
mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
item.mGraphicBuffer->getHeight(), item.mFrameNumber);
-
+
// If this layer is orphaned, then we run a fake vsync pulse so that
// dequeueBuffer doesn't block indefinitely.
if (isRemovedFromCurrentState()) {
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 425f5c7..1f71a44 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -21,6 +21,8 @@
#include "BufferStateLayer.h"
+#include "TimeStats/TimeStats.h"
+
#include <private/gui/SyncFeatures.h>
#include <renderengine/Image.h>
@@ -336,6 +338,9 @@
}
PixelFormat BufferStateLayer::getPixelFormat() const {
+ if (!mActiveBuffer) {
+ return PIXEL_FORMAT_NONE;
+ }
return mActiveBuffer->format;
}
@@ -454,7 +459,7 @@
ALOGE("[%s] rejecting buffer: "
"bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
mName.string(), bufferWidth, bufferHeight, s.active.w, s.active.h);
- mTimeStats.removeTimeRecord(layerID, getFrameNumber());
+ mFlinger->mTimeStats->removeTimeRecord(layerID, getFrameNumber());
return BAD_VALUE;
}
@@ -466,7 +471,7 @@
if (SyncFeatures::getInstance().useNativeFenceSync() && releaseFence != Fence::NO_FENCE) {
// TODO(alecmouri): Fail somewhere upstream if the fence is invalid.
if (!releaseFence->isValid()) {
- mTimeStats.clearLayerRecord(layerID);
+ mFlinger->mTimeStats->onDestroy(layerID);
return UNKNOWN_ERROR;
}
@@ -476,7 +481,7 @@
auto currentStatus = s.acquireFence->getStatus();
if (currentStatus == Fence::Status::Invalid) {
ALOGE("Existing fence has invalid state");
- mTimeStats.clearLayerRecord(layerID);
+ mFlinger->mTimeStats->onDestroy(layerID);
return BAD_VALUE;
}
@@ -484,7 +489,7 @@
if (incomingStatus == Fence::Status::Invalid) {
ALOGE("New fence has invalid state");
mDrawingState.acquireFence = releaseFence;
- mTimeStats.clearLayerRecord(layerID);
+ mFlinger->mTimeStats->onDestroy(layerID);
return BAD_VALUE;
}
@@ -500,7 +505,7 @@
// synchronization is broken, the best we can do is hope fences
// signal in order so the new fence will act like a union
mDrawingState.acquireFence = releaseFence;
- mTimeStats.clearLayerRecord(layerID);
+ mFlinger->mTimeStats->onDestroy(layerID);
return BAD_VALUE;
}
mDrawingState.acquireFence = mergedFence;
@@ -523,15 +528,15 @@
// a GL-composited layer) not at all.
status_t err = bindTextureImage();
if (err != NO_ERROR) {
- mTimeStats.clearLayerRecord(layerID);
+ mFlinger->mTimeStats->onDestroy(layerID);
return BAD_VALUE;
}
}
// TODO(marissaw): properly support mTimeStats
- mTimeStats.setPostTime(layerID, getFrameNumber(), getName().c_str(), latchTime);
- mTimeStats.setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
- mTimeStats.setLatchTime(layerID, getFrameNumber(), latchTime);
+ mFlinger->mTimeStats->setPostTime(layerID, getFrameNumber(), getName().c_str(), latchTime);
+ mFlinger->mTimeStats->setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
+ mFlinger->mTimeStats->setLatchTime(layerID, getFrameNumber(), latchTime);
return NO_ERROR;
}
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
index 9c34308..f27f6aa 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -45,9 +45,18 @@
renderengine::Mesh mesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2);
computeGeometry(renderArea, mesh, useIdentityTransform);
auto& engine(mFlinger->getRenderEngine());
+
+ Rect win{computeBounds()};
+
+ const auto roundedCornerState = getRoundedCornerState();
+ const auto cropRect = roundedCornerState.cropRect;
+ setupRoundedCornersCropCoordinates(win, cropRect);
+
engine.setupLayerBlending(getPremultipledAlpha(), false /* opaque */,
- true /* disableTexture */, color);
+ true /* disableTexture */, color, roundedCornerState.radius);
+
engine.setSourceDataSpace(mCurrentDataSpace);
+ engine.setupCornerRadiusCropSize(cropRect.getWidth(), cropRect.getHeight());
engine.drawMesh(mesh);
engine.disableBlending();
}
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 55ce434..1215bd9 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -225,7 +225,6 @@
mNativeWindow(args.nativeWindow),
mGraphicBuffer(nullptr),
mDisplaySurface(args.displaySurface),
- mSurface{std::move(args.renderSurface)},
mDisplayInstallOrientation(args.displayInstallOrientation),
mPageFlipCount(0),
mIsVirtual(args.isVirtual),
@@ -247,7 +246,6 @@
ALOGE_IF(!mNativeWindow, "No native window was set for display");
ALOGE_IF(!mDisplaySurface, "No display surface was set for display");
- ALOGE_IF(!mSurface, "No render surface was set for display");
std::vector<Hdr> types = args.hdrCapabilities.getSupportedHdrTypes();
for (Hdr hdrType : types) {
@@ -443,12 +441,6 @@
mDisplaySurface->onFrameCommitted();
}
-bool DisplayDevice::makeCurrent() const {
- bool success = mFlinger->getRenderEngine().setCurrentSurface(*mSurface);
- setViewportAndProjection();
- return success;
-}
-
void DisplayDevice::setViewportAndProjection() const {
size_t w = mDisplayWidth;
size_t h = mDisplayHeight;
@@ -611,12 +603,8 @@
void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) {
dirtyRegion.set(getBounds());
- mSurface->setNativeWindow(nullptr);
-
mDisplaySurface->resizeBuffers(newWidth, newHeight);
- ANativeWindow* const window = mNativeWindow.get();
- mSurface->setNativeWindow(window);
mDisplayWidth = newWidth;
mDisplayHeight = newHeight;
}
@@ -732,12 +720,11 @@
ANativeWindow* const window = mNativeWindow.get();
result.appendFormat("+ %s\n", getDebugName().c_str());
result.appendFormat(" layerStack=%u, (%4dx%4d), ANativeWindow=%p "
- "(%d:%d:%d:%d), orient=%2d (type=%08x), "
- "flips=%u, isSecure=%d, powerMode=%d, activeConfig=%d, numLayers=%zu\n",
+ "format=%d, orient=%2d (type=%08x), flips=%u, isSecure=%d, "
+ "powerMode=%d, activeConfig=%d, numLayers=%zu\n",
mLayerStack, mDisplayWidth, mDisplayHeight, window,
- mSurface->queryRedSize(), mSurface->queryGreenSize(),
- mSurface->queryBlueSize(), mSurface->queryAlphaSize(), mOrientation,
- tr.getType(), getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig,
+ ANativeWindow_getFormat(window), mOrientation, tr.getType(),
+ getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig,
mVisibleLayersSortedByZ.size());
result.appendFormat(" v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], s:[%d,%d,%d,%d],"
"transform:[[%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f]]\n",
@@ -748,9 +735,9 @@
auto const surface = static_cast<Surface*>(window);
ui::Dataspace dataspace = surface->getBuffersDataSpace();
result.appendFormat(" wideColorGamut=%d, hdr10=%d, colorMode=%s, dataspace: %s (%d)\n",
- mHasWideColorGamut, mHasHdr10,
- decodeColorMode(mActiveColorMode).c_str(),
- dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(), dataspace);
+ mHasWideColorGamut, mHasHdr10, decodeColorMode(mActiveColorMode).c_str(),
+ dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(),
+ dataspace);
String8 surfaceDump;
mDisplaySurface->dumpAsString(surfaceDump);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 560a958..eb2c5c3 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -24,12 +24,13 @@
#include <string>
#include <unordered_map>
+#include <android/native_window.h>
#include <binder/IBinder.h>
#include <gui/LayerState.h>
#include <hardware/hwcomposer_defs.h>
#include <math/mat4.h>
#include <renderengine/RenderEngine.h>
-#include <renderengine/Surface.h>
+#include <system/window.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
#include <ui/Region.h>
@@ -42,8 +43,6 @@
#include "DisplayHardware/DisplayIdentification.h"
#include "RenderArea.h"
-struct ANativeWindow;
-
namespace android {
class DisplaySurface;
@@ -163,7 +162,6 @@
void setDisplayName(const std::string& displayName);
const std::string& getDisplayName() const { return mDisplayName; }
- bool makeCurrent() const;
// Acquires a new buffer for GPU composition.
void readyNewBuffer();
// Marks the current buffer has finished, so that it can be presented and
@@ -220,7 +218,6 @@
// that drawing to the buffer is now complete.
base::unique_fd mBufferReady;
- std::unique_ptr<renderengine::Surface> mSurface;
int mDisplayWidth;
int mDisplayHeight;
const int mDisplayInstallOrientation;
@@ -340,7 +337,6 @@
bool isSecure{false};
sp<ANativeWindow> nativeWindow;
sp<DisplaySurface> displaySurface;
- std::unique_ptr<renderengine::Surface> renderSurface;
int displayInstallOrientation{DisplayState::eOrientationDefault};
bool hasWideColorGamut{false};
HdrCapabilities hdrCapabilities;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index f0bccaa..5b641a2 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1023,6 +1023,50 @@
return Error::NONE;
}
+Error Composer::getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+ Dataspace* outDataspace,
+ uint8_t* outComponentMask) {
+ if (!outFormat || !outDataspace || !outComponentMask) {
+ return Error::BAD_PARAMETER;
+ }
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+ Error error = kDefaultError;
+ mClient_2_3->getDisplayedContentSamplingAttributes(display,
+ [&](const auto tmpError,
+ const auto& tmpFormat,
+ const auto& tmpDataspace,
+ const auto& tmpComponentMask) {
+ error = tmpError;
+ if (error == Error::NONE) {
+ *outFormat = tmpFormat;
+ *outDataspace = tmpDataspace;
+ *outComponentMask =
+ static_cast<uint8_t>(
+ tmpComponentMask);
+ }
+ });
+ return error;
+}
+
+Error Composer::getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) {
+ if (!mClient_2_3) {
+ return Error::UNSUPPORTED;
+ }
+ Error error = kDefaultError;
+ mClient_2_3->getDisplayCapabilities(display,
+ [&](const auto& tmpError, const auto& tmpCapabilities) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+ *outCapabilities = tmpCapabilities;
+ });
+ return error;
+}
+
CommandReader::~CommandReader()
{
resetData();
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 4188352..61af348 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -48,24 +48,20 @@
using types::V1_0::ColorTransform;
using types::V1_0::Hdr;
using types::V1_0::Transform;
-
using types::V1_1::PixelFormat;
using types::V1_1::RenderIntent;
using types::V1_2::ColorMode;
using types::V1_2::Dataspace;
-
using V2_1::Config;
using V2_1::Display;
using V2_1::Error;
using V2_1::IComposerCallback;
using V2_1::Layer;
-
using V2_3::CommandReaderBase;
using V2_3::CommandWriterBase;
-
using V2_3::IComposer;
using V2_3::IComposerClient;
-
+using DisplayCapability = IComposerClient::DisplayCapability;
using PerFrameMetadata = IComposerClient::PerFrameMetadata;
using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
@@ -193,6 +189,11 @@
std::vector<uint8_t>* outData) = 0;
virtual Error setLayerColorTransform(Display display, Layer layer,
const float* matrix) = 0;
+ virtual Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+ Dataspace* outDataspace,
+ uint8_t* outComponentMask) = 0;
+ virtual Error getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) = 0;
};
namespace impl {
@@ -392,6 +393,11 @@
Error getDisplayIdentificationData(Display display, uint8_t* outPort,
std::vector<uint8_t>* outData) override;
Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
+ Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat,
+ Dataspace* outDataspace,
+ uint8_t* outComponentMask) override;
+ Error getDisplayCapabilities(Display display,
+ std::vector<DisplayCapability>* outCapabilities) override;
private:
class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 393041d..2df2d3b 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -234,6 +234,22 @@
mId(id),
mIsConnected(false),
mType(type) {
+ std::vector<Hwc2::DisplayCapability> tmpCapabilities;
+ auto error = static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities));
+ if (error == Error::None) {
+ for (auto capability : tmpCapabilities) {
+ mDisplayCapabilities.emplace(static_cast<DisplayCapability>(capability));
+ }
+ } else if (error == Error::Unsupported) {
+ if (capabilities.count(Capability::SkipClientColorTransform)) {
+ mDisplayCapabilities.emplace(DisplayCapability::SkipClientColorTransform);
+ }
+ bool dozeSupport = false;
+ error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport));
+ if (error == Error::None && dozeSupport) {
+ mDisplayCapabilities.emplace(DisplayCapability::Doze);
+ }
+ }
ALOGV("Created display %" PRIu64, id);
}
@@ -507,15 +523,8 @@
return Error::None;
}
-Error Display::supportsDoze(bool* outSupport) const
-{
- bool intSupport = false;
- auto intError = mComposer.getDozeSupport(mId, &intSupport);
- auto error = static_cast<Error>(intError);
- if (error != Error::None) {
- return error;
- }
- *outSupport = static_cast<bool>(intSupport);
+Error Display::supportsDoze(bool* outSupport) const {
+ *outSupport = mDisplayCapabilities.count(DisplayCapability::Doze) > 0;
return Error::None;
}
@@ -538,6 +547,14 @@
return Error::None;
}
+Error Display::getDisplayedContentSamplingAttributes(PixelFormat* outFormat,
+ Dataspace* outDataspace,
+ uint8_t* outComponentMask) const {
+ auto intError = mComposer.getDisplayedContentSamplingAttributes(mId, outFormat, outDataspace,
+ outComponentMask);
+ return static_cast<Error>(intError);
+}
+
Error Display::getReleaseFences(
std::unordered_map<Layer*, sp<Fence>>* outFences) const
{
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index d274631..8c0f50c 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -237,6 +237,9 @@
[[clang::warn_unused_result]] Error supportsDoze(bool* outSupport) const;
[[clang::warn_unused_result]] Error getHdrCapabilities(
android::HdrCapabilities* outCapabilities) const;
+ [[clang::warn_unused_result]] Error getDisplayedContentSamplingAttributes(
+ android::ui::PixelFormat* outFormat, android::ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const;
[[clang::warn_unused_result]] Error getReleaseFences(
std::unordered_map<Layer*,
android::sp<android::Fence>>* outFences) const;
@@ -269,6 +272,9 @@
hwc2_display_t getId() const { return mId; }
bool isConnected() const { return mIsConnected; }
void setConnected(bool connected); // For use by Device only
+ const std::unordered_set<DisplayCapability>& getCapabilities() const {
+ return mDisplayCapabilities;
+ };
private:
int32_t getAttribute(hwc2_config_t configId, Attribute attribute);
@@ -295,6 +301,7 @@
DisplayType mType;
std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers;
std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
+ std::unordered_set<DisplayCapability> mDisplayCapabilities;
};
// Convenience C++ class to access hwc2_device_t Layer functions directly.
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 0f25b52..a752a7d 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -20,31 +20,12 @@
#define LOG_TAG "HWComposer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <inttypes.h>
-#include <math.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
#include <utils/Errors.h>
-#include <utils/misc.h>
-#include <utils/NativeHandle.h>
-#include <utils/String8.h>
-#include <utils/Thread.h>
#include <utils/Trace.h>
-#include <utils/Vector.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
-#include <hardware/hardware.h>
-#include <hardware/hwcomposer.h>
-
-#include <android/configuration.h>
-
-#include <cutils/properties.h>
#include <log/log.h>
#include "HWComposer.h"
@@ -85,11 +66,7 @@
namespace android {
-#define MIN_HWC_HEADER_VERSION HWC_HEADER_VERSION
-
-// ---------------------------------------------------------------------------
-
-HWComposer::HWComposer(std::unique_ptr<android::Hwc2::Composer> composer)
+HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer)
: mHwcDevice(std::make_unique<HWC2::Device>(std::move(composer))) {}
HWComposer::~HWComposer() {
@@ -118,6 +95,15 @@
return mHwcDevice->getCapabilities().count(capability) > 0;
}
+bool HWComposer::hasDisplayCapability(const std::optional<DisplayId>& displayId,
+ HWC2::DisplayCapability capability) const {
+ if (!displayId) {
+ return false;
+ }
+ RETURN_IF_INVALID_DISPLAY(*displayId, false);
+ return mDisplayData.at(*displayId).hwcDisplay->getCapabilities().count(capability) > 0;
+}
+
void HWComposer::validateChange(HWC2::Composition from, HWC2::Composition to) {
bool valid = true;
switch (from) {
@@ -184,30 +170,31 @@
RETURN_IF_INVALID_DISPLAY(*displayId, false);
- const auto& displayData = mDisplayData[*displayId];
+ auto& displayData = mDisplayData[*displayId];
if (displayData.isVirtual) {
LOG_DISPLAY_ERROR(*displayId, "Invalid operation on virtual display");
return false;
}
{
- Mutex::Autolock _l(mLock);
+ std::lock_guard lock(displayData.lastHwVsyncLock);
// There have been reports of HWCs that signal several vsync events
// with the same timestamp when turning the display off and on. This
// is a bug in the HWC implementation, but filter the extra events
// out here so they don't cause havoc downstream.
- if (timestamp == mLastHwVSync[*displayId]) {
+ if (timestamp == displayData.lastHwVsync) {
ALOGW("Ignoring duplicate VSYNC event from HWC for display %s (t=%" PRId64 ")",
to_string(*displayId).c_str(), timestamp);
return false;
}
- mLastHwVSync[*displayId] = timestamp;
+ displayData.lastHwVsync = timestamp;
}
const auto tag = "HW_VSYNC_" + to_string(*displayId);
- ATRACE_INT(tag.c_str(), ++mVSyncCounts[*displayId] & 1);
+ ATRACE_INT(tag.c_str(), displayData.vsyncTraceToggle);
+ displayData.vsyncTraceToggle = !displayData.vsyncTraceToggle;
return true;
}
@@ -270,13 +257,14 @@
nsecs_t HWComposer::getRefreshTimestamp(DisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, 0);
+ const auto& displayData = mDisplayData.at(displayId);
// this returns the last refresh timestamp.
// if the last one is not available, we estimate it based on
// the refresh period and whatever closest timestamp we have.
- Mutex::Autolock _l(mLock);
+ std::lock_guard lock(displayData.lastHwVsyncLock);
nsecs_t now = systemTime(CLOCK_MONOTONIC);
auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod();
- return now - ((now - mLastHwVSync[displayId]) % vsyncPeriod);
+ return now - ((now - displayData.lastHwVsync) % vsyncPeriod);
}
bool HWComposer::isConnected(DisplayId displayId) const {
@@ -375,17 +363,19 @@
// into the HWC with the lock held, and we want to make sure
// that even if HWC blocks (which it shouldn't), it won't
// affect other threads.
- Mutex::Autolock _l(mVsyncLock);
- if (enabled != displayData.vsyncEnabled) {
- ATRACE_CALL();
- auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
- RETURN_IF_HWC_ERROR(error, displayId);
-
- displayData.vsyncEnabled = enabled;
-
- const auto tag = "HW_VSYNC_ON_" + to_string(displayId);
- ATRACE_INT(tag.c_str(), enabled == HWC2::Vsync::Enable ? 1 : 0);
+ std::lock_guard lock(displayData.vsyncEnabledLock);
+ if (enabled == displayData.vsyncEnabled) {
+ return;
}
+
+ ATRACE_CALL();
+ auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
+ RETURN_IF_HWC_ERROR(error, displayId);
+
+ displayData.vsyncEnabled = enabled;
+
+ const auto tag = "HW_VSYNC_ON_" + to_string(displayId);
+ ATRACE_INT(tag.c_str(), enabled == HWC2::Vsync::Enable ? 1 : 0);
}
status_t HWComposer::setClientTarget(DisplayId displayId, uint32_t slot,
@@ -405,8 +395,6 @@
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
- Mutex::Autolock _l(mDisplayLock);
-
auto& displayData = mDisplayData[displayId];
auto& hwcDisplay = displayData.hwcDisplay;
if (!hwcDisplay->isConnected()) {
@@ -428,7 +416,7 @@
// back to validate when there is any client layer.
displayData.validateWasSkipped = false;
if (!displayData.hasClientComposition) {
- sp<android::Fence> outPresentFence;
+ sp<Fence> outPresentFence;
uint32_t state = UINT32_MAX;
error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
if (error != HWC2::Error::HasChanges) {
@@ -689,7 +677,6 @@
const auto hwcDisplayId = displayData.hwcDisplay->getId();
mPhysicalDisplayIdMap.erase(hwcDisplayId);
mDisplayData.erase(displayId);
- mVSyncCounts.erase(displayId);
// TODO(b/74619554): Select internal/external display from remaining displays.
if (hwcDisplayId == mInternalHwcDisplayId) {
@@ -755,25 +742,19 @@
return matrix;
}
-// Converts a PixelFormat to a human-readable string. Max 11 chars.
-// (Could use a table of prefab String8 objects.)
-/*
-static String8 getFormatStr(PixelFormat format) {
- switch (format) {
- case PIXEL_FORMAT_RGBA_8888: return String8("RGBA_8888");
- case PIXEL_FORMAT_RGBX_8888: return String8("RGBx_8888");
- case PIXEL_FORMAT_RGB_888: return String8("RGB_888");
- case PIXEL_FORMAT_RGB_565: return String8("RGB_565");
- case PIXEL_FORMAT_BGRA_8888: return String8("BGRA_8888");
- case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
- return String8("ImplDef");
- default:
- String8 result;
- result.appendFormat("? %08x", format);
- return result;
- }
+status_t HWComposer::getDisplayedContentSamplingAttributes(DisplayId displayId,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error =
+ mDisplayData[displayId]
+ .hwcDisplay->getDisplayedContentSamplingAttributes(outFormat, outDataspace,
+ outComponentMask);
+ if (error == HWC2::Error::Unsupported) RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
}
-*/
bool HWComposer::isUsingVrComposer() const {
return getComposer()->isUsingVrComposer();
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b78433d..0375f74 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -17,49 +17,26 @@
#ifndef ANDROID_SF_HWCOMPOSER_H
#define ANDROID_SF_HWCOMPOSER_H
-#include "HWC2.h"
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <ui/Fence.h>
-#include <ui/GraphicTypes.h>
-#include <utils/BitSet.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
-#include <utils/StrongPointer.h>
-#include <utils/Thread.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
-
+#include <cstdint>
#include <memory>
+#include <mutex>
#include <optional>
#include <unordered_map>
#include <unordered_set>
#include <vector>
+#include <android-base/thread_annotations.h>
+#include <ui/Fence.h>
+#include <ui/GraphicTypes.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
#include "DisplayIdentification.h"
-
-extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
- const struct timespec *request,
- struct timespec *remain);
-
-struct framebuffer_device_t;
-
-namespace HWC2 {
- class Device;
- class Display;
-}
+#include "HWC2.h"
namespace android {
-// ---------------------------------------------------------------------------
-class DisplayDevice;
-class Fence;
-class FloatRect;
class GraphicBuffer;
-class NativeHandle;
-class Region;
class String8;
class TestableSurfaceFlinger;
struct CompositionInfo;
@@ -71,7 +48,7 @@
class HWComposer
{
public:
- explicit HWComposer(std::unique_ptr<android::Hwc2::Composer> composer);
+ explicit HWComposer(std::unique_ptr<Hwc2::Composer> composer);
~HWComposer();
@@ -82,6 +59,8 @@
DisplayIdentificationData* outData) const;
bool hasCapability(HWC2::Capability capability) const;
+ bool hasDisplayCapability(const std::optional<DisplayId>& displayId,
+ HWC2::DisplayCapability capability) const;
// Attempts to allocate a virtual display and returns its ID if created on the HWC device.
std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
@@ -148,6 +127,11 @@
mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace);
+ // Returns the attributes of the color sampling engine.
+ status_t getDisplayedContentSamplingAttributes(DisplayId displayId, ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask);
+
// Events handling ---------------------------------------------------------
// Returns stable display ID (and display name on connection of new or previously disconnected
@@ -177,7 +161,7 @@
// for debugging ----------------------------------------------------------
void dump(String8& out) const;
- android::Hwc2::Composer* getComposer() const { return mHwcDevice->getComposer(); }
+ Hwc2::Composer* getComposer() const { return mHwcDevice->getComposer(); }
// TODO(b/74619554): Remove special cases for internal/external display.
std::optional<hwc2_display_t> getInternalHwcDisplayId() const { return mInternalHwcDisplayId; }
@@ -194,8 +178,6 @@
static void validateChange(HWC2::Composition from, HWC2::Composition to);
- struct cb_context;
-
struct DisplayData {
bool isVirtual = false;
bool hasClientComposition = false;
@@ -209,11 +191,16 @@
mutable std::unordered_map<int32_t,
std::shared_ptr<const HWC2::Display::Config>> configMap;
- // protected by mVsyncLock
- HWC2::Vsync vsyncEnabled = HWC2::Vsync::Disable;
-
bool validateWasSkipped;
HWC2::Error presentError;
+
+ bool vsyncTraceToggle = false;
+
+ std::mutex vsyncEnabledLock;
+ HWC2::Vsync vsyncEnabled GUARDED_BY(vsyncEnabledLock) = HWC2::Vsync::Disable;
+
+ mutable std::mutex lastHwVsyncLock;
+ nsecs_t lastHwVsync GUARDED_BY(lastHwVsyncLock) = 0;
};
std::unordered_map<DisplayId, DisplayData> mDisplayData;
@@ -227,25 +214,11 @@
std::optional<hwc2_display_t> mExternalHwcDisplayId;
bool mHasMultiDisplaySupport = false;
- // protect mDisplayData from races between prepare and dump
- mutable Mutex mDisplayLock;
-
- cb_context* mCBContext = nullptr;
- std::unordered_map<DisplayId, size_t> mVSyncCounts;
-
std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
uint32_t mNextVirtualDisplayId = 0;
uint32_t mRemainingHwcVirtualDisplays{mHwcDevice->getMaxVirtualDisplayCount()};
-
- // protected by mLock
- mutable Mutex mLock;
- mutable std::unordered_map<DisplayId, nsecs_t> mLastHwVSync;
-
- // thread-safe
- mutable Mutex mVsyncLock;
};
-// ---------------------------------------------------------------------------
-}; // namespace android
+} // namespace android
#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 1e910ce..4116ea0 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -52,6 +52,7 @@
#include "SurfaceFlinger.h"
#include "DisplayHardware/HWComposer.h"
+#include "TimeStats/TimeStats.h"
#include <renderengine/RenderEngine.h>
@@ -101,6 +102,7 @@
mCurrentState.dataspace = ui::Dataspace::UNKNOWN;
mCurrentState.hdrMetadata.validTypes = 0;
mCurrentState.surfaceDamageRegion.clear();
+ mCurrentState.cornerRadius = 0.0f;
mCurrentState.api = -1;
mCurrentState.hasColorTransform = false;
@@ -123,7 +125,7 @@
mFrameTracker.logAndResetStats(mName);
- destroyAllHwcLayers();
+ destroyAllHwcLayersPlusChildren();
mFlinger->onLayerDestroyed();
}
@@ -175,6 +177,14 @@
}
}
+void Layer::addToCurrentState() {
+ mRemovedFromCurrentState = false;
+
+ for (const auto& child : mCurrentChildren) {
+ child->addToCurrentState();
+ }
+}
+
// ---------------------------------------------------------------------------
// set-up
// ---------------------------------------------------------------------------
@@ -226,17 +236,21 @@
return true;
}
-void Layer::destroyAllHwcLayers() {
+void Layer::destroyHwcLayersForAllDisplays() {
size_t numLayers = getBE().mHwcLayers.size();
for (size_t i = 0; i < numLayers; ++i) {
LOG_ALWAYS_FATAL_IF(getBE().mHwcLayers.empty(), "destroyAllHwcLayers failed");
destroyHwcLayer(getBE().mHwcLayers.begin()->first);
}
+}
+
+void Layer::destroyAllHwcLayersPlusChildren() {
+ destroyHwcLayersForAllDisplays();
LOG_ALWAYS_FATAL_IF(!getBE().mHwcLayers.empty(),
"All hardware composer layers should have been destroyed");
for (const sp<Layer>& child : mDrawingChildren) {
- child->destroyAllHwcLayers();
+ child->destroyAllHwcLayersPlusChildren();
}
}
@@ -367,30 +381,43 @@
// layerstack space, and convert-back to layer space.
// if there are no window scaling involved, this operation will map to full
// pixels in the buffer.
- // FIXME: the 3 lines below can produce slightly incorrect clipping when we have
- // a viewport clipping and a window transform. we should use floating point to fix this.
- Rect activeCrop(getActiveWidth(s), getActiveHeight(s));
- Rect crop = getCrop(s);
- if (!crop.isEmpty()) {
- activeCrop.intersect(crop, &activeCrop);
- }
-
+ FloatRect activeCropFloat = computeBounds();
ui::Transform t = getTransform();
- activeCrop = t.transform(activeCrop);
- if (!activeCrop.intersect(display->getViewport(), &activeCrop)) {
+ // Transform to screen space.
+ activeCropFloat = t.transform(activeCropFloat);
+ activeCropFloat = activeCropFloat.intersect(display->getViewport().toFloatRect());
+ // Back to layer space to work with the content crop.
+ activeCropFloat = t.inverse().transform(activeCropFloat);
+ // This needs to be here as transform.transform(Rect) computes the
+ // transformed rect and then takes the bounding box of the result before
+ // returning. This means
+ // transform.inverse().transform(transform.transform(Rect)) != Rect
+ // in which case we need to make sure the final rect is clipped to the
+ // display bounds.
+ Rect activeCrop{activeCropFloat};
+ if (!activeCrop.intersect(getBufferSize(s), &activeCrop)) {
activeCrop.clear();
}
-
- const auto& p = mDrawingParent.promote();
- if (p != nullptr) {
- auto parentCrop = p->computeInitialCrop(display);
- activeCrop.intersect(parentCrop, &activeCrop);
- }
-
return activeCrop;
}
+void Layer::setupRoundedCornersCropCoordinates(Rect win,
+ const FloatRect& roundedCornersCrop) const {
+ // Translate win by the rounded corners rect coordinates, to have all values in
+ // layer coordinate space.
+ win.left -= roundedCornersCrop.left;
+ win.right -= roundedCornersCrop.left;
+ win.top -= roundedCornersCrop.top;
+ win.bottom -= roundedCornersCrop.top;
+
+ renderengine::Mesh::VertexArray<vec2> cropCoords(getBE().mMesh.getCropCoordArray<vec2>());
+ cropCoords[0] = vec2(win.left, win.top);
+ cropCoords[1] = vec2(win.left, win.top + win.getHeight());
+ cropCoords[2] = vec2(win.right, win.top + win.getHeight());
+ cropCoords[3] = vec2(win.right, win.top);
+}
+
FloatRect Layer::computeCrop(const sp<const DisplayDevice>& display) const {
// the content crop is the area of the content that gets scaled to the
// layer's size. This is in buffer space.
@@ -399,24 +426,8 @@
// In addition there is a WM-specified crop we pull from our drawing state.
const State& s(getDrawingState());
- // Screen space to make reduction to parent crop clearer.
Rect activeCrop = computeInitialCrop(display);
- ui::Transform t = getTransform();
- // Back to layer space to work with the content crop.
- activeCrop = t.inverse().transform(activeCrop);
-
- // This needs to be here as transform.transform(Rect) computes the
- // transformed rect and then takes the bounding box of the result before
- // returning. This means
- // transform.inverse().transform(transform.transform(Rect)) != Rect
- // in which case we need to make sure the final rect is clipped to the
- // display bounds.
- if (!activeCrop.intersect(Rect(getActiveWidth(s), getActiveHeight(s)), &activeCrop)) {
- activeCrop.clear();
- }
-
- // subtract the transparent region and snap to the bounds
- activeCrop = reduce(activeCrop, getActiveTransparentRegion(s));
+ Rect bufferSize = getBufferSize(s);
// Transform the window crop to match the buffer coordinate system,
// which means using the inverse of the current transform set on the
@@ -437,8 +448,8 @@
ui::Transform(invTransform)).getOrientation();
}
- int winWidth = getActiveWidth(s);
- int winHeight = getActiveHeight(s);
+ int winWidth = bufferSize.getWidth();
+ int winHeight = bufferSize.getHeight();
if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
// If the activeCrop has been rotate the ends are rotated but not
// the space itself so when transforming ends back we can't rely on
@@ -450,10 +461,10 @@
if (is_h_flipped == is_v_flipped) {
invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H;
}
- winWidth = getActiveHeight(s);
- winHeight = getActiveWidth(s);
+ std::swap(winWidth, winHeight);
}
- const Rect winCrop = activeCrop.transform(invTransform, getActiveWidth(s), getActiveHeight(s));
+ const Rect winCrop =
+ activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight());
// below, crop is intersected with winCrop expressed in crop's coordinate space
float xScale = crop.getWidth() / float(winWidth);
@@ -489,6 +500,8 @@
// this gives us only the "orientation" component of the transform
const State& s(getDrawingState());
+ const int bufferWidth = getBufferSize(s).getWidth();
+ const int bufferHeight = getBufferSize(s).getHeight();
auto blendMode = HWC2::BlendMode::None;
if (!isOpaque(s) || getAlpha() != 1.0f) {
blendMode =
@@ -519,16 +532,15 @@
// transform.inverse().transform(transform.transform(Rect)) != Rect
// in which case we need to make sure the final rect is clipped to the
// display bounds.
- if (!activeCrop.intersect(Rect(getActiveWidth(s), getActiveHeight(s)), &activeCrop)) {
+ if (!activeCrop.intersect(Rect(bufferWidth, bufferHeight), &activeCrop)) {
activeCrop.clear();
}
// mark regions outside the crop as transparent
- activeTransparentRegion.orSelf(Rect(0, 0, getActiveWidth(s), activeCrop.top));
- activeTransparentRegion.orSelf(
- Rect(0, activeCrop.bottom, getActiveWidth(s), getActiveHeight(s)));
+ activeTransparentRegion.orSelf(Rect(0, 0, bufferWidth, activeCrop.top));
+ activeTransparentRegion.orSelf(Rect(0, activeCrop.bottom, bufferWidth, bufferHeight));
activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom));
activeTransparentRegion.orSelf(
- Rect(activeCrop.right, activeCrop.top, getActiveWidth(s), activeCrop.bottom));
+ Rect(activeCrop.right, activeCrop.top, bufferWidth, activeCrop.bottom));
}
// computeBounds returns a FloatRect to provide more accuracy during the
@@ -664,11 +676,7 @@
// Apply the layer's transform, followed by the display's global transform
// Here we're guaranteed that the layer's transform preserves rects
- Rect win(getActiveWidth(s), getActiveHeight(s));
- Rect crop = getCrop(s);
- if (!crop.isEmpty()) {
- win.intersect(crop, &win);
- }
+ Rect win = getCroppedBufferSize(s);
// Subtract the transparent region and snap to the bounds
Rect bounds = reduce(win, getActiveTransparentRegion(s));
Rect frame(getTransform().transform(bounds));
@@ -1021,6 +1029,10 @@
uint32_t Layer::doTransaction(uint32_t flags) {
ATRACE_CALL();
+ if (mLayerDetached) {
+ return 0;
+ }
+
pushPendingState();
State c = getCurrentState();
if (!applyPendingStates(&c)) {
@@ -1053,6 +1065,11 @@
clearSyncPoints();
}
+ if (mCurrentState.inputInfoChanged) {
+ flags |= eInputInfoChanged;
+ mCurrentState.inputInfoChanged = false;
+ }
+
// Commit the transaction
commitTransaction(c);
mCurrentState.callbackHandles = {};
@@ -1221,6 +1238,17 @@
return true;
}
+bool Layer::setCornerRadius(float cornerRadius) {
+ if (mCurrentState.cornerRadius == cornerRadius)
+ return false;
+
+ mCurrentState.sequence++;
+ mCurrentState.cornerRadius = cornerRadius;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix,
bool allowNonRectPreservingTransforms) {
ui::Transform t;
@@ -1495,14 +1523,14 @@
void Layer::onDisconnect() {
Mutex::Autolock lock(mFrameEventHistoryMutex);
mFrameEventHistory.onDisconnect();
- mTimeStats.onDisconnect(getSequence());
+ mFlinger->mTimeStats->onDestroy(getSequence());
}
void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
if (newTimestamps) {
- mTimeStats.setPostTime(getSequence(), newTimestamps->frameNumber, getName().c_str(),
- newTimestamps->postedTime);
+ mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
+ getName().c_str(), newTimestamps->postedTime);
}
Mutex::Autolock lock(mFrameEventHistoryMutex);
@@ -1553,6 +1581,9 @@
return false;
}
+ if (attachChildren()) {
+ setTransactionFlags(eTransactionNeeded);
+ }
for (const sp<Layer>& child : mCurrentChildren) {
newParent->addChild(child);
@@ -1590,6 +1621,10 @@
}
newParent->addChild(this);
+ if (!newParent->isRemovedFromCurrentState()) {
+ addToCurrentState();
+ }
+
sp<Client> client(mClientRef.promote());
sp<Client> newParentClient(newParent->mClientRef.promote());
@@ -1597,6 +1632,13 @@
client->updateParent(newParent);
}
+ if (mLayerDetached) {
+ mLayerDetached = false;
+ setTransactionFlags(eTransactionNeeded);
+ }
+ if (attachChildren()) {
+ setTransactionFlags(eTransactionNeeded);
+ }
return true;
}
@@ -1605,7 +1647,7 @@
sp<Client> parentClient = mClientRef.promote();
sp<Client> client(child->mClientRef.promote());
if (client != nullptr && parentClient != client) {
- client->detachLayer(child.get());
+ child->mLayerDetached = true;
child->detachChildren();
}
}
@@ -1613,6 +1655,23 @@
return true;
}
+bool Layer::attachChildren() {
+ bool changed = false;
+ for (const sp<Layer>& child : mCurrentChildren) {
+ sp<Client> parentClient = mClientRef.promote();
+ sp<Client> client(child->mClientRef.promote());
+ if (client != nullptr && parentClient != client) {
+ if (child->mLayerDetached) {
+ child->mLayerDetached = false;
+ changed = true;
+ }
+ changed |= child->attachChildren();
+ }
+ }
+
+ return changed;
+}
+
bool Layer::setColorTransform(const mat4& matrix) {
static const mat4 identityMatrix = mat4();
@@ -1888,6 +1947,26 @@
return half4(color.r, color.g, color.b, getAlpha());
}
+Layer::RoundedCornerState Layer::getRoundedCornerState() const {
+ const auto& p = mDrawingParent.promote();
+ if (p != nullptr) {
+ RoundedCornerState parentState = p->getRoundedCornerState();
+ if (parentState.radius > 0) {
+ ui::Transform t = getActiveTransform(getDrawingState());
+ t = t.inverse();
+ parentState.cropRect = t.transform(parentState.cropRect);
+ // The rounded corners shader only accepts 1 corner radius for performance reasons,
+ // but a transform matrix can define horizontal and vertical scales.
+ // Let's take the average between both of them and pass into the shader, practically we
+ // never do this type of transformation on windows anyway.
+ parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
+ return parentState;
+ }
+ }
+ const float radius = getDrawingState().cornerRadius;
+ return radius > 0 ? RoundedCornerState(computeBounds(), radius) : RoundedCornerState();
+}
+
void Layer::commitChildList() {
for (size_t i = 0; i < mCurrentChildren.size(); i++) {
const auto& child = mCurrentChildren[i];
@@ -1897,6 +1976,13 @@
mDrawingParent = mCurrentParent;
}
+void Layer::setInputInfo(const InputWindowInfo& info) {
+ mCurrentState.inputInfo = info;
+ mCurrentState.modified = true;
+ mCurrentState.inputInfoChanged = true;
+ setTransactionFlags(eTransactionNeeded);
+}
+
void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) {
const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
@@ -1941,6 +2027,7 @@
size->set_h(state.active_legacy.h);
LayerProtoHelper::writeToProto(state.crop_legacy, layerInfo->mutable_crop());
+ layerInfo->set_corner_radius(getRoundedCornerState().radius);
layerInfo->set_is_opaque(isOpaque(state));
layerInfo->set_invalidate(contentDirty);
@@ -2024,6 +2111,33 @@
return mRemovedFromCurrentState;
}
+InputWindowInfo Layer::fillInputInfo(const Rect& screenBounds) {
+ InputWindowInfo info = mDrawingState.inputInfo;
+ info.frameLeft = screenBounds.left + info.surfaceInset;
+ info.frameTop = screenBounds.top + info.surfaceInset;
+ info.frameRight = screenBounds.right - info.surfaceInset;
+ info.frameBottom = screenBounds.bottom - info.surfaceInset;
+
+ ui::Transform t = getTransform();
+ const float xScale = t.sx();
+ const float yScale = t.sy();
+ if (xScale != 1.0f || yScale != 1.0f) {
+ info.windowXScale *= 1.0f / xScale;
+ info.windowYScale *= 1.0f / yScale;
+ info.touchableRegion.scaleSelf(xScale, yScale);
+ }
+
+ info.touchableRegion = info.touchableRegion.translate(
+ screenBounds.left,
+ screenBounds.top);
+ info.visible = isVisible();
+ return info;
+}
+
+bool Layer::hasInput() const {
+ return mDrawingState.inputInfo.token != nullptr;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 40ebe1e..84fafc5 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -22,6 +22,7 @@
#include <gui/BufferQueue.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerState.h>
+#include <input/InputWindow.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/vec4.h>
#include <renderengine/Mesh.h>
@@ -47,7 +48,6 @@
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
-#include "TimeStats/TimeStats.h"
#include "TransactionCompletedThread.h"
#include "DisplayHardware/HWComposer.h"
@@ -109,6 +109,7 @@
enum { // flags for doTransaction()
eDontUpdateGeometryState = 0x00000001,
eVisibleRegion = 0x00000002,
+ eInputInfoChanged = 0x00000004
};
struct Geometry {
@@ -123,6 +124,17 @@
inline bool operator!=(const Geometry& rhs) const { return !operator==(rhs); }
};
+ struct RoundedCornerState {
+ RoundedCornerState() = default;
+ RoundedCornerState(FloatRect cropRect, float radius)
+ : cropRect(cropRect), radius(radius) {}
+
+ // Rounded rectangle in local layer coordinate space.
+ FloatRect cropRect = FloatRect();
+ // Radius of the rounded rectangle.
+ float radius = 0.0f;
+ };
+
struct State {
Geometry active_legacy;
Geometry requested_legacy;
@@ -165,6 +177,10 @@
SortedVector<wp<Layer>> zOrderRelatives;
half4 color;
+ float cornerRadius;
+
+ bool inputInfoChanged;
+ InputWindowInfo inputInfo;
// The fields below this point are only used by BufferStateLayer
Geometry active;
@@ -250,6 +266,13 @@
virtual bool setAlpha(float alpha);
virtual bool setColor(const half3& color);
+
+ // Set rounded corner radius for this layer and its children.
+ //
+ // We only support 1 radius per layer in the hierarchy, where parent layers have precedence.
+ // The shape of the rounded corner rectangle is specified by the crop rectangle of the layer
+ // from which we inferred the rounded corner radius.
+ virtual bool setCornerRadius(float cornerRadius);
virtual bool setTransparentRegionHint(const Region& transparent);
virtual bool setFlags(uint8_t flags, uint8_t mask);
virtual bool setLayerStack(uint32_t layerStack);
@@ -263,6 +286,8 @@
virtual void setChildrenDrawingParent(const sp<Layer>& layer);
virtual bool reparent(const sp<IBinder>& newParentHandle);
virtual bool detachChildren();
+ bool attachChildren();
+ bool isLayerDetached() const { return mLayerDetached; }
virtual bool setColorTransform(const mat4& matrix);
virtual const mat4& getColorTransform() const;
virtual bool hasColorTransform() const;
@@ -355,7 +380,6 @@
// to avoid grabbing the lock again to avoid deadlock
virtual bool isCreatedFromMainThread() const { return false; }
-
bool isRemovedFromCurrentState() const;
void writeToProto(LayerProto* layerInfo,
@@ -485,6 +509,11 @@
*/
void onRemovedFromCurrentState();
+ /*
+ * Called when the layer is added back to the current state list.
+ */
+ void addToCurrentState();
+
// Updates the transform hint in our SurfaceFlingerConsumer to match
// the current orientation of the display device.
void updateTransformHint(const sp<const DisplayDevice>& display) const;
@@ -506,7 +535,8 @@
bool createHwcLayer(HWComposer* hwc, DisplayId displayId);
bool destroyHwcLayer(DisplayId displayId);
- void destroyAllHwcLayers();
+ void destroyHwcLayersForAllDisplays();
+ void destroyAllHwcLayersPlusChildren();
bool hasHwcLayer(DisplayId displayId) const { return getBE().mHwcLayers.count(displayId) > 0; }
@@ -561,6 +591,13 @@
half getAlpha() const;
half4 getColor() const;
+ // Returns how rounded corners should be drawn for this layer.
+ // This will traverse the hierarchy until it reaches its root, finding topmost rounded
+ // corner definition and converting it into current layer's coordinates.
+ // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
+ // ignored.
+ RoundedCornerState getRoundedCornerState() const;
+
void traverseInReverseZOrder(LayerVector::StateSet stateSet,
const LayerVector::Visitor& visitor);
void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
@@ -590,6 +627,12 @@
int32_t getZ() const;
virtual void pushPendingState();
+ /**
+ * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
+ * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
+ */
+ virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
+
protected:
// constant
sp<SurfaceFlinger> mFlinger;
@@ -627,11 +670,15 @@
// IGraphicBufferProducer client, as that should not affect child clipping.
// Returns in screen space.
Rect computeInitialCrop(const sp<const DisplayDevice>& display) const;
+ /**
+ * Setup rounded corners coordinates of this layer, taking into account the layer bounds and
+ * crop coordinates, transforming them into layer space.
+ */
+ void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
// drawing
void clearWithOpenGL(const RenderArea& renderArea, float r, float g, float b,
float alpha) const;
-
void setParent(const sp<Layer>& layer);
LayerVector makeTraversalList(LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers);
@@ -707,6 +754,10 @@
bool getPremultipledAlpha() const;
bool mPendingHWCDestroy{false};
+ void setInputInfo(const InputWindowInfo& info);
+
+ InputWindowInfo fillInputInfo(const Rect& screenBounds);
+ bool hasInput() const;
protected:
// -----------------------------------------------------------------------
@@ -737,8 +788,6 @@
FenceTimeline mAcquireTimeline;
FenceTimeline mReleaseTimeline;
- TimeStats& mTimeStats = TimeStats::getInstance();
-
// main thread
sp<GraphicBuffer> mActiveBuffer;
ui::Dataspace mCurrentDataSpace = ui::Dataspace::UNKNOWN;
@@ -776,6 +825,9 @@
mutable LayerBE mBE;
+ // Can only be accessed with the SF state lock held.
+ bool mLayerDetached{false};
+
private:
/**
* Returns an unsorted vector of all layers that are part of this tree.
@@ -806,12 +858,6 @@
* bounds are constrained by its parent bounds.
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
-
- /**
- * Returns active buffer size in the correct orientation. Buffer size is determined by undoing
- * any buffer transformations. If the layer has no buffer then return INVALID_RECT.
- */
- virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; }
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index e944eb9..d5ccbe1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -26,6 +26,8 @@
#include <utils/Timers.h>
#include <utils/Trace.h>
+#include "SchedulerUtils.h"
+
namespace android {
LayerHistory::LayerHistory() {}
@@ -38,12 +40,17 @@
void LayerHistory::incrementCounter() {
mCounter++;
- mCounter = mCounter % ARRAY_SIZE;
+ mCounter = mCounter % scheduler::ARRAY_SIZE;
+ // Clear all the previous data from the history. This is a ring buffer, so we are
+ // reusing memory.
mElements[mCounter].clear();
}
const std::unordered_map<std::string, nsecs_t>& LayerHistory::get(size_t index) const {
- return mElements.at(index);
+ // For the purposes of the layer history, the index = 0 always needs to start at the
+ // current counter, and then decrement to access the layers in correct historical order.
+ return mElements.at((scheduler::ARRAY_SIZE + (mCounter - (index % scheduler::ARRAY_SIZE))) %
+ scheduler::ARRAY_SIZE);
}
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 1a7f9cd..c6fab07 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -25,6 +25,8 @@
#include <utils/Timers.h>
+#include "SchedulerUtils.h"
+
namespace android {
/*
@@ -42,19 +44,21 @@
~LayerHistory();
// Method for inserting layers and their requested present time into the ring buffer.
- // The elements are going to be inserted into an unordered_map at the position of
- // mCounter.
+ // The elements are going to be inserted into an unordered_map at the position 'now'.
void insert(const std::string layerName, nsecs_t presentTime);
// Method for incrementing the current slot in the ring buffer. It also clears the
// unordered_map, if it was created previously.
void incrementCounter();
- // Returns unordered_map at the given at index.
+ // Returns unordered_map at the given at index. The index is decremented from 'now'. For
+ // example, 0 is now, 1 is previous frame.
const std::unordered_map<std::string, nsecs_t>& get(size_t index) const;
+ // Returns the total size of the ring buffer. The value is always the same regardless
+ // of how many slots we filled in.
+ static constexpr size_t getSize() { return scheduler::ARRAY_SIZE; }
private:
size_t mCounter = 0;
- static constexpr size_t ARRAY_SIZE = 30;
- std::array<std::unordered_map<std::string, nsecs_t>, ARRAY_SIZE> mElements;
+ std::array<std::unordered_map<std::string, nsecs_t>, scheduler::ARRAY_SIZE> mElements;
};
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 4286cc9..4457f72 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -18,6 +18,7 @@
#include "Scheduler.h"
+#include <algorithm>
#include <cinttypes>
#include <cstdint>
#include <memory>
@@ -30,6 +31,7 @@
#include <gui/ISurfaceComposer.h>
#include <ui/DisplayStatInfo.h>
+#include <utils/Timers.h>
#include <utils/Trace.h>
#include "DispSync.h"
@@ -37,6 +39,7 @@
#include "EventControlThread.h"
#include "EventThread.h"
#include "InjectVSyncSource.h"
+#include "SchedulerUtils.h"
namespace android {
@@ -205,44 +208,20 @@
mHWVsyncAvailable = makeAvailable;
}
-void Scheduler::addNewFrameTimestamp(const nsecs_t newFrameTimestamp, bool isAutoTimestamp) {
- ATRACE_INT("AutoTimestamp", isAutoTimestamp);
- // Video does not have timestamp automatically set, so we discard timestamps that are
- // coming in from other sources for now.
- if (isAutoTimestamp) {
- return;
- }
- int64_t differenceMs = (newFrameTimestamp - mPreviousFrameTimestamp) / 1000000;
- mPreviousFrameTimestamp = newFrameTimestamp;
+void Scheduler::addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
+ const std::string layerName) {
+ // This is V1 logic. It calculates the average FPS based on the timestamp frequency
+ // regardless of which layer the timestamp came from.
+ // For now, the averages and FPS are recorded in the systrace.
+ determineTimestampAverage(isAutoTimestamp, framePresentTime);
- if (differenceMs < 10 || differenceMs > 100) {
- // Dismiss noise.
- return;
- }
- ATRACE_INT("TimestampDiff", differenceMs);
-
- mTimeDifferences[mCounter % ARRAY_SIZE] = differenceMs;
- mCounter++;
- nsecs_t average = calculateAverage();
- ATRACE_INT("TimestampAverage", average);
-
- // TODO(b/113612090): This are current numbers from trial and error while running videos
- // from YouTube at 24, 30, and 60 fps.
- if (average > 14 && average < 18) {
- ATRACE_INT("FPS", 60);
- } else if (average > 31 && average < 34) {
- ATRACE_INT("FPS", 30);
- updateFrameSkipping(1);
- return;
- } else if (average > 39 && average < 42) {
- ATRACE_INT("FPS", 24);
- }
- updateFrameSkipping(0);
+ // This is V2 logic. It calculates the average and median timestamp difference based on the
+ // individual layer history. The results are recorded in the systrace.
+ determineLayerTimestampStats(layerName, framePresentTime);
}
-nsecs_t Scheduler::calculateAverage() const {
- nsecs_t sum = std::accumulate(mTimeDifferences.begin(), mTimeDifferences.end(), 0);
- return (sum / ARRAY_SIZE);
+void Scheduler::incrementFrameCounter() {
+ mLayerHistory.incrementCounter();
}
void Scheduler::updateFrameSkipping(const int64_t skipCount) {
@@ -254,4 +233,80 @@
}
}
+void Scheduler::determineLayerTimestampStats(const std::string layerName,
+ const nsecs_t framePresentTime) {
+ mLayerHistory.insert(layerName, framePresentTime);
+ std::vector<int64_t> differencesMs;
+
+ // Traverse through the layer history, and determine the differences in present times.
+ nsecs_t newestPresentTime = framePresentTime;
+ std::string differencesText = "";
+ for (int i = 1; i < mLayerHistory.getSize(); i++) {
+ std::unordered_map<std::string, nsecs_t> layers = mLayerHistory.get(i);
+ for (auto layer : layers) {
+ if (layer.first != layerName) {
+ continue;
+ }
+ int64_t differenceMs = (newestPresentTime - layer.second) / 1000000;
+ // Dismiss noise.
+ if (differenceMs > 10 && differenceMs < 60) {
+ differencesMs.push_back(differenceMs);
+ }
+ IF_ALOGV() { differencesText += (std::to_string(differenceMs) + " "); }
+ newestPresentTime = layer.second;
+ }
+ }
+ ALOGV("Layer %s timestamp intervals: %s", layerName.c_str(), differencesText.c_str());
+
+ if (!differencesMs.empty()) {
+ // Mean/Average is a good indicator for when 24fps videos are playing, because the frames
+ // come in 33, and 49 ms intervals with occasional 41ms.
+ const int64_t meanMs = scheduler::calculate_mean(differencesMs);
+ const auto tagMean = "TimestampMean_" + layerName;
+ ATRACE_INT(tagMean.c_str(), meanMs);
+
+ // Mode and median are good indicators for 30 and 60 fps videos, because the majority of
+ // frames come in 16, or 33 ms intervals.
+ const auto tagMedian = "TimestampMedian_" + layerName;
+ ATRACE_INT(tagMedian.c_str(), scheduler::calculate_median(&differencesMs));
+
+ const auto tagMode = "TimestampMode_" + layerName;
+ ATRACE_INT(tagMode.c_str(), scheduler::calculate_mode(differencesMs));
+ }
+}
+
+void Scheduler::determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime) {
+ ATRACE_INT("AutoTimestamp", isAutoTimestamp);
+
+ // Video does not have timestamp automatically set, so we discard timestamps that are
+ // coming in from other sources for now.
+ if (isAutoTimestamp) {
+ return;
+ }
+ int64_t differenceMs = (framePresentTime - mPreviousFrameTimestamp) / 1000000;
+ mPreviousFrameTimestamp = framePresentTime;
+
+ if (differenceMs < 10 || differenceMs > 100) {
+ // Dismiss noise.
+ return;
+ }
+ ATRACE_INT("TimestampDiff", differenceMs);
+
+ mTimeDifferences[mCounter % scheduler::ARRAY_SIZE] = differenceMs;
+ mCounter++;
+ int64_t mean = scheduler::calculate_mean(mTimeDifferences);
+ ATRACE_INT("AutoTimestampMean", mean);
+
+ // TODO(b/113612090): This are current numbers from trial and error while running videos
+ // from YouTube at 24, 30, and 60 fps.
+ if (mean > 14 && mean < 18) {
+ ATRACE_INT("FPS", 60);
+ } else if (mean > 31 && mean < 34) {
+ ATRACE_INT("FPS", 30);
+ return;
+ } else if (mean > 39 && mean < 42) {
+ ATRACE_INT("FPS", 24);
+ }
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index dd1f24b..764ad00 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -26,6 +26,8 @@
#include "EventControlThread.h"
#include "EventThread.h"
#include "InjectVSyncSource.h"
+#include "LayerHistory.h"
+#include "SchedulerUtils.h"
namespace android {
@@ -103,7 +105,11 @@
void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
void setIgnorePresentFences(bool ignore);
void makeHWSyncAvailable(bool makeAvailable);
- void addNewFrameTimestamp(const nsecs_t newFrameTimestamp, bool isAutoTimestamp);
+ // Adds the present time for given layer to the history of present times.
+ void addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
+ const std::string layerName);
+ // Increments counter in the layer history to indicate that SF has started a new frame.
+ void incrementFrameCounter();
protected:
virtual std::unique_ptr<EventThread> makeEventThread(
@@ -114,6 +120,12 @@
private:
nsecs_t calculateAverage() const;
void updateFrameSkipping(const int64_t skipCount);
+ // Collects the statistical mean (average) and median between timestamp
+ // intervals for each frame for each layer.
+ void determineLayerTimestampStats(const std::string layerName, const nsecs_t framePresentTime);
+ // Collects the average difference between timestamps for each frame regardless
+ // of which layer the timestamp came from.
+ void determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime);
// TODO(b/113612090): Instead of letting BufferQueueLayer to access mDispSync directly, it
// should make request to Scheduler to compute next refresh.
@@ -147,9 +159,10 @@
// simulate 30Hz rendering, we skip every other frame, and this variable is set
// to 1.
int64_t mSkipCount = 0;
- static constexpr size_t ARRAY_SIZE = 30;
- std::array<int64_t, ARRAY_SIZE> mTimeDifferences;
+ std::array<int64_t, scheduler::ARRAY_SIZE> mTimeDifferences{};
size_t mCounter = 0;
+
+ LayerHistory mLayerHistory;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
new file mode 100644
index 0000000..191022d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SchedulerUtils.h"
+
+#include <cinttypes>
+#include <numeric>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace scheduler {
+
+int64_t calculate_median(std::vector<int64_t>* v) {
+ if (!v || v->empty()) {
+ return 0;
+ }
+
+ size_t n = v->size() / 2;
+ nth_element(v->begin(), v->begin() + n, v->end());
+ return v->at(n);
+}
+
+int64_t calculate_mode(const std::vector<int64_t>& v) {
+ if (v.empty()) {
+ return 0;
+ }
+
+ // Create a map with all the counts for the indivicual values in the vector.
+ std::unordered_map<int64_t, int64_t> counts;
+ for (int64_t value : v) {
+ counts[value]++;
+ }
+
+ // Sort the map, and return the number with the highest count. If two numbers have
+ // the same count, first one is returned.
+ using ValueType = const decltype(counts)::value_type&;
+ const auto compareCounts = [](ValueType l, ValueType r) { return l.second <= r.second; };
+ return std::max_element(counts.begin(), counts.end(), compareCounts)->first;
+}
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
new file mode 100644
index 0000000..17c57db
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cinttypes>
+#include <numeric>
+#include <vector>
+
+namespace android {
+namespace scheduler {
+// This number is used to set the size of the arrays in scheduler that hold information
+// about layers.
+static constexpr size_t ARRAY_SIZE = 30;
+
+// Calculates the statistical mean (average) in the data structure (array, vector). The
+// function does not modify the contents of the array.
+template <typename T>
+auto calculate_mean(const T& v) {
+ using V = typename T::value_type;
+ V sum = std::accumulate(v.begin(), v.end(), 0);
+ return sum / static_cast<V>(v.size());
+}
+
+// Calculates the statistical median in the vector. Return 0 if the vector is empty. The
+// function modifies the vector contents.
+int64_t calculate_median(std::vector<int64_t>* v);
+
+// Calculates the statistical mode in the vector. Return 0 if the vector is empty.
+int64_t calculate_mode(const std::vector<int64_t>& v);
+
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7150660..a8de576 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -37,6 +37,8 @@
#include <dvr/vr_flinger.h>
+#include <input/IInputFlinger.h>
+
#include <ui/ColorSpace.h>
#include <ui/DebugUtils.h>
#include <ui/DisplayInfo.h>
@@ -93,6 +95,7 @@
#include "Scheduler/InjectVSyncSource.h"
#include "Scheduler/MessageQueue.h"
#include "Scheduler/Scheduler.h"
+#include "TimeStats/TimeStats.h"
#include <cutils/compiler.h>
@@ -261,6 +264,7 @@
mDebugInTransaction(0),
mLastTransactionTime(0),
mForceFullDamage(false),
+ mTimeStats(factory.createTimeStats()),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
mRefreshStartTime(0),
@@ -554,6 +558,13 @@
if (window != 0) {
window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
}
+ sp<IBinder> input(defaultServiceManager()->getService(
+ String16("inputflinger")));
+ if (input == nullptr) {
+ ALOGE("Failed to link to input service");
+ } else {
+ mInputFlinger = interface_cast<IInputFlinger>(input);
+ }
if (mVrFlinger) {
mVrFlinger->OnBootFinished();
@@ -1105,6 +1116,23 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
+ ui::PixelFormat* outFormat,
+ ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const {
+ if (!outFormat || !outDataspace || !outComponentMask) {
+ return BAD_VALUE;
+ }
+ Mutex::Autolock _l(mStateLock);
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display || !display->getId()) {
+ ALOGE("getDisplayedContentSamplingAttributes: Bad display token: %p", display.get());
+ return BAD_VALUE;
+ }
+ return getHwComposer().getDisplayedContentSamplingAttributes(*display->getId(), outFormat,
+ outDataspace, outComponentMask);
+}
+
status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
postMessageSync(new LambdaMessage([&] {
Mutex::Autolock _l(mStateLock);
@@ -1489,6 +1517,10 @@
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
+ if (mUseScheduler) {
+ // This call is made each time SF wakes up and creates a new frame.
+ mScheduler->incrementFrameCounter();
+ }
bool frameMissed = !mHadClientComposition &&
mPreviousPresentFence != Fence::NO_FENCE &&
(mPreviousPresentFence->getSignalTime() ==
@@ -1496,7 +1528,7 @@
mFrameMissedCount += frameMissed;
ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
if (frameMissed) {
- mTimeStats.incrementMissedFrames();
+ mTimeStats->incrementMissedFrames();
if (mPropagateBackpressure) {
signalLayerUpdate();
break;
@@ -1657,6 +1689,10 @@
layer->forceClientComposition(*displayId);
}
+ if (layer->getRoundedCornerState().radius > 0.0f) {
+ layer->forceClientComposition(*displayId);
+ }
+
if (layer->getForceClientComposition(*displayId)) {
ALOGV("[%s] Requesting Client composition", layer->getName().string());
layer->setCompositionType(*displayId, HWC2::Composition::Client);
@@ -1679,8 +1715,6 @@
mDrawingState.colorMatrixChanged = false;
for (const auto& [token, display] : mDisplays) {
- getBE().mCompositionInfo[token].clear();
-
for (auto& layer : display->getVisibleLayersSortedByZ()) {
const auto displayId = display->getId();
layer->getBE().compositionInfo.compositionType = layer->getCompositionType(displayId);
@@ -1919,12 +1953,12 @@
mAnimFrameTracker.advanceFrame();
}
- mTimeStats.incrementTotalFrames();
+ mTimeStats->incrementTotalFrames();
if (mHadClientComposition) {
- mTimeStats.incrementClientCompositionFrames();
+ mTimeStats->incrementClientCompositionFrames();
}
- mTimeStats.setPresentFenceGlobal(presentFenceTime);
+ mTimeStats->setPresentFenceGlobal(presentFenceTime);
if (display && getHwComposer().isConnected(*display->getId()) && !display->isPoweredOn()) {
return;
@@ -1964,6 +1998,17 @@
ATRACE_CALL();
ALOGV("rebuildLayerStacks");
+ // We need to clear these out now as these may be holding on to a
+ // HWC2::Layer reference at the same time as the LayerBE::HWCInfo structure
+ // also holds a reference. When the set of visible layers is recomputed,
+ // some layers may be destroyed if the only thing keeping them alive was
+ // that list of visible layers associated with each display. The layer
+ // destruction code asserts that the HWC2::Layer is properly destroyed, but
+ // that doesn't happen if SurfaceFlingerBE::mCompositionInfo keeps it alive.
+ for (const auto& [token, display] : mDisplays) {
+ getBE().mCompositionInfo[token].clear();
+ }
+
// rebuild the visible layer list per screen
if (CC_UNLIKELY(mVisibleRegionsDirty)) {
ATRACE_NAME("rebuildLayerStacks VR Dirty");
@@ -2337,14 +2382,6 @@
auto nativeWindow = nativeWindowSurface->getNativeWindow();
creationArgs.nativeWindow = nativeWindow;
- /*
- * Create our display's surface
- */
- std::unique_ptr<renderengine::Surface> renderSurface = getRenderEngine().createSurface();
- renderSurface->setCritical(isInternalDisplay);
- renderSurface->setAsync(state.isVirtual());
- creationArgs.renderSurface = std::move(renderSurface);
-
// Make sure that composition can never be stalled by a virtual display
// consumer that isn't processing buffers fast enough. We have to do this
// here, in case the display is composed entirely by HWC.
@@ -2568,6 +2605,7 @@
* (perform the transaction for each of them if needed)
*/
+ bool inputChanged = false;
if (transactionFlags & eTraversalNeeded) {
mCurrentState.traverseInZOrder([&](Layer* layer) {
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
@@ -2576,6 +2614,10 @@
const uint32_t flags = layer->doTransaction(0);
if (flags & Layer::eVisibleRegion)
mVisibleRegionsDirty = true;
+
+ if (flags & Layer::eInputInfoChanged) {
+ inputChanged = true;
+ }
});
}
@@ -2685,9 +2727,26 @@
commitTransaction();
+ if ((inputChanged || mVisibleRegionsDirty) && mInputFlinger) {
+ updateInputWindows();
+ }
+
updateCursorAsync();
}
+void SurfaceFlinger::updateInputWindows() {
+ ATRACE_CALL();
+
+ Vector<InputWindowInfo> inputHandles;
+
+ mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
+ if (layer->hasInput()) {
+ inputHandles.add(layer->fillInputInfo(layer->computeScreenBounds()));
+ }
+ });
+ mInputFlinger->setInputWindows(inputHandles);
+}
+
void SurfaceFlinger::updateCursorAsync()
{
for (const auto& [token, display] : mDisplays) {
@@ -2701,6 +2760,17 @@
}
}
+void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) {
+ if (layer->hasReadyFrame()) {
+ const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime();
+ if (layer->shouldPresentNow(expectedPresentTime)) {
+ bool ignored = false;
+ layer->latchBuffer(ignored, systemTime(), Fence::NO_FENCE);
+ }
+ }
+ layer->releasePendingBuffer(systemTime());
+}
+
void SurfaceFlinger::commitTransaction()
{
if (!mLayersPendingRemoval.isEmpty()) {
@@ -2714,11 +2784,9 @@
// showing at its last configured state until we eventually
// abandon the buffer queue.
if (l->isRemovedFromCurrentState()) {
- l->destroyAllHwcLayers();
- // destroyAllHwcLayers traverses to children, but releasePendingBuffer
- // doesn't in other scenarios. So we have to traverse explicitly here.
l->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* child) {
- child->releasePendingBuffer(systemTime());
+ child->destroyHwcLayersForAllDisplays();
+ latchAndReleaseBuffer(child);
});
}
}
@@ -2795,6 +2863,7 @@
if (CC_LIKELY(layer->isVisible())) {
const bool translucent = !layer->isOpaque(s);
Rect bounds(layer->computeScreenBounds());
+
visibleRegion.set(bounds);
ui::Transform tr = layer->getTransform();
if (!visibleRegion.isEmpty()) {
@@ -2813,6 +2882,7 @@
// compute the opaque region
const int32_t layerOrientation = tr.getOrientation();
if (layer->getAlpha() == 1.0f && !translucent &&
+ layer->getRoundedCornerState().radius == 0.0f &&
((layerOrientation & ui::Transform::ROT_INVALID) == false)) {
// the opaque region is the layer's footprint
opaqueRegion = visibleRegion;
@@ -3022,8 +3092,10 @@
display->getHdrCapabilities().getDesiredMaxLuminance());
const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(displayId);
- const bool skipClientColorTransform = getBE().mHwc->hasCapability(
- HWC2::Capability::SkipClientColorTransform);
+ const bool skipClientColorTransform =
+ getBE().mHwc
+ ->hasDisplayCapability(displayId,
+ HWC2::DisplayCapability::SkipClientColorTransform);
// Compute the global color transform matrix.
applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
@@ -3101,7 +3173,7 @@
const Layer::State& state(layer->getDrawingState());
if (layer->getClearClientTarget(*displayId) && !firstLayer &&
layer->isOpaque(state) && (layer->getAlpha() == 1.0f) &&
- hasClientComposition) {
+ layer->getRoundedCornerState().radius == 0.0f && hasClientComposition) {
// never clear the very first layer since we're
// guaranteed the FB is already cleared
layer->clearWithOpenGL(renderArea);
@@ -3170,7 +3242,7 @@
} else {
if (parent->isRemovedFromCurrentState()) {
ALOGE("addClientLayer called with a removed parent");
- return NAME_NOT_FOUND;
+ lbc->onRemovedFromCurrentState();
}
parent->addChild(lbc);
}
@@ -3198,6 +3270,10 @@
}
status_t SurfaceFlinger::removeLayerLocked(const Mutex& lock, const sp<Layer>& layer) {
+ if (layer->isLayerDetached()) {
+ return NO_ERROR;
+ }
+
const auto& p = layer->getParent();
ssize_t index;
if (p != nullptr) {
@@ -3517,6 +3593,10 @@
if (layer->setCrop_legacy(s.crop_legacy, !geometryAppliesWithResize))
flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eCornerRadiusChanged) {
+ if (layer->setCornerRadius(s.cornerRadius))
+ flags |= eTraversalNeeded;
+ }
if (what & layer_state_t::eLayerStackChanged) {
ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
// We only allow setting layer stacks for top level layers,
@@ -3606,7 +3686,10 @@
if (what & layer_state_t::eSidebandStreamChanged) {
if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
}
-
+ if (what & layer_state_t::eInputInfoChanged) {
+ layer->setInputInfo(s.inputInfo);
+ flags |= eTraversalNeeded;
+ }
std::vector<sp<CallbackHandle>> callbackHandles;
if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) {
mTransactionCompletedThread.run();
@@ -3663,11 +3746,24 @@
result = createBufferStateLayer(client, uniqueName, w, h, flags, handle, &layer);
break;
case ISurfaceComposerClient::eFXSurfaceColor:
+ // check if buffer size is set for color layer.
+ if (w > 0 || h > 0) {
+ ALOGE("createLayer() failed, w or h cannot be set for color layer (w=%d, h=%d)",
+ int(w), int(h));
+ return BAD_VALUE;
+ }
+
result = createColorLayer(client,
uniqueName, w, h, flags,
handle, &layer);
break;
case ISurfaceComposerClient::eFXSurfaceContainer:
+ // check if buffer size is set for container layer.
+ if (w > 0 || h > 0) {
+ ALOGE("createLayer() failed, w or h cannot be set for container layer (w=%d, h=%d)",
+ int(w), int(h));
+ return BAD_VALUE;
+ }
result = createContainerLayer(client,
uniqueName, w, h, flags,
handle, &layer);
@@ -3962,7 +4058,7 @@
}
if (display->isPrimary()) {
- mTimeStats.setPowerMode(mode);
+ mTimeStats->setPowerMode(mode);
}
ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
@@ -4105,7 +4201,7 @@
if ((index < numArgs) && (args[index] == String16("--timestats"))) {
index++;
- mTimeStats.parseArgs(asProto, args, index, result);
+ mTimeStats->parseArgs(asProto, args, index, result);
dumpAll = false;
}
}
@@ -4681,7 +4777,8 @@
case SET_ACTIVE_CONFIG:
case SET_ACTIVE_COLOR_MODE:
case INJECT_VSYNC:
- case SET_POWER_MODE: {
+ case SET_POWER_MODE:
+ case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: {
if (!callingThreadHasUnscopedSurfaceFlingerAccess()) {
IPCThreadState* ipc = IPCThreadState::self();
ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
@@ -5117,12 +5214,14 @@
const ui::Transform& getTransform() const override { return mTransform; }
Rect getBounds() const override {
const Layer::State& layerState(mLayer->getDrawingState());
- return Rect(mLayer->getActiveWidth(layerState), mLayer->getActiveHeight(layerState));
+ return mLayer->getBufferSize(layerState);
}
int getHeight() const override {
- return mLayer->getActiveHeight(mLayer->getDrawingState());
+ return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
}
- int getWidth() const override { return mLayer->getActiveWidth(mLayer->getDrawingState()); }
+ int getWidth() const override {
+ return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth();
+ }
bool isSecure() const override { return false; }
bool needsFiltering() const override { return mNeedsFiltering; }
Rect getSourceCrop() const override {
@@ -5196,12 +5295,12 @@
Rect crop(sourceCrop);
if (sourceCrop.width() <= 0) {
crop.left = 0;
- crop.right = parent->getActiveWidth(parent->getCurrentState());
+ crop.right = parent->getBufferSize(parent->getCurrentState()).getWidth();
}
if (sourceCrop.height() <= 0) {
crop.top = 0;
- crop.bottom = parent->getActiveHeight(parent->getCurrentState());
+ crop.bottom = parent->getBufferSize(parent->getCurrentState()).getHeight();
}
int32_t reqWidth = crop.width() * frameScale;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f98dddf..9039a14 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -72,7 +72,6 @@
#include "Scheduler/MessageQueue.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VSyncModulator.h"
-#include "TimeStats/TimeStats.h"
#include <map>
#include <mutex>
@@ -98,10 +97,12 @@
class EventThread;
class IGraphicBufferConsumer;
class IGraphicBufferProducer;
+class IInputFlinger;
class InjectVSyncSource;
class Layer;
class Surface;
class SurfaceFlingerBE;
+class TimeStats;
class VSyncSource;
struct CompositionInfo;
@@ -479,6 +480,9 @@
status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
ui::Dataspace* outWideColorGamutDataspace,
ui::PixelFormat* outWideColorGamutPixelFormat) const override;
+ virtual status_t getDisplayedContentSamplingAttributes(
+ const sp<IBinder>& display, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace,
+ uint8_t* outComponentMask) const override;
/* ------------------------------------------------------------------------
* DeathRecipient interface
@@ -531,6 +535,7 @@
void handleTransaction(uint32_t transactionFlags);
void handleTransactionLocked(uint32_t transactionFlags);
+ void updateInputWindows();
void updateCursorAsync();
/* handlePageFlip - latch a new buffer if available and compute the dirty
@@ -547,6 +552,7 @@
// Can only be called from the main thread or with mStateLock held
uint32_t setTransactionFlags(uint32_t flags);
uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
+ void latchAndReleaseBuffer(const sp<Layer>& layer);
void commitTransaction();
bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
uint32_t setClientStateLocked(const ComposerState& composerState);
@@ -920,7 +926,7 @@
std::unique_ptr<SurfaceInterceptor> mInterceptor{mFactory.createSurfaceInterceptor(this)};
SurfaceTracing mTracing;
LayerStats mLayerStats;
- TimeStats& mTimeStats = TimeStats::getInstance();
+ std::unique_ptr<TimeStats> mTimeStats;
bool mUseHwcVirtualDisplays = false;
std::atomic<uint32_t> mFrameMissedCount{0};
@@ -986,6 +992,8 @@
std::unique_ptr<Scheduler> mScheduler;
sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
+
+ sp<IInputFlinger> mInputFlinger;
};
}; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
index 1261aee..88649e3 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -33,6 +33,7 @@
#include "Scheduler/EventControlThread.h"
#include "Scheduler/MessageQueue.h"
#include "Scheduler/Scheduler.h"
+#include "TimeStats/TimeStats.h"
namespace android::surfaceflinger {
@@ -116,6 +117,10 @@
sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) override {
return new ColorLayer(args);
}
+
+ std::unique_ptr<TimeStats> createTimeStats() override {
+ return std::make_unique<TimeStats>();
+ }
};
static Factory factory;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 496bbf0..1c27cc7 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -44,6 +44,7 @@
class StartPropertySetThread;
class SurfaceFlinger;
class SurfaceInterceptor;
+class TimeStats;
struct DisplayDeviceCreationArgs;
struct LayerCreationArgs;
@@ -82,6 +83,8 @@
virtual sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) = 0;
virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
+ virtual std::unique_ptr<TimeStats> createTimeStats() = 0;
+
protected:
~Factory() = default;
};
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 55cfa4b..7bfe033 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -109,6 +109,7 @@
layer->mCurrentState.activeTransparentRegion_legacy);
addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
+ addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
addDeferTransactionLocked(transaction, layerId,
layer->mCurrentState.barrierLayer_legacy.promote(),
@@ -289,6 +290,14 @@
setProtoRectLocked(protoRect, rect);
}
+void SurfaceInterceptor::addCornerRadiusLocked(Transaction* transaction, int32_t layerId,
+ float cornerRadius)
+{
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ CornerRadiusChange* cornerRadiusChange(change->mutable_corner_radius());
+ cornerRadiusChange->set_corner_radius(cornerRadius);
+}
+
void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
const sp<const Layer>& layer, uint64_t frameNumber)
{
@@ -350,6 +359,9 @@
if (state.what & layer_state_t::eCropChanged_legacy) {
addCropLocked(transaction, layerId, state.crop_legacy);
}
+ if (state.what & layer_state_t::eCornerRadiusChanged) {
+ addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
+ }
if (state.what & layer_state_t::eDeferTransaction_legacy) {
sp<Layer> otherLayer = nullptr;
if (state.barrierHandle_legacy != nullptr) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 394b99b..563a44c 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -144,6 +144,7 @@
void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags);
void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
+ void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
const sp<const Layer>& layer, uint64_t frameNumber);
void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 9730e8c..2b9f5c8 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -32,11 +32,6 @@
namespace android {
-TimeStats& TimeStats::getInstance() {
- static TimeStats sInstance;
- return sInstance;
-}
-
void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, size_t& index,
String8& result) {
ATRACE_CALL();
@@ -268,11 +263,9 @@
if (!mTimeStatsTracker.count(layerID)) return;
LayerRecord& layerRecord = mTimeStatsTracker[layerID];
if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
- ALOGE("[%d]-[%s]-timeRecords is already at its maximum size[%zu]. Please file a bug.",
+ ALOGE("[%d]-[%s]-timeRecords is at its maximum size[%zu]. Ignore this when unittesting.",
layerID, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
- layerRecord.timeRecords.clear();
- layerRecord.prevTimeRecord.ready = false;
- layerRecord.waitData = -1;
+ mTimeStatsTracker.erase(layerID);
return;
}
// For most media content, the acquireFence is invalid because the buffer is
@@ -283,7 +276,9 @@
{
.frameNumber = frameNumber,
.postTime = postTime,
+ .latchTime = postTime,
.acquireTime = postTime,
+ .desiredTime = postTime,
},
};
layerRecord.timeRecords.push_back(timeRecord);
@@ -394,18 +389,6 @@
flushAvailableRecordsToStatsLocked(layerID);
}
-void TimeStats::onDisconnect(int32_t layerID) {
- if (!mEnabled.load()) return;
-
- ATRACE_CALL();
- ALOGV("[%d]-onDisconnect", layerID);
-
- std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStatsTracker.count(layerID)) return;
- flushAvailableRecordsToStatsLocked(layerID);
- mTimeStatsTracker.erase(layerID);
-}
-
void TimeStats::onDestroy(int32_t layerID) {
if (!mEnabled.load()) return;
@@ -414,25 +397,9 @@
std::lock_guard<std::mutex> lock(mMutex);
if (!mTimeStatsTracker.count(layerID)) return;
- flushAvailableRecordsToStatsLocked(layerID);
mTimeStatsTracker.erase(layerID);
}
-void TimeStats::clearLayerRecord(int32_t layerID) {
- if (!mEnabled.load()) return;
-
- ATRACE_CALL();
- ALOGV("[%d]-clearLayerRecord", layerID);
-
- std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStatsTracker.count(layerID)) return;
- LayerRecord& layerRecord = mTimeStatsTracker[layerID];
- layerRecord.timeRecords.clear();
- layerRecord.prevTimeRecord.ready = false;
- layerRecord.waitData = -1;
- layerRecord.droppedFrames = 0;
-}
-
void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) {
if (!mEnabled.load()) return;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 184bf40..0b24c46 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -38,10 +38,6 @@
class String8;
class TimeStats {
- // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
- // static const size_t MAX_NUM_LAYER_RECORDS = 200;
- static const size_t MAX_NUM_TIME_RECORDS = 64;
-
struct FrameTime {
uint64_t frameNumber = 0;
nsecs_t postTime = 0;
@@ -80,8 +76,12 @@
};
public:
- static TimeStats& getInstance();
+ TimeStats() = default;
+ ~TimeStats() = default;
+
void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, String8& result);
+ bool isEnabled();
+
void incrementTotalFrames();
void incrementMissedFrames();
void incrementClientCompositionFrames();
@@ -96,21 +96,19 @@
void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime);
void setPresentFence(int32_t layerID, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence);
- // On producer disconnect with BufferQueue.
- void onDisconnect(int32_t layerID);
- // On layer tear down.
+ // Clean up the layer record
void onDestroy(int32_t layerID);
- // When SF is cleaning up the queue, clear the LayerRecord as well.
- void clearLayerRecord(int32_t layerID);
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
void removeTimeRecord(int32_t layerID, uint64_t frameNumber);
void setPowerMode(int32_t powerMode);
void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence);
-private:
- TimeStats() = default;
+ // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU
+ // static const size_t MAX_NUM_LAYER_RECORDS = 200;
+ static const size_t MAX_NUM_TIME_RECORDS = 64;
+private:
bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerID);
void flushPowerTimeLocked();
@@ -119,7 +117,6 @@
void enable();
void disable();
void clear();
- bool isEnabled();
void dump(bool asProto, std::optional<uint32_t> maxLayers, String8& result);
std::atomic<bool> mEnabled = false;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
index bef6b7c..b937f41 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
@@ -17,7 +17,6 @@
},
cppflags: [
- "-std=c++1z",
"-Werror",
"-Wno-c++98-compat-pedantic",
"-Wno-disabled-macro-expansion",
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 599ff5c..75ce4be 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include "timestatsproto/TimeStatsHelper.h"
+
#include <android-base/stringprintf.h>
-#include <timestatsproto/TimeStatsHelper.h>
+#include <inttypes.h>
#include <array>
@@ -46,6 +48,14 @@
hist[*iter]++;
}
+int64_t TimeStatsHelper::Histogram::totalTime() const {
+ int64_t ret = 0;
+ for (const auto& ele : hist) {
+ ret += ele.first * ele.second;
+ }
+ return ret;
+}
+
float TimeStatsHelper::Histogram::averageTime() const {
int64_t ret = 0;
int64_t count = 0;
@@ -87,12 +97,13 @@
std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> maxLayers) const {
std::string result = "SurfaceFlinger TimeStats:\n";
- StringAppendF(&result, "statsStart = %lld\n", static_cast<long long int>(statsStart));
- StringAppendF(&result, "statsEnd = %lld\n", static_cast<long long int>(statsEnd));
+ StringAppendF(&result, "statsStart = %" PRId64 "\n", statsStart);
+ StringAppendF(&result, "statsEnd = %" PRId64 "\n", statsEnd);
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "missedFrames = %d\n", missedFrames);
StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
- StringAppendF(&result, "displayOnTime = %lld ms\n", static_cast<long long int>(displayOnTime));
+ StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
+ StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
StringAppendF(&result, "presentToPresent histogram is as below:\n");
result.append(presentToPresent.toString());
const auto dumpStats = generateDumpStats(maxLayers);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 7d0fe79..5f40a1a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -34,6 +34,7 @@
std::unordered_map<int32_t, int32_t> hist;
void insert(int32_t delta);
+ int64_t totalTime() const;
float averageTime() const;
std::string toString() const;
};
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 9b9dc57..389118a 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -30,6 +30,8 @@
namespace android {
TransactionCompletedThread::~TransactionCompletedThread() {
+ std::lock_guard lockThread(mThreadMutex);
+
{
std::lock_guard lock(mMutex);
mKeepRunning = false;
@@ -50,11 +52,13 @@
void TransactionCompletedThread::run() {
std::lock_guard lock(mMutex);
- if (mRunning) {
+ if (mRunning || !mKeepRunning) {
return;
}
mDeathRecipient = new ThreadDeathRecipient();
mRunning = true;
+
+ std::lock_guard lockThread(mThreadMutex);
mThread = std::thread(&TransactionCompletedThread::threadMain, this);
}
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index 5af4097..1612f69 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -90,7 +90,10 @@
}
};
- std::thread mThread;
+ // Protects the creation and destruction of mThread
+ std::mutex mThreadMutex;
+
+ std::thread mThread GUARDED_BY(mThreadMutex);
std::mutex mMutex;
std::condition_variable_any mConditionVariable;
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index 1d7fb67..d020a39 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -121,6 +121,7 @@
layer.appId = layerProto.app_id();
layer.hwcCompositionType = layerProto.hwc_composition_type();
layer.isProtected = layerProto.is_protected();
+ layer.cornerRadius = layerProto.corner_radius();
return layer;
}
@@ -294,6 +295,7 @@
size.y);
StringAppendF(&result, "crop=%s, ", crop.to_string().c_str());
+ StringAppendF(&result, "cornerRadius=%f, ", cornerRadius);
StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str());
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 6b3b497..a794ca5 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -114,6 +114,7 @@
int32_t appId;
int32_t hwcCompositionType;
bool isProtected;
+ float cornerRadius;
std::string to_string() const;
};
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index c141ee3..b100438 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -87,6 +87,8 @@
// If active_buffer is not null, record its transform.
optional TransformProto buffer_transform = 39;
optional int32 effective_scaling_mode = 40;
+ // Layer's corner radius.
+ optional float corner_radius = 41;
}
message PositionProto {
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 740d2fa..e506757 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -44,6 +44,7 @@
constexpr uint32_t STACK_UPDATE = 1;
constexpr uint64_t DEFERRED_UPDATE = 0;
constexpr float ALPHA_UPDATE = 0.29f;
+constexpr float CORNER_RADIUS_UPDATE = 0.2f;
constexpr float POSITION_UPDATE = 121;
const Rect CROP_UPDATE(16, 16, 32, 32);
@@ -167,6 +168,7 @@
bool alphaUpdateFound(const SurfaceChange& change, bool foundAlpha);
bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
+ bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
@@ -198,6 +200,7 @@
void alphaUpdate(Transaction&);
void layerUpdate(Transaction&);
void cropUpdate(Transaction&);
+ void cornerRadiusUpdate(Transaction&);
void matrixUpdate(Transaction&);
void overrideScalingModeUpdate(Transaction&);
void transparentRegionHintUpdate(Transaction&);
@@ -313,6 +316,10 @@
t.setAlpha(mBGSurfaceControl, ALPHA_UPDATE);
}
+void SurfaceInterceptorTest::cornerRadiusUpdate(Transaction& t) {
+ t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE);
+}
+
void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
}
@@ -369,6 +376,7 @@
runInTransaction(&SurfaceInterceptorTest::positionUpdate);
runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
+ runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
runInTransaction(&SurfaceInterceptorTest::layerUpdate);
runInTransaction(&SurfaceInterceptorTest::cropUpdate);
runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
@@ -430,6 +438,17 @@
return foundAlpha;
}
+bool SurfaceInterceptorTest::cornerRadiusUpdateFound(const SurfaceChange &change,
+ bool foundCornerRadius) {
+ bool hasCornerRadius(change.corner_radius().corner_radius() == CORNER_RADIUS_UPDATE);
+ if (hasCornerRadius && !foundCornerRadius) {
+ foundCornerRadius = true;
+ } else if (hasCornerRadius && foundCornerRadius) {
+ [] () { FAIL(); }();
+ }
+ return foundCornerRadius;
+}
+
bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
bool hasLayer(change.layer().layer() == LAYER_UPDATE);
if (hasLayer && !foundLayer) {
@@ -572,6 +591,9 @@
case SurfaceChange::SurfaceChangeCase::kCrop:
foundUpdate = cropUpdateFound(change, foundUpdate);
break;
+ case SurfaceChange::SurfaceChangeCase::kCornerRadius:
+ foundUpdate = cornerRadiusUpdateFound(change, foundUpdate);
+ break;
case SurfaceChange::SurfaceChangeCase::kMatrix:
foundUpdate = matrixUpdateFound(change, foundUpdate);
break;
@@ -730,6 +752,11 @@
captureTest(&SurfaceInterceptorTest::cropUpdate, SurfaceChange::SurfaceChangeCase::kCrop);
}
+TEST_F(SurfaceInterceptorTest, InterceptCornerRadiusUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::cornerRadiusUpdate,
+ SurfaceChange::SurfaceChangeCase::kCornerRadius);
+}
+
TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
}
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 988454a..e414991 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -334,16 +334,10 @@
virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
const char* name, uint32_t width, uint32_t height,
uint32_t flags = 0) {
- auto layer =
- client->createSurface(String8(name), width, height, PIXEL_FORMAT_RGBA_8888, flags);
- EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
+ auto layer = createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags);
Transaction t;
t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
- // If we are creating a color layer, set its crop since its size will be ignored.
- if (flags == ISurfaceComposerClient::eFXSurfaceColor) {
- t.setCrop_legacy(layer, Rect(0, 0, width, height));
- }
status_t error = t.apply();
if (error != NO_ERROR) {
@@ -354,6 +348,15 @@
return layer;
}
+ virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client,
+ const char* name, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t flags,
+ SurfaceControl* parent = nullptr) {
+ auto layer = client->createSurface(String8(name), width, height, format, flags, parent);
+ EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
+ return layer;
+ }
+
virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
uint32_t flags = 0) {
return createLayer(mClient, name, width, height, flags);
@@ -509,9 +512,9 @@
mDisplayLayerStack = 0;
- mBlackBgSurface = mClient->createSurface(String8("BaseSurface"), mDisplayWidth,
- mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor);
+ mBlackBgSurface =
+ createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
// set layer stack (b/68888219)
Transaction t;
@@ -834,8 +837,9 @@
TEST_P(LayerTypeTransactionTest, SetZNegative) {
sp<SurfaceControl> parent =
- LayerTransactionTest::createLayer("Parent", mDisplayWidth, mDisplayHeight,
+ LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceContainer);
+ Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
@@ -892,8 +896,9 @@
TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) {
sp<SurfaceControl> parent =
- LayerTransactionTest::createLayer("Parent", mDisplayWidth, mDisplayHeight,
+ LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
ISurfaceComposerClient::eFXSurfaceContainer);
+ Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
sp<SurfaceControl> layerR;
sp<SurfaceControl> layerG;
sp<SurfaceControl> layerB;
@@ -1220,15 +1225,42 @@
}
}
+TEST_P(LayerTypeTransactionTest, SetCornerRadius) {
+ sp<SurfaceControl> layer;
+ const uint8_t size = 64;
+ const uint8_t testArea = 4;
+ const float cornerRadius = 16.0f;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
+ ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
+
+ Transaction()
+ .setCornerRadius(layer, cornerRadius)
+ .apply();
+ {
+ auto shot = screenshot();
+ // Transparent corners
+ shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+ shot->expectColor(Rect(0, size - testArea, testArea, testArea), Color::BLACK);
+ shot->expectColor(Rect(size - testArea, 0, testArea, testArea), Color::BLACK);
+ shot->expectColor(Rect(size - testArea, size - testArea, testArea, testArea),
+ Color::BLACK);
+ }
+}
+
TEST_F(LayerTransactionTest, SetColorBasic) {
sp<SurfaceControl> bufferLayer;
sp<SurfaceControl> colorLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
- ASSERT_NO_FATAL_FAILURE(
- colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+ ASSERT_NO_FATAL_FAILURE(colorLayer =
+ createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
- Transaction().setLayer(colorLayer, mLayerZBase + 1).apply();
+ Transaction()
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setLayer(colorLayer, mLayerZBase + 1)
+ .apply();
+
{
SCOPED_TRACE("default color");
screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
@@ -1248,10 +1280,14 @@
TEST_F(LayerTransactionTest, SetColorClamped) {
sp<SurfaceControl> colorLayer;
- ASSERT_NO_FATAL_FAILURE(
- colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+ ASSERT_NO_FATAL_FAILURE(colorLayer =
+ createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+ Transaction()
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setColor(colorLayer, half3(2.0f, -1.0f, 0.0f))
+ .apply();
- Transaction().setColor(colorLayer, half3(2.0f, -1.0f, 0.0f)).apply();
screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED);
}
@@ -1260,8 +1296,10 @@
sp<SurfaceControl> colorLayer;
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
- ASSERT_NO_FATAL_FAILURE(
- colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
+ ASSERT_NO_FATAL_FAILURE(colorLayer =
+ createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+ Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
const float alpha = 0.25f;
@@ -1285,9 +1323,10 @@
ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parentWithAlpha", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
- ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer(
- "childWithColor", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
-
+ ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
+ 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+ Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
const float alpha = 0.25f;
const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
@@ -2129,10 +2168,13 @@
TEST_F(LayerTransactionTest, SetColorTransformBasic) {
sp<SurfaceControl> colorLayer;
- ASSERT_NO_FATAL_FAILURE(
- colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor));
-
- Transaction().setLayer(colorLayer, mLayerZBase + 1).apply();
+ ASSERT_NO_FATAL_FAILURE(colorLayer =
+ createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+ ISurfaceComposerClient::eFXSurfaceColor));
+ Transaction()
+ .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+ .setLayer(colorLayer, mLayerZBase + 1)
+ .apply();
{
SCOPED_TRACE("default color");
screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
@@ -3024,11 +3066,10 @@
std::unique_ptr<ScreenCapture> sc;
sp<SurfaceControl> childNoBuffer =
- mClient->createSurface(String8("Bufferless child"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- sp<SurfaceControl> childBuffer =
- mClient->createSurface(String8("Buffered child"), 20, 20,
- PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
+ createSurface(mClient, "Bufferless child", 0 /* buffer width */, 0 /* buffer height */,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> childBuffer = createSurface(mClient, "Buffered child", 20, 20,
+ PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
fillSurfaceRGBA8(childBuffer, 200, 200, 200);
SurfaceComposerClient::Transaction{}
.setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
@@ -3078,8 +3119,8 @@
protected:
void SetUp() override {
LayerUpdateTest::SetUp();
- mChild = mClient->createSurface(String8("Child surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+ mFGSurfaceControl.get());
fillSurfaceRGBA8(mChild, 200, 200, 200);
{
@@ -3254,8 +3295,7 @@
TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) {
sp<SurfaceControl> mGrandChild =
- mClient->createSurface(String8("Grand Child"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+ createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
{
@@ -3318,10 +3358,9 @@
TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
sp<SurfaceControl> mChildNewClient =
- mNewComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- ASSERT_TRUE(mChildNewClient != nullptr);
ASSERT_TRUE(mChildNewClient->isValid());
fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
@@ -3356,6 +3395,61 @@
}
}
+TEST_F(ChildLayerTest, DetachChildrenThenAttach) {
+ sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+ sp<SurfaceControl> childNewClient =
+ newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+ ASSERT_TRUE(childNewClient != nullptr);
+ ASSERT_TRUE(childNewClient->isValid());
+
+ fillSurfaceRGBA8(childNewClient, 200, 200, 200);
+
+ Transaction()
+ .hide(mChild)
+ .show(childNewClient)
+ .setPosition(childNewClient, 10, 10)
+ .setPosition(mFGSurfaceControl, 64, 64)
+ .apply();
+
+ {
+ mCapture = screenshot();
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // But 10 pixels in we should see the child surface
+ mCapture->expectChildColor(74, 74);
+ // And 10 more pixels we should be back to the foreground surface
+ mCapture->expectFGColor(84, 84);
+ }
+
+ Transaction().detachChildren(mFGSurfaceControl).apply();
+ Transaction().hide(childNewClient).apply();
+
+ // Nothing should have changed.
+ {
+ mCapture = screenshot();
+ mCapture->expectFGColor(64, 64);
+ mCapture->expectChildColor(74, 74);
+ mCapture->expectFGColor(84, 84);
+ }
+
+ sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0);
+ fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32,
+ 32);
+ Transaction()
+ .setLayer(newParentSurface, INT32_MAX - 1)
+ .show(newParentSurface)
+ .setPosition(newParentSurface, 20, 20)
+ .reparent(childNewClient, newParentSurface->getHandle())
+ .apply();
+ {
+ mCapture = screenshot();
+ // Child is now hidden.
+ mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED);
+ }
+}
+
TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
asTransaction([&](Transaction& t) {
t.show(mChild);
@@ -3427,9 +3521,8 @@
mChild.clear();
// Now recreate it as hidden
- mChild = mClient->createSurface(String8("Child surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
- mFGSurfaceControl.get());
+ mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eHidden, mFGSurfaceControl.get());
// Show the child layer in a deferred transaction
asTransaction([&](Transaction& t) {
@@ -3545,9 +3638,8 @@
}
TEST_F(ChildLayerTest, NestedChildren) {
- sp<SurfaceControl> grandchild =
- mClient->createSurface(String8("Grandchild surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
fillSurfaceRGBA8(grandchild, 50, 50, 50);
{
@@ -3585,9 +3677,8 @@
// Verify setting a size on a buffer layer has no effect.
TEST_F(BoundlessLayerTest, BufferLayerIgnoresSize) {
sp<SurfaceControl> bufferLayer =
- mClient->createSurface(String8("BufferLayer"), 45, 45, PIXEL_FORMAT_RGBA_8888, 0,
- mFGSurfaceControl.get());
- ASSERT_TRUE(bufferLayer != nullptr);
+ createSurface(mClient, "BufferLayer", 45, 45, PIXEL_FORMAT_RGBA_8888, 0,
+ mFGSurfaceControl.get());
ASSERT_TRUE(bufferLayer->isValid());
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::BLACK, 30, 30));
asTransaction([&](Transaction& t) { t.show(bufferLayer); });
@@ -3606,10 +3697,8 @@
// which will crop the color layer.
TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentBufferBounds) {
sp<SurfaceControl> colorLayer =
- mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor,
- mFGSurfaceControl.get());
- ASSERT_TRUE(colorLayer != nullptr);
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
ASSERT_TRUE(colorLayer->isValid());
asTransaction([&](Transaction& t) {
t.setColor(colorLayer, half3{0, 0, 0});
@@ -3629,15 +3718,12 @@
// Verify a boundless color layer will fill its parent bounds. The parent has no buffer but has
// a crop which will be used to crop the color layer.
TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentCropBounds) {
- sp<SurfaceControl> cropLayer =
- mClient->createSurface(String8("CropLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- 0 /* flags */, mFGSurfaceControl.get());
- ASSERT_TRUE(cropLayer != nullptr);
+ sp<SurfaceControl> cropLayer = createSurface(mClient, "CropLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ 0 /* flags */, mFGSurfaceControl.get());
ASSERT_TRUE(cropLayer->isValid());
sp<SurfaceControl> colorLayer =
- mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get());
- ASSERT_TRUE(colorLayer != nullptr);
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get());
ASSERT_TRUE(colorLayer->isValid());
asTransaction([&](Transaction& t) {
t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
@@ -3661,10 +3747,8 @@
// Verify for boundless layer with no children, their transforms have no effect.
TEST_F(BoundlessLayerTest, BoundlessColorLayerTransformHasNoEffect) {
sp<SurfaceControl> colorLayer =
- mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor,
- mFGSurfaceControl.get());
- ASSERT_TRUE(colorLayer != nullptr);
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
ASSERT_TRUE(colorLayer->isValid());
asTransaction([&](Transaction& t) {
t.setPosition(colorLayer, 320, 320);
@@ -3686,20 +3770,16 @@
// Verify for boundless layer with children, their transforms have an effect.
TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) {
sp<SurfaceControl> boundlessLayerRightShift =
- mClient->createSurface(String8("BoundlessLayerRightShift"), 0, 0,
- PIXEL_FORMAT_RGBA_8888, 0 /* flags */, mFGSurfaceControl.get());
- ASSERT_TRUE(boundlessLayerRightShift != nullptr);
+ createSurface(mClient, "BoundlessLayerRightShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ 0 /* flags */, mFGSurfaceControl.get());
ASSERT_TRUE(boundlessLayerRightShift->isValid());
sp<SurfaceControl> boundlessLayerDownShift =
- mClient->createSurface(String8("BoundlessLayerLeftShift"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- 0 /* flags */, boundlessLayerRightShift.get());
- ASSERT_TRUE(boundlessLayerDownShift != nullptr);
+ createSurface(mClient, "BoundlessLayerLeftShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ 0 /* flags */, boundlessLayerRightShift.get());
ASSERT_TRUE(boundlessLayerDownShift->isValid());
sp<SurfaceControl> colorLayer =
- mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor,
- boundlessLayerDownShift.get());
- ASSERT_TRUE(colorLayer != nullptr);
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, boundlessLayerDownShift.get());
ASSERT_TRUE(colorLayer->isValid());
asTransaction([&](Transaction& t) {
t.setPosition(boundlessLayerRightShift, 32, 0);
@@ -3760,16 +3840,13 @@
// Verify for boundless root layers with children, their transforms have an effect.
TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) {
- sp<SurfaceControl> rootBoundlessLayer =
- mClient->createSurface(String8("RootBoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- 0 /* flags */);
- ASSERT_TRUE(rootBoundlessLayer != nullptr);
+ sp<SurfaceControl> rootBoundlessLayer = createSurface(mClient, "RootBoundlessLayer", 0, 0,
+ PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
ASSERT_TRUE(rootBoundlessLayer->isValid());
sp<SurfaceControl> colorLayer =
- mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor,
- rootBoundlessLayer.get());
- ASSERT_TRUE(colorLayer != nullptr);
+ createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor, rootBoundlessLayer.get());
+
ASSERT_TRUE(colorLayer->isValid());
asTransaction([&](Transaction& t) {
t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
@@ -3809,9 +3886,8 @@
TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
auto fgHandle = mFGSurfaceControl->getHandle();
- sp<SurfaceControl> child =
- mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
SurfaceComposerClient::Transaction().show(child).apply(true);
@@ -3825,9 +3901,8 @@
TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
auto fgHandle = mFGSurfaceControl->getHandle();
- sp<SurfaceControl> child =
- mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
SurfaceComposerClient::Transaction().show(child).apply(true);
@@ -3839,9 +3914,8 @@
}
TEST_F(ScreenCaptureTest, CaptureTransparent) {
- sp<SurfaceControl> child =
- mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
@@ -3859,9 +3933,9 @@
TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
auto fgHandle = mFGSurfaceControl->getHandle();
- sp<SurfaceControl> child =
- mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ ASSERT_NE(nullptr, child.get()) << "failed to create surface";
sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
fillSurfaceRGBA8(child, 200, 200, 200);
fillSurfaceRGBA8(relative, 100, 100, 100);
@@ -3882,12 +3956,10 @@
TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
auto fgHandle = mFGSurfaceControl->getHandle();
- sp<SurfaceControl> child =
- mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
- sp<SurfaceControl> relative =
- mClient->createSurface(String8("Relative surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
fillSurfaceRGBA8(relative, 100, 100, 100);
@@ -3916,9 +3988,8 @@
void SetUp() override {
LayerUpdateTest::SetUp();
- mChild =
- mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+ mFGSurfaceControl.get());
fillSurfaceRGBA8(mChild, 200, 200, 200);
SurfaceComposerClient::Transaction().show(mChild).apply(true);
@@ -3974,14 +4045,12 @@
TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
auto fgHandle = mFGSurfaceControl->getHandle();
- sp<SurfaceControl> child =
- mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
- sp<SurfaceControl> grandchild =
- mClient->createSurface(String8("Grandchild surface"), 5, 5,
- PIXEL_FORMAT_RGBA_8888, 0, child.get());
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
fillSurfaceRGBA8(grandchild, 50, 50, 50);
SurfaceComposerClient::Transaction()
@@ -3998,9 +4067,8 @@
}
TEST_F(ScreenCaptureTest, CaptureChildOnly) {
- sp<SurfaceControl> child =
- mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
auto childHandle = child->getHandle();
@@ -4013,15 +4081,13 @@
}
TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
- sp<SurfaceControl> child =
- mClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888,
- 0, mFGSurfaceControl.get());
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
fillSurfaceRGBA8(child, 200, 200, 200);
auto childHandle = child->getHandle();
- sp<SurfaceControl> grandchild =
- mClient->createSurface(String8("Grandchild surface"), 5, 5,
- PIXEL_FORMAT_RGBA_8888, 0, child.get());
+ sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
fillSurfaceRGBA8(grandchild, 50, 50, 50);
SurfaceComposerClient::Transaction()
@@ -4040,9 +4106,8 @@
TEST_F(ScreenCaptureTest, CaptureCrop) {
sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
- sp<SurfaceControl> blueLayer =
- mClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888,
- 0, redLayer.get());
+ sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+ PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
@@ -4073,9 +4138,8 @@
TEST_F(ScreenCaptureTest, CaptureSize) {
sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
- sp<SurfaceControl> blueLayer =
- mClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888,
- 0, redLayer.get());
+ sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+ PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 356a880..16e0891 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -1214,7 +1214,7 @@
protected:
void SetUp() override {
TransactionTest::SetUp();
- mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
+ mChild = mComposerClient->createSurface(String8("Child surface"), 0, 0,
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceColor,
mFGSurfaceControl.get());
@@ -1222,6 +1222,7 @@
TransactionScope ts(*sFakeComposer);
ts.setColor(mChild,
{LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
+ ts.setCrop_legacy(mChild, Rect(0, 0, 10, 10));
}
sFakeComposer->runVSyncAndWait();
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 58f1e9c..42b7146 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -31,6 +31,8 @@
"EventThreadTest.cpp",
"LayerHistoryTest.cpp",
"SchedulerTest.cpp",
+ "SchedulerUtilsTest.cpp",
+ "TimeStatsTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplaySurface.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 6d3e715..5a6aa92 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -94,6 +94,10 @@
EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
EXPECT_CALL(*mPrimaryDispSync, getPeriod())
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
setupComposer(0);
@@ -140,7 +144,6 @@
sp<DisplayDevice> mDisplay;
sp<DisplayDevice> mExternalDisplay;
sp<mock::DisplaySurface> mDisplaySurface = new mock::DisplaySurface();
- renderengine::mock::Surface* mRenderSurface = new renderengine::mock::Surface();
mock::NativeWindow* mNativeWindow = new mock::NativeWindow();
sp<GraphicBuffer> mBuffer = new GraphicBuffer();
@@ -246,8 +249,6 @@
test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, DEFAULT_DISPLAY_ID,
false /* isVirtual */, true /* isPrimary */)
.setDisplaySurface(test->mDisplaySurface)
- .setRenderSurface(std::unique_ptr<renderengine::Surface>(
- test->mRenderSurface))
.setNativeWindow(test->mNativeWindow)
.setSecure(Derived::IS_SECURE)
.setPowerMode(Derived::INIT_POWER_MODE)
@@ -579,7 +580,8 @@
EXPECT_CALL(*test->mRenderEngine,
setupLayerBlending(true, false, false,
half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
- LayerProperties::COLOR[2], LayerProperties::COLOR[3])))
+ LayerProperties::COLOR[2], LayerProperties::COLOR[3]),
+ 0.0f))
.Times(1);
EXPECT_CALL(*test->mRenderEngine, createImage())
@@ -625,7 +627,8 @@
EXPECT_CALL(*test->mRenderEngine,
setupLayerBlending(true, false, true,
half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
- LayerProperties::COLOR[2], LayerProperties::COLOR[3])))
+ LayerProperties::COLOR[2], LayerProperties::COLOR[3]),
+ 0.0f))
.Times(1);
EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1);
@@ -689,7 +692,7 @@
EXPECT_CALL(*test->mRenderEngine,
setupLayerBlending(true, false, false,
half4(Base::COLOR[0], Base::COLOR[1], Base::COLOR[2],
- Base::COLOR[3])))
+ Base::COLOR[3]), 0.0f))
.Times(1);
EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1);
EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1);
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index e8c8894..34cee3e 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -135,7 +135,6 @@
sp<mock::GraphicBufferConsumer> mConsumer;
sp<mock::GraphicBufferProducer> mProducer;
surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
- renderengine::mock::Surface* mRenderSurface = nullptr;
};
DisplayTransactionTest::DisplayTransactionTest() {
@@ -342,22 +341,10 @@
EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
.WillOnce(Return(test->mNativeWindow));
- // For simplicity, we only expect to create a single render surface for
- // each test.
- ASSERT_TRUE(test->mRenderSurface == nullptr);
- test->mRenderSurface = new renderengine::mock::Surface();
- EXPECT_CALL(*test->mRenderEngine, createSurface())
- .WillOnce(Return(ByMove(
- std::unique_ptr<renderengine::Surface>(test->mRenderSurface))));
EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
-
- // Creating a DisplayDevice requires getting default dimensions from the
- // native window.
- EXPECT_CALL(*test->mRenderSurface, setAsync(static_cast<bool>(ASYNC))).Times(1);
- EXPECT_CALL(*test->mRenderSurface, setCritical(static_cast<bool>(CRITICAL))).Times(1);
}
static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
@@ -1285,7 +1272,7 @@
// Insert display data so that the HWC thinks it created the virtual display.
const auto displayId = Case::Display::DISPLAY_ID::get();
ASSERT_TRUE(displayId);
- mFlinger.mutableHwcDisplayData()[*displayId] = {};
+ mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
setupNewDisplayDeviceInternalTest<Case>();
}
@@ -1798,7 +1785,7 @@
// A virtual display is set up but is removed from the current state.
const auto displayId = Case::Display::DISPLAY_ID::get();
ASSERT_TRUE(displayId);
- mFlinger.mutableHwcDisplayData()[*displayId] = {};
+ mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
Case::Display::injectHwcDisplay(this);
auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
existing.inject();
@@ -1946,12 +1933,10 @@
// A display is set up
auto nativeWindow = new mock::NativeWindow();
auto displaySurface = new mock::DisplaySurface();
- auto renderSurface = new renderengine::mock::Surface();
sp<GraphicBuffer> buf = new GraphicBuffer();
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.setNativeWindow(nativeWindow);
display.setDisplaySurface(displaySurface);
- display.setRenderSurface(std::unique_ptr<renderengine::Surface>(renderSurface));
// Setup injection expections
EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
.WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
@@ -1992,12 +1977,10 @@
// A display is set up
auto nativeWindow = new mock::NativeWindow();
auto displaySurface = new mock::DisplaySurface();
- auto renderSurface = new renderengine::mock::Surface();
sp<GraphicBuffer> buf = new GraphicBuffer();
auto display = Case::Display::makeFakeExistingDisplayInjector(this);
display.setNativeWindow(nativeWindow);
display.setDisplaySurface(displaySurface);
- display.setRenderSurface(std::unique_ptr<renderengine::Surface>(renderSurface));
// Setup injection expections
EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
.WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
@@ -2570,14 +2553,23 @@
*/
// Used when we simulate a display that supports doze.
+template <typename Display>
struct DozeIsSupportedVariant {
static constexpr bool DOZE_SUPPORTED = true;
static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
IComposerClient::PowerMode::DOZE;
static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
IComposerClient::PowerMode::DOZE_SUSPEND;
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>(
+ {Hwc2::DisplayCapability::DOZE})),
+ Return(Error::NONE)));
+ }
};
+template <typename Display>
// Used when we simulate a display that does not support doze.
struct DozeNotSupportedVariant {
static constexpr bool DOZE_SUPPORTED = false;
@@ -2585,6 +2577,12 @@
IComposerClient::PowerMode::ON;
static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
IComposerClient::PowerMode::ON;
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+ Return(Error::NONE)));
+ }
};
struct EventThreadBaseSupportedVariant {
@@ -2841,17 +2839,19 @@
// A sample configuration for the primary display.
// In addition to having event thread support, we emulate doze support.
template <typename TransitionVariant>
-using PrimaryDisplayPowerCase = DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant,
- EventThreadIsSupportedVariant,
- DispSyncIsSupportedVariant, TransitionVariant>;
+using PrimaryDisplayPowerCase =
+ DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>,
+ EventThreadIsSupportedVariant, DispSyncIsSupportedVariant,
+ TransitionVariant>;
// A sample configuration for the external display.
// In addition to not having event thread support, we emulate not having doze
// support.
template <typename TransitionVariant>
-using ExternalDisplayPowerCase = DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant,
- EventThreadNotSupportedVariant,
- DispSyncNotSupportedVariant, TransitionVariant>;
+using ExternalDisplayPowerCase =
+ DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
+ EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
+ TransitionVariant>;
class SetPowerModeInternalTest : public DisplayTransactionTest {
public:
@@ -2873,6 +2873,7 @@
// --------------------------------------------------------------------
// Preconditions
+ Case::Doze::setupComposerCallExpectations(this);
auto display =
Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
Case::setInitialPrimaryHWVsyncEnabled(this,
@@ -2931,7 +2932,7 @@
// Insert display data so that the HWC thinks it created the virtual display.
const auto displayId = Case::Display::DISPLAY_ID::get();
ASSERT_TRUE(displayId);
- mFlinger.mutableHwcDisplayData()[*displayId] = {};
+ mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
// A virtual display device is set up
Case::Display::injectHwcDisplay(this);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index b518f10..3fb1a6e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -85,11 +85,13 @@
// Because the counter was incremented, the elements were inserted into different
// containers.
+ // We expect the get method to access the slot at the current counter of the index
+ // is 0.
const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
EXPECT_EQ(1, testMap1.size());
- auto element = testMap1.find("TestLayer0");
- EXPECT_EQ("TestLayer0", element->first);
- EXPECT_EQ(0, element->second);
+ auto element = testMap1.find("TestLayer3");
+ EXPECT_EQ("TestLayer3", element->first);
+ EXPECT_EQ(3, element->second);
const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(1);
EXPECT_EQ(2, testMap2.size());
@@ -102,9 +104,9 @@
const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(2);
EXPECT_EQ(1, testMap3.size());
- element = testMap3.find("TestLayer3");
- EXPECT_EQ("TestLayer3", element->first);
- EXPECT_EQ(3, element->second);
+ element = testMap3.find("TestLayer0");
+ EXPECT_EQ("TestLayer0", element->first);
+ EXPECT_EQ(0, element->second);
// Testing accessing object at an empty container will return an empty map.
const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(3);
@@ -113,7 +115,6 @@
TEST_F(LayerHistoryTest, clearTheMap) {
mLayerHistory->insert("TestLayer0", 0);
- mLayerHistory->incrementCounter();
const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
EXPECT_EQ(1, testMap1.size());
@@ -121,6 +122,7 @@
EXPECT_EQ("TestLayer0", element->first);
EXPECT_EQ(0, element->second);
+ mLayerHistory->incrementCounter();
// The array currently contains 30 elements.
for (int i = 1; i < 30; ++i) {
mLayerHistory->insert("TestLayer0", i);
@@ -140,5 +142,33 @@
EXPECT_EQ(testMap3.end(), element);
}
+TEST_F(LayerHistoryTest, testingGet) {
+ // The array currently contains 30 elements.
+ for (int i = 0; i < 30; ++i) {
+ const auto name = "TestLayer" + std::to_string(i);
+ mLayerHistory->insert(name, i);
+ mLayerHistory->incrementCounter();
+ }
+
+ // The counter should be set to 0, and the map at 0 should be cleared.
+ const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0);
+ EXPECT_EQ(0, testMap1.size());
+
+ // This should return (ARRAY_SIZE + (counter - 3)) % ARRAY_SIZE
+ const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(3);
+ EXPECT_EQ(1, testMap2.size());
+ auto element = testMap2.find("TestLayer27");
+ EXPECT_EQ("TestLayer27", element->first);
+ EXPECT_EQ(27, element->second);
+
+ // If the user gives an out of bound index, we should mod it with ARRAY_SIZE first,
+ // so requesting element 40 would be the same as requesting element 10.
+ const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(40);
+ EXPECT_EQ(1, testMap3.size());
+ element = testMap3.find("TestLayer20");
+ EXPECT_EQ("TestLayer20", element->first);
+ EXPECT_EQ(20, element->second);
+}
+
} // namespace
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index be91a9c..d0cf1b7 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -184,6 +184,5 @@
EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
}
-
} // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
new file mode 100644
index 0000000..5865579
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <array>
+
+#include "Scheduler/SchedulerUtils.h"
+
+namespace android {
+namespace scheduler {
+
+class SchedulerUtilsTest : public testing::Test {
+public:
+ SchedulerUtilsTest() = default;
+ ~SchedulerUtilsTest() override = default;
+};
+
+namespace {
+TEST_F(SchedulerUtilsTest, calculate_mean) {
+ std::array<int64_t, 30> testArray{};
+ // Calling the function on empty array returns 0.
+ EXPECT_EQ(0, calculate_mean(testArray));
+
+ testArray[0] = 33;
+ EXPECT_EQ(1, calculate_mean(testArray));
+ testArray[1] = 33;
+ testArray[2] = 33;
+ EXPECT_EQ(3, calculate_mean(testArray));
+ testArray[3] = 42;
+ EXPECT_EQ(4, calculate_mean(testArray));
+ testArray[4] = 33;
+ EXPECT_EQ(5, calculate_mean(testArray));
+ testArray[5] = 42;
+ EXPECT_EQ(7, calculate_mean(testArray));
+ for (int i = 6; i < 30; i++) {
+ testArray[i] = 33;
+ }
+ EXPECT_EQ(33, calculate_mean(testArray));
+}
+
+TEST_F(SchedulerUtilsTest, calculate_median) {
+ std::vector<int64_t> testVector;
+ // Calling the function on empty vector returns 0.
+ EXPECT_EQ(0, calculate_median(&testVector));
+
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_median(&testVector));
+ testVector.push_back(33);
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_median(&testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_median(&testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_median(&testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_median(&testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_median(&testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(42, calculate_median(&testVector));
+ testVector.push_back(60);
+ EXPECT_EQ(42, calculate_median(&testVector));
+ testVector.push_back(60);
+ EXPECT_EQ(42, calculate_median(&testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(42, calculate_median(&testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(42, calculate_median(&testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_median(&testVector));
+}
+
+TEST_F(SchedulerUtilsTest, calculate_mode) {
+ std::vector<int64_t> testVector;
+ // Calling the function on empty vector returns 0.
+ EXPECT_EQ(0, calculate_mode(testVector));
+
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(33);
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(60);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(60);
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(33);
+ // 5 occurences of 33.
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(42);
+ // 5 occurences of 33, 5 occurences of 42. We choose the first one.
+ EXPECT_EQ(33, calculate_mode(testVector));
+ testVector.push_back(42);
+ // 5 occurences of 33, 6 occurences of 42.
+ EXPECT_EQ(42, calculate_mode(testVector));
+ testVector.push_back(42);
+ // 5 occurences of 33, 7 occurences of 42.
+ EXPECT_EQ(42, calculate_mode(testVector));
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index a519f1f..6d4f7ef 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -28,6 +28,8 @@
#include "SurfaceFlingerFactory.h"
#include "SurfaceInterceptor.h"
+#include "TimeStats/TimeStats.h"
+
namespace android {
class EventThread;
@@ -131,6 +133,11 @@
return nullptr;
}
+ std::unique_ptr<TimeStats> createTimeStats() override {
+ // TODO: Use test-fixture controlled factory
+ return std::make_unique<TimeStats>();
+ }
+
using CreateBufferQueueFunction =
std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
sp<IGraphicBufferConsumer>* /* outConsumer */,
@@ -474,11 +481,6 @@
return *this;
}
- auto& setRenderSurface(std::unique_ptr<renderengine::Surface> renderSurface) {
- mCreationArgs.renderSurface = std::move(renderSurface);
- return *this;
- }
-
auto& setSecure(bool secure) {
mCreationArgs.isSecure = secure;
return *this;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
new file mode 100644
index 0000000..186ed79
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -0,0 +1,513 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gtest/gtest.h>
+
+#include <log/log.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <random>
+
+#include "TimeStats/TimeStats.h"
+
+using namespace android::surfaceflinger;
+using namespace google::protobuf;
+
+namespace android {
+namespace {
+
+// clang-format off
+#define FMT_PROTO true
+#define FMT_STRING false
+#define LAYER_ID_0 0
+#define LAYER_ID_1 1
+#define LAYER_ID_INVALID -1
+#define NUM_LAYERS 1
+#define NUM_LAYERS_INVALID "INVALID"
+
+enum InputCommand : int32_t {
+ ENABLE = 0,
+ DISABLE = 1,
+ CLEAR = 2,
+ DUMP_ALL = 3,
+ DUMP_MAXLAYERS_1 = 4,
+ DUMP_MAXLAYERS_INVALID = 5,
+ INPUT_COMMAND_BEGIN = ENABLE,
+ INPUT_COMMAND_END = DUMP_MAXLAYERS_INVALID,
+ INPUT_COMMAND_RANGE = INPUT_COMMAND_END - INPUT_COMMAND_BEGIN + 1,
+};
+
+enum TimeStamp : int32_t {
+ POST = 0,
+ ACQUIRE = 1,
+ ACQUIRE_FENCE = 2,
+ LATCH = 3,
+ DESIRED = 4,
+ PRESENT = 5,
+ PRESENT_FENCE = 6,
+ TIME_STAMP_BEGIN = POST,
+ TIME_STAMP_END = PRESENT,
+ TIME_STAMP_RANGE = TIME_STAMP_END - TIME_STAMP_BEGIN + 1,
+};
+
+static const TimeStamp NORMAL_SEQUENCE[] = {
+ TimeStamp::POST,
+ TimeStamp::ACQUIRE,
+ TimeStamp::LATCH,
+ TimeStamp::DESIRED,
+ TimeStamp::PRESENT,
+};
+
+static const TimeStamp NORMAL_SEQUENCE_2[] = {
+ TimeStamp::POST,
+ TimeStamp::ACQUIRE_FENCE,
+ TimeStamp::LATCH,
+ TimeStamp::DESIRED,
+ TimeStamp::PRESENT_FENCE,
+};
+
+static const TimeStamp UNORDERED_SEQUENCE[] = {
+ TimeStamp::ACQUIRE,
+ TimeStamp::LATCH,
+ TimeStamp::POST,
+ TimeStamp::DESIRED,
+ TimeStamp::PRESENT,
+};
+
+static const TimeStamp INCOMPLETE_SEQUENCE[] = {
+ TimeStamp::POST,
+};
+// clang-format on
+
+class TimeStatsTest : public testing::Test {
+public:
+ TimeStatsTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ ~TimeStatsTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ }
+
+ std::string inputCommand(InputCommand cmd, bool useProto);
+
+ void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts);
+
+ int32_t genRandomInt32(int32_t begin, int32_t end);
+
+ template <size_t N>
+ void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
+ nsecs_t ts) {
+ for (size_t i = 0; i < N; i++, ts += 1000000) {
+ setTimeStamp(sequence[i], id, frameNumber, ts);
+ }
+ }
+
+ std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
+ std::unique_ptr<TimeStats> mTimeStats = std::make_unique<TimeStats>();
+};
+
+std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
+ size_t index = 0;
+ String8 result;
+ Vector<String16> args;
+
+ switch (cmd) {
+ case InputCommand::ENABLE:
+ args.push_back(String16("-enable"));
+ break;
+ case InputCommand::DISABLE:
+ args.push_back(String16("-disable"));
+ break;
+ case InputCommand::CLEAR:
+ args.push_back(String16("-clear"));
+ break;
+ case InputCommand::DUMP_ALL:
+ args.push_back(String16("-dump"));
+ break;
+ case InputCommand::DUMP_MAXLAYERS_1:
+ args.push_back(String16("-dump"));
+ args.push_back(String16("-maxlayers"));
+ args.push_back(String16(std::to_string(NUM_LAYERS).c_str()));
+ break;
+ case InputCommand::DUMP_MAXLAYERS_INVALID:
+ args.push_back(String16("-dump"));
+ args.push_back(String16("-maxlayers"));
+ args.push_back(String16(NUM_LAYERS_INVALID));
+ break;
+ default:
+ ALOGD("Invalid control command");
+ }
+
+ EXPECT_NO_FATAL_FAILURE(mTimeStats->parseArgs(useProto, args, index, result));
+ return std::string(result.string(), result.size());
+}
+
+static std::string genLayerName(int32_t layerID) {
+ return (layerID < 0 ? "invalid.dummy" : "com.dummy#") + std::to_string(layerID);
+}
+
+void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
+ switch (type) {
+ case TimeStamp::POST:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), ts));
+ break;
+ case TimeStamp::ACQUIRE:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts));
+ break;
+ case TimeStamp::ACQUIRE_FENCE:
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setAcquireFence(id, frameNumber, std::make_shared<FenceTime>(ts)));
+ break;
+ case TimeStamp::LATCH:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setLatchTime(id, frameNumber, ts));
+ break;
+ case TimeStamp::DESIRED:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts));
+ break;
+ case TimeStamp::PRESENT:
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts));
+ break;
+ case TimeStamp::PRESENT_FENCE:
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts)));
+ break;
+ default:
+ ALOGD("Invalid timestamp type");
+ }
+}
+
+int32_t TimeStatsTest::genRandomInt32(int32_t begin, int32_t end) {
+ std::uniform_int_distribution<int32_t> distr(begin, end);
+ return distr(mRandomEngine);
+}
+
+TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+ ASSERT_TRUE(mTimeStats->isEnabled());
+
+ EXPECT_TRUE(inputCommand(InputCommand::DISABLE, FMT_STRING).empty());
+ ASSERT_FALSE(mTimeStats->isEnabled());
+}
+
+TEST_F(TimeStatsTest, canIncreaseGlobalStats) {
+ constexpr size_t TOTAL_FRAMES = 5;
+ constexpr size_t MISSED_FRAMES = 4;
+ constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
+
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ for (size_t i = 0; i < TOTAL_FRAMES; i++) {
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementTotalFrames());
+ }
+ for (size_t i = 0; i < MISSED_FRAMES; i++) {
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
+ }
+ for (size_t i = 0; i < CLIENT_COMPOSITION_FRAMES; i++) {
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
+ }
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_TRUE(globalProto.has_total_frames());
+ EXPECT_EQ(TOTAL_FRAMES, globalProto.total_frames());
+ ASSERT_TRUE(globalProto.has_missed_frames());
+ EXPECT_EQ(MISSED_FRAMES, globalProto.missed_frames());
+ ASSERT_TRUE(globalProto.has_client_composition_frames());
+ EXPECT_EQ(CLIENT_COMPOSITION_FRAMES, globalProto.client_composition_frames());
+}
+
+TEST_F(TimeStatsTest, canInsertGlobalPresentToPresent) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(2000000)));
+
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000)));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000)));
+
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_OFF));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(6000000)));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(8000000)));
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.present_to_present_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto = globalProto.present_to_present().Get(0);
+ EXPECT_EQ(1, histogramProto.frame_count());
+ EXPECT_EQ(2, histogramProto.time_millis());
+}
+
+TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_layer_name());
+ EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+ ASSERT_EQ(6, layerProto.deltas_size());
+ for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
+ ASSERT_EQ(1, deltaProto.histograms_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+ EXPECT_EQ(1, histogramProto.frame_count());
+ if ("post2acquire" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else if ("post2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(4, histogramProto.time_millis());
+ } else if ("acquire2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(3, histogramProto.time_millis());
+ } else if ("latch2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(2, histogramProto.time_millis());
+ } else if ("desired2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else if ("present2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else {
+ FAIL() << "Unknown delta_name: " << deltaProto.delta_name();
+ }
+ }
+}
+
+TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_INVALID, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_INVALID, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canInsertMultipleLayersTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_EQ(2, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canInsertUnorderedLayerTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(UNORDERED_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_layer_name());
+ EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+ ASSERT_EQ(6, layerProto.deltas_size());
+ for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
+ ASSERT_EQ(1, deltaProto.histograms_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0);
+ EXPECT_EQ(1, histogramProto.frame_count());
+ if ("post2acquire" == deltaProto.delta_name()) {
+ EXPECT_EQ(0, histogramProto.time_millis());
+ } else if ("post2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(2, histogramProto.time_millis());
+ } else if ("acquire2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(2, histogramProto.time_millis());
+ } else if ("latch2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(2, histogramProto.time_millis());
+ } else if ("desired2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else if ("present2present" == deltaProto.delta_name()) {
+ EXPECT_EQ(1, histogramProto.time_millis());
+ } else {
+ FAIL() << "Unknown delta_name: " << deltaProto.delta_name();
+ }
+ }
+}
+
+TEST_F(TimeStatsTest, canRemoveTimeRecord) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(0, 2));
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canRecoverFromIncompleteTimeRecordError) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ uint64_t frameNumber = 1;
+ nsecs_t ts = 1000000;
+ insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ for (size_t i = 0; i < TimeStats::MAX_NUM_TIME_RECORDS + 2; i++) {
+ frameNumber++;
+ ts += 1000000;
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, frameNumber, ts);
+ }
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, layerTimeStatsOnDestroy) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(0));
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(1, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canClearTimeStats) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementTotalFrames());
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(2000000)));
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+ EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ EXPECT_EQ(0, globalProto.total_frames());
+ EXPECT_EQ(0, globalProto.missed_frames());
+ EXPECT_EQ(0, globalProto.client_composition_frames());
+ EXPECT_EQ(0, globalProto.present_to_present_size());
+ EXPECT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 2000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 3, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(
+ globalProto.ParseFromString(inputCommand(InputCommand::DUMP_MAXLAYERS_1, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.stats_size());
+ const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0);
+ ASSERT_TRUE(layerProto.has_layer_name());
+ EXPECT_EQ(genLayerName(LAYER_ID_1), layerProto.layer_name());
+ ASSERT_TRUE(layerProto.has_total_frames());
+ EXPECT_EQ(2, layerProto.total_frames());
+}
+
+TEST_F(TimeStatsTest, canDumpWithInvalidMaxLayers) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(
+ inputCommand(InputCommand::DUMP_MAXLAYERS_INVALID, FMT_PROTO)));
+
+ ASSERT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, canSurviveMonkey) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ for (size_t i = 0; i < 10000000; ++i) {
+ const int32_t layerID = genRandomInt32(-1, 10);
+ const int32_t frameNumber = genRandomInt32(1, 10);
+ switch (genRandomInt32(0, 100)) {
+ case 0:
+ ALOGV("removeTimeRecord");
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(layerID, frameNumber));
+ continue;
+ case 1:
+ ALOGV("onDestroy");
+ ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(layerID));
+ continue;
+ }
+ TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
+ const int32_t ts = genRandomInt32(1, 1000000000);
+ ALOGV("type[%d], layerID[%d], frameNumber[%d], ts[%d]", type, layerID, frameNumber, ts);
+ setTimeStamp(type, layerID, frameNumber, ts);
+ }
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index c0395c0..5a46c26 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -113,6 +113,9 @@
MOCK_METHOD4(setLayerInfo, Error(Display, Layer, uint32_t, uint32_t));
MOCK_METHOD3(getRenderIntents, Error(Display, ColorMode, std::vector<RenderIntent>*));
MOCK_METHOD3(setLayerColorTransform, Error(Display, Layer, const float*));
+ MOCK_METHOD4(getDisplayedContentSamplingAttributes,
+ Error(Display, PixelFormat*, Dataspace*, uint8_t*));
+ MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
};
} // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
index af54df6..fbfbc3f 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp
@@ -26,9 +26,6 @@
RenderEngine::RenderEngine() = default;
RenderEngine::~RenderEngine() = default;
-Surface::Surface() = default;
-Surface::~Surface() = default;
-
Image::Image() = default;
Image::~Image() = default;
diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
index afca63a..a416808 100644
--- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
+++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h
@@ -23,7 +23,6 @@
#include <renderengine/LayerSettings.h>
#include <renderengine/Mesh.h>
#include <renderengine/RenderEngine.h>
-#include <renderengine/Surface.h>
#include <renderengine/Texture.h>
#include <ui/GraphicBuffer.h>
@@ -37,15 +36,12 @@
~RenderEngine() override;
MOCK_METHOD0(createFramebuffer, std::unique_ptr<Framebuffer>());
- MOCK_METHOD0(createSurface, std::unique_ptr<renderengine::Surface>());
MOCK_METHOD0(createImage, std::unique_ptr<renderengine::Image>());
MOCK_CONST_METHOD0(primeCache, void());
MOCK_METHOD1(dump, void(String8&));
MOCK_CONST_METHOD0(useNativeFenceSync, bool());
MOCK_CONST_METHOD0(useWaitSync, bool());
MOCK_CONST_METHOD0(isCurrent, bool());
- MOCK_METHOD1(setCurrentSurface, bool(const renderengine::Surface&));
- MOCK_METHOD0(resetCurrentSurface, void());
MOCK_METHOD0(flush, base::unique_fd());
MOCK_METHOD0(finish, bool());
MOCK_METHOD1(waitFence, bool(base::unique_fd*));
@@ -60,10 +56,11 @@
MOCK_CONST_METHOD0(checkErrors, void());
MOCK_METHOD4(setViewportAndProjection,
void(size_t, size_t, Rect, ui::Transform::orientation_flags));
- MOCK_METHOD4(setupLayerBlending, void(bool, bool, bool, const half4&));
+ MOCK_METHOD5(setupLayerBlending, void(bool, bool, bool, const half4&, float));
MOCK_METHOD1(setupLayerTexturing, void(const Texture&));
MOCK_METHOD0(setupLayerBlackedOut, void());
MOCK_METHOD4(setupFillWithColor, void(float, float, float, float));
+ MOCK_METHOD2(setupCornerRadiusCropSize, void(float, float));
MOCK_METHOD1(setColorTransform, void(const mat4&));
MOCK_METHOD1(setSaturationMatrix, void(const mat4&));
MOCK_METHOD0(disableTexturing, void());
@@ -82,23 +79,6 @@
ANativeWindowBuffer* const, base::unique_fd*));
};
-class Surface : public renderengine::Surface {
-public:
- Surface();
- ~Surface() override;
-
- MOCK_METHOD1(setCritical, void(bool));
- MOCK_METHOD1(setAsync, void(bool));
- MOCK_METHOD1(setNativeWindow, void(ANativeWindow*));
- MOCK_CONST_METHOD0(swapBuffers, void());
- MOCK_CONST_METHOD0(queryRedSize, int32_t());
- MOCK_CONST_METHOD0(queryGreenSize, int32_t());
- MOCK_CONST_METHOD0(queryBlueSize, int32_t());
- MOCK_CONST_METHOD0(queryAlphaSize, int32_t());
- MOCK_CONST_METHOD0(getWidth, int32_t());
- MOCK_CONST_METHOD0(getHeight, int32_t());
-};
-
class Image : public renderengine::Image {
public:
Image();
diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp
index b5e6bb4..0a59f29 100644
--- a/services/vr/bufferhubd/Android.bp
+++ b/services/vr/bufferhubd/Android.bp
@@ -75,5 +75,3 @@
name: "bufferhubd",
init_rc: ["bufferhubd.rc"],
}
-
-subdirs = ["tests"]
\ No newline at end of file
diff --git a/services/vr/bufferhubd/IBufferHub.cpp b/services/vr/bufferhubd/IBufferHub.cpp
index 022a9cc..5980873 100644
--- a/services/vr/bufferhubd/IBufferHub.cpp
+++ b/services/vr/bufferhubd/IBufferHub.cpp
@@ -30,7 +30,7 @@
uint32_t format, uint64_t usage,
uint64_t user_metadata_size) {
Parcel data, reply;
- status_t ret = NO_ERROR;
+ status_t ret = OK;
ret |= data.writeInterfaceToken(IBufferHub::getInterfaceDescriptor());
ret |= data.writeUint32(width);
ret |= data.writeUint32(height);
@@ -39,13 +39,13 @@
ret |= data.writeUint64(usage);
ret |= data.writeUint64(user_metadata_size);
- if (ret != NO_ERROR) {
+ if (ret != OK) {
ALOGE("BpBufferHub::createBuffer: failed to write into parcel");
return nullptr;
}
ret = remote()->transact(CREATE_BUFFER, data, &reply);
- if (ret == NO_ERROR) {
+ if (ret == OK) {
return interface_cast<IBufferClient>(reply.readStrongBinder());
} else {
ALOGE("BpBufferHub::createBuffer: failed to transact; errno=%d", ret);
@@ -56,18 +56,18 @@
status_t BpBufferHub::importBuffer(uint64_t token,
sp<IBufferClient>* outClient) {
Parcel data, reply;
- status_t ret = NO_ERROR;
+ status_t ret = OK;
ret |= data.writeInterfaceToken(IBufferHub::getInterfaceDescriptor());
ret |= data.writeUint64(token);
- if (ret != NO_ERROR) {
+ if (ret != OK) {
ALOGE("BpBufferHub::importBuffer: failed to write into parcel");
return ret;
}
ret = remote()->transact(IMPORT_BUFFER, data, &reply);
- if (ret == NO_ERROR) {
+ if (ret == OK) {
*outClient = interface_cast<IBufferClient>(reply.readStrongBinder());
- return NO_ERROR;
+ return OK;
} else {
ALOGE("BpBufferHub::importBuffer: failed to transact; errno=%d", ret);
return ret;
@@ -94,7 +94,7 @@
uint64_t token = data.readUint64();
sp<IBufferClient> client;
status_t ret = importBuffer(token, &client);
- if (ret == NO_ERROR) {
+ if (ret == OK) {
return reply->writeStrongBinder(IInterface::asBinder(client));
} else {
return ret;
diff --git a/services/vr/bufferhubd/buffer_channel.cpp b/services/vr/bufferhubd/buffer_channel.cpp
index 589b31a..cf072b6 100644
--- a/services/vr/bufferhubd/buffer_channel.cpp
+++ b/services/vr/bufferhubd/buffer_channel.cpp
@@ -52,8 +52,7 @@
buffer_id(), /*consumer_count=*/0, buffer_node_->buffer_desc().width,
buffer_node_->buffer_desc().height, buffer_node_->buffer_desc().layers,
buffer_node_->buffer_desc().format, buffer_node_->buffer_desc().usage,
- /*pending_count=*/0, /*state=*/0, /*signaled_mask=*/0,
- /*index=*/0);
+ /*state=*/0, /*signaled_mask=*/0, /*index=*/0);
}
void BufferChannel::HandleImpulse(Message& /*message*/) {
@@ -83,10 +82,13 @@
ATRACE_NAME("BufferChannel::OnImport");
ALOGD_IF(TRACE, "BufferChannel::OnImport: buffer=%d.", buffer_id());
+ BorrowedHandle ashmem_handle =
+ BorrowedHandle(buffer_node_->metadata().ashmem_fd().get());
+
// TODO(b/112057680) Move away from the GraphicBuffer-based IonBuffer.
return BufferTraits<BorrowedHandle>{
/*buffer_handle=*/buffer_node_->buffer_handle(),
- /*metadata_handle=*/buffer_node_->metadata().ashmem_handle().Borrow(),
+ /*metadata_handle=*/ashmem_handle,
/*id=*/buffer_id(),
/*client_state_mask=*/client_state_mask_,
/*metadata_size=*/buffer_node_->metadata().metadata_size(),
diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp
index b73c47d..f50d292 100644
--- a/services/vr/bufferhubd/buffer_hub.cpp
+++ b/services/vr/bufferhubd/buffer_hub.cpp
@@ -56,8 +56,6 @@
stream << " ";
stream << std::setw(10) << "Usage";
stream << " ";
- stream << std::setw(9) << "Pending";
- stream << " ";
stream << std::setw(18) << "State";
stream << " ";
stream << std::setw(18) << "Signaled";
@@ -90,8 +88,6 @@
stream << std::setw(8) << info.usage;
stream << std::dec << std::setfill(' ');
stream << " ";
- stream << std::setw(9) << info.pending_count;
- stream << " ";
stream << "0x" << std::hex << std::setfill('0');
stream << std::setw(16) << info.state;
stream << " ";
diff --git a/services/vr/bufferhubd/buffer_hub_binder.cpp b/services/vr/bufferhubd/buffer_hub_binder.cpp
index afd34aa..580433e 100644
--- a/services/vr/bufferhubd/buffer_hub_binder.cpp
+++ b/services/vr/bufferhubd/buffer_hub_binder.cpp
@@ -25,7 +25,7 @@
String16(getServiceName()), service,
/*allowIsolated =*/false,
/*dump flags =*/IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
- if (result != NO_ERROR) {
+ if (result != OK) {
ALOGE("Publishing bufferhubd failed with error %d", result);
return result;
}
@@ -62,7 +62,7 @@
}
fclose(out);
- return NO_ERROR;
+ return OK;
}
status_t BufferHubBinderService::registerToken(
@@ -72,7 +72,7 @@
} while (token_map_.find(*outToken) != token_map_.end());
token_map_.emplace(*outToken, node);
- return NO_ERROR;
+ return OK;
}
sp<IBufferClient> BufferHubBinderService::createBuffer(
@@ -122,7 +122,7 @@
token_map_.erase(iter);
client_list_.push_back(client);
- return NO_ERROR;
+ return OK;
}
} // namespace dvr
diff --git a/services/vr/bufferhubd/bufferhubd.cpp b/services/vr/bufferhubd/bufferhubd.cpp
index 3e10b26..e44cc2a 100644
--- a/services/vr/bufferhubd/bufferhubd.cpp
+++ b/services/vr/bufferhubd/bufferhubd.cpp
@@ -41,7 +41,7 @@
dispatcher->AddService(pdx_service);
ret = android::dvr::BufferHubBinderService::start(pdx_service);
- CHECK_ERROR(ret != android::NO_ERROR, error,
+ CHECK_ERROR(ret != android::OK, error,
"Failed to create bufferhub binder service\n");
ret = dvrSetSchedulerClass(0, "graphics");
diff --git a/services/vr/bufferhubd/consumer_channel.cpp b/services/vr/bufferhubd/consumer_channel.cpp
index 98ef917..f936e95 100644
--- a/services/vr/bufferhubd/consumer_channel.cpp
+++ b/services/vr/bufferhubd/consumer_channel.cpp
@@ -153,11 +153,10 @@
}
}
-bool ConsumerChannel::OnProducerPosted() {
+void ConsumerChannel::OnProducerPosted() {
acquired_ = false;
released_ = false;
SignalAvailable();
- return true;
}
void ConsumerChannel::OnProducerClosed() {
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_hub.h b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
index e47ffa3..676617d 100644
--- a/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h
@@ -52,7 +52,6 @@
uint32_t layer_count = 0;
uint32_t format = 0;
uint64_t usage = 0;
- size_t pending_count = 0;
uint64_t state = 0;
uint64_t signaled_mask = 0;
uint64_t index = 0;
@@ -63,8 +62,7 @@
BufferInfo(int id, size_t consumer_count, uint32_t width, uint32_t height,
uint32_t layer_count, uint32_t format, uint64_t usage,
- size_t pending_count, uint64_t state, uint64_t signaled_mask,
- uint64_t index)
+ uint64_t state, uint64_t signaled_mask, uint64_t index)
: id(id),
type(kProducerType),
consumer_count(consumer_count),
@@ -73,7 +71,6 @@
layer_count(layer_count),
format(format),
usage(usage),
- pending_count(pending_count),
state(state),
signaled_mask(signaled_mask),
index(index) {}
diff --git a/services/vr/bufferhubd/include/private/dvr/consumer_channel.h b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
index 3298529..de0f23c 100644
--- a/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h
@@ -26,7 +26,7 @@
uint64_t client_state_mask() const { return client_state_mask_; }
BufferInfo GetBufferInfo() const override;
- bool OnProducerPosted();
+ void OnProducerPosted();
void OnProducerClosed();
private:
diff --git a/services/vr/bufferhubd/include/private/dvr/producer_channel.h b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
index 3ad9c70..4734439 100644
--- a/services/vr/bufferhubd/include/private/dvr/producer_channel.h
+++ b/services/vr/bufferhubd/include/private/dvr/producer_channel.h
@@ -62,8 +62,7 @@
pdx::Status<void> OnConsumerRelease(Message& message,
LocalFence release_fence);
- void DecrementPendingConsumers();
- void OnConsumerOrphaned(ConsumerChannel* channel);
+ void OnConsumerOrphaned(const uint64_t& consumer_state_mask);
void AddConsumer(ConsumerChannel* channel);
void RemoveConsumer(ConsumerChannel* channel);
@@ -74,9 +73,6 @@
private:
std::vector<ConsumerChannel*> consumer_channels_;
- // This counts the number of consumers left to process this buffer. If this is
- // zero then the producer can re-acquire ownership.
- int pending_consumers_;
IonBuffer buffer_;
@@ -91,7 +87,6 @@
// highest bit is reserved for the producer and should not be set.
uint64_t orphaned_consumer_bit_mask_{0ULL};
- bool producer_owns_;
LocalFence post_fence_;
LocalFence returned_fence_;
size_t user_metadata_size_; // size of user requested buffer buffer size.
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index c6e8ea9..ab1d94c 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -56,8 +56,6 @@
uint64_t usage, size_t user_metadata_size,
int* error)
: BufferHubChannel(service, channel_id, channel_id, kProducerType),
- pending_consumers_(0),
- producer_owns_(true),
user_metadata_size_(user_metadata_size),
metadata_buf_size_(BufferHubDefs::kMetadataHeaderSize +
user_metadata_size) {
@@ -184,7 +182,7 @@
return BufferInfo(buffer_id(), consumer_channels_.size(), buffer_.width(),
buffer_.height(), buffer_.layer_count(), buffer_.format(),
- buffer_.usage(), pending_consumers_,
+ buffer_.usage(),
buffer_state_->load(std::memory_order_acquire),
signaled_mask, metadata_header_->queue_index);
}
@@ -255,16 +253,16 @@
// that release active_clients_bit_mask_ need to be visible here.
uint64_t current_active_clients_bit_mask =
active_clients_bit_mask_->load(std::memory_order_acquire);
- uint64_t client_state_mask = BufferHubDefs::FindNextAvailableClientStateMask(
- current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
- if (client_state_mask == 0ULL) {
- ALOGE(
- "ProducerChannel::CreateConsumer: reached the maximum mumber of "
- "consumers per producer: 63.");
+ uint64_t consumer_state_mask =
+ BufferHubDefs::FindNextAvailableClientStateMask(
+ current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
+ if (consumer_state_mask == 0ULL) {
+ ALOGE("%s: reached the maximum mumber of consumers per producer: 63.",
+ __FUNCTION__);
return ErrorStatus(E2BIG);
}
uint64_t updated_active_clients_bit_mask =
- current_active_clients_bit_mask | client_state_mask;
+ current_active_clients_bit_mask | consumer_state_mask;
// Set the updated value only if the current value stays the same as what was
// read before. If the comparison succeeds, update the value without
// reordering anything before or after this read-modify-write in the current
@@ -276,24 +274,24 @@
while (!active_clients_bit_mask_->compare_exchange_weak(
current_active_clients_bit_mask, updated_active_clients_bit_mask,
std::memory_order_acq_rel, std::memory_order_acquire)) {
- ALOGE("Current active clients bit mask is changed to %" PRIx64
+ ALOGE("%s: Current active clients bit mask is changed to %" PRIx64
", which was expected to be %" PRIx64
". Trying to generate a new client state mask to resolve race "
"condition.",
- updated_active_clients_bit_mask, current_active_clients_bit_mask);
- client_state_mask = BufferHubDefs::FindNextAvailableClientStateMask(
+ __FUNCTION__, updated_active_clients_bit_mask,
+ current_active_clients_bit_mask);
+ consumer_state_mask = BufferHubDefs::FindNextAvailableClientStateMask(
current_active_clients_bit_mask | orphaned_consumer_bit_mask_);
- if (client_state_mask == 0ULL) {
- ALOGE(
- "ProducerChannel::CreateConsumer: reached the maximum mumber of "
- "consumers per producer: 63.");
+ if (consumer_state_mask == 0ULL) {
+ ALOGE("%s: reached the maximum mumber of consumers per producer: %d.",
+ __FUNCTION__, (BufferHubDefs::kMaxNumberOfClients - 1));
return ErrorStatus(E2BIG);
}
updated_active_clients_bit_mask =
- current_active_clients_bit_mask | client_state_mask;
+ current_active_clients_bit_mask | consumer_state_mask;
}
- return {client_state_mask};
+ return {consumer_state_mask};
}
void ProducerChannel::RemoveConsumerClientMask(uint64_t consumer_state_mask) {
@@ -311,17 +309,14 @@
Status<RemoteChannelHandle> ProducerChannel::CreateConsumer(
Message& message, uint64_t consumer_state_mask) {
- ATRACE_NAME("ProducerChannel::CreateConsumer");
- ALOGD_IF(TRACE,
- "ProducerChannel::CreateConsumer: buffer_id=%d, producer_owns=%d",
- buffer_id(), producer_owns_);
+ ATRACE_NAME(__FUNCTION__);
+ ALOGD_IF(TRACE, "%s: buffer_id=%d", __FUNCTION__, buffer_id());
int channel_id;
auto status = message.PushChannel(0, nullptr, &channel_id);
if (!status) {
- ALOGE(
- "ProducerChannel::CreateConsumer: Failed to push consumer channel: %s",
- status.GetErrorMessage().c_str());
+ ALOGE("%s: Failed to push consumer channel: %s", __FUNCTION__,
+ status.GetErrorMessage().c_str());
RemoveConsumerClientMask(consumer_state_mask);
return ErrorStatus(ENOMEM);
}
@@ -331,22 +326,51 @@
shared_from_this());
const auto channel_status = service()->SetChannel(channel_id, consumer);
if (!channel_status) {
- ALOGE(
- "ProducerChannel::CreateConsumer: failed to set new consumer channel: "
- "%s",
- channel_status.GetErrorMessage().c_str());
+ ALOGE("%s: failed to set new consumer channel: %s.", __FUNCTION__,
+ channel_status.GetErrorMessage().c_str());
RemoveConsumerClientMask(consumer_state_mask);
return ErrorStatus(ENOMEM);
}
uint64_t current_buffer_state =
buffer_state_->load(std::memory_order_acquire);
- if (!producer_owns_ &&
- (BufferHubDefs::IsBufferPosted(current_buffer_state) ||
- BufferHubDefs::IsBufferAcquired(current_buffer_state))) {
- // Signal the new consumer when adding it to a posted producer.
- if (consumer->OnProducerPosted())
- pending_consumers_++;
+ if (BufferHubDefs::IsBufferReleased(current_buffer_state) ||
+ BufferHubDefs::AnyClientGained(current_buffer_state)) {
+ return {status.take()};
+ }
+
+ // Signal the new consumer when adding it to a posted producer.
+ bool update_buffer_state = true;
+ if (!BufferHubDefs::IsClientPosted(current_buffer_state,
+ consumer_state_mask)) {
+ uint64_t updated_buffer_state =
+ current_buffer_state ^
+ (consumer_state_mask & BufferHubDefs::kHighBitsMask);
+ while (!buffer_state_->compare_exchange_weak(
+ current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
+ std::memory_order_acquire)) {
+ ALOGI(
+ "%s: Failed to post to the new consumer. "
+ "Current buffer state was changed to %" PRIx64
+ " when trying to acquire the buffer and modify the buffer state to "
+ "%" PRIx64
+ ". About to try again if the buffer is still not gained nor fully "
+ "released.",
+ __FUNCTION__, current_buffer_state, updated_buffer_state);
+ if (BufferHubDefs::IsBufferReleased(current_buffer_state) ||
+ BufferHubDefs::AnyClientGained(current_buffer_state)) {
+ ALOGI("%s: buffer is gained or fully released, state=%" PRIx64 ".",
+ __FUNCTION__, current_buffer_state);
+ update_buffer_state = false;
+ break;
+ }
+ updated_buffer_state =
+ current_buffer_state ^
+ (consumer_state_mask & BufferHubDefs::kHighBitsMask);
+ }
+ }
+ if (update_buffer_state) {
+ consumer->OnProducerPosted();
}
return {status.take()};
@@ -366,10 +390,6 @@
LocalFence acquire_fence) {
ATRACE_NAME("ProducerChannel::OnProducerPost");
ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: buffer_id=%d", buffer_id());
- if (!producer_owns_) {
- ALOGE("ProducerChannel::OnProducerPost: Not in gained state!");
- return ErrorStatus(EBUSY);
- }
epoll_event event;
event.events = 0;
@@ -400,18 +420,13 @@
dummy_fence_count, buffer_id());
post_fence_ = std::move(acquire_fence);
- producer_owns_ = false;
// Signal any interested consumers. If there are none, the buffer will stay
// in posted state until a consumer comes online. This behavior guarantees
// that no frame is silently dropped.
- pending_consumers_ = 0;
for (auto consumer : consumer_channels_) {
- if (consumer->OnProducerPosted())
- pending_consumers_++;
+ consumer->OnProducerPosted();
}
- ALOGD_IF(TRACE, "ProducerChannel::OnProducerPost: %d pending consumers",
- pending_consumers_);
return {};
}
@@ -419,23 +434,8 @@
Status<LocalFence> ProducerChannel::OnProducerGain(Message& /*message*/) {
ATRACE_NAME("ProducerChannel::OnGain");
ALOGD_IF(TRACE, "ProducerChannel::OnGain: buffer_id=%d", buffer_id());
- if (producer_owns_) {
- ALOGE("ProducerChanneL::OnGain: Already in gained state: channel=%d",
- channel_id());
- return ErrorStatus(EALREADY);
- }
-
- // There are still pending consumers, return busy.
- if (pending_consumers_ > 0) {
- ALOGE(
- "ProducerChannel::OnGain: Producer (id=%d) is gaining a buffer that "
- "still has %d pending consumer(s).",
- buffer_id(), pending_consumers_);
- return ErrorStatus(EBUSY);
- }
ClearAvailable();
- producer_owns_ = true;
post_fence_.close();
return {std::move(returned_fence_)};
}
@@ -449,10 +449,12 @@
buffer_id());
uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+ if (!BufferHubDefs::IsClientGained(
+ buffer_state, BufferHubDefs::kFirstClientStateMask)) {
// Can only detach a BufferProducer when it's in gained state.
ALOGW(
- "ProducerChannel::OnProducerDetach: The buffer (id=%d, state=0x%" PRIx64
+ "ProducerChannel::OnProducerDetach: The buffer (id=%d, state=%"
+ PRIx64
") is not in gained state.",
buffer_id(), buffer_state);
return {};
@@ -485,12 +487,11 @@
const auto channel_status =
service()->SetChannel(channel_id, std::move(channel));
if (!channel_status) {
- // Technically, this should never fail, as we just pushed the channel. Note
- // that LOG_FATAL will be stripped out in non-debug build.
+ // Technically, this should never fail, as we just pushed the channel.
+ // Note that LOG_FATAL will be stripped out in non-debug build.
LOG_FATAL(
- "ProducerChannel::OnProducerDetach: Failed to set new detached buffer "
- "channel: %s.",
- channel_status.GetErrorMessage().c_str());
+ "ProducerChannel::OnProducerDetach: Failed to set new detached "
+ "buffer channel: %s.", channel_status.GetErrorMessage().c_str());
}
return status;
@@ -500,13 +501,9 @@
ATRACE_NAME("ProducerChannel::OnConsumerAcquire");
ALOGD_IF(TRACE, "ProducerChannel::OnConsumerAcquire: buffer_id=%d",
buffer_id());
- if (producer_owns_) {
- ALOGE("ProducerChannel::OnConsumerAcquire: Not in posted state!");
- return ErrorStatus(EBUSY);
- }
- // Return a borrowed fd to avoid unnecessary duplication of the underlying fd.
- // Serialization just needs to read the handle.
+ // Return a borrowed fd to avoid unnecessary duplication of the underlying
+ // fd. Serialization just needs to read the handle.
return {std::move(post_fence_)};
}
@@ -515,10 +512,6 @@
ATRACE_NAME("ProducerChannel::OnConsumerRelease");
ALOGD_IF(TRACE, "ProducerChannel::OnConsumerRelease: buffer_id=%d",
buffer_id());
- if (producer_owns_) {
- ALOGE("ProducerChannel::OnConsumerRelease: Not in acquired state!");
- return ErrorStatus(EBUSY);
- }
// Attempt to merge the fences if necessary.
if (release_fence) {
@@ -538,75 +531,53 @@
}
}
- DecrementPendingConsumers();
- if (pending_consumers_ == 0) {
- // Clear the producer bit atomically to transit into released state. This
- // has to done by BufferHub as it requries synchronization among all
- // consumers.
- BufferHubDefs::ModifyBufferState(buffer_state_,
- BufferHubDefs::kFirstClientBitMask, 0ULL);
- ALOGD_IF(TRACE,
- "ProducerChannel::OnConsumerRelease: releasing last consumer: "
- "buffer_id=%d state=%" PRIx64 ".",
- buffer_id(), buffer_state_->load(std::memory_order_acquire));
-
+ uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (BufferHubDefs::IsClientReleased(current_buffer_state,
+ ~orphaned_consumer_bit_mask_)) {
+ SignalAvailable();
if (orphaned_consumer_bit_mask_) {
ALOGW(
- "ProducerChannel::OnConsumerRelease: orphaned buffer detected "
- "during the this acquire/release cycle: id=%d orphaned=0x%" PRIx64
- " queue_index=%" PRIu64 ".",
- buffer_id(), orphaned_consumer_bit_mask_,
+ "%s: orphaned buffer detected during the this acquire/release cycle: "
+ "id=%d orphaned=0x%" PRIx64 " queue_index=%" PRIx64 ".",
+ __FUNCTION__, buffer_id(), orphaned_consumer_bit_mask_,
metadata_header_->queue_index);
orphaned_consumer_bit_mask_ = 0;
}
-
- SignalAvailable();
}
- ALOGE_IF(
- pending_consumers_ && BufferHubDefs::IsBufferReleased(
- buffer_state_->load(std::memory_order_acquire)),
- "ProducerChannel::OnConsumerRelease: buffer state inconsistent: "
- "pending_consumers=%d, buffer buffer is in releaed state.",
- pending_consumers_);
return {};
}
-void ProducerChannel::DecrementPendingConsumers() {
- if (pending_consumers_ == 0) {
- ALOGE("ProducerChannel::DecrementPendingConsumers: no pending consumer.");
- return;
+void ProducerChannel::OnConsumerOrphaned(const uint64_t& consumer_state_mask) {
+ // Remember the ignored consumer so that newly added consumer won't be
+ // taking the same state mask as this orphaned consumer.
+ ALOGE_IF(orphaned_consumer_bit_mask_ & consumer_state_mask,
+ "%s: Consumer (consumer_state_mask=%" PRIx64
+ ") is already orphaned.",
+ __FUNCTION__, consumer_state_mask);
+ orphaned_consumer_bit_mask_ |= consumer_state_mask;
+
+ uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (BufferHubDefs::IsClientReleased(current_buffer_state,
+ ~orphaned_consumer_bit_mask_)) {
+ SignalAvailable();
}
- --pending_consumers_;
- ALOGD_IF(TRACE,
- "ProducerChannel::DecrementPendingConsumers: buffer_id=%d %d "
- "consumers left",
- buffer_id(), pending_consumers_);
-}
-
-void ProducerChannel::OnConsumerOrphaned(ConsumerChannel* channel) {
- // Ignore the orphaned consumer.
- DecrementPendingConsumers();
-
- const uint64_t client_state_mask = channel->client_state_mask();
- ALOGE_IF(orphaned_consumer_bit_mask_ & client_state_mask,
- "ProducerChannel::OnConsumerOrphaned: Consumer "
- "(client_state_mask=%" PRIx64 ") is already orphaned.",
- client_state_mask);
- orphaned_consumer_bit_mask_ |= client_state_mask;
-
// Atomically clear the fence state bit as an orphaned consumer will never
- // signal a release fence. Also clear the buffer state as it won't be released
- // as well.
- fence_state_->fetch_and(~client_state_mask);
- BufferHubDefs::ModifyBufferState(buffer_state_, client_state_mask, 0ULL);
+ // signal a release fence.
+ fence_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
+
+ // Atomically set the buffer state of this consumer to released state.
+ buffer_state_->fetch_and(~consumer_state_mask, std::memory_order_release);
ALOGW(
- "ProducerChannel::OnConsumerOrphaned: detected new orphaned consumer "
- "buffer_id=%d client_state_mask=%" PRIx64 " queue_index=%" PRIu64
+ "%s: detected new orphaned consumer buffer_id=%d "
+ "consumer_state_mask=%" PRIx64 " queue_index=%" PRIx64
" buffer_state=%" PRIx64 " fence_state=%" PRIx64 ".",
- buffer_id(), client_state_mask, metadata_header_->queue_index,
+ __FUNCTION__, buffer_id(), consumer_state_mask,
+ metadata_header_->queue_index,
buffer_state_->load(std::memory_order_acquire),
fence_state_->load(std::memory_order_acquire));
}
@@ -620,45 +591,64 @@
std::find(consumer_channels_.begin(), consumer_channels_.end(), channel));
// Restore the consumer state bit and make it visible in other threads that
// acquire the active_clients_bit_mask_.
- active_clients_bit_mask_->fetch_and(~channel->client_state_mask(),
- std::memory_order_release);
+ uint64_t consumer_state_mask = channel->client_state_mask();
+ uint64_t current_active_clients_bit_mask =
+ active_clients_bit_mask_->load(std::memory_order_acquire);
+ uint64_t updated_active_clients_bit_mask =
+ current_active_clients_bit_mask & (~consumer_state_mask);
+ while (!active_clients_bit_mask_->compare_exchange_weak(
+ current_active_clients_bit_mask, updated_active_clients_bit_mask,
+ std::memory_order_acq_rel, std::memory_order_acquire)) {
+ ALOGI(
+ "%s: Failed to remove consumer state mask. Current active clients bit "
+ "mask is changed to %" PRIu64
+ " when trying to acquire and modify it to %" PRIu64
+ ". About to try again.",
+ __FUNCTION__, current_active_clients_bit_mask,
+ updated_active_clients_bit_mask);
+ updated_active_clients_bit_mask =
+ current_active_clients_bit_mask & (~consumer_state_mask);
+ }
- const uint64_t buffer_state = buffer_state_->load(std::memory_order_acquire);
- if (BufferHubDefs::IsBufferPosted(buffer_state) ||
- BufferHubDefs::IsBufferAcquired(buffer_state)) {
+ const uint64_t current_buffer_state =
+ buffer_state_->load(std::memory_order_acquire);
+ if (BufferHubDefs::IsClientPosted(current_buffer_state,
+ consumer_state_mask) ||
+ BufferHubDefs::IsClientAcquired(current_buffer_state,
+ consumer_state_mask)) {
// The consumer client is being destoryed without releasing. This could
// happen in corner cases when the consumer crashes. Here we mark it
// orphaned before remove it from producer.
- OnConsumerOrphaned(channel);
+ OnConsumerOrphaned(consumer_state_mask);
+ return;
}
- if (BufferHubDefs::IsBufferReleased(buffer_state) ||
- BufferHubDefs::IsBufferGained(buffer_state)) {
+ if (BufferHubDefs::IsClientReleased(current_buffer_state,
+ consumer_state_mask) ||
+ BufferHubDefs::AnyClientGained(current_buffer_state)) {
// The consumer is being close while it is suppose to signal a release
// fence. Signal the dummy fence here.
- if (fence_state_->load(std::memory_order_acquire) &
- channel->client_state_mask()) {
+ if (fence_state_->load(std::memory_order_acquire) & consumer_state_mask) {
epoll_event event;
event.events = EPOLLIN;
- event.data.u64 = channel->client_state_mask();
+ event.data.u64 = consumer_state_mask;
if (epoll_ctl(release_fence_fd_.Get(), EPOLL_CTL_MOD,
dummy_fence_fd_.Get(), &event) < 0) {
ALOGE(
- "ProducerChannel::RemoveConsumer: Failed to modify the shared "
- "release fence to include the dummy fence: %s",
- strerror(errno));
+ "%s: Failed to modify the shared release fence to include the "
+ "dummy fence: %s",
+ __FUNCTION__, strerror(errno));
return;
}
- ALOGW(
- "ProducerChannel::RemoveConsumer: signal dummy release fence "
- "buffer_id=%d",
- buffer_id());
+ ALOGW("%s: signal dummy release fence buffer_id=%d", __FUNCTION__,
+ buffer_id());
eventfd_write(dummy_fence_fd_.Get(), 1);
}
}
}
-// Returns true if the given parameters match the underlying buffer parameters.
+// Returns true if the given parameters match the underlying buffer
+// parameters.
bool ProducerChannel::CheckParameters(uint32_t width, uint32_t height,
uint32_t layer_count, uint32_t format,
uint64_t usage,
diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp
index 6b5027c..6b33f50 100644
--- a/services/vr/bufferhubd/producer_queue_channel.cpp
+++ b/services/vr/bufferhubd/producer_queue_channel.cpp
@@ -319,7 +319,12 @@
return ErrorStatus(EINVAL);
}
uint64_t buffer_state = producer_channel->buffer_state();
- if (!BufferHubDefs::IsBufferGained(buffer_state)) {
+ // TODO(b/112007999) add an atomic variable in metadata header in shared
+ // memory to indicate which client is the last producer of the buffer.
+ // Currently, the first client is the only producer to the buffer.
+ // Thus, it checks whether the first client gains the buffer below.
+ if (!BufferHubDefs::IsClientGained(buffer_state,
+ BufferHubDefs::kFirstClientBitMask)) {
// Rejects the request if the requested buffer is not in Gained state.
ALOGE(
"ProducerQueueChannel::InsertBuffer: The buffer (cid=%d, "
diff --git a/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp b/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
index 7c00fa6..94b422a 100644
--- a/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
+++ b/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
@@ -47,24 +47,24 @@
uint64_t token1 = 0ULL;
status_t ret = bufferClient->duplicate(&token1);
- EXPECT_EQ(ret, NO_ERROR);
+ EXPECT_EQ(ret, OK);
// Tokens should be unique even corresponding to the same buffer
uint64_t token2 = 0ULL;
ret = bufferClient->duplicate(&token2);
- EXPECT_EQ(ret, NO_ERROR);
+ EXPECT_EQ(ret, OK);
EXPECT_NE(token2, token1);
sp<IBufferClient> bufferClient1;
ret = service->importBuffer(token1, &bufferClient1);
- EXPECT_EQ(ret, NO_ERROR);
+ EXPECT_EQ(ret, OK);
ASSERT_THAT(bufferClient1, NotNull());
EXPECT_TRUE(bufferClient1->isValid());
// Consumes the token to keep the service clean
sp<IBufferClient> bufferClient2;
ret = service->importBuffer(token2, &bufferClient2);
- EXPECT_EQ(ret, NO_ERROR);
+ EXPECT_EQ(ret, OK);
ASSERT_THAT(bufferClient2, NotNull());
EXPECT_TRUE(bufferClient2->isValid());
}
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index 417460c..fb7932d 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -1016,7 +1016,7 @@
std::lock_guard<std::mutex> guard(mutex_);
if (callback_ != nullptr)
callback_->onVsync(kDefaultDisplayId, vsync_timestamp);
- return NO_ERROR;
+ return OK;
}
void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) {