Merge "BnGraphicBufferProducer: Add null fence checks"
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 1fbd2b3..12de2d2 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2735,8 +2735,8 @@
if (ics != nullptr) {
MYLOGD("Checking user consent via incidentcompanion service\n");
android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
- calling_uid, calling_package, 0x1 /* FLAG_CONFIRMATION_DIALOG */,
- consent_callback_.get());
+ calling_uid, calling_package, String16(), String16(),
+ 0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get());
} else {
MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n");
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 92fac66..c730ab9 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -171,7 +171,7 @@
}
binder::Status checkArgumentPackageName(const std::string& packageName) {
- if (is_valid_package_name(packageName.c_str())) {
+ if (is_valid_package_name(packageName)) {
return ok();
} else {
return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 20142aa..a5cc0df 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1564,7 +1564,7 @@
if (vdex_fd >= 0) {
AddArg(vdex_fd_arg);
}
- AddArg(zip_fd_arg.c_str());
+ AddArg(zip_fd_arg);
if (profile_was_updated) {
AddArg(assume_profile_changed);
}
@@ -1572,9 +1572,9 @@
AddArg(downgrade_flag);
}
if (class_loader_context != nullptr) {
- AddArg(class_loader_context_arg.c_str());
+ AddArg(class_loader_context_arg);
if (!class_loader_context_fds.empty()) {
- AddArg(class_loader_context_fds_arg.c_str());
+ AddArg(class_loader_context_fds_arg);
}
}
@@ -2259,7 +2259,7 @@
drop_capabilities(uid);
const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
- if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
+ if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr,
uid, storage_flag)) {
LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
_exit(kReconcileSecondaryDexValidationError);
diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h
index 71a8f20..7f04611 100644
--- a/include/input/InputApplication.h
+++ b/include/input/InputApplication.h
@@ -50,19 +50,19 @@
class InputApplicationHandle : public RefBase {
public:
inline const InputApplicationInfo* getInfo() const {
- return mInfo;
+ return &mInfo;
}
inline std::string getName() const {
- return mInfo ? mInfo->name : "<invalid>";
+ return !mInfo.name.empty() ? mInfo.name : "<invalid>";
}
inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const {
- return mInfo ? mInfo->dispatchingTimeout : defaultValue;
+ return mInfo.token ? mInfo.dispatchingTimeout : defaultValue;
}
inline sp<IBinder> getApplicationToken() const {
- return mInfo ? mInfo->token : nullptr;
+ return mInfo.token;
}
/**
@@ -75,18 +75,11 @@
* Returns true on success, or false if the handle is no longer valid.
*/
virtual bool updateInfo() = 0;
-
- /**
- * Releases the storage used by the associated information when it is
- * no longer needed.
- */
- void releaseInfo();
-
protected:
InputApplicationHandle();
virtual ~InputApplicationHandle();
- InputApplicationInfo* mInfo;
+ InputApplicationInfo mInfo;
};
} // namespace android
diff --git a/libs/incidentcompanion/Android.bp b/libs/incidentcompanion/Android.bp
index 45eab00..63411b9 100644
--- a/libs/incidentcompanion/Android.bp
+++ b/libs/incidentcompanion/Android.bp
@@ -35,8 +35,12 @@
},
srcs: [
":incidentcompanion_aidl",
+ "src/IncidentManager.cpp",
],
- export_include_dirs: ["binder"],
+ export_include_dirs: [
+ "binder",
+ "include",
+ ],
cflags: [
"-Wall",
"-Werror",
diff --git a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
index 6bf98d2..98c2814 100644
--- a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
+++ b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.IIncidentAuthListener;
+import android.os.IncidentManager;
/**
* Helper service for incidentd and dumpstated to provide user feedback
@@ -35,6 +36,10 @@
* returns via the callback whether the application should be trusted. It is up
* to the caller to actually implement the restriction to take or not take
* the incident or bug report.
+ * @param receiverClass The class that will be the eventual broacast receiver for the
+ * INCIDENT_REPORT_READY message. Used as part of the id in incidentd.
+ * @param reportId The incident report ID. Incidentd should call with this parameter, but
+ * everyone else should pass null or empty string.
* @param flags FLAG_CONFIRMATION_DIALOG (0x1) - to show this as a dialog. Otherwise
* a dialog will be shown as a notification.
* @param callback Interface to receive results. The results may not come back for
@@ -44,6 +49,7 @@
* to send their report.
*/
oneway void authorizeReport(int callingUid, String callingPackage,
+ String receiverClass, String reportId,
int flags, IIncidentAuthListener callback);
/**
@@ -52,6 +58,11 @@
oneway void cancelAuthorization(IIncidentAuthListener callback);
/**
+ * Send the report ready broadcast on behalf of incidentd.
+ */
+ oneway void sendReportReadyBroadcast(String pkg, String cls);
+
+ /**
* Return the list of pending approvals.
*/
List<String> getPendingReports();
@@ -69,4 +80,26 @@
* @param uri the report.
*/
void denyReport(String uri);
+
+ /**
+ * List the incident reports for the given ComponentName. The receiver
+ * must be for a package inside the caller.
+ */
+ List<String> getIncidentReportList(String pkg, String cls);
+
+ /**
+ * Get the IncidentReport object.
+ */
+ IncidentManager.IncidentReport getIncidentReport(String pkg, String cls, String id);
+
+ /**
+ * Signal that the client is done with this incident report and it can be deleted.
+ */
+ void deleteIncidentReports(String pkg, String cls, String id);
+
+ /**
+ * Signal that the client is done with all incident reports from this package.
+ * Especially useful for testing.
+ */
+ void deleteAllIncidentReports(String pkg);
}
diff --git a/libs/incidentcompanion/binder/android/os/IncidentManager.aidl b/libs/incidentcompanion/binder/android/os/IncidentManager.aidl
new file mode 100644
index 0000000..d17823e
--- /dev/null
+++ b/libs/incidentcompanion/binder/android/os/IncidentManager.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+parcelable IncidentManager.IncidentReport cpp_header "android/os/IncidentManager.h";
+
diff --git a/libs/incidentcompanion/include/android/os/IncidentManager.h b/libs/incidentcompanion/include/android/os/IncidentManager.h
new file mode 100644
index 0000000..07b6d82
--- /dev/null
+++ b/libs/incidentcompanion/include/android/os/IncidentManager.h
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2019, 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 <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <utils/String16.h>
+
+#include <set>
+#include <vector>
+
+namespace android {
+namespace os {
+
+class IncidentManager : public virtual RefBase {
+public:
+ class IncidentReport : public Parcelable {
+ public:
+ IncidentReport();
+ virtual ~IncidentReport();
+
+ virtual status_t writeToParcel(Parcel* out) const;
+ virtual status_t readFromParcel(const Parcel* in);
+
+ void setTimestampNs(int64_t val) { mTimestampNs = val; }
+ int64_t getTimestampNs() const { return mTimestampNs; }
+ int64_t getTimestampMs() const { return mTimestampNs / 1000000; }
+
+ void setPrivacyPolicy(int32_t val) { mPrivacyPolicy = val; }
+ // This was accidentally published as a long in the java api.
+ int64_t getPrivacyPolicy() const { return mPrivacyPolicy; }
+ // Dups the fd, so you retain ownership of the original one. If there is a
+ // previously set fd, closes that, since this object owns its own fd.
+ status_t setFileDescriptor(int fd);
+
+ // Does not dup the fd, so ownership is passed to this object. If there is a
+ // previously set fd, closes that, since this object owns its own fd.
+ void takeFileDescriptor(int fd);
+
+ // Returns the fd, which you don't own. Call dup if you need a copy.
+ int getFileDescriptor() const { return mFileDescriptor; }
+
+ private:
+ int64_t mTimestampNs;
+ int32_t mPrivacyPolicy;
+ int mFileDescriptor;
+ };
+
+
+private:
+ // Not implemented for now.
+ IncidentManager();
+ virtual ~IncidentManager();
+};
+}
+}
+
diff --git a/libs/incidentcompanion/src/IncidentManager.cpp b/libs/incidentcompanion/src/IncidentManager.cpp
new file mode 100644
index 0000000..f7c8a5e
--- /dev/null
+++ b/libs/incidentcompanion/src/IncidentManager.cpp
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/os/IncidentManager.h>
+
+namespace android {
+namespace os {
+
+// ============================================================
+IncidentManager::IncidentReport::IncidentReport()
+ :mTimestampNs(0),
+ mPrivacyPolicy(0),
+ mFileDescriptor(-1) {
+}
+
+IncidentManager::IncidentReport::~IncidentReport() {
+ if (mFileDescriptor >= 0) {
+ close(mFileDescriptor);
+ }
+}
+
+status_t IncidentManager::IncidentReport::writeToParcel(Parcel* out) const {
+ status_t err;
+
+ err = out->writeInt64(mTimestampNs);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+
+ err = out->writeInt32(mPrivacyPolicy);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (mFileDescriptor >= 0) {
+ err = out->writeInt32(1);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = out->writeDupParcelFileDescriptor(mFileDescriptor);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ } else {
+ err = out->writeInt32(0);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t IncidentManager::IncidentReport::readFromParcel(const Parcel* in) {
+ status_t err;
+ int32_t hasField;
+
+ err = in->readInt64(&mTimestampNs);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = in->readInt32(&mPrivacyPolicy);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ err = in->readInt32(&hasField);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ if (hasField) {
+ int fd = in->readParcelFileDescriptor();
+ if (fd >= 0) {
+ mFileDescriptor = dup(fd);
+ if (mFileDescriptor < 0) {
+ return -errno;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t IncidentManager::IncidentReport::setFileDescriptor(int fd) {
+ if (mFileDescriptor >= 0) {
+ close(mFileDescriptor);
+ }
+ if (fd < 0) {
+ mFileDescriptor = -1;
+ } else {
+ mFileDescriptor = dup(fd);
+ if (mFileDescriptor < 0) {
+ return -errno;
+ }
+ }
+ return NO_ERROR;
+}
+
+void IncidentManager::IncidentReport::takeFileDescriptor(int fd) {
+ if (mFileDescriptor >= 0) {
+ close(mFileDescriptor);
+ }
+ if (fd < 0) {
+ mFileDescriptor = -1;
+ } else {
+ mFileDescriptor = fd;
+ }
+}
+
+// ============================================================
+IncidentManager::~IncidentManager() {
+}
+
+}
+}
+
+
diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp
index 7936f50..1d9f8a7 100644
--- a/libs/input/InputApplication.cpp
+++ b/libs/input/InputApplication.cpp
@@ -24,19 +24,10 @@
// --- InputApplicationHandle ---
-InputApplicationHandle::InputApplicationHandle() :
- mInfo(nullptr) {
+InputApplicationHandle::InputApplicationHandle() {
}
InputApplicationHandle::~InputApplicationHandle() {
- delete mInfo;
-}
-
-void InputApplicationHandle::releaseInfo() {
- if (mInfo) {
- delete mInfo;
- mInfo = nullptr;
- }
}
InputApplicationInfo InputApplicationInfo::read(const Parcel& from) {
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index bf80481..9bd3095 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -96,7 +96,12 @@
int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage,
int32_t fence, const ARect* rect, void** outVirtualAddress,
int32_t* outBytesPerPixel, int32_t* outBytesPerStride) {
- if (!buffer) return BAD_VALUE;
+ if (outBytesPerPixel) *outBytesPerPixel = -1;
+ if (outBytesPerStride) *outBytesPerStride = -1;
+
+ if (!buffer) {
+ return BAD_VALUE;
+ }
if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK |
AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) {
@@ -127,15 +132,19 @@
} else {
bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom));
}
- int result = gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, outBytesPerPixel, outBytesPerStride);
+ int32_t bytesPerPixel;
+ int32_t bytesPerStride;
+ int result = gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, &bytesPerPixel, &bytesPerStride);
// if hardware returns -1 for bytes per pixel or bytes per stride, we fail
// and unlock the buffer
- if (*outBytesPerPixel == -1 || *outBytesPerStride == -1) {
+ if (bytesPerPixel == -1 || bytesPerStride == -1) {
gbuffer->unlock();
return INVALID_OPERATION;
}
+ if (outBytesPerPixel) *outBytesPerPixel = bytesPerPixel;
+ if (outBytesPerStride) *outBytesPerStride = bytesPerStride;
return result;
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index f651309..1980f50 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -595,11 +595,7 @@
}
void GLESRenderEngine::setScissor(const Rect& region) {
- // Invert y-coordinate to map to GL-space.
- int32_t canvasHeight = mFboHeight;
- int32_t glBottom = canvasHeight - region.bottom;
-
- glScissor(region.left, glBottom, region.getWidth(), region.getHeight());
+ glScissor(region.left, region.top, region.getWidth(), region.getHeight());
glEnable(GL_SCISSOR_TEST);
}
@@ -719,6 +715,36 @@
return cropWin;
}
+void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display,
+ const LayerSettings& layer, const Mesh& mesh) {
+ // We separate the layer into 3 parts essentially, such that we only turn on blending for the
+ // top rectangle and the bottom rectangle, and turn off blending for the middle rectangle.
+ FloatRect bounds = layer.geometry.roundedCornersCrop;
+ const auto transformMatrix = display.globalTransform * layer.geometry.positionTransform;
+ const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0);
+ const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0);
+ const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate;
+ const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate;
+ bounds = FloatRect(leftTopCoordinateInBuffer[0], leftTopCoordinateInBuffer[1],
+ rightBottomCoordinateInBuffer[0], rightBottomCoordinateInBuffer[1]);
+ const int32_t radius = ceil(layer.geometry.roundedCornersRadius);
+
+ const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius);
+ setScissor(topRect);
+ drawMesh(mesh);
+ const Rect bottomRect(bounds.left, bounds.bottom - radius, bounds.right, bounds.bottom);
+ setScissor(bottomRect);
+ drawMesh(mesh);
+
+ // The middle part of the layer can turn off blending.
+ const Rect middleRect(bounds.left, bounds.top + radius, bounds.right, bounds.bottom - radius);
+ setScissor(middleRect);
+ mState.cornerRadius = 0.0;
+ disableBlending();
+ drawMesh(mesh);
+ disableScissor();
+}
+
status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) {
ATRACE_CALL();
GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer);
@@ -738,8 +764,6 @@
glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
- mFboHeight = glFramebuffer->getBufferHeight();
-
uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
@@ -750,7 +774,6 @@
void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
ATRACE_CALL();
- mFboHeight = 0;
// back to main framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -917,7 +940,14 @@
}
setSourceDataSpace(layer.sourceDataspace);
- drawMesh(mesh);
+ // We only want to do a special handling for rounded corners when having rounded corners
+ // is the only reason it needs to turn on blending, otherwise, we handle it like the
+ // usual way since it needs to turn on blending anyway.
+ if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ handleRoundedCorners(display, layer, mesh);
+ } else {
+ drawMesh(mesh);
+ }
// Cleanup if there's a buffer source
if (layer.source.buffer.buffer != nullptr) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index efb6ef0..8c8f308 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -69,8 +69,6 @@
void clearWithColor(float red, float green, float blue, float alpha) override;
void fillRegionWithColor(const Region& region, float red, float green, float blue,
float alpha) override;
- void setScissor(const Rect& region) override;
- void disableScissor() override;
void genTextures(size_t count, uint32_t* names) override;
void deleteTextures(size_t count, uint32_t const* names) override;
void bindExternalTextureImage(uint32_t texName, const Image& image) override;
@@ -141,6 +139,8 @@
Protection protection);
static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config,
int hwcFormat, Protection protection);
+ void setScissor(const Rect& region);
+ void disableScissor();
bool waitSync(EGLSyncKHR sync, EGLint flags);
// A data space is considered HDR data space if it has BT2020 color space
@@ -156,6 +156,13 @@
// coordinates for the mesh.
FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh);
+ // We do a special handling for rounded corners when it's possible to turn off blending
+ // for the majority of the layer. The rounded corners needs to turn on blending such that
+ // we can set the alpha value correctly, however, only the corners need this, and since
+ // blending is an expensive operation, we want to turn off blending when it's not necessary.
+ void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer,
+ const Mesh& mesh);
+
EGLDisplay mEGLDisplay;
EGLConfig mEGLConfig;
EGLContext mEGLContext;
@@ -185,7 +192,6 @@
bool mInProtectedContext = false;
// If set to true, then enables tracing flush() and finish() to systrace.
bool mTraceGpuCompletion = false;
- int32_t mFboHeight = 0;
// Maximum size of mFramebufferImageCache. If more images would be cached, then (approximately)
// the last recently used buffer should be kicked out.
uint32_t mFramebufferImageCacheSize = 0;
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index ab34274..b211551 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -103,9 +103,6 @@
virtual void clearWithColor(float red, float green, float blue, float alpha) = 0;
virtual void fillRegionWithColor(const Region& region, float red, float green, float blue,
float alpha) = 0;
-
- virtual void setScissor(const Rect& region) = 0;
- virtual void disableScissor() = 0;
virtual void genTextures(size_t count, uint32_t* names) = 0;
virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index ddf7420..479c7ac 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -48,8 +48,6 @@
bool waitFence(base::unique_fd fd) override { return waitFence(&fd); };
MOCK_METHOD4(clearWithColor, void(float, float, float, float));
MOCK_METHOD5(fillRegionWithColor, void(const Region&, float, float, float, float));
- MOCK_METHOD1(setScissor, void(const Rect&));
- MOCK_METHOD0(disableScissor, void());
MOCK_METHOD2(genTextures, void(size_t, uint32_t*));
MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*));
MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&));
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 40df260..3fc6a2d 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -194,6 +194,7 @@
if (inFormat != format) return true;
if (inLayerCount != layerCount) return true;
if ((usage & inUsage) != inUsage) return true;
+ if ((usage & USAGE_PROTECTED) != (inUsage & USAGE_PROTECTED)) return true;
return false;
}
@@ -394,7 +395,7 @@
status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const {
#ifndef LIBUI_IN_VNDK
if (mBufferHubBuffer != nullptr) {
- return flattenBufferHubBuffer(buffer, size, fds, count);
+ return flattenBufferHubBuffer(buffer, size);
}
#endif
size_t sizeNeeded = GraphicBuffer::getFlattenedSize();
@@ -437,6 +438,11 @@
status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& fds,
size_t& count) {
+ // Check if size is not smaller than buf[0] is supposed to take.
+ if (size < sizeof(int)) {
+ return NO_MEMORY;
+ }
+
int const* buf = static_cast<int const*>(buffer);
// NOTE: it turns out that some media code generates a flattened GraphicBuffer manually!!!!!
@@ -450,7 +456,7 @@
flattenWordCount = 12;
} else if (buf[0] == 'BHBB') { // BufferHub backed buffer.
#ifndef LIBUI_IN_VNDK
- return unflattenBufferHubBuffer(buffer, size, fds, count);
+ return unflattenBufferHubBuffer(buffer, size);
#else
return BAD_TYPE;
#endif
@@ -561,8 +567,7 @@
}
#ifndef LIBUI_IN_VNDK
-status_t GraphicBuffer::flattenBufferHubBuffer(void*& buffer, size_t& size, int*& fds,
- size_t& count) const {
+status_t GraphicBuffer::flattenBufferHubBuffer(void*& buffer, size_t& size) const {
sp<NativeHandle> tokenHandle = mBufferHubBuffer->duplicate();
if (tokenHandle == nullptr || tokenHandle->handle() == nullptr ||
tokenHandle->handle()->numFds != 0) {
@@ -586,14 +591,10 @@
memcpy(buf + 2, tokenHandle->handle()->data, static_cast<size_t>(numIntsInToken) * sizeof(int));
buf[2 + numIntsInToken] = static_cast<int32_t>(mGenerationNumber);
- // Do not pass fds if it is BufferHubBuffer backed GraphicBuffer. Not modifying fds or count.
- fds += 0;
- count -= 0;
return NO_ERROR;
}
-status_t GraphicBuffer::unflattenBufferHubBuffer(void const*& buffer, size_t& size, int const*& fds,
- size_t& count) {
+status_t GraphicBuffer::unflattenBufferHubBuffer(void const*& buffer, size_t& size) {
const int* buf = static_cast<const int*>(buffer);
int numIntsInToken = buf[1];
// Size needed for one label, one number of ints inside the token, one generation number and
@@ -627,10 +628,6 @@
mBufferId = bufferHubBuffer->id();
mBufferHubBuffer.reset(std::move(bufferHubBuffer.get()));
- // BufferHubBuffer backed GraphicBuffer does not have flattened handle. Not modifying fds or
- // count.
- fds += 0;
- count -= 0;
return NO_ERROR;
}
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index c137860..c195342 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -302,7 +302,7 @@
#ifndef LIBUI_IN_VNDK
// Flatten this GraphicBuffer object if backed by BufferHubBuffer.
- status_t flattenBufferHubBuffer(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+ status_t flattenBufferHubBuffer(void*& buffer, size_t& size) const;
// Unflatten into BufferHubBuffer backed GraphicBuffer.
// Unflatten will fail if the original GraphicBuffer object is destructed. For instance, a
@@ -310,8 +310,7 @@
// to process/thread B through a socket, BufferHubBuffer_1 dies and bufferhub invalidated the
// token. Race condition occurs between the invalidation of the token in bufferhub process and
// process/thread B trying to unflatten and import the buffer with that token.
- status_t unflattenBufferHubBuffer(void const*& buffer, size_t& size, int const*& fds,
- size_t& count);
+ status_t unflattenBufferHubBuffer(void const*& buffer, size_t& size);
// Stores a BufferHubBuffer that handles buffer signaling, identification.
std::unique_ptr<BufferHubBuffer> mBufferHubBuffer;
diff --git a/opengl/libs/EGL/GLES_layers.md b/opengl/libs/EGL/GLES_layers.md
new file mode 100644
index 0000000..bfc44db
--- /dev/null
+++ b/opengl/libs/EGL/GLES_layers.md
@@ -0,0 +1,258 @@
+# GLES Layers
+
+## EGL Loader Initialization
+After standard entrypoints have all been populated unmodified, a GLES LayerLoader will be instantiated. If debug layers are enabled, the LayerLoader will scan specified directories for layers, just like the Vulkan loader does.
+
+If layering is enabled, the loader will search for and enumerate a specified layer list. The layer list will be specified by colon separated filenames (see [Enabling layers](#Enabling-layers) below).
+
+The layers will be traversed in the order they are specified, so the first layer will be directly below the application. For each layer, it will track two entrypoints from the layer. `AndroidGLESLayer_Initialize` and `AndroidGLESLayer_GetProcAddress`.
+```cpp
+typedef void* (*PFNEGLGETNEXTLAYERPROCADDRESSPROC)(void*, const char*);
+void* AndroidGLESLayer_Initialize(void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address))
+```
+
+`AndroidGLESLayer_Initialize` is a new function that provides an identifier for the layer to use (layer_id) and an entrypoint that can be called to look up functions below the layer. The entrypoint can be used like so:
+```cpp
+const char* func = "eglFoo";
+void* gpa = get_next_layer_proc_address(layer_id, func);
+```
+
+Note that only GLES2+ entrypoints will be provided. If a layer tries to make independent GLES 1.x calls, they will be routed to GLES2+ libraries, which may not behave as expected. Application calls to 1.x will not be affected.
+
+AndroidGLESLayer_GetProcAddress is a new function designed for this layering system. It takes the address of the next call in the chain that the layer should call when finished. If there is only one layer, next will point directly to the driver for most functions.
+```cpp
+void* AndroidGLESLayer_GetProcAddress(const char *funcName, EGLFuncPointer next)
+```
+
+For each layer found, the GLES LayerLoader will call `AndroidGLESLayer_Initialize`, and then walk libEGL’s function lists and call `AndroidGLESLayer_GetProcAddress` for all known functions. The layer can track that next address with any means it wants. If the layer does not intercept the function, `AndroidGLESLayer_GetProcAddress` must return the same function address it was passed. The LayerLoader will then update the function hook list to point to the layer’s entrypoint.
+
+The layers are not required to do anything with the info provided by `AndroidGLESLayer_Initialize` or get_next_layer_proc_address, but providing them makes it easier for existing layers (like GAPID and RenderDoc) to support Android. That way a layer can look up functions independently (i.e. not wait for calls to `AndroidGLESLayer_GetProcAddress`). Layers must be sure to use gen_next_layer_proc_address if they look up function calls instead of eglGetProcAddress or they will not get an accurate answer. eglGetProcAddress must be passed down the chain to the platform.
+
+## Placing layers
+
+Where layers can be found, in order of priority
+ 1. System location for root
+ This requires root access
+ ```bash
+ adb root
+ adb disable-verity
+ adb reboot
+ adb root
+ adb shell setenforce 0
+ adb shell mkdir -p /data/local/debug/gles
+ adb push <layer>.so /data/local/debug/gles/
+ ```
+
+ 2. Application's base directory
+ Target application must be debuggable, or you must have root access:
+ ```bash
+ adb push libGLTrace.so /data/local/tmp
+ adb shell run-as com.android.gl2jni cp /data/local/tmp/libGLTrace.so .
+ adb shell run-as com.android.gl2jni ls | grep libGLTrace
+ libGLTrace.so
+ ```
+
+ 3. External APK
+ Determine the ABI of your target application, then install an APK containing the layers you wish to load:
+ ```bash
+ adb install --abi armeabi-v7a layers.apk
+ ```
+
+ 4. In the target application's APK
+
+## Enabling layers
+
+### Per application
+Note these settings will persist across reboots:
+```bash
+# Enable layers
+adb shell settings put global enable_gpu_debug_layers 1
+
+# Specify target application
+adb shell settings put global gpu_debug_app <package_name>
+
+# Specify layer list (from top to bottom)
+adb shell settings put global gpu_debug_layers_gles <layer1:layer2:layerN>
+
+# Specify a package to search for layers
+adb shell settings put global gpu_debug_layer_app <layer_package>
+```
+To disable the per-app layers:
+```
+adb shell settings delete global enable_gpu_debug_layers
+adb shell settings delete global gpu_debug_app
+adb shell settings delete global gpu_debug_layers_gles
+adb shell settings delete global gpu_debug_layer_app
+```
+
+### Globally
+These will be cleared on reboot:
+```bash
+# This will attempt to load layers for all applications, including native executables
+adb shell setprop debug.gles.layers <layer1:layer2:layerN>
+```
+
+
+## Creating a layer
+
+Layers must expose the following two functions described above:
+```cpp
+AndroidGLESLayer_Initialize
+AndroidGLESLayer_GetProcAddress
+```
+
+For a simple layer that just wants to intercept a handful of functions, a passively initialized layer is the way to go. It can simply wait for the EGL Loader to initialize the function it cares about. See below for an example of creating a passive layer.
+
+For more formalized layers that need to fully initialize up front, or layers that needs to look up extensions not known to the EGL loader, active layer initialization is the way to go. The layer can utilize get_next_layer_proc_address provided by `AndroidGLESLayer_Initialize` to look up a function at any time. The layer must still respond to `AndroidGLESLayer_GetProcAddress` requests from the loader so the platform knows where to route calls. See below for an example of creating an active layer.
+
+### Example Passive Layer Initialization
+```cpp
+namespace {
+
+std::unordered_map<std::string, EGLFuncPointer> funcMap;
+
+EGLAPI EGLBoolean EGLAPIENTRY glesLayer_eglChooseConfig (
+ EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size,
+ EGLint *num_config) {
+
+ EGLFuncPointer entry = funcMap["eglChooseConfig"];
+
+ typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)(
+ EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*);
+
+ PFNEGLCHOOSECONFIGPROC next = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(entry);
+
+ return next(dpy, attrib_list, configs, config_size, num_config);
+}
+
+EGLAPI EGLFuncPointer EGLAPIENTRY eglGPA(const char* funcName) {
+
+ #define GETPROCADDR(func) if(!strcmp(funcName, #func)) { \
+ return (EGLFuncPointer)glesLayer_##func; }
+
+ GETPROCADDR(eglChooseConfig);
+
+ // Don't return anything for unrecognized functions
+ return nullptr;
+}
+
+EGLAPI void EGLAPIENTRY glesLayer_InitializeLayer(
+ void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) {
+ // This function is purposefully empty, since this layer does not proactively
+ // look up any entrypoints
+ }
+
+EGLAPI EGLFuncPointer EGLAPIENTRY glesLayer_GetLayerProcAddress(
+ const char* funcName, EGLFuncPointer next) {
+ EGLFuncPointer entry = eglGPA(funcName);
+ if (entry != nullptr) {
+ funcMap[std::string(funcName)] = next;
+ return entry;
+ }
+ return next;
+}
+
+} // namespace
+
+extern "C" {
+ __attribute((visibility("default"))) EGLAPI void AndroidGLESLayer_Initialize(
+ void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) {
+ return (void)glesLayer_InitializeLayer(layer_id, get_next_layer_proc_address);
+ }
+ __attribute((visibility("default"))) EGLAPI void* AndroidGLESLayer_GetProcAddres(
+ const char *funcName, EGLFuncPointer next) {
+ return (void*)glesLayer_GetLayerProcAddress(funcName, next);
+ }
+}
+```
+
+### Example Active Layer Initialization
+```cpp
+namespace {
+
+std::unordered_map<std::string, EGLFuncPointer> funcMap;
+
+EGLAPI EGLBoolean EGLAPIENTRY glesLayer_eglChooseConfig (
+ EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size,
+ EGLint *num_config) {
+
+ EGLFuncPointer entry = funcMap["eglChooseConfig"];
+
+ typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)(
+ EGLDisplay, const EGLint*, EGLConfig*, EGLint, EGLint*);
+
+ PFNEGLCHOOSECONFIGPROC next = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(entry);
+
+ return next(dpy, attrib_list, configs, config_size, num_config);
+}
+
+EGLAPI EGLFuncPointer EGLAPIENTRY eglGPA(const char* funcName) {
+
+ #define GETPROCADDR(func) if(!strcmp(funcName, #func)) { \
+ return (EGLFuncPointer)glesLayer_##func; }
+
+ GETPROCADDR(eglChooseConfig);
+
+ // Don't return anything for unrecognized functions
+ return nullptr;
+}
+
+EGLAPI void EGLAPIENTRY glesLayer_InitializeLayer(
+ void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) {
+
+ // Note: This is where the layer would populate its function map with all the
+ // functions it cares about
+ const char* func = “eglChooseConfig”;
+ funcMap[func] = get_next_layer_proc_address(layer_id, func);
+}
+
+EGLAPI EGLFuncPointer EGLAPIENTRY glesLayer_GetLayerProcAddress(
+ const char* funcName, EGLFuncPointer next) {
+ EGLFuncPointer entry = eglGPA(funcName);
+ if (entry != nullptr) {
+ return entry;
+ }
+
+ return next;
+}
+
+} // namespace
+
+extern "C" {
+ __attribute((visibility("default"))) EGLAPI void AndroidGLESLayer_Initialize(
+ void* layer_id, PFNEGLGETNEXTLAYERPROCADDRESSPROC get_next_layer_proc_address) {
+ return (void)glesLayer_InitializeLayer(layer_id, get_next_layer_proc_address);
+ }
+ __attribute((visibility("default"))) EGLAPI void* AndroidGLESLayer_GetProcAddres(
+ const char *funcName, EGLFuncPointer next) {
+ return (void*)glesLayer_GetLayerProcAddress(funcName, next);
+ }
+}
+```
+
+## Caveats
+Only supports GLES 2.0+.
+
+When layering is enabled, GLES 1.x exclusive functions will continue to route to GLES 1.x drivers. But functions shared with GLES 2.0+ (like glGetString) will be routed to 2.0+ drivers, which can cause confusion.
+
+## FAQ
+ - Who can use layers?
+ - GLES Layers can be loaded by any debuggable application, or for any application if you have root access
+ - How do we know if layers are working on a device?
+ - This feature is backed by Android CTS, so you can run `atest CtsGpuToolsHostTestCases`
+ - How does a app determine if this feature is supported?
+ - There are two ways. First you can check against the version of Android.
+ ```bash
+ # Q is the first that will support this, so look for `Q` or 10 for release
+ adb shell getprop ro.build.version.sdk
+ # Or look for the SDK version, which should be 29 for Q
+ adb shell getprop ro.build.version.sdk
+ ```
+ - Secondly, if you want to determine from an application that can't call out to ADB for this, you can check for the [EGL_ANDROID_GLES_layers](../../specs/EGL_ANDROID_GLES_layers.txt). It simply indicates support of this layering system:
+ ```cpp
+ std::string display_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+ if (display_extension.find("EGL_ANDROID_GLES_layers") != std::string::npos)
+ {
+ // Layers are supported!
+ }
+ ```
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index f936ac0..ac01dc8 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -37,9 +37,8 @@
// 2. If none enabled, check system properties
//
// - Layer initializing -
-// TODO: ADD DETAIL ABOUT NEW INTERFACES
-// - InitializeLayer (provided by layer, called by loader)
-// - GetLayerProcAddress (provided by layer, called by loader)
+// - AndroidGLESLayer_Initialize (provided by layer, called by loader)
+// - AndroidGLESLayer_GetProcAddress (provided by layer, called by loader)
// - getNextLayerProcAddress (provided by loader, called by layer)
//
// 1. Walk through defs for egl and each gl version
@@ -73,27 +72,30 @@
const void* getNextLayerProcAddress(void* layer_id, const char* name) {
// Use layer_id to find funcs for layer below current
- // This is the same key provided in InitializeLayer
+ // This is the same key provided in AndroidGLESLayer_Initialize
auto next_layer_funcs = reinterpret_cast<FunctionTable*>(layer_id);
EGLFuncPointer val;
+ ALOGV("getNextLayerProcAddress servicing %s", name);
+
if (func_indices.find(name) == func_indices.end()) {
// No entry for this function - it is an extension
// call down the GPA chain directly to the impl
- ALOGV("getNextLayerProcAddress servicing %s", name);
+ ALOGV("getNextLayerProcAddress - name(%s) no func_indices entry found", name);
// Look up which GPA we should use
int gpaIndex = func_indices["eglGetProcAddress"];
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name, gpaIndex);
EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex];
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this address", name, gpaIndex, (unsigned long long)gpaNext);
- ALOGV("Calling down the GPA chain (%llu) for %s", (unsigned long long)gpaNext, name);
// Call it for the requested function
typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*);
PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext);
val = reinterpret_cast<EGLFuncPointer>(next(name));
- ALOGV("Got back %llu for %s", (unsigned long long)val, name);
+ ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from GPA", name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val);
// We should store it now, but to do that, we need to move func_idx to the class so we can
// increment it separately
@@ -101,10 +103,10 @@
return reinterpret_cast<void*>(val);
}
- // int index = func_indices[name];
- // val = (*next_layer_funcs)[index];
- // return reinterpret_cast<void*>(val);
- return reinterpret_cast<void*>((*next_layer_funcs)[func_indices[name]]);
+ int index = func_indices[name];
+ val = (*next_layer_funcs)[index];
+ ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known entry", name, index, (unsigned long long)val);
+ return reinterpret_cast<void*>(val);
}
void SetupFuncMaps(FunctionTable& functions, char const* const* entries, EGLFuncPointer* curr,
@@ -115,14 +117,20 @@
// Some names overlap, only fill with initial entry
// This does mean that some indices will not be used
if (func_indices.find(name) == func_indices.end()) {
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning now", name, func_idx);
func_names[func_idx] = name;
func_indices[name] = func_idx;
+ } else {
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name, func_idx);
}
// Populate layer_functions once with initial value
// These values will arrive in priority order, starting with platform entries
if (functions[func_idx] == nullptr) {
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning (%llu)", name, func_idx, (unsigned long long) *curr);
functions[func_idx] = *curr;
+ } else {
+ ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name, func_idx, (unsigned long long) functions[func_idx]);
}
entries++;
@@ -400,7 +408,7 @@
}
// Find the layer's Initialize function
- std::string init_func = "InitializeLayer";
+ std::string init_func = "AndroidGLESLayer_Initialize";
ALOGV("Looking for entrypoint %s", init_func.c_str());
layer_init_func LayerInit =
@@ -414,7 +422,7 @@
}
// Find the layer's setup function
- std::string setup_func = "GetLayerProcAddress";
+ std::string setup_func = "AndroidGLESLayer_GetProcAddress";
ALOGV("Looking for entrypoint %s", setup_func.c_str());
layer_setup_func LayerSetup =
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 872631f..34262f3 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -139,7 +139,8 @@
char const * const gClientExtensionString =
"EGL_EXT_client_extensions "
"EGL_KHR_platform_android "
- "EGL_ANGLE_platform_angle";
+ "EGL_ANGLE_platform_angle "
+ "EGL_ANDROID_GLES_layers";
// clang-format on
// extensions not exposed to applications but used by the ANDROID system
diff --git a/opengl/specs/EGL_ANDROID_GLES_layers.txt b/opengl/specs/EGL_ANDROID_GLES_layers.txt
new file mode 100644
index 0000000..eb2a7d9
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_GLES_layers.txt
@@ -0,0 +1,64 @@
+Name
+
+ ANDROID_GLES_layers
+
+Name Strings
+
+ EGL_ANDROID_GLES_layers
+
+Contributors
+
+ Cody Northrop
+
+Contact
+
+ Cody Northrop, Google LLC (cnorthrop 'at' google.com)
+
+Status
+
+ Draft
+
+Version
+
+ Version 1, March 3, 2019
+
+Number
+
+ EGL Extension #132
+
+Extension Type
+
+ EGL client extension
+
+Dependencies
+
+ Requires EGL 1.5 or EGL_EXT_client_extensions
+
+Overview
+
+ This extension indicates the EGL loader supports GLES layering on Android.
+ It does not add any requirements to drivers or hardware.
+
+ See frameworks/native/opengl/libs/EGL/GLES_layers.md in Android for
+ more information.
+
+New Types
+
+ None
+
+New Procedures and Functions
+
+ None
+
+New Tokens
+
+ None
+
+Issues
+
+ None
+
+Revision History
+
+#1 (Cody Northrop, March 3, 2019)
+ - Initial draft.
diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index 1317620..0d5bc15 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -1958,17 +1958,17 @@
bool wasEmpty = connection->outboundQueue.isEmpty();
// Enqueue dispatch entries for the requested modes.
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_IS);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
- enqueueDispatchEntry(connection, eventEntry, inputTarget,
+ enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
// If the outbound queue was previously empty, start the dispatch cycle going.
@@ -1977,7 +1977,7 @@
}
}
-void InputDispatcher::enqueueDispatchEntry(
+void InputDispatcher::enqueueDispatchEntryLocked(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
int32_t inputTargetFlags = inputTarget->flags;
@@ -2054,6 +2054,10 @@
delete dispatchEntry;
return; // skip the inconsistent event
}
+
+ dispatchPointerDownOutsideFocusIfNecessary(motionEntry->source,
+ dispatchEntry->resolvedAction, inputTarget->inputChannel->getToken());
+
break;
}
}
@@ -2066,6 +2070,32 @@
// Enqueue the dispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
traceOutboundQueueLength(connection);
+
+}
+
+void InputDispatcher::dispatchPointerDownOutsideFocusIfNecessary(uint32_t source, int32_t action,
+ const sp<IBinder>& newToken) {
+ int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+ if (source != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
+ return;
+ }
+
+ sp<InputWindowHandle> inputWindowHandle = getWindowHandleLocked(newToken);
+ if (inputWindowHandle == nullptr) {
+ return;
+ }
+
+ int32_t displayId = inputWindowHandle->getInfo()->displayId;
+ sp<InputWindowHandle> focusedWindowHandle =
+ getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+
+ bool hasFocusChanged = !focusedWindowHandle || focusedWindowHandle->getToken() != newToken;
+
+ if (!hasFocusChanged) {
+ return;
+ }
+
+ // Dispatch onPointerDownOutsideFocus to the policy.
}
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -2385,7 +2415,7 @@
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- enqueueDispatchEntry(connection, cancelationEventEntry, // increments ref
+ enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
&target, InputTarget::FLAG_DISPATCH_AS_IS);
cancelationEventEntry->release();
@@ -3221,13 +3251,11 @@
if (oldFocusedApplicationHandle != inputApplicationHandle) {
if (oldFocusedApplicationHandle != nullptr) {
resetANRTimeoutsLocked();
- oldFocusedApplicationHandle->releaseInfo();
}
mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
}
} else if (oldFocusedApplicationHandle != nullptr) {
resetANRTimeoutsLocked();
- oldFocusedApplicationHandle->releaseInfo();
oldFocusedApplicationHandle.clear();
mFocusedApplicationHandlesByDisplay.erase(displayId);
}
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 4d2c216..3735a0b 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -1136,8 +1136,9 @@
EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
- void enqueueDispatchEntry(const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode);
+ void enqueueDispatchEntryLocked(const sp<Connection>& connection,
+ EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode)
+ REQUIRES(mLock);
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
@@ -1147,6 +1148,9 @@
void drainDispatchQueue(Queue<DispatchEntry>* queue);
void releaseDispatchEntry(DispatchEntry* dispatchEntry);
static int handleReceiveCallback(int fd, int events, void* data);
+ // The action sent should only be of type AMOTION_EVENT_*
+ void dispatchPointerDownOutsideFocusIfNecessary(uint32_t source, int32_t action,
+ const sp<IBinder>& newToken) REQUIRES(mLock);
void synthesizeCancelationEventsForAllConnectionsLocked(
const CancelationOptions& options) REQUIRES(mLock);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index d63ff8c..745fac0 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -340,10 +340,7 @@
virtual ~FakeApplicationHandle() {}
virtual bool updateInfo() {
- if (!mInfo) {
- mInfo = new InputApplicationInfo();
- }
- mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
return true;
}
};
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 189ae36..a6ed75f 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -359,7 +359,7 @@
for (const auto & s : mSensorList) {
int32_t handle = s.handle;
const Info& info = mActivationCount.valueFor(handle);
- if (info.batchParams.isEmpty()) continue;
+ if (info.numActiveClients() == 0) continue;
result.appendFormat("0x%08x) active-count = %zu; ", handle, info.batchParams.size());
@@ -730,6 +730,15 @@
return mDisabledClients.indexOf(ident) >= 0;
}
+bool SensorDevice::isSensorActive(int handle) const {
+ Mutex::Autolock _l(mLock);
+ ssize_t activationIndex = mActivationCount.indexOfKey(handle);
+ if (activationIndex < 0) {
+ return false;
+ }
+ return mActivationCount.valueAt(activationIndex).numActiveClients() > 0;
+}
+
void SensorDevice::enableAllSensors() {
if (mSensors == nullptr) return;
Mutex::Autolock _l(mLock);
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 2a69654..71b918f 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -120,6 +120,8 @@
return mReconnecting;
}
+ bool isSensorActive(int handle) const;
+
// Dumpable
virtual std::string dump() const;
private:
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index b66cbcf..c4cfdc6 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -78,7 +78,14 @@
void SensorService::SensorEventConnection::dump(String8& result) {
Mutex::Autolock _l(mConnectionLock);
- result.appendFormat("\tOperating Mode: %s\n",mDataInjectionMode ? "DATA_INJECTION" : "NORMAL");
+ result.appendFormat("\tOperating Mode: ");
+ if (!mService->isWhiteListedPackage(getPackageName())) {
+ result.append("RESTRICTED\n");
+ } else if (mDataInjectionMode) {
+ result.append("DATA_INJECTION\n");
+ } else {
+ result.append("NORMAL\n");
+ }
result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | "
"max cache size %d\n", mPackageName.string(), mWakeLockRefCount, mUid, mCacheSize,
mMaxCacheSize);
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 3fbd61e..e3dfde4 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -441,12 +441,15 @@
}
result.append("Active sensors:\n");
+ SensorDevice& dev = SensorDevice::getInstance();
for (size_t i=0 ; i<mActiveSensors.size() ; i++) {
int handle = mActiveSensors.keyAt(i);
- result.appendFormat("%s (handle=0x%08x, connections=%zu)\n",
- getSensorName(handle).string(),
- handle,
- mActiveSensors.valueAt(i)->getNumConnections());
+ if (dev.isSensorActive(handle)) {
+ result.appendFormat("%s (handle=0x%08x, connections=%zu)\n",
+ getSensorName(handle).string(),
+ handle,
+ mActiveSensors.valueAt(i)->getNumConnections());
+ }
}
result.appendFormat("Socket Buffer size = %zd events\n",
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index e4179ee..d3b36fe 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -361,8 +361,12 @@
uint32_t hwcSlot = 0;
sp<GraphicBuffer> hwcBuffer;
+
+ // INVALID_BUFFER_SLOT is used to identify BufferStateLayers. Default to 0
+ // for BufferQueueLayers
+ int slot = (mActiveBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) ? 0 : mActiveBufferSlot;
(*outputLayer->editState().hwc)
- .hwcBufferCache.getHwcBuffer(mActiveBuffer, &hwcSlot, &hwcBuffer);
+ .hwcBufferCache.getHwcBuffer(slot, mActiveBuffer, &hwcSlot, &hwcBuffer);
auto acquireFence = mConsumer->getCurrentFence();
auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 5efa4e0..64dfdc3 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -585,10 +585,10 @@
const State& s(getDrawingState());
- // obtain slot
- uint32_t hwcSlot = 0;
+ uint32_t hwcSlot;
sp<GraphicBuffer> buffer;
- hwcInfo.hwcBufferCache.getHwcBuffer(s.buffer, &hwcSlot, &buffer);
+ hwcInfo.hwcBufferCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, s.buffer, &hwcSlot,
+ &buffer);
auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence);
if (error != HWC2::Error::None) {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index 02d7890..97bdc8f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -45,7 +45,7 @@
//
// outBuffer is set to buffer when buffer is not in the HWC cache;
// otherwise, outBuffer is set to nullptr.
- void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
sp<GraphicBuffer>* outBuffer);
protected:
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index 8613210..a941e09 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -48,12 +48,20 @@
return std::distance(std::begin(mBuffers), iter);
}
-void HwcBufferCache::getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
sp<GraphicBuffer>* outBuffer) {
- bool cached = getSlot(buffer, outSlot);
+ // if this slot corresponds to a BufferStateLayer, generate the slot
+ if (slot == BufferQueue::INVALID_BUFFER_SLOT) {
+ getSlot(buffer, outSlot);
+ } else if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) {
+ *outSlot = 0;
+ } else {
+ *outSlot = slot;
+ }
auto& [currentCounter, currentBuffer] = mBuffers[*outSlot];
- if (cached) {
+ wp<GraphicBuffer> weakCopy(buffer);
+ if (currentBuffer == weakCopy) {
// already cached in HWC, skip sending the buffer
*outBuffer = nullptr;
currentCounter = getCounter();
diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
index ac04cb3..b261493 100644
--- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp
@@ -24,9 +24,9 @@
class TestableHwcBufferCache : public impl::HwcBufferCache {
public:
- void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
+ void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot,
sp<GraphicBuffer>* outBuffer) {
- HwcBufferCache::getHwcBuffer(buffer, outSlot, outBuffer);
+ HwcBufferCache::getHwcBuffer(slot, buffer, outSlot, outBuffer);
}
bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) {
return HwcBufferCache::getSlot(buffer, outSlot);
@@ -38,64 +38,88 @@
public:
~HwcBufferCacheTest() override = default;
- TestableHwcBufferCache mCache;
+ void testSlot(const int inSlot, const uint32_t expectedSlot) {
+ uint32_t outSlot;
+ sp<GraphicBuffer> outBuffer;
+
+ // The first time, the output is the same as the input
+ mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(mBuffer1, outBuffer);
+
+ // The second time with the same buffer, the outBuffer is nullptr.
+ mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+
+ // With a new buffer, the outBuffer is the input.
+ mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(mBuffer2, outBuffer);
+
+ // Again, the second request with the same buffer sets outBuffer to nullptr.
+ mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+
+ // Setting a slot to use nullptr lookslike works, but note that
+ // the output values make it look like no new buffer is being set....
+ mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer);
+ EXPECT_EQ(expectedSlot, outSlot);
+ EXPECT_EQ(nullptr, outBuffer.get());
+ }
+
+ impl::HwcBufferCache mCache;
sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
};
-TEST_F(HwcBufferCacheTest, testSlot) {
+TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) {
+ testSlot(0, 0);
+}
+
+TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) {
+ testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1);
+}
+
+TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) {
+ testSlot(-123, 0);
+}
+
+TEST_F(HwcBufferCacheTest, cacheGeneratesSlotForInvalidBufferSlot) {
uint32_t outSlot;
sp<GraphicBuffer> outBuffer;
- // The first time, the output is the same as the input
- mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer1, &outSlot, &outBuffer);
EXPECT_EQ(0, outSlot);
EXPECT_EQ(mBuffer1, outBuffer);
- // The second time with the same buffer, the outBuffer is nullptr.
- mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer1, &outSlot, &outBuffer);
EXPECT_EQ(0, outSlot);
EXPECT_EQ(nullptr, outBuffer.get());
- // With a new buffer, the outBuffer is the input.
- mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer);
EXPECT_EQ(1, outSlot);
EXPECT_EQ(mBuffer2, outBuffer);
- // Again, the second request with the same buffer sets outBuffer to nullptr.
- mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer);
EXPECT_EQ(1, outSlot);
EXPECT_EQ(nullptr, outBuffer.get());
- // Setting a slot to use nullptr lookslike works, but note that
- // the output values make it look like no new buffer is being set....
- mCache.getHwcBuffer(sp<GraphicBuffer>(), &outSlot, &outBuffer);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, sp<GraphicBuffer>(), &outSlot,
+ &outBuffer);
EXPECT_EQ(2, outSlot);
EXPECT_EQ(nullptr, outBuffer.get());
-}
-TEST_F(HwcBufferCacheTest, testGetLeastRecentlyUsedSlot) {
- int slot;
- uint32_t outSlot;
- sp<GraphicBuffer> outBuffer;
-
- // fill up cache
- for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
- sp<GraphicBuffer> buf{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)};
- mCache.getHwcBuffer(buf, &outSlot, &outBuffer);
- EXPECT_EQ(buf, outBuffer);
- EXPECT_EQ(i, outSlot);
- }
-
- slot = mCache.getLeastRecentlyUsedSlot();
- EXPECT_EQ(0, slot);
-
- mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer);
- EXPECT_EQ(0, outSlot);
+ // note that sending mBuffer1 with explicit slot 1 will overwrite mBuffer2
+ // and also cause mBuffer1 to be stored in two places
+ mCache.getHwcBuffer(1, mBuffer1, &outSlot, &outBuffer);
+ EXPECT_EQ(1, outSlot);
EXPECT_EQ(mBuffer1, outBuffer);
- slot = mCache.getLeastRecentlyUsedSlot();
- EXPECT_EQ(1, slot);
+ mCache.getHwcBuffer(BufferQueue::INVALID_BUFFER_SLOT, mBuffer2, &outSlot, &outBuffer);
+ EXPECT_EQ(3, outSlot);
+ EXPECT_EQ(mBuffer2, outBuffer);
}
} // namespace
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index 7927fa9..ad08a92 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -42,4 +42,32 @@
void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&, const ui::Transform&,
const Rect&, int32_t, const ui::Dataspace) {}
+Layer::RoundedCornerState ContainerLayer::getRoundedCornerStateInternal(
+ const FloatRect bounds) const {
+ const auto& p = mDrawingParent.promote();
+ if (p != nullptr) {
+ RoundedCornerState parentState = p->getRoundedCornerStateInternal(bounds);
+ 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;
+ if (radius > 0) {
+ const Rect crop = getCrop(getDrawingState());
+ if (!crop.isEmpty()) {
+ return RoundedCornerState(bounds.intersect(crop.toFloatRect()), radius);
+ }
+ return RoundedCornerState(bounds, radius);
+ }
+ return RoundedCornerState();
+}
+
} // namespace android
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index 7222a3e..cd8e722 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -40,6 +40,7 @@
bool isCreatedFromMainThread() const override { return true; }
bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
+ Layer::RoundedCornerState getRoundedCornerStateInternal(const FloatRect bounds) const override;
protected:
bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 775fb80..7370b0c 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -111,7 +111,7 @@
BufferItem item;
status_t err = acquireBufferLocked(&item, 0);
if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
- mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer);
+ mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
return NO_ERROR;
} else if (err != NO_ERROR) {
ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err);
@@ -137,7 +137,7 @@
mCurrentFence = item.mFence;
outFence = item.mFence;
- mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer);
+ mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer);
outDataspace = static_cast<Dataspace>(item.mDataSpace);
status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
if (result != NO_ERROR) {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 613dc77..4e0e4df 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -224,7 +224,7 @@
if (fbBuffer != nullptr) {
uint32_t hwcSlot = 0;
sp<GraphicBuffer> hwcBuffer;
- mHwcBufferCache.getHwcBuffer(fbBuffer, &hwcSlot, &hwcBuffer);
+ mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer);
// TODO: Correctly propagate the dataspace from GL composition
result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 898d37e..73f27e4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1791,10 +1791,10 @@
return getRoundedCornerStateInternal(mSourceBounds);
}
-Layer::RoundedCornerState Layer::getRoundedCornerStateInternal(const FloatRect bounds) const {
+Layer::RoundedCornerState Layer::getRoundedCornerStateInternal(const FloatRect) const {
const auto& p = mDrawingParent.promote();
if (p != nullptr) {
- RoundedCornerState parentState = p->getRoundedCornerStateInternal(bounds);
+ RoundedCornerState parentState = p->getRoundedCornerStateInternal(mSourceBounds);
if (parentState.radius > 0) {
ui::Transform t = getActiveTransform(getDrawingState());
t = t.inverse();
@@ -1809,7 +1809,8 @@
}
const float radius = getDrawingState().cornerRadius;
return radius > 0
- ? RoundedCornerState(bounds.intersect(getCrop(getDrawingState()).toFloatRect()), radius)
+ ? RoundedCornerState(mSourceBounds.intersect(getCrop(getDrawingState()).toFloatRect()),
+ radius)
: RoundedCornerState();
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 83ff3b6..b9dc7ec 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -452,13 +452,13 @@
virtual void setPostTime(nsecs_t /*postTime*/) {}
virtual void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/) {}
+ virtual RoundedCornerState getRoundedCornerStateInternal(const FloatRect bounds) const;
protected:
virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
bool useIdentityTransform, Region& clearRegion,
const bool supportProtectedContent,
renderengine::LayerSettings& layer);
-
public:
/*
* compositionengine::LayerFE overrides
@@ -908,8 +908,6 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
- RoundedCornerState getRoundedCornerStateInternal(const FloatRect bounds) const;
-
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling.
ui::Transform mEffectiveTransform;
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 6599f9e..a2a6bd8 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -211,13 +211,14 @@
const nsecs_t numPeriodsSinceReference = baseTime / mPeriod;
const nsecs_t predictedReference = mReferenceTime + numPeriodsSinceReference * mPeriod;
const nsecs_t phaseCorrection = mPhase + listener.mPhase;
- const nsecs_t predictedLastEventTime = predictedReference + phaseCorrection;
- if (predictedLastEventTime >= now) {
- // Make sure that the last event time does not exceed the current time.
- // If it would, then back the last event time by a period.
- listener.mLastEventTime = predictedLastEventTime - mPeriod;
- } else {
- listener.mLastEventTime = predictedLastEventTime;
+ listener.mLastEventTime = predictedReference + phaseCorrection;
+ // If we're very close in time to the predicted last event time,
+ // then we need to back up the last event time so that we can
+ // attempt to fire an event immediately.
+ //
+ // Otherwise, keep the last event time that we predicted.
+ if (isShorterThanPeriod(now - listener.mLastEventTime)) {
+ listener.mLastEventTime -= mPeriod;
}
} else {
listener.mLastEventTime = now + mPhase - mWakeupLatency;
@@ -314,7 +315,7 @@
// Sanity check that the duration is close enough in length to a period without
// falling into double-rate vsyncs.
- bool isCloseToPeriod(nsecs_t duration) {
+ bool isShorterThanPeriod(nsecs_t duration) {
// Ratio of 3/5 is arbitrary, but it must be greater than 1/2.
return duration < (3 * mPeriod) / 5;
}
@@ -330,7 +331,7 @@
nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo);
if (t < now) {
- if (isCloseToPeriod(now - eventListener.mLastCallbackTime)) {
+ if (isShorterThanPeriod(now - eventListener.mLastCallbackTime)) {
eventListener.mLastEventTime = t;
eventListener.mLastCallbackTime = now;
ALOGV("[%s] [%s] Skipping event due to model error", mName,
@@ -391,7 +392,7 @@
// Check that it's been slightly more than half a period since the last
// event so that we don't accidentally fall into double-rate vsyncs
- if (isCloseToPeriod(t - listener.mLastEventTime)) {
+ if (isShorterThanPeriod(t - listener.mLastEventTime)) {
t += mPeriod;
ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t));
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index cbcc031..9e95f95 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -57,16 +57,23 @@
}
~RefreshRateConfigs() = default;
- const std::unordered_map<RefreshRateType, RefreshRate>& getRefreshRates() {
+ const std::unordered_map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() {
return mRefreshRates;
}
- const RefreshRate& getRefreshRate(RefreshRateType type) { return mRefreshRates[type]; }
+ std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) {
+ const auto& refreshRate = mRefreshRates.find(type);
+ if (refreshRate != mRefreshRates.end()) {
+ return refreshRate->second;
+ }
+ return nullptr;
+ }
private:
void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
// This is the rate that HWC encapsulates right now when the device is in DOZE mode.
mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
- RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0});
+ std::make_shared<RefreshRate>(
+ RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0}));
if (configs.size() < 1) {
ALOGE("Device does not have valid configs. Config size is 0.");
@@ -90,9 +97,10 @@
if (vsyncPeriod != 0) {
const float fps = 1e9 / vsyncPeriod;
mRefreshRates.emplace(RefreshRateType::DEFAULT,
- RefreshRate{configIdToVsyncPeriod[0].first,
- base::StringPrintf("%2.ffps", fps),
- static_cast<uint32_t>(fps)});
+ std::make_shared<RefreshRate>(
+ RefreshRate{configIdToVsyncPeriod[0].first,
+ base::StringPrintf("%2.ffps", fps),
+ static_cast<uint32_t>(fps)}));
}
if (configs.size() < 2) {
@@ -105,13 +113,14 @@
if (vsyncPeriod != 0) {
const float fps = 1e9 / vsyncPeriod;
mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
- RefreshRate{configIdToVsyncPeriod[1].first,
- base::StringPrintf("%2.ffps", fps),
- static_cast<uint32_t>(fps)});
+ std::make_shared<RefreshRate>(
+ RefreshRate{configIdToVsyncPeriod[1].first,
+ base::StringPrintf("%2.ffps", fps),
+ static_cast<uint32_t>(fps)}));
}
}
- std::unordered_map<RefreshRateType, RefreshRate> mRefreshRates;
+ std::unordered_map<RefreshRateType, std::shared_ptr<RefreshRate>> mRefreshRates;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index d4ae330..ff63faf 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -85,10 +85,13 @@
std::unordered_map<std::string, int64_t> totalTime;
for (auto [type, config] : mRefreshRateConfigs->getRefreshRates()) {
int64_t totalTimeForConfig = 0;
- if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) {
- totalTimeForConfig = mConfigModesTotalTime.at(config.configId);
+ if (!config) {
+ continue;
}
- totalTime[config.name] = totalTimeForConfig;
+ if (mConfigModesTotalTime.find(config->configId) != mConfigModesTotalTime.end()) {
+ totalTimeForConfig = mConfigModesTotalTime.at(config->configId);
+ }
+ totalTime[config->name] = totalTimeForConfig;
}
return totalTime;
}
@@ -124,8 +127,11 @@
mConfigModesTotalTime[mode] += timeElapsedMs;
for (const auto& [type, config] : mRefreshRateConfigs->getRefreshRates()) {
- if (config.configId == mode) {
- mTimeStats->recordRefreshRate(config.fps, timeElapsed);
+ if (!config) {
+ continue;
+ }
+ if (config->configId == mode) {
+ mTimeStats->recordRefreshRate(config->fps, timeElapsed);
}
}
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 63a1e4f..a5f025d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -592,10 +592,11 @@
const auto displayId = getInternalDisplayIdLocked();
LOG_ALWAYS_FATAL_IF(!displayId);
- const auto performanceRefreshRate =
+ const auto& performanceRefreshRate =
mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
- if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
+ if (performanceRefreshRate &&
+ isConfigAllowed(*displayId, performanceRefreshRate->configId)) {
setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
} else {
setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
@@ -1471,17 +1472,24 @@
LOG_ALWAYS_FATAL_IF(!displayId);
const auto displayToken = getInternalDisplayTokenLocked();
- auto desiredConfigId = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate).configId;
- const auto display = getDisplayDeviceLocked(displayToken);
- if (desiredConfigId == display->getActiveConfig()) {
+ const auto& refreshRateConfig = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate);
+ if (!refreshRateConfig) {
+ ALOGV("Skipping refresh rate change request for unsupported rate.");
return;
}
+ const int desiredConfigId = refreshRateConfig->configId;
+
if (!isConfigAllowed(*displayId, desiredConfigId)) {
ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
return;
}
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (desiredConfigId == display->getActiveConfig()) {
+ return;
+ }
+
mPhaseOffsets->setRefreshRateType(refreshRate);
setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event);
}
@@ -2511,6 +2519,9 @@
state.displayName = info->name;
mCurrentState.displays.add(mPhysicalDisplayTokens[info->id], state);
mInterceptor->saveDisplayCreation(state);
+ // TODO(b/123715322): Removes the per-display state that was added to the scheduler.
+ mRefreshRateConfigs[info->id] = std::make_shared<scheduler::RefreshRateConfigs>(
+ getHwComposer().getConfigs(info->id));
}
} else {
ALOGV("Removing display %s", to_string(info->id).c_str());
@@ -2522,6 +2533,7 @@
mCurrentState.displays.removeItemsAt(index);
}
mPhysicalDisplayTokens.erase(info->id);
+ mRefreshRateConfigs.erase(info->id);
}
processDisplayChangesLocked();
@@ -5797,12 +5809,12 @@
int currentConfigIndex = getHwComposer().getActiveConfigIndex(*displayId);
if (!isConfigAllowed(*displayId, currentConfigIndex)) {
for (const auto& [type, config] : mRefreshRateConfigs[*displayId]->getRefreshRates()) {
- if (isConfigAllowed(*displayId, config.configId)) {
+ if (config && isConfigAllowed(*displayId, config->configId)) {
// TODO: we switch to the first allowed config. In the future
// we may want to enhance this logic to pick a similar config
// to the current one
- ALOGV("Old config is not allowed - switching to config %d", config.configId);
- setDesiredActiveConfig(displayToken, config.configId,
+ ALOGV("Old config is not allowed - switching to config %d", config->configId);
+ setDesiredActiveConfig(displayToken, config->configId,
Scheduler::ConfigEvent::Changed);
break;
}
@@ -5812,9 +5824,10 @@
// If idle timer and fps detection are disabled and we are in RefreshRateType::DEFAULT,
// there is no trigger to move to RefreshRateType::PERFORMANCE, even if it is an allowed.
if (!mScheduler->isIdleTimerEnabled() && !mUseSmart90ForVideo) {
- const auto performanceRefreshRate =
+ const auto& performanceRefreshRate =
mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
- if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
+ if (performanceRefreshRate &&
+ isConfigAllowed(*displayId, performanceRefreshRate->configId)) {
setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::Changed);
}
}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 25ce4ac..12b41fd 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -47,6 +47,7 @@
"LayerMetadataTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
+ "RefreshRateConfigsTest.cpp",
"RefreshRateStatsTest.cpp",
"TimeStatsTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
new file mode 100644
index 0000000..b218ad6
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2019 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 <log/log.h>
+#include <thread>
+
+#include "DisplayHardware/HWC2.h"
+#include "Scheduler/RefreshRateConfigs.h"
+#include "mock/DisplayHardware/MockDisplay.h"
+
+using namespace std::chrono_literals;
+using testing::_;
+
+namespace android {
+namespace scheduler {
+
+using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+using RefreshRate = RefreshRateConfigs::RefreshRate;
+
+class RefreshRateConfigsTest : public testing::Test {
+protected:
+ static constexpr int CONFIG_ID_60 = 0;
+ static constexpr int CONFIG_ID_90 = 1;
+ static constexpr int64_t VSYNC_60 = 16666667;
+ static constexpr int64_t VSYNC_90 = 11111111;
+
+ RefreshRateConfigsTest();
+ ~RefreshRateConfigsTest();
+
+ void assertRatesEqual(const RefreshRate& left, const RefreshRate& right) {
+ ASSERT_EQ(left.configId, right.configId);
+ ASSERT_EQ(left.name, right.name);
+ ASSERT_EQ(left.fps, right.fps);
+ }
+};
+
+RefreshRateConfigsTest::RefreshRateConfigsTest() {
+ 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());
+}
+
+RefreshRateConfigsTest::~RefreshRateConfigsTest() {
+ 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());
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) {
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+ RefreshRateConfigs configs(displayConfigs);
+
+ // We always store a configuration for screen off.
+ const auto& rates = configs.getRefreshRates();
+ ASSERT_EQ(1, rates.size());
+ const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+ ASSERT_NE(rates.end(), powerSavingRate);
+ ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
+ ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT));
+
+ RefreshRate expectedConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+ assertRatesEqual(expectedConfig, *powerSavingRate->second);
+
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ assertRatesEqual(expectedConfig, *configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ ASSERT_FALSE(configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+ ASSERT_FALSE(configs.getRefreshRate(RefreshRateType::DEFAULT));
+
+ // Sanity check that getRefreshRate() does not modify the underlying configs.
+ ASSERT_EQ(1, configs.getRefreshRates().size());
+}
+
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) {
+ auto display = new Hwc2::mock::Display();
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+ auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+ config60.setVsyncPeriod(VSYNC_60);
+ displayConfigs.push_back(config60.build());
+ RefreshRateConfigs configs(displayConfigs);
+
+ const auto& rates = configs.getRefreshRates();
+ ASSERT_EQ(2, rates.size());
+ const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+ const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
+ ASSERT_NE(rates.end(), powerSavingRate);
+ ASSERT_NE(rates.end(), defaultRate);
+ ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
+
+ RefreshRate expectedPowerSavingConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+ assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
+ RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60};
+ assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
+
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ assertRatesEqual(expectedPowerSavingConfig,
+ *configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::DEFAULT));
+ assertRatesEqual(expectedDefaultConfig, *configs.getRefreshRate(RefreshRateType::DEFAULT));
+ ASSERT_FALSE(configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+
+ // Sanity check that getRefreshRate() does not modify the underlying configs.
+ ASSERT_EQ(2, configs.getRefreshRates().size());
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) {
+ auto display = new Hwc2::mock::Display();
+ std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
+ auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
+ config60.setVsyncPeriod(VSYNC_60);
+ displayConfigs.push_back(config60.build());
+ auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
+ config90.setVsyncPeriod(VSYNC_90);
+ displayConfigs.push_back(config90.build());
+ RefreshRateConfigs configs(displayConfigs);
+
+ const auto& rates = configs.getRefreshRates();
+ ASSERT_EQ(3, rates.size());
+ const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
+ const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
+ const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
+ ASSERT_NE(rates.end(), powerSavingRate);
+ ASSERT_NE(rates.end(), defaultRate);
+ ASSERT_NE(rates.end(), performanceRate);
+
+ RefreshRate expectedPowerSavingConfig = RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0};
+ assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
+ RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60};
+ assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
+ RefreshRate expectedPerformanceConfig = RefreshRate{CONFIG_ID_90, "90fps", 90};
+ assertRatesEqual(expectedPerformanceConfig, *performanceRate->second);
+
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ assertRatesEqual(expectedPowerSavingConfig,
+ *configs.getRefreshRate(RefreshRateType::POWER_SAVING));
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::DEFAULT));
+ assertRatesEqual(expectedDefaultConfig, *configs.getRefreshRate(RefreshRateType::DEFAULT));
+ ASSERT_TRUE(configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+ assertRatesEqual(expectedPerformanceConfig,
+ *configs.getRefreshRate(RefreshRateType::PERFORMANCE));
+}
+} // namespace
+} // namespace scheduler
+} // namespace android