Merge changes from topic "sfinput4"
* changes:
SurfaceFlinger Input: Correct screen magnification.
Remove inputflinger.rc as init file
SurfaceFlinger Input: Shrink frame by surfaceInsets.
InputDispatcher: Notify policy of focus changes.
Rework InputApplicationInfo
Replace InputWindowInfo#inputChannel with an IBinder token.
Add some tests for native input.
Forward input windows from SurfaceFlinger to InputDispatcher.
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/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..75b348c 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,11 @@
// 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.
+ static const int32_t kUnsetWorkSource = -1;
+
private:
IPCThreadState();
~IPCThreadState();
@@ -167,6 +176,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/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/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/renderengine/gl/GLES20RenderEngine.cpp b/libs/renderengine/gl/GLES20RenderEngine.cpp
index d35762d..b10b52b 100644
--- a/libs/renderengine/gl/GLES20RenderEngine.cpp
+++ b/libs/renderengine/gl/GLES20RenderEngine.cpp
@@ -240,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:
@@ -908,6 +877,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 6c50938..77dba62 100644
--- a/libs/renderengine/gl/GLES20RenderEngine.h
+++ b/libs/renderengine/gl/GLES20RenderEngine.h
@@ -112,6 +112,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.
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/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/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 78ab23a..162f391 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -352,9 +352,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 +383,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..5ffd8ad 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -336,6 +336,9 @@
}
PixelFormat BufferStateLayer::getPixelFormat() const {
+ if (!mActiveBuffer) {
+ return PIXEL_FORMAT_NONE;
+ }
return mActiveBuffer->format;
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index e944eb9..1f47949 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,11 +39,15 @@
void LayerHistory::incrementCounter() {
mCounter++;
mCounter = mCounter % 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((ARRAY_SIZE + (mCounter - (index % ARRAY_SIZE))) % 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..76c1352 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -42,14 +42,17 @@
~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 ARRAY_SIZE; }
private:
size_t mCounter = 0;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 4286cc9..4d3ae33 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -30,6 +30,7 @@
#include <gui/ISurfaceComposer.h>
#include <ui/DisplayStatInfo.h>
+#include <utils/Timers.h>
#include <utils/Trace.h>
#include "DispSync.h"
@@ -205,15 +206,95 @@
mHWVsyncAvailable = makeAvailable;
}
-void Scheduler::addNewFrameTimestamp(const nsecs_t newFrameTimestamp, bool isAutoTimestamp) {
+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);
+
+ // 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);
+}
+
+void Scheduler::incrementFrameCounter() {
+ mLayerHistory.incrementCounter();
+}
+
+nsecs_t Scheduler::calculateAverage() const {
+ nsecs_t sum = std::accumulate(mTimeDifferences.begin(), mTimeDifferences.end(), 0);
+ return (sum / ARRAY_SIZE);
+}
+
+void Scheduler::updateFrameSkipping(const int64_t skipCount) {
+ ATRACE_INT("FrameSkipCount", skipCount);
+ if (mSkipCount != skipCount) {
+ // Only update DispSync if it hasn't been updated yet.
+ mPrimaryDispSync->setRefreshSkipCount(skipCount);
+ mSkipCount = skipCount;
+ }
+}
+
+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;
+ 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;
+ ALOGD("%d Layer %s: %" PRId64, i, layerName.c_str(), differenceMs);
+ // Dismiss noise.
+ if (differenceMs > 10 && differenceMs < 60) {
+ differencesMs.push_back(differenceMs);
+ }
+ newestPresentTime = layer.second;
+ }
+ }
+ // Average is a good indicator for when 24fps videos are playing, because the frames come in
+ // 33, and 49 ms intervals with occasional 41ms.
+ int64_t average =
+ std::accumulate(differencesMs.begin(), differencesMs.end(), 0) / differencesMs.size();
+ const auto tag = "TimestampAvg_" + layerName;
+ ATRACE_INT(tag.c_str(), average);
+
+ // Mode and median are good indicators for 30 and 60 fps videos, because the majority of frames
+ // come in 16, or 33 ms intervals.
+ // TODO(b/113612090): Calculate mode. Median is good for now, since we want a given interval to
+ // repeat at least ARRAY_SIZE/2 + 1 times.
+ if (differencesMs.size() > 0) {
+ const auto tagMedian = "TimestampMedian_" + layerName;
+ ATRACE_INT(tagMedian.c_str(), calculateMedian(&differencesMs));
+ }
+}
+
+int64_t Scheduler::calculateMedian(std::vector<int64_t>* v) {
+ if (!v || v->size() == 0) {
+ return 0;
+ }
+
+ size_t n = v->size() / 2;
+ nth_element(v->begin(), v->begin() + n, v->end());
+ return v->at(n);
+}
+
+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 = (newFrameTimestamp - mPreviousFrameTimestamp) / 1000000;
- mPreviousFrameTimestamp = newFrameTimestamp;
+ int64_t differenceMs = (framePresentTime - mPreviousFrameTimestamp) / 1000000;
+ mPreviousFrameTimestamp = framePresentTime;
if (differenceMs < 10 || differenceMs > 100) {
// Dismiss noise.
@@ -240,18 +321,4 @@
updateFrameSkipping(0);
}
-nsecs_t Scheduler::calculateAverage() const {
- nsecs_t sum = std::accumulate(mTimeDifferences.begin(), mTimeDifferences.end(), 0);
- return (sum / ARRAY_SIZE);
-}
-
-void Scheduler::updateFrameSkipping(const int64_t skipCount) {
- ATRACE_INT("FrameSkipCount", skipCount);
- if (mSkipCount != skipCount) {
- // Only update DispSync if it hasn't been updated yet.
- mPrimaryDispSync->setRefreshSkipCount(skipCount);
- mSkipCount = skipCount;
- }
-}
-
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index dd1f24b..adfc071 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -26,6 +26,7 @@
#include "EventControlThread.h"
#include "EventThread.h"
#include "InjectVSyncSource.h"
+#include "LayerHistory.h"
namespace android {
@@ -103,9 +104,15 @@
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:
+ friend class SchedulerTest;
+
virtual std::unique_ptr<EventThread> makeEventThread(
const std::string& connectionName, DispSync* dispSync, int64_t phaseOffsetNs,
impl::EventThread::ResyncWithRateLimitCallback resyncCallback,
@@ -114,6 +121,15 @@
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);
+ // Calculates the statistical median in the vector. Return 0 if the vector is empty. The
+ // function modifies the vector contents.
+ int64_t calculateMedian(std::vector<int64_t>* v);
+ // 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.
@@ -150,6 +166,8 @@
static constexpr size_t ARRAY_SIZE = 30;
std::array<int64_t, ARRAY_SIZE> mTimeDifferences;
size_t mCounter = 0;
+
+ LayerHistory mLayerHistory;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e9dd6bc..1db8791 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1498,6 +1498,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() ==
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/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..e967742 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -59,6 +59,8 @@
SchedulerTest();
~SchedulerTest() override;
+ int64_t calculateMedian(std::vector<int64_t>* v);
+
sp<Scheduler::ConnectionHandle> mConnectionHandle;
mock::DispSync* mPrimaryDispSync = new mock::DispSync();
mock::EventThread* mEventThread;
@@ -95,6 +97,10 @@
ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
+int64_t SchedulerTest::calculateMedian(std::vector<int64_t>* v) {
+ return mScheduler->calculateMedian(v);
+}
+
namespace {
/* ------------------------------------------------------------------------
* Test cases
@@ -185,5 +191,37 @@
ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
}
+TEST_F(SchedulerTest, calculateMedian) {
+ std::vector<int64_t> testVector;
+ // Calling the function on empty vector returns 0.
+ EXPECT_EQ(0, calculateMedian(&testVector));
+
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculateMedian(&testVector));
+ testVector.push_back(33);
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculateMedian(&testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculateMedian(&testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculateMedian(&testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculateMedian(&testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(33, calculateMedian(&testVector));
+ testVector.push_back(42);
+ EXPECT_EQ(42, calculateMedian(&testVector));
+ testVector.push_back(60);
+ EXPECT_EQ(42, calculateMedian(&testVector));
+ testVector.push_back(60);
+ EXPECT_EQ(42, calculateMedian(&testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(42, calculateMedian(&testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(42, calculateMedian(&testVector));
+ testVector.push_back(33);
+ EXPECT_EQ(33, calculateMedian(&testVector));
+}
+
} // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android