Merge "SF: VSyncReactor ensure period change confirmation"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 20bfe65..4a84884 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -14,10 +14,12 @@
libs/renderengine/
libs/ui/
libs/vr/
+ opengl/libs/
services/bufferhub/
services/inputflinger/
services/surfaceflinger/
services/vr/
+ vulkan/
[Hook Scripts]
owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 6b14bee..a10baa6 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -108,6 +108,8 @@
* Property to control if app data isolation is enabled.
*/
static constexpr const char* kAppDataIsolationEnabledProperty = "persist.zygote.app_data_isolation";
+static constexpr const char* kMntSdcardfs = "/mnt/runtime/default/";
+static constexpr const char* kMntFuse = "/mnt/pass_through/0/";
static std::atomic<bool> sAppDataIsolationEnabled(false);
@@ -2686,15 +2688,13 @@
std::getline(in, ignored);
if (android::base::GetBoolProperty(kFuseProp, false)) {
- // TODO(b/146139106): Use sdcardfs mounts on devices running sdcardfs so we don't bypass
- // it's VFS cache
- if (target.compare(0, 17, "/mnt/pass_through") == 0) {
+ if (target.find(kMntFuse) == 0) {
LOG(DEBUG) << "Found storage mount " << source << " at " << target;
mStorageMounts[source] = target;
}
} else {
#if !BYPASS_SDCARDFS
- if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
+ if (target.find(kMntSdcardfs) == 0) {
LOG(DEBUG) << "Found storage mount " << source << " at " << target;
mStorageMounts[source] = target;
}
@@ -2792,17 +2792,6 @@
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
auto path = StringPrintf("%s/media", create_data_path(uuid_).c_str());
- if (android::base::GetBoolProperty(kFuseProp, false)) {
- // TODO(b/146139106): This is only safe on devices not running sdcardfs where there is no
- // risk of bypassing the sdcardfs VFS cache
-
- // Always use the lower filesystem path on FUSE enabled devices not running sdcardfs
- // The upper filesystem path, /mnt/pass_through/<userid>/<vol>/ which was a bind mount
- // to the lower filesytem may have been unmounted already when a user is
- // removed and the path will now be pointing to a tmpfs without content
- return StringPrintf("%s/%u", path.c_str(), userid);
- }
-
auto resolved = mStorageMounts[path];
if (resolved.empty()) {
LOG(WARNING) << "Failed to find storage mount for " << path;
diff --git a/data/etc/android.hardware.telephony.cdma.xml b/data/etc/android.hardware.telephony.cdma.xml
index 082378d..b598f68 100644
--- a/data/etc/android.hardware.telephony.cdma.xml
+++ b/data/etc/android.hardware.telephony.cdma.xml
@@ -18,4 +18,5 @@
<permissions>
<feature name="android.hardware.telephony" />
<feature name="android.hardware.telephony.cdma" />
+ <feature name="android.hardware.telephony.data" />
</permissions>
diff --git a/data/etc/android.hardware.telephony.gsm.xml b/data/etc/android.hardware.telephony.gsm.xml
index 7927fa8..fe8a5cf 100644
--- a/data/etc/android.hardware.telephony.gsm.xml
+++ b/data/etc/android.hardware.telephony.gsm.xml
@@ -18,4 +18,5 @@
<permissions>
<feature name="android.hardware.telephony" />
<feature name="android.hardware.telephony.gsm" />
+ <feature name="android.hardware.telephony.data" />
</permissions>
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
new file mode 100644
index 0000000..50daaba
--- /dev/null
+++ b/include/android/imagedecoder.h
@@ -0,0 +1,299 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup ImageDecoder
+ * @{
+ */
+
+/**
+ * @file imageDecoder.h
+ */
+
+#ifndef ANDROID_IMAGE_DECODER_H
+#define ANDROID_IMAGE_DECODER_H
+
+#include "bitmap.h"
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AAsset;
+struct ARect;
+
+#if __ANDROID_API__ >= 30
+
+/** AImageDecoder functions result code. */
+enum {
+ // Decoding was successful and complete.
+ ANDROID_IMAGE_DECODER_SUCCESS = 0,
+ // The input was incomplete. In decodeImage, this means a partial
+ // image was decoded. Undecoded lines are all zeroes.
+ // In AImageDecoder_create*, no AImageDecoder was created.
+ ANDROID_IMAGE_DECODER_INCOMPLETE = -1,
+ // The input contained an error after decoding some lines. Similar to
+ // INCOMPLETE, above.
+ ANDROID_IMAGE_DECODER_ERROR = -2,
+ // Could not convert, e.g. attempting to decode an image with
+ // alpha to an opaque format.
+ ANDROID_IMAGE_DECODER_INVALID_CONVERSION = -3,
+ // The scale is invalid. It may have overflowed, or it may be incompatible
+ // with the current alpha setting.
+ ANDROID_IMAGE_DECODER_INVALID_SCALE = -4,
+ // Some other parameter was bad (e.g. pixels)
+ ANDROID_IMAGE_DECODER_BAD_PARAMETER = -5,
+ // Input was invalid i.e. broken before decoding any pixels.
+ ANDROID_IMAGE_DECODER_INVALID_INPUT = -6,
+ // A seek was required, and failed.
+ ANDROID_IMAGE_DECODER_SEEK_ERROR = -7,
+ // Some other error (e.g. OOM)
+ ANDROID_IMAGE_DECODER_INTERNAL_ERROR = -8,
+ // We did not recognize the format
+ ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT = -9
+};
+
+struct AImageDecoder;
+
+/**
+ * Opaque handle for decoding images.
+ *
+ * Create using one of the following:
+ * - {@link AImageDecoder_createFromAAsset}
+ * - {@link AImageDecoder_createFromFd}
+ * - {@link AImageDecoder_createFromBuffer}
+ */
+typedef struct AImageDecoder AImageDecoder;
+
+/**
+ * Create a new AImageDecoder from an AAsset.
+ *
+ * @param asset {@link AAsset} containing encoded image data. Client is still
+ * responsible for calling {@link AAsset_close} on it.
+ * @param outDecoder On success (i.e. return value is
+ * {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to
+ * a newly created {@link AImageDecoder}. Caller is
+ * responsible for calling {@link AImageDecoder_delete} on it.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ * indicating reason for the failure.
+ */
+int AImageDecoder_createFromAAsset(AAsset* asset, AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+
+/**
+ * Create a new AImageDecoder from a file descriptor.
+ *
+ * @param fd Seekable, readable, open file descriptor for encoded data.
+ * Client is still responsible for closing it, which may be done
+ * *after* deleting the returned AImageDecoder.
+ * @param outDecoder On success (i.e. return value is
+ * {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to
+ * a newly created {@link AImageDecoder}. Caller is
+ * responsible for calling {@link AImageDecoder_delete} on it.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ * indicating reason for the failure.
+ */
+int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+
+/**
+ * Create a new AImageDecoder from a buffer.
+ *
+ * @param buffer Pointer to encoded data. Must be valid for the entire time
+ * the AImageDecoder is used.
+ * @param length Byte length of buffer.
+ * @param outDecoder On success (i.e. return value is
+ * {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to
+ * a newly created {@link AImageDecoder}. Caller is
+ * responsible for calling {@link AImageDecoder_delete} on it.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ * indicating reason for the failure.
+ */
+int AImageDecoder_createFromBuffer(const void* buffer, size_t length,
+ AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+
+/**
+ * Delete the AImageDecoder.
+ */
+void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30);
+
+/**
+ * Choose the desired output format.
+ *
+ * @param format AndroidBitmapFormat to use
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} if the format is compatible
+ * with the image and {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}
+ * otherwise. In the latter case, the AImageDecoder uses the
+ * format it was already planning to use (either its default
+ * or a previously successful setting from this function).
+ */
+int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*,
+ int32_t format) __INTRODUCED_IN(30);
+
+/*
+ * Choose the desired output format.
+ *
+ * Must be one of:
+ * {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}
+ * {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE}
+ * {@link ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL}
+ *
+ * Note: An OPAQUE image may be set to any of them.
+ * A non-OPAQUE image may not be set to OPAQUE
+ *
+ * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION} if the conversion
+ * is not possible
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} for bad parameters
+ */
+int AImageDecoder_setAlphaFlags(AImageDecoder*, int alphaFlags) __INTRODUCED_IN(30);
+
+/**
+ * Specify the output size for a decoded image.
+ *
+ * Future calls to {@link AImageDecoder_decodeImage} will sample or scale the
+ * encoded image to reach the desired size. If a crop rect is set (via
+ * {@link AImageDecoder_setCrop}), it must be contained within the dimensions
+ * specified by width and height, and the output image will be the size of the
+ * crop rect.
+ *
+ * @param width Width of the output (prior to cropping).
+ * This will affect future calls to
+ * {@link AImageDecoder_getMinimumStride}, which will now return
+ * a value based on this width.
+ * @param height Height of the output (prior to cropping).
+ * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if the AImageDecoder
+ * pointer is null, width or height is <= 0, or any existing crop is
+ * not contained by the image dimensions.
+ */
+int AImageDecoder_setTargetSize(AImageDecoder*, int width, int height) __INTRODUCED_IN(30);
+
+/**
+ * Specify how to crop the output after scaling (if any).
+ *
+ * Future calls to {@link AImageDecoder_decodeImage} will crop their output to
+ * the specified {@link ARect}. Clients will only need to allocate enough memory
+ * for the cropped ARect.
+ *
+ * @param crop Rectangle describing a crop of the decode. It must be contained inside of
+ * the (possibly scaled, by {@link AImageDecoder_setTargetSize})
+ * image dimensions. This will affect future calls to
+ * {@link AImageDecoder_getMinimumStride}, which will now return a
+ * value based on the width of the crop. An empty ARect -
+ * specifically { 0, 0, 0, 0 } - may be used to remove the cropping
+ * behavior. Any other empty or unsorted ARects will result in
+ * returning ANDROID_IMAGE_DECODER_BAD_PARAMETER.
+ * @return - {@link ANDROID_IMAGE_DECODER_SUCCESS} on success
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER} if the AImageDecoder
+ * pointer is null or the crop is not contained by the image
+ * dimensions.
+ */
+int AImageDecoder_setCrop(AImageDecoder*, ARect crop) __INTRODUCED_IN(30);
+
+/**
+ * Opaque handle for reading header info.
+ */
+struct AImageDecoderHeaderInfo;
+typedef struct AImageDecoderHeaderInfo AImageDecoderHeaderInfo;
+
+/**
+ * Return an opaque handle for reading header info.
+ *
+ * This is owned by the {@link AImageDecoder} and will be destroyed when the
+ * AImageDecoder is destroyed via {@link AImageDecoder_delete}.
+ */
+const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(
+ const AImageDecoder*) __INTRODUCED_IN(30);
+
+/**
+ * Report the native width of the encoded image.
+ */
+int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
+ * Report the native height of the encoded image.
+ */
+int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
+ * Report the mimeType of the encoded image.
+ *
+ * @return a string literal describing the mime type.
+ */
+const char* AImageDecoderHeaderInfo_getMimeType(
+ const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
+ * Report whether the encoded image represents an animation.
+ */
+bool AImageDecoderHeaderInfo_isAnimated(
+ const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
+ * Report the AndroidBitmapFormat the AImageDecoder will decode to
+ * by default. AImageDecoder will try to choose one that is sensible
+ * for the image and the system. Note that this does not indicate the
+ * encoded format of the image.
+ */
+AndroidBitmapFormat AImageDecoderHeaderInfo_getAndroidBitmapFormat(
+ const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
+ * Report how the AImageDecoder will handle alpha by default. If the image
+ * contains no alpha (according to its header), this will return
+ * {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE}. If the image may contain alpha,
+ * this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}.
+ *
+ * For animated images only the opacity of the first frame is reported.
+ */
+int AImageDecoderHeaderInfo_getAlphaFlags(
+ const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
+ * Return the minimum stride that can be used, taking the specified
+ * (or default) (possibly scaled) width, crop rect and
+ * {@link AndroidBitmapFormat} into account.
+ */
+size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30);
+
+/**
+ * Decode the image into pixels, using the settings of the AImageDecoder.
+ *
+ * @param decoder Opaque object representing the decoder.
+ * @param pixels On success, will be filled with the result
+ * of the decode. Must be large enough to fit |size| bytes.
+ * @param stride Width in bytes of a single row. Must be at least
+ * {@link AImageDecoder_getMinimumStride}.
+ * @param size Size of the pixel buffer in bytes. Must be at least
+ * stride * (height - 1) +
+ * {@link AImageDecoder_getMinimumStride}.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success, or an error code
+ * from the same enum describing the failure.
+ */
+int AImageDecoder_decodeImage(AImageDecoder* decoder,
+ void* pixels, size_t stride,
+ size_t size) __INTRODUCED_IN(30);
+
+#endif // __ANDROID_API__ >= 30
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_IMAGE_DECODER_H
+
+/** @} */
diff --git a/libs/android_runtime_lazy/android_runtime_lazy.cpp b/libs/android_runtime_lazy/android_runtime_lazy.cpp
index 98d8e8a..8062be6 100644
--- a/libs/android_runtime_lazy/android_runtime_lazy.cpp
+++ b/libs/android_runtime_lazy/android_runtime_lazy.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "ANDROID_RUNTIME_LAZY"
#include "android_runtime/AndroidRuntime.h"
+#include "android_os_Parcel.h"
#include "android_util_Binder.h"
#include <dlfcn.h>
@@ -28,12 +29,18 @@
std::once_flag loadFlag;
typedef JNIEnv* (*getJNIEnv_t)();
+
+// android_util_Binder.h
typedef sp<IBinder> (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj);
typedef jobject (*javaObjectForIBinder_t)(JNIEnv* env, const sp<IBinder>& val);
+// android_os_Parcel.h
+typedef Parcel* (*parcelForJavaObject_t)(JNIEnv* env, jobject obj);
+
getJNIEnv_t _getJNIEnv;
ibinderForJavaObject_t _ibinderForJavaObject;
javaObjectForIBinder_t _javaObjectForIBinder;
+parcelForJavaObject_t _parcelForJavaObject;
void load() {
std::call_once(loadFlag, []() {
@@ -64,6 +71,13 @@
ALOGW("Could not find javaObjectForIBinder.");
// no return
}
+
+ _parcelForJavaObject = reinterpret_cast<parcelForJavaObject_t>(
+ dlsym(handle, "_ZN7android19parcelForJavaObjectEP7_JNIEnvP8_jobject"));
+ if (_parcelForJavaObject == nullptr) {
+ ALOGW("Could not find parcelForJavaObject.");
+ // no return
+ }
});
}
@@ -95,4 +109,12 @@
return _javaObjectForIBinder(env, val);
}
+Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) {
+ load();
+ if (_parcelForJavaObject == nullptr) {
+ return nullptr;
+ }
+ return _parcelForJavaObject(env, obj);
+}
+
} // namespace android
diff --git a/libs/android_runtime_lazy/include/android_os_Parcel.h b/libs/android_runtime_lazy/include/android_os_Parcel.h
new file mode 100644
index 0000000..19b094d
--- /dev/null
+++ b/libs/android_runtime_lazy/include/android_os_Parcel.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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 "jni.h"
+
+namespace android {
+
+// The name of this file is same with the file in frameworks/base/core/jni/
+// This is intentional to make the client use these exported functions
+// in the same way with the original.
+
+Parcel* parcelForJavaObject(JNIEnv* env, jobject obj);
+
+} // namespace android
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index fa07d04..b37db43 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -50,6 +50,7 @@
"ibinder.cpp",
"ibinder_jni.cpp",
"parcel.cpp",
+ "parcel_jni.cpp",
"process.cpp",
"stability.cpp",
"status.cpp",
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
index be3029c..cd1ff1f 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
@@ -36,13 +36,13 @@
/**
* Converts an android.os.IBinder object into an AIBinder* object.
*
- * If either env or the binder is null, null is returned. If this binder object was originally an
+ * If the binder is null, null is returned. If this binder object was originally an
* AIBinder object, the original object is returned. The returned object has one refcount
* associated with it, and so this should be accompanied with an AIBinder_decStrong call.
*
* Available since API level 29.
*
- * \param env Java environment.
+ * \param env Java environment. Must not be null.
* \param binder android.os.IBinder java object.
*
* \return an AIBinder object representing the Java binder object. If either parameter is null, or
@@ -54,12 +54,12 @@
/**
* Converts an AIBinder* object into an android.os.IBinder object.
*
- * If either env or the binder is null, null is returned. If this binder object was originally an
- * IBinder object, the original java object will be returned.
+ * If the binder is null, null is returned. If this binder object was originally an IBinder object,
+ * the original java object will be returned.
*
* Available since API level 29.
*
- * \param env Java environment.
+ * \param env Java environment. Must not be null.
* \param binder the object to convert.
*
* \return an android.os.IBinder object or null if the parameters were null.
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h b/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h
new file mode 100644
index 0000000..65e1704
--- /dev/null
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_parcel_jni.h
+ * @brief Conversions between AParcel and android.os.Parcel
+ */
+
+#pragma once
+
+#include <android/binder_parcel.h>
+
+#include <jni.h>
+
+__BEGIN_DECLS
+#if __ANDROID_API__ >= 30
+
+/**
+ * Converts an android.os.Parcel object into an AParcel* object.
+ *
+ * If the parcel is null, null is returned.
+ *
+ * Available since API level 30.
+ *
+ * \param env Java environment. Must not be null.
+ * \param parcel android.os.Parcel java object.
+ *
+ * \return an AParcel object representing the Java parcel object. If either parameter is null, this
+ * will return null. This must be deleted with AParcel_delete. This does not take ownership of the
+ * jobject and is only good for as long as the jobject is alive.
+ */
+__attribute__((warn_unused_result)) AParcel* AParcel_fromJavaParcel(JNIEnv* env, jobject parcel)
+ __INTRODUCED_IN(30);
+
+#endif //__ANDROID_API__ >= 30
+__END_DECLS
+
+/** @} */
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 71d8103..f3158d7 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -105,6 +105,7 @@
AIBinder_setExtension;
AStatus_getDescription;
AStatus_deleteDescription;
+ AParcel_fromJavaParcel;
AIBinder_markSystemStability; # apex
AIBinder_markVendorStability; # llndk
diff --git a/libs/binder/ndk/parcel_jni.cpp b/libs/binder/ndk/parcel_jni.cpp
new file mode 100644
index 0000000..53b2d7c
--- /dev/null
+++ b/libs/binder/ndk/parcel_jni.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/binder_parcel_jni.h>
+#include "parcel_internal.h"
+
+#include <android_os_Parcel.h>
+
+using ::android::Parcel;
+using ::android::parcelForJavaObject;
+
+AParcel* AParcel_fromJavaParcel(JNIEnv* env, jobject jbinder) {
+ if (jbinder == nullptr) {
+ return nullptr;
+ }
+
+ Parcel* parcel = parcelForJavaObject(env, jbinder);
+
+ if (parcel == nullptr) {
+ return nullptr;
+ }
+
+ return new AParcel(nullptr /*binder*/, parcel, false /*shouldOwn*/);
+}
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 0477801..875059c 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -34,7 +34,6 @@
"/system/bin/mediametrics", // media.metrics
"/system/bin/mediaserver",
"/system/bin/netd",
- "/system/bin/vold",
"/system/bin/sdcard",
"/system/bin/statsd",
"/system/bin/surfaceflinger",
@@ -44,6 +43,13 @@
NULL,
};
+
+// Native processes to dump on debuggable builds.
+static const char* debuggable_native_processes_to_dump[] = {
+ "/system/bin/vold",
+ NULL,
+};
+
/* list of hal interface to dump containing process during native dumps */
static const char* hal_interfaces_to_dump[] {
"android.hardware.audio@2.0::IDevicesFactory",
@@ -106,6 +112,15 @@
return true;
}
}
+
+ if (android::base::GetBoolProperty("ro.debuggable", false)) {
+ for (const char** p = debuggable_native_processes_to_dump; *p; p++) {
+ if (!strcmp(*p, path)) {
+ return true;
+ }
+ }
+ }
+
return false;
}
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 354703b..befabee 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -145,6 +145,12 @@
void GraphicsEnv::hintActivityLaunch() {
ATRACE_CALL();
+ {
+ std::lock_guard<std::mutex> lock(mStatsLock);
+ if (mActivityLaunched) return;
+ mActivityLaunched = true;
+ }
+
std::thread trySendGpuStatsThread([this]() {
// If there's already graphics driver preloaded in the process, just send
// the stats info to GpuStats directly through async binder.
@@ -228,12 +234,11 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mStatsLock);
- const bool doNotSend = mGpuStats.appPackageName.empty();
if (api == GpuStatsInfo::Api::API_GL) {
- if (doNotSend) mGpuStats.glDriverToSend = true;
+ mGpuStats.glDriverToSend = true;
mGpuStats.glDriverLoadingTime = driverLoadingTime;
} else {
- if (doNotSend) mGpuStats.vkDriverToSend = true;
+ mGpuStats.vkDriverToSend = true;
mGpuStats.vkDriverLoadingTime = driverLoadingTime;
}
@@ -250,10 +255,18 @@
return interface_cast<IGpuService>(binder);
}
+bool GraphicsEnv::readyToSendGpuStatsLocked() {
+ // Only send stats for processes having at least one activity launched and that process doesn't
+ // skip the GraphicsEnvironment setup.
+ return mActivityLaunched && !mGpuStats.appPackageName.empty();
+}
+
void GraphicsEnv::setTargetStats(const GpuStatsInfo::Stats stats, const uint64_t value) {
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mStatsLock);
+ if (!readyToSendGpuStatsLocked()) return;
+
const sp<IGpuService> gpuService = getGpuService();
if (gpuService) {
gpuService->setTargetStats(mGpuStats.appPackageName, mGpuStats.driverVersionCode, stats,
@@ -265,8 +278,7 @@
int64_t driverLoadingTime) {
ATRACE_CALL();
- // Do not sendGpuStats for those skipping the GraphicsEnvironment setup
- if (mGpuStats.appPackageName.empty()) return;
+ if (!readyToSendGpuStatsLocked()) return;
ALOGV("sendGpuStats:\n"
"\tdriverPackageName[%s]\n"
@@ -599,6 +611,10 @@
return mDriverNamespace;
}
+std::string GraphicsEnv::getDriverPath() const {
+ return mDriverPath;
+}
+
android_namespace_t* GraphicsEnv::getAngleNamespace() {
std::lock_guard<std::mutex> lock(mNamespaceMutex);
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index c6dc1f8..22a2332 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -58,6 +58,7 @@
void setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries);
// Get the updatable driver namespace.
android_namespace_t* getDriverNamespace();
+ std::string getDriverPath() const;
/*
* Apis for GpuStats
@@ -131,6 +132,8 @@
void updateUseAngle();
// Link updatable driver namespace with llndk and vndk-sp libs.
bool linkDriverNamespaceLocked(android_namespace_t* vndkNamespace);
+ // Check whether this process is ready to send stats.
+ bool readyToSendGpuStatsLocked();
// Send the initial complete GpuStats to GpuService.
void sendGpuStatsLocked(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime);
@@ -141,6 +144,8 @@
std::string mSphalLibraries;
// This mutex protects mGpuStats and get gpuservice call.
std::mutex mStatsLock;
+ // Cache the activity launch info
+ bool mActivityLaunched = false;
// Information bookkept for GpuStats.
GpuStatsInfo mGpuStats;
// Path to ANGLE libs.
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index f378fc5..9fba3d5 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -225,9 +225,11 @@
.surfaceControls[surfaceStats.surfaceControl],
surfaceStats.acquireTime, surfaceStats.previousReleaseFence,
surfaceStats.transformHint);
- callbacksMap[callbackId]
- .surfaceControls[surfaceStats.surfaceControl]
- ->setTransformHint(surfaceStats.transformHint);
+ if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
+ callbacksMap[callbackId]
+ .surfaceControls[surfaceStats.surfaceControl]
+ ->setTransformHint(surfaceStats.transformHint);
+ }
}
callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 1f77393..15d937e 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -160,10 +160,11 @@
void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb) {
{
AutoMutex _l{mLock};
- std::remove_if(mRefreshRateCallbacks.begin(), mRefreshRateCallbacks.end(),
- [&](const RefreshRateCallback& callback) {
- return cb == callback.callback;
- });
+ mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
+ mRefreshRateCallbacks.end(),
+ [&](const RefreshRateCallback& callback) {
+ return cb == callback.callback;
+ }));
if (mRefreshRateCallbacks.empty()) {
toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedSuppress);
}
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 0ba01f4..842af18 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -138,6 +138,11 @@
static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));
+ static_assert(static_cast<int>(ADATASPACE_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_ADOBE_RGB));
+ static_assert(static_cast<int>(ADATASPACE_BT2020) == static_cast<int>(HAL_DATASPACE_BT2020));
+ static_assert(static_cast<int>(ADATASPACE_BT709) == static_cast<int>(HAL_DATASPACE_V0_BT709));
+ static_assert(static_cast<int>(ADATASPACE_DCI_P3) == static_cast<int>(HAL_DATASPACE_DCI_P3));
+ static_assert(static_cast<int>(ADATASPACE_SRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SRGB_LINEAR));
if (!window || !query(window, NATIVE_WINDOW_IS_VALID) ||
!isDataSpaceValid(window, dataSpace)) {
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index 2899bcf..e759513 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -101,6 +101,56 @@
* Use full range, SMPTE 2084 (PQ) transfer and BT2020 standard
*/
ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
+
+ /**
+ * Adobe RGB
+ *
+ * Use full range, gamma 2.2 transfer and Adobe RGB primaries
+ * Note: Application is responsible for gamma encoding the data as
+ * a 2.2 gamma encoding is not supported in HW.
+ */
+ ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
+
+ /**
+ * ITU-R Recommendation 2020 (BT.2020)
+ *
+ * Ultra High-definition television
+ *
+ * Use full range, BT.709 transfer and BT2020 standard
+ */
+ ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL
+
+ /**
+ * ITU-R Recommendation 709 (BT.709)
+ *
+ * High-definition television
+ *
+ * Use limited range, BT.709 transfer and BT.709 standard.
+ */
+ ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+
+ /**
+ * SMPTE EG 432-1 and SMPTE RP 431-2.
+ *
+ * Digital Cinema DCI-P3
+ *
+ * Use full range, gamma 2.6 transfer and D65 DCI-P3 standard
+ * Note: Application is responsible for gamma encoding the data as
+ * a 2.6 gamma encoding is not supported in HW.
+ */
+ ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL
+
+ /**
+ * sRGB linear encoding:
+ *
+ * The red, green, and blue components are stored in sRGB space, but
+ * are linear, not gamma-encoded.
+ * The RGB primaries and the white point are the same as BT.709.
+ *
+ * The values are encoded using the full range ([0,255] for 8-bit) for all
+ * components.
+ */
+ ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
};
__END_DECLS
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 188ac6b..b771538 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -1215,6 +1215,12 @@
return Void();
}
+Return<void> HardwareComposer::ComposerCallback::onSeamlessPossible(
+ Hwc2::Display /*display*/) {
+ LOG_ALWAYS_FATAL("Unexpected onSeamlessPossible callback");
+ return Void();
+}
+
void HardwareComposer::ComposerCallback::SetVsyncService(
const sp<VsyncService>& vsync_service) {
std::lock_guard<std::mutex> lock(mutex_);
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index 8698814..bfce10b 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -381,6 +381,7 @@
hardware::Return<void> onVsyncPeriodTimingChanged(
Hwc2::Display display,
const Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override;
+ hardware::Return<void> onSeamlessPossible(Hwc2::Display display) override;
bool GotFirstHotplug() { return got_first_hotplug_; }
void SetVsyncService(const sp<VsyncService>& vsync_service);
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index e143260..51878e9 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -25,7 +25,6 @@
#include <dlfcn.h>
#include <android/dlext.h>
-#include <cutils/properties.h>
#include <log/log.h>
#include <utils/Timers.h>
@@ -206,6 +205,7 @@
}
cnx->systemDriverUnloaded = true;
+ cnx->loadedDriverType = egl_connection_t::GLES_NO_DRIVER;
}
void* Loader::open(egl_connection_t* cnx)
@@ -228,6 +228,7 @@
} else {
cnx->shouldUseAngle = false;
}
+ cnx->loadedDriverType = egl_connection_t::GLES_NO_DRIVER;
// Firstly, try to load ANGLE driver.
driver_t* hnd = attempt_to_load_angle(cnx);
@@ -237,22 +238,31 @@
}
bool failToLoadFromDriverSuffixProperty = false;
+ cnx->systemDriverUseExactName = true;
if (!hnd) {
+ // If updated driver apk is set but fail to load, abort here.
+ if (android::GraphicsEnv::getInstance().getDriverNamespace()) {
+ LOG_ALWAYS_FATAL("couldn't find an OpenGL ES implementation from %s",
+ android::GraphicsEnv::getInstance().getDriverPath().c_str());
+ }
// Finally, try to load system driver, start by searching for the library name appended by
// the system properties of the GLES userspace driver in both locations.
// i.e.:
// libGLES_${prop}.so, or:
// libEGL_${prop}.so, libGLESv1_CM_${prop}.so, libGLESv2_${prop}.so
- char prop[PROPERTY_VALUE_MAX + 1];
for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
- if (property_get(key, prop, nullptr) <= 0) {
+ if (property_get(key, cnx->systemDriverProperty, nullptr) <= 0) {
continue;
}
- hnd = attempt_to_load_system_driver(cnx, prop, true);
+ hnd = attempt_to_load_system_driver(cnx, cnx->systemDriverProperty,
+ cnx->systemDriverUseExactName);
if (hnd) {
+ cnx->systemDriverUseProperty = true;
break;
- } else if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) {
- failToLoadFromDriverSuffixProperty = true;
+ } else {
+ if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) {
+ failToLoadFromDriverSuffixProperty = true;
+ }
}
}
}
@@ -263,11 +273,12 @@
// i.e.:
// libGLES.so, or:
// libEGL.so, libGLESv1_CM.so, libGLESv2.so
- hnd = attempt_to_load_system_driver(cnx, nullptr, true);
+ hnd = attempt_to_load_system_driver(cnx, nullptr, cnx->systemDriverUseExactName);
}
if (!hnd && !failToLoadFromDriverSuffixProperty) {
- hnd = attempt_to_load_system_driver(cnx, nullptr, false);
+ cnx->systemDriverUseExactName = false;
+ hnd = attempt_to_load_system_driver(cnx, nullptr, cnx->systemDriverUseExactName);
}
if (!hnd) {
@@ -282,14 +293,11 @@
if (!cnx->libEgl) {
cnx->libEgl = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so");
}
- if (!cnx->libGles1) {
- cnx->libGles1 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so");
- }
if (!cnx->libGles2) {
cnx->libGles2 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv2.so");
}
- if (!cnx->libEgl || !cnx->libGles2 || !cnx->libGles1) {
+ if (!cnx->libEgl || !cnx->libGles2) {
android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL,
false, systemTime() - openTime);
}
@@ -297,8 +305,7 @@
LOG_ALWAYS_FATAL_IF(!cnx->libEgl,
"couldn't load system EGL wrapper libraries");
- LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1,
- "couldn't load system OpenGL ES wrapper libraries");
+ LOG_ALWAYS_FATAL_IF(!cnx->libGles2, "couldn't load system OpenGL ESv2+ wrapper libraries");
android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL, true,
systemTime() - openTime);
@@ -314,7 +321,7 @@
cnx->shouldUseAngle = false;
cnx->angleDecided = false;
- cnx->useAngle = false;
+ cnx->loadedDriverType = egl_connection_t::GLES_NO_DRIVER;
if (cnx->vendorEGL) {
dlclose(cnx->vendorEGL);
@@ -519,7 +526,6 @@
if (so) {
ALOGV("Loaded ANGLE %s library for '%s' (instead of native)", kind,
android::GraphicsEnv::getInstance().getAngleAppName().c_str());
- cnx->useAngle = true;
char prop[PROPERTY_VALUE_MAX];
@@ -562,7 +568,6 @@
} else {
ALOGV("Loaded native %s library for '%s' (instead of ANGLE)", kind,
android::GraphicsEnv::getInstance().getAngleAppName().c_str());
- cnx->useAngle = false;
}
cnx->angleDecided = true;
@@ -592,6 +597,7 @@
Loader::driver_t* Loader::attempt_to_load_angle(egl_connection_t* cnx) {
ATRACE_CALL();
+ assert(cnx->loadedDriverType == egl_connection_t::GLES_NO_DRIVER);
android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
if (!ns) {
return nullptr;
@@ -606,19 +612,17 @@
initialize_api(dso, cnx, EGL);
hnd = new driver_t(dso);
- dso = load_angle("GLESv1_CM", ns, cnx);
- initialize_api(dso, cnx, GLESv1_CM);
- hnd->set(dso, GLESv1_CM);
-
dso = load_angle("GLESv2", ns, cnx);
initialize_api(dso, cnx, GLESv2);
hnd->set(dso, GLESv2);
+ cnx->loadedDriverType = egl_connection_t::GLES_ANGLE_STANDALONE_DRIVER;
}
return hnd;
}
Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) {
ATRACE_CALL();
+ assert(cnx->loadedDriverType == egl_connection_t::GLES_NO_DRIVER);
#ifndef __ANDROID_VNDK__
android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace();
if (!ns) {
@@ -630,8 +634,9 @@
driver_t* hnd = nullptr;
void* dso = load_updated_driver("GLES", ns);
if (dso) {
- initialize_api(dso, cnx, EGL | GLESv1_CM | GLESv2);
+ initialize_api(dso, cnx, EGL | GLESv2);
hnd = new driver_t(dso);
+ if (hnd) cnx->loadedDriverType = egl_connection_t::GLES_UPDATED_COMBO_DRIVER;
return hnd;
}
@@ -640,13 +645,10 @@
initialize_api(dso, cnx, EGL);
hnd = new driver_t(dso);
- dso = load_updated_driver("GLESv1_CM", ns);
- initialize_api(dso, cnx, GLESv1_CM);
- hnd->set(dso, GLESv1_CM);
-
dso = load_updated_driver("GLESv2", ns);
initialize_api(dso, cnx, GLESv2);
hnd->set(dso, GLESv2);
+ cnx->loadedDriverType = egl_connection_t::GLES_UPDATED_STANDALONE_DRIVER;
}
return hnd;
#else
@@ -657,30 +659,94 @@
Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
const bool exact) {
ATRACE_CALL();
+ assert(cnx->loadedDriverType == egl_connection_t::GLES_NO_DRIVER);
android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL);
driver_t* hnd = nullptr;
void* dso = load_system_driver("GLES", suffix, exact);
if (dso) {
- initialize_api(dso, cnx, EGL | GLESv1_CM | GLESv2);
+ initialize_api(dso, cnx, EGL | GLESv2);
hnd = new driver_t(dso);
+ if (hnd) cnx->loadedDriverType = egl_connection_t::GLES_SYSTEM_COMBO_DRIVER;
return hnd;
}
+
dso = load_system_driver("EGL", suffix, exact);
if (dso) {
initialize_api(dso, cnx, EGL);
hnd = new driver_t(dso);
- dso = load_system_driver("GLESv1_CM", suffix, exact);
- initialize_api(dso, cnx, GLESv1_CM);
- hnd->set(dso, GLESv1_CM);
-
dso = load_system_driver("GLESv2", suffix, exact);
initialize_api(dso, cnx, GLESv2);
hnd->set(dso, GLESv2);
+ cnx->loadedDriverType = egl_connection_t::GLES_SYSTEM_STANDALONE_DRIVER;
}
return hnd;
}
+bool Loader::load_glesv1_driver(egl_connection_t* cnx) {
+ ATRACE_CALL();
+ void* dso = nullptr;
+
+ driver_t* hnd = (driver_t*)cnx->dso;
+ // EGL driver must have been loaded. if not, something went wrong.
+ if (!hnd || cnx->loadedDriverType == egl_connection_t::GLES_NO_DRIVER) {
+ return false;
+ }
+
+ if (cnx->libGles1) return EGL_TRUE;
+
+ // If there is a GLES driver, use that first
+ switch (cnx->loadedDriverType) {
+ case egl_connection_t::GLES_SYSTEM_COMBO_DRIVER:
+ case egl_connection_t::GLES_UPDATED_COMBO_DRIVER: {
+ dso = hnd->dso[0];
+ initialize_api(dso, cnx, GLESv1_CM);
+ break;
+ }
+ case egl_connection_t::GLES_ANGLE_STANDALONE_DRIVER: {
+ android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace();
+ dso = load_angle("GLESv1_CM", ns, cnx);
+ if (dso) {
+ initialize_api(dso, cnx, GLESv1_CM);
+ hnd->set(dso, GLESv1_CM);
+ }
+ break;
+ }
+ case egl_connection_t::GLES_UPDATED_STANDALONE_DRIVER: {
+ android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace();
+ void* dso = load_updated_driver("GLESv1_CM", ns);
+ if (dso) {
+ initialize_api(dso, cnx, GLESv1_CM);
+ hnd->set(dso, GLESv1_CM);
+ }
+ break;
+ }
+ case egl_connection_t::GLES_SYSTEM_STANDALONE_DRIVER: {
+ dso = load_system_driver("GLESv1_CM",
+ cnx->systemDriverUseProperty ? cnx->systemDriverProperty
+ : nullptr,
+ cnx->systemDriverUseExactName);
+ if (dso) {
+ initialize_api(dso, cnx, GLESv1_CM);
+ hnd->set(dso, GLESv1_CM);
+ }
+ break;
+ }
+ default: {
+ ALOGE("Bad loadedDriverType (%d)", cnx->loadedDriverType);
+ break;
+ }
+ }
+
+ if (!cnx->libGles1) {
+ cnx->libGles1 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so");
+ }
+
+ LOG_ALWAYS_FATAL_IF(!cnx->libGles1, "couldn't load system OpenGL ES V1 wrapper libraries");
+
+ return dso ? true : false;
+}
+
void Loader::initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask) {
if (mask & EGL) {
getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress");
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 6f31ab4..4199753 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -53,6 +53,8 @@
void* open(egl_connection_t* cnx);
void close(egl_connection_t* cnx);
+ bool load_glesv1_driver(egl_connection_t* cnx);
+
private:
Loader();
driver_t* attempt_to_load_angle(egl_connection_t* cnx);
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 25b1009..768d3b5 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -202,6 +202,15 @@
return cnx->dso ? EGL_TRUE : EGL_FALSE;
}
+static EGLBoolean egl_init_glesv1_drivers_locked() {
+ // get our driver loader
+ Loader& loader(Loader::getInstance());
+
+ // dynamically load our GLESV1 implementation
+ egl_connection_t* cnx = &gEGLImpl;
+ return loader.load_glesv1_driver(cnx);
+}
+
static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER;
EGLBoolean egl_init_drivers() {
@@ -212,6 +221,14 @@
return res;
}
+EGLBoolean egl_init_glesv1_drivers() {
+ EGLBoolean res;
+ pthread_mutex_lock(&sInitDriverMutex);
+ res = egl_init_glesv1_drivers_locked();
+ pthread_mutex_unlock(&sInitDriverMutex);
+ return res;
+}
+
static pthread_mutex_t sLogPrintMutex = PTHREAD_MUTEX_INITIALIZER;
static std::chrono::steady_clock::time_point sLogPrintTime;
static constexpr std::chrono::seconds DURATION(1);
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 67d69b4..3b19b32 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -220,7 +220,7 @@
if (cnx->dso) {
EGLDisplay dpy = EGL_NO_DISPLAY;
- if (cnx->useAngle) {
+ if (cnx->loadedDriverType == egl_connection_t::GLES_ANGLE_STANDALONE_DRIVER) {
EGLint error;
dpy = getPlatformDisplayAngle(display, cnx, attrib_list, &error);
if (error != EGL_NONE) {
@@ -464,7 +464,7 @@
egl_connection_t* const cnx = &gEGLImpl;
if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
// If we're using ANGLE reset any custom DisplayPlatform
- if (cnx->useAngle) {
+ if (cnx->loadedDriverType == egl_connection_t::GLES_ANGLE_STANDALONE_DRIVER) {
angle::resetAnglePlatform(disp.dpy);
}
if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 5162ba4..e2bc5c6 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -277,6 +277,7 @@
extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
extern EGLBoolean egl_init_drivers();
+extern EGLBoolean egl_init_glesv1_drivers();
extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
extern gl_hooks_t gHooksTrace;
@@ -956,44 +957,42 @@
egl_context_t* const c = get_context(share_list);
share_list = c->context;
}
- // b/111083885 - If we are presenting EGL 1.4 interface to apps
- // error out on robust access attributes that are invalid
- // in EGL 1.4 as the driver may be fine with them but dEQP expects
- // tests to fail according to spec.
- if (attrib_list && (cnx->driverVersion < EGL_MAKE_VERSION(1, 5, 0))) {
- const EGLint* attrib_ptr = attrib_list;
- while (*attrib_ptr != EGL_NONE) {
- GLint attr = *attrib_ptr++;
- GLint value = *attrib_ptr++;
- if (attr == EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR) {
- // We are GL ES context with EGL 1.4, this is an invalid
- // attribute
+
+ // figure out if it's a GLESv1 or GLESv2
+ int gles_version_idx = egl_connection_t::GLESv1_INDEX;
+ if (attrib_list) {
+ const EGLint* ptr = attrib_list; // so that we don't modify attrib_list
+ while (*ptr != EGL_NONE) {
+ GLint attr = *ptr++;
+ GLint value = *ptr++;
+ if (attr == EGL_CONTEXT_CLIENT_VERSION) {
+ if (value == 1) {
+ gles_version_idx = egl_connection_t::GLESv1_INDEX;
+ } else if (value == 2 || value == 3) {
+ gles_version_idx = egl_connection_t::GLESv2_INDEX;
+ }
+ } else if (attr == EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR &&
+ cnx->driverVersion < EGL_MAKE_VERSION(1, 5, 0)) {
+ // b/111083885 - If we are presenting EGL 1.4 interface to apps
+ // error out on robust access attributes that are invalid
+ // in EGL 1.4 as the driver may be fine with them but dEQP expects
+ // tests to fail according to spec.
return setError(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
}
- };
+ }
}
+
+ // GLESV1 driver is lazily loaded and initialized
+ if (gles_version_idx == egl_connection_t::GLESv1_INDEX) {
+ android::GraphicsEnv::getInstance().setTargetStats(
+ android::GpuStatsInfo::Stats::GLES_1_IN_USE);
+ if (!egl_init_glesv1_drivers()) return EGL_NO_CONTEXT;
+ }
+
EGLContext context = cnx->egl.eglCreateContext(
dp->disp.dpy, config, share_list, attrib_list);
if (context != EGL_NO_CONTEXT) {
- // figure out if it's a GLESv1 or GLESv2
- int version = 0;
- if (attrib_list) {
- while (*attrib_list != EGL_NONE) {
- GLint attr = *attrib_list++;
- GLint value = *attrib_list++;
- if (attr == EGL_CONTEXT_CLIENT_VERSION) {
- if (value == 1) {
- version = egl_connection_t::GLESv1_INDEX;
- android::GraphicsEnv::getInstance().setTargetStats(
- android::GpuStatsInfo::Stats::GLES_1_IN_USE);
- } else if (value == 2 || value == 3) {
- version = egl_connection_t::GLESv2_INDEX;
- }
- }
- };
- }
- egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
- version);
+ egl_context_t* c = new egl_context_t(dpy, context, config, cnx, gles_version_idx);
return c;
}
}
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 7bb9b59..4e9d7d0 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -17,11 +17,12 @@
#ifndef ANDROID_EGLDEFS_H
#define ANDROID_EGLDEFS_H
+#include <cutils/properties.h>
+#include <log/log.h>
+
#include "../hooks.h"
#include "egl_platform_entries.h"
-#include <log/log.h>
-
#define VERSION_MAJOR 1
#define VERSION_MINOR 4
#define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
@@ -44,6 +45,15 @@
GLESv2_INDEX = 1
};
+ enum {
+ GLES_NO_DRIVER = 0, // no driver is loaded
+ GLES_SYSTEM_COMBO_DRIVER = 1, // GLES combo driver in system folder is loaded
+ GLES_SYSTEM_STANDALONE_DRIVER = 2, // standalone system drivers are loaded
+ GLES_ANGLE_STANDALONE_DRIVER = 3, // ANGLE (always standalone) drivers are loaded.
+ GLES_UPDATED_COMBO_DRIVER = 4, // GLES combo driver in updated driver folder is loaded
+ GLES_UPDATED_STANDALONE_DRIVER = 5, // standalone drivers in updated driver folder are loaded
+ };
+
inline egl_connection_t() : dso(nullptr),
libEgl(nullptr),
libGles1(nullptr),
@@ -83,9 +93,12 @@
bool systemDriverUnloaded;
bool shouldUseAngle; // Should we attempt to load ANGLE
bool angleDecided; // Have we tried to load ANGLE
- bool useAngle; // Was ANGLE successfully loaded
EGLint angleBackend;
+ int loadedDriverType;
void* vendorEGL;
+ bool systemDriverUseExactName;
+ bool systemDriverUseProperty;
+ char systemDriverProperty[PROPERTY_VALUE_MAX + 1];
};
// clang-format on
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 26c2d3f..6157d99 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -256,6 +256,67 @@
return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
}
+static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+ EventEntry* eventEntry,
+ int32_t inputTargetFlags) {
+ if (inputTarget.useDefaultPointerInfo()) {
+ const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo();
+ return std::make_unique<DispatchEntry>(eventEntry, // increments ref
+ inputTargetFlags, pointerInfo.xOffset,
+ pointerInfo.yOffset, inputTarget.globalScaleFactor,
+ pointerInfo.windowXScale, pointerInfo.windowYScale);
+ }
+
+ ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+
+ PointerCoords pointerCoords[MAX_POINTERS];
+
+ // Use the first pointer information to normalize all other pointers. This could be any pointer
+ // as long as all other pointers are normalized to the same value and the final DispatchEntry
+ // uses the offset and scale for the normalized pointer.
+ const PointerInfo& firstPointerInfo =
+ inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()];
+
+ // Iterate through all pointers in the event to normalize against the first.
+ for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) {
+ const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
+ uint32_t pointerId = uint32_t(pointerProperties.id);
+ const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId];
+
+ // The scale factor is the ratio of the current pointers scale to the normalized scale.
+ float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale;
+ float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale;
+
+ pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
+ // First apply the current pointers offset to set the window at 0,0
+ pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset);
+ // Next scale the coordinates.
+ pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff);
+ // Lastly, offset the coordinates so they're in the normalized pointer's frame.
+ pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset,
+ -firstPointerInfo.yOffset);
+ }
+
+ MotionEntry* combinedMotionEntry =
+ new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId, motionEntry.policyFlags,
+ motionEntry.action, motionEntry.actionButton, motionEntry.flags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, motionEntry.edgeFlags,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+ motionEntry.downTime, motionEntry.pointerCount,
+ motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */,
+ 0 /* yOffset */);
+
+ return std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
+ inputTargetFlags, firstPointerInfo.xOffset,
+ firstPointerInfo.yOffset, inputTarget.globalScaleFactor,
+ firstPointerInfo.windowXScale,
+ firstPointerInfo.windowYScale);
+}
+
// --- InputDispatcherThread ---
class InputDispatcher::InputDispatcherThread : public Thread {
@@ -1111,7 +1172,7 @@
sp<Connection> connection =
getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
if (connection != nullptr) {
- prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
+ prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
} else {
if (DEBUG_FOCUS) {
ALOGD("Dropping event delivery to target with channel '%s' because it "
@@ -1785,23 +1846,34 @@
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) {
- sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
- if (inputChannel == nullptr) {
- ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
- return;
- }
+ std::vector<InputTarget>::iterator it =
+ std::find_if(inputTargets.begin(), inputTargets.end(),
+ [&windowHandle](const InputTarget& inputTarget) {
+ return inputTarget.inputChannel->getConnectionToken() ==
+ windowHandle->getToken();
+ });
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- InputTarget target;
- target.inputChannel = inputChannel;
- target.flags = targetFlags;
- target.xOffset = -windowInfo->frameLeft;
- target.yOffset = -windowInfo->frameTop;
- target.globalScaleFactor = windowInfo->globalScaleFactor;
- target.windowXScale = windowInfo->windowXScale;
- target.windowYScale = windowInfo->windowYScale;
- target.pointerIds = pointerIds;
- inputTargets.push_back(target);
+
+ if (it == inputTargets.end()) {
+ InputTarget inputTarget;
+ sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+ if (inputChannel == nullptr) {
+ ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
+ return;
+ }
+ inputTarget.inputChannel = inputChannel;
+ inputTarget.flags = targetFlags;
+ inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
+ inputTargets.push_back(inputTarget);
+ it = inputTargets.end() - 1;
+ }
+
+ ALOG_ASSERT(it->flags == targetFlags);
+ ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
+
+ it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
+ windowInfo->windowXScale, windowInfo->windowYScale);
}
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
@@ -1824,10 +1896,7 @@
InputTarget target;
target.inputChannel = monitor.inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.xOffset = xOffset;
- target.yOffset = yOffset;
- target.pointerIds.clear();
- target.globalScaleFactor = 1.0f;
+ target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */);
inputTargets.push_back(target);
}
@@ -2045,7 +2114,7 @@
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection,
EventEntry* eventEntry,
- const InputTarget* inputTarget) {
+ const InputTarget& inputTarget) {
if (ATRACE_ENABLED()) {
std::string message =
StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
@@ -2054,11 +2123,10 @@
}
#if DEBUG_DISPATCH_CYCLE
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
- "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
- "windowScaleFactor=(%f, %f), pointerIds=0x%x",
- connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset,
- inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale,
- inputTarget->windowYScale, inputTarget->pointerIds.value);
+ "globalScaleFactor=%f, pointerIds=0x%x %s",
+ connection->getInputChannelName().c_str(), inputTarget.flags,
+ inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
+ inputTarget.getPointerInfoString().c_str());
#endif
// Skip this event if the connection status is not normal.
@@ -2072,13 +2140,15 @@
}
// Split a motion event if needed.
- if (inputTarget->flags & InputTarget::FLAG_SPLIT) {
- ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
+ if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
+ LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION,
+ "Entry type %s should not have FLAG_SPLIT",
+ EventEntry::typeToString(eventEntry->type));
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
- if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) {
+ if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
MotionEntry* splitMotionEntry =
- splitMotionEvent(originalMotionEntry, inputTarget->pointerIds);
+ splitMotionEvent(originalMotionEntry, inputTarget.pointerIds);
if (!splitMotionEntry) {
return; // split event was dropped
}
@@ -2100,7 +2170,7 @@
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection,
EventEntry* eventEntry,
- const InputTarget* inputTarget) {
+ const InputTarget& inputTarget) {
if (ATRACE_ENABLED()) {
std::string message =
StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32
@@ -2133,7 +2203,7 @@
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
EventEntry* eventEntry,
- const InputTarget* inputTarget,
+ const InputTarget& inputTarget,
int32_t dispatchMode) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",
@@ -2141,7 +2211,7 @@
dispatchModeToString(dispatchMode).c_str());
ATRACE_NAME(message.c_str());
}
- int32_t inputTargetFlags = inputTarget->flags;
+ int32_t inputTargetFlags = inputTarget.flags;
if (!(inputTargetFlags & dispatchMode)) {
return;
}
@@ -2149,16 +2219,16 @@
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
- DispatchEntry* dispatchEntry =
- new DispatchEntry(eventEntry, // increments ref
- inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->globalScaleFactor, inputTarget->windowXScale,
- inputTarget->windowYScale);
+ std::unique_ptr<DispatchEntry> dispatchEntry =
+ createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);
+ // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
+ // different EventEntry than what was passed in.
+ EventEntry* newEntry = dispatchEntry->eventEntry;
// Apply target flags and update the connection's input state.
- switch (eventEntry->type) {
+ switch (newEntry->type) {
case EventEntry::Type::KEY: {
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*eventEntry);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry);
dispatchEntry->resolvedAction = keyEntry.action;
dispatchEntry->resolvedFlags = keyEntry.flags;
@@ -2168,14 +2238,13 @@
ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
connection->getInputChannelName().c_str());
#endif
- delete dispatchEntry;
return; // skip the inconsistent event
}
break;
}
case EventEntry::Type::MOTION: {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry);
if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
} else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
@@ -2215,30 +2284,29 @@
"event",
connection->getInputChannelName().c_str());
#endif
- delete dispatchEntry;
return; // skip the inconsistent event
}
dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
- inputTarget->inputChannel->getConnectionToken());
+ inputTarget.inputChannel->getConnectionToken());
break;
}
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s events should not go to apps",
- EventEntry::typeToString(eventEntry->type));
+ EventEntry::typeToString(newEntry->type));
break;
}
}
// Remember that we are waiting for this dispatch to complete.
if (dispatchEntry->hasForegroundTarget()) {
- incrementPendingForegroundDispatches(eventEntry);
+ incrementPendingForegroundDispatches(newEntry);
}
// Enqueue the dispatch entry.
- connection->outboundQueue.push_back(dispatchEntry);
+ connection->outboundQueue.push_back(dispatchEntry.release());
traceOutboundQueueLength(connection);
}
@@ -2611,21 +2679,15 @@
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.xOffset = -windowInfo->frameLeft;
- target.yOffset = -windowInfo->frameTop;
+ target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
+ windowInfo->windowXScale, windowInfo->windowYScale);
target.globalScaleFactor = windowInfo->globalScaleFactor;
- target.windowXScale = windowInfo->windowXScale;
- target.windowYScale = windowInfo->windowYScale;
- } else {
- target.xOffset = 0;
- target.yOffset = 0;
- target.globalScaleFactor = 1.0f;
}
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
- &target, InputTarget::FLAG_DISPATCH_AS_IS);
+ target, InputTarget::FLAG_DISPATCH_AS_IS);
cancelationEventEntry->release();
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index f9eca01..50b5250 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -378,13 +378,13 @@
// with the mutex held makes it easier to ensure that connection invariants are maintained.
// If needed, the methods post commands to run later once the critical bits are done.
void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget)
+ EventEntry* eventEntry, const InputTarget& inputTarget)
REQUIRES(mLock);
void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget)
+ EventEntry* eventEntry, const InputTarget& inputTarget)
REQUIRES(mLock);
void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry,
- const InputTarget* inputTarget, int32_t dispatchMode)
+ const InputTarget& inputTarget, int32_t dispatchMode)
REQUIRES(mLock);
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 80fa2cb..0588374 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -42,4 +42,63 @@
return StringPrintf("%" PRId32, dispatchMode);
}
+void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset,
+ float windowXScale, float windowYScale) {
+ // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
+ // and non splittable windows since we will just use all the pointers from the input event.
+ if (newPointerIds.isEmpty()) {
+ setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale);
+ return;
+ }
+
+ // Ensure that the new set of pointers doesn't overlap with the current set of pointers.
+ ALOG_ASSERT((pointerIds & newPointerIds) == 0);
+
+ pointerIds |= newPointerIds;
+ while (!newPointerIds.isEmpty()) {
+ int32_t pointerId = newPointerIds.clearFirstMarkedBit();
+ pointerInfos[pointerId].xOffset = xOffset;
+ pointerInfos[pointerId].yOffset = yOffset;
+ pointerInfos[pointerId].windowXScale = windowXScale;
+ pointerInfos[pointerId].windowYScale = windowYScale;
+ }
+}
+
+void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
+ float windowYScale) {
+ pointerIds.clear();
+ pointerInfos[0].xOffset = xOffset;
+ pointerInfos[0].yOffset = yOffset;
+ pointerInfos[0].windowXScale = windowXScale;
+ pointerInfos[0].windowYScale = windowYScale;
+}
+
+bool InputTarget::useDefaultPointerInfo() const {
+ return pointerIds.isEmpty();
+}
+
+const PointerInfo& InputTarget::getDefaultPointerInfo() const {
+ return pointerInfos[0];
+}
+
+std::string InputTarget::getPointerInfoString() const {
+ if (useDefaultPointerInfo()) {
+ const PointerInfo& pointerInfo = getDefaultPointerInfo();
+ return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)",
+ pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale,
+ pointerInfo.windowYScale);
+ }
+
+ std::string out;
+ for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) {
+ if (!pointerIds.hasBit(i)) {
+ continue;
+ }
+ out += StringPrintf("\n pointerId %d: xOffset=%.1f, yOffset=%.1f "
+ "windowScaleFactor=(%.1f, %.1f)",
+ i, pointerInfos[i].xOffset, pointerInfos[i].yOffset,
+ pointerInfos[i].windowXScale, pointerInfos[i].windowYScale);
+ }
+ return out;
+}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 2e9bca2..499a75f 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -24,6 +24,22 @@
namespace android::inputdispatcher {
/*
+ * Information about each pointer for an InputTarget. This includes offset and scale so
+ * all pointers can be normalized to a single offset and scale.
+ *
+ * These values are ignored for KeyEvents
+ */
+struct PointerInfo {
+ // The x and y offset to add to a MotionEvent as it is delivered.
+ float xOffset = 0.0f;
+ float yOffset = 0.0f;
+
+ // Scaling factor to apply to MotionEvent as it is delivered.
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
+};
+
+/*
* An input target specifies how an input event is to be dispatched to a particular window
* including the window's input channel, control flags, a timeout, and an X / Y offset to
* be added to input event coordinates to compensate for the absolute position of the
@@ -95,20 +111,37 @@
// Flags for the input target.
int32_t flags = 0;
- // The x and y offset to add to a MotionEvent as it is delivered.
- // (ignored for KeyEvents)
- float xOffset = 0.0f;
- float yOffset = 0.0f;
-
// Scaling factor to apply to MotionEvent as it is delivered.
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
- BitSet32 pointerIds{};
+ BitSet32 pointerIds;
+ // The data is stored by the pointerId. Use the bit position of pointerIds to look up
+ // PointerInfo per pointerId.
+ PointerInfo pointerInfos[MAX_POINTERS];
+
+ void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale,
+ float windowYScale);
+ void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
+ float windowYScale);
+
+ /**
+ * Returns whether the default pointer information should be used. This will be true when the
+ * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors
+ * and non splittable windows since we want all pointers for the EventEntry to go to this
+ * target.
+ */
+ bool useDefaultPointerInfo() const;
+
+ /**
+ * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is
+ * true.
+ */
+ const PointerInfo& getDefaultPointerInfo() const;
+
+ std::string getPointerInfoString() const;
};
std::string dispatchModeToString(int32_t dispatchMode);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 5ffc89d..bd8d2a4 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1439,4 +1439,107 @@
consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
}
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) {
+ mWindow2->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ std::vector<PointF> touchedPoints = {PointF{10, 10}};
+ std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchedPoints.emplace_back(PointF{150, 150});
+ expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+ motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) {
+ mWindow2->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ std::vector<PointF> touchedPoints = {PointF{10, 10}};
+ std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchedPoints.emplace_back(PointF{150, 150});
+ expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+ motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+
+ // Move both windows
+ touchedPoints = {{20, 20}, {175, 175}};
+ expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+ getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
+ mWindow1->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ std::vector<PointF> touchedPoints = {PointF{10, 10}};
+ std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchedPoints.emplace_back(PointF{150, 150});
+ expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+ motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+
+ // Move both windows
+ touchedPoints = {{20, 20}, {175, 175}};
+ expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+ getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+}
+
} // namespace android::inputdispatcher
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 1e471e5..63f3242 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -344,6 +344,11 @@
return willPresent;
}
+void BufferStateLayer::forceSendCallbacks() {
+ mFlinger->getTransactionCompletedThread().finalizePendingCallbackHandles(
+ mCurrentState.callbackHandles);
+}
+
bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) {
mCurrentState.transparentRegionHint = transparent;
mCurrentState.modified = true;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 8e6a70c..574bc51 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -77,6 +77,7 @@
bool setApi(int32_t api) override;
bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
+ void forceSendCallbacks() override;
// Override to ignore legacy layer state properties that are not used by BufferStateLayer
bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; }
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 8b532e3..e7cf5ff 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -76,6 +76,7 @@
virtual void onVsyncPeriodTimingChangedReceived(
int32_t sequenceId, hwc2_display_t display,
const hwc_vsync_period_change_timeline_t& updatedTimeline) = 0;
+ virtual void onSeamlessPossible(int32_t sequenceId, hwc2_display_t display) = 0;
virtual ~ComposerCallback() = default;
};
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 0a7009b..1960f43 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -125,6 +125,11 @@
return android::hardware::Void();
}
+ android::hardware::Return<void> onSeamlessPossible(android::Hwc2::Display display) override {
+ mCallback->onSeamlessPossible(mSequenceId, display);
+ return android::hardware::Void();
+ }
+
private:
HWC2::ComposerCallback* mCallback;
const int32_t mSequenceId;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 6fd1629..5bc0860 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -848,7 +848,7 @@
// inevitably waiting on a buffer to return. We recreate this semantic for BufferQueue
// even though it is a little consistent. detachChildren is shortly slated for removal
// by the hierarchy mirroring work so we don't need to worry about it too much.
- mDrawingState.callbackHandles = mCurrentState.callbackHandles;
+ forceSendCallbacks();
mCurrentState.callbackHandles = {};
return flags;
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index d697a6a..553408c 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -333,6 +333,7 @@
const std::vector<sp<CallbackHandle>>& /*handles*/) {
return false;
};
+ virtual void forceSendCallbacks() {}
virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
virtual bool setColorSpaceAgnostic(const bool agnostic);
bool setShadowRadius(float shadowRadius);
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 69d8c89..4ef64b6 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -5,4 +5,5 @@
lpy@google.com
marissaw@google.com
racarr@google.com
-stoza@google.com
\ No newline at end of file
+stoza@google.com
+vhau@google.com
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 6598bd8..c69859e 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -192,10 +192,20 @@
}
void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
+ const auto display = mFlinger.getDefaultDisplayDeviceLocked();
+ if (!display) {
+ return;
+ }
+
+ const int32_t left = display->getWidth() / 32;
+ const int32_t top = display->getHeight() / 32;
+ const int32_t right = left + display->getWidth() / 8;
+ const int32_t buttom = top + display->getHeight() / 32;
+
auto buffer = mBufferCache[refreshRate.fps];
mLayer->setBuffer(buffer, 0, 0, {});
- mLayer->setFrame(Rect(20, 120, 20 + SevenSegmentDrawer::getWidth(),
- 120 + SevenSegmentDrawer::getHeight()));
+
+ mLayer->setFrame(Rect(left, top, right, buttom));
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 146ec1b..cf79d9f 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -34,7 +34,7 @@
#include "LayerInfo.h"
#include "SchedulerUtils.h"
-namespace android::scheduler {
+namespace android::scheduler::impl {
namespace {
@@ -157,4 +157,4 @@
mActiveLayersEnd = 0;
}
-} // namespace android::scheduler
+} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 745c4c1..d92e5c3 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -32,33 +32,51 @@
namespace scheduler {
+class LayerHistoryTest;
class LayerInfo;
-// Records per-layer history of scheduling-related information (primarily present time),
-// heuristically categorizes layers as active or inactive, and summarizes stats about
-// active layers (primarily maximum refresh rate). See go/content-fps-detection-in-scheduler.
class LayerHistory {
public:
- LayerHistory();
- ~LayerHistory();
+ virtual ~LayerHistory() = default;
// Layers are unregistered when the weak reference expires.
- void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate);
+ virtual void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate) = 0;
// Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, nsecs_t now);
+ virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0;
struct Summary {
float maxRefreshRate; // Maximum refresh rate among recently active layers.
};
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
- Summary summarize(nsecs_t now);
+ virtual Summary summarize(nsecs_t now) = 0;
- void clear();
+ virtual void clear() = 0;
+};
+
+namespace impl {
+// Records per-layer history of scheduling-related information (primarily present time),
+// heuristically categorizes layers as active or inactive, and summarizes stats about
+// active layers (primarily maximum refresh rate). See go/content-fps-detection-in-scheduler.
+class LayerHistory : public android::scheduler::LayerHistory {
+public:
+ LayerHistory();
+ virtual ~LayerHistory();
+
+ // Layers are unregistered when the weak reference expires.
+ void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate) override;
+
+ // Marks the layer as active, and records the given state to its history.
+ void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
+
+ // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
+ android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
+
+ void clear() override;
private:
- friend class LayerHistoryTest;
+ friend class android::scheduler::LayerHistoryTest;
friend TestableScheduler;
using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
@@ -90,5 +108,6 @@
const bool mTraceEnabled;
};
+} // namespace impl
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 4330742..13014c7 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -31,6 +31,11 @@
return std::nullopt;
}
+bool fpsEqualsWithMargin(float fpsA, float fpsB) {
+ static constexpr float MARGIN = 0.01f;
+ return std::abs(fpsA - fpsB) <= MARGIN;
+}
+
} // namespace
namespace android::scheduler {
@@ -61,16 +66,15 @@
mThresholdForNextVsync);
}
-std::unordered_map<float, PhaseDurations::Offsets> PhaseOffsets::initializeOffsets(
+std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
const scheduler::RefreshRateConfigs& refreshRateConfigs) const {
- std::unordered_map<float, PhaseDurations::Offsets> offsets;
+ std::unordered_map<float, Offsets> offsets;
for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) {
- const nsecs_t vsyncDuration = static_cast<nsecs_t>(1e9f / refreshRate.fps);
if (refreshRate.fps > 65.0f) {
- offsets.emplace(refreshRate.fps, getHighFpsOffsets(vsyncDuration));
+ offsets.emplace(refreshRate.fps, getHighFpsOffsets(refreshRate.vsyncPeriod));
} else {
- offsets.emplace(refreshRate.fps, getDefaultOffsets(vsyncDuration));
+ offsets.emplace(refreshRate.fps, getDefaultOffsets(refreshRate.vsyncPeriod));
}
}
return offsets;
@@ -150,6 +154,15 @@
};
}
+PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
+ const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
+ [&fps](const std::pair<float, Offsets>& candidateFps) {
+ return fpsEqualsWithMargin(fps, candidateFps.first);
+ });
+ LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
+ return iter->second;
+}
+
static void validateSysprops() {
const auto validatePropertyBool = [](const char* prop) {
LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
@@ -207,7 +220,7 @@
std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
const std::vector<float>& refreshRates) const {
- std::unordered_map<float, PhaseDurations::Offsets> offsets;
+ std::unordered_map<float, Offsets> offsets;
for (const auto fps : refreshRates) {
const nsecs_t vsyncDuration = static_cast<nsecs_t>(1e9f / fps);
@@ -275,7 +288,9 @@
mRefreshRateFps(currentFps) {}
PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const {
- const auto iter = mOffsets.find(fps);
+ const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), [=](const auto& candidateFps) {
+ return fpsEqualsWithMargin(fps, candidateFps.first);
+ });
LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
return iter->second;
}
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index c10efde..7b1bdfd 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -54,11 +54,7 @@
PhaseOffsets(const scheduler::RefreshRateConfigs&);
// Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- Offsets getOffsetsForRefreshRate(float fps) const override {
- const auto iter = mOffsets.find(fps);
- LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
- return iter->second;
- }
+ Offsets getOffsetsForRefreshRate(float fps) const override;
// Returns early, early GL, and late offsets for Apps and SF.
Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
@@ -71,7 +67,7 @@
void dump(std::string& result) const override;
private:
- std::unordered_map<float, PhaseOffsets::Offsets> initializeOffsets(
+ std::unordered_map<float, Offsets> initializeOffsets(
const scheduler::RefreshRateConfigs&) const;
Offsets getDefaultOffsets(nsecs_t vsyncDuration) const;
Offsets getHighFpsOffsets(nsecs_t vsyncDuration) const;
@@ -111,8 +107,7 @@
nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration);
private:
- std::unordered_map<float, PhaseDurations::Offsets> initializeOffsets(
- const std::vector<float>&) const;
+ std::unordered_map<float, Offsets> initializeOffsets(const std::vector<float>&) const;
const nsecs_t mSfDuration;
const nsecs_t mAppDuration;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 847e20c..692ded9 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -126,7 +126,7 @@
return BAD_VALUE;
}
const RefreshRate& refreshRate = mRefreshRates.at(defaultConfigId);
- if (refreshRate.fps < minRefreshRate || refreshRate.fps > maxRefreshRate) {
+ if (!refreshRate.inPolicy(minRefreshRate, maxRefreshRate)) {
return BAD_VALUE;
}
mDefaultConfig = defaultConfigId;
@@ -180,8 +180,8 @@
group.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
getSortedRefreshRateList(
[&](const RefreshRate& refreshRate) REQUIRES(mLock) {
- return refreshRate.configGroup == group && refreshRate.fps >= mMinRefreshRateFps &&
- refreshRate.fps <= mMaxRefreshRateFps;
+ return refreshRate.configGroup == group &&
+ refreshRate.inPolicy(mMinRefreshRateFps, mMaxRefreshRateFps);
},
&mAvailableRefreshRates);
LOG_ALWAYS_FATAL_IF(mAvailableRefreshRates.empty(),
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 1e740ca..0c3369a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -44,6 +44,9 @@
class RefreshRateConfigs {
public:
struct RefreshRate {
+ // The tolerance within which we consider FPS approximately equals.
+ static constexpr float FPS_EPSILON = 0.001f;
+
RefreshRate(HwcConfigIndexType configId, nsecs_t vsyncPeriod,
HwcConfigGroupType configGroup, std::string name, float fps)
: configId(configId),
@@ -63,6 +66,12 @@
// Refresh rate in frames per second
const float fps = 0;
+ // Checks whether the fps of this RefreshRate struct is within a given min and max refresh
+ // rate passed in. FPS_EPSILON is applied to the boundaries for approximation.
+ bool inPolicy(float minRefreshRate, float maxRefreshRate) const {
+ return (fps >= (minRefreshRate - FPS_EPSILON) && fps <= (maxRefreshRate + FPS_EPSILON));
+ }
+
bool operator!=(const RefreshRate& other) const {
return configId != other.configId || vsyncPeriod != other.vsyncPeriod ||
configGroup != other.configGroup;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index ff9cf86..73dc753 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -108,7 +108,7 @@
using namespace sysprop;
if (property_get_bool("debug.sf.use_smart_90_for_video", 0) || use_smart_90_for_video(false)) {
- mLayerHistory.emplace();
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
}
const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
@@ -493,7 +493,7 @@
const bool supported = mRefreshRateConfigs.refreshRateSwitchingSupported();
StringAppendF(&result, "+ Refresh rate switching: %s\n", states[supported]);
- StringAppendF(&result, "+ Content detection: %s\n", states[mLayerHistory.has_value()]);
+ StringAppendF(&result, "+ Content detection: %s\n", states[mLayerHistory != nullptr]);
StringAppendF(&result, "+ Idle timer: %s\n",
mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 2cdb757..c6430c3 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -193,7 +193,7 @@
std::unique_ptr<EventControlThread> mEventControlThread;
// Used to choose refresh rate if content detection is enabled.
- std::optional<scheduler::LayerHistory> mLayerHistory;
+ std::unique_ptr<scheduler::LayerHistory> mLayerHistory;
// Whether to use idle timer callbacks that support the kernel timer.
const bool mSupportKernelTimer;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8c1d168..004240b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1570,6 +1570,11 @@
mScheduler->onNewVsyncPeriodChangeTimeline(updatedTimeline);
}
+void SurfaceFlinger::onSeamlessPossible(int32_t /*sequenceId*/, hwc2_display_t /*display*/) {
+ // TODO(b/142753666): use constraints when calling to setActiveConfigWithConstrains and
+ // use this callback to know when to retry in case of SEAMLESS_NOT_POSSIBLE.
+}
+
void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*hwcDisplayId*/) {
Mutex::Autolock lock(mStateLock);
if (sequenceId != getBE().mComposerSequenceId) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 2f84b13..dcd9dc2 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -505,6 +505,7 @@
void onVsyncPeriodTimingChangedReceived(
int32_t sequenceId, hwc2_display_t display,
const hwc_vsync_period_change_timeline_t& updatedTimeline) override;
+ void onSeamlessPossible(int32_t sequenceId, hwc2_display_t display) override;
/* ------------------------------------------------------------------------
* ISchedulerCallback
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 1895777..a5fabf2 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -618,6 +618,8 @@
mTimeStats.clientCompositionFrames = 0;
mTimeStats.displayOnTime = 0;
mTimeStats.presentToPresent.hist.clear();
+ mTimeStats.frameDuration.hist.clear();
+ mTimeStats.renderEngineTiming.hist.clear();
mTimeStats.refreshRateStats.clear();
mPowerTime.prevTime = systemTime();
mGlobalRecord.prevPresentTime = 0;
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 4c1baaf..1475889 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -310,8 +310,8 @@
// we get pointers that compare unequal in the SF process.
interface_cast<ITransactionCompletedListener>(listenerStats.listener)
->onTransactionCompleted(listenerStats);
- listener->unlinkToDeath(mDeathRecipient);
if (transactionStatsDeque.empty()) {
+ listener->unlinkToDeath(mDeathRecipient);
completedTransactionsItr =
mCompletedTransactions.erase(completedTransactionsItr);
} else {
diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc
index aea602b..d3942e8 100644
--- a/services/surfaceflinger/surfaceflinger.rc
+++ b/services/surfaceflinger/surfaceflinger.rc
@@ -2,6 +2,7 @@
class core animation
user system
group graphics drmrpc readproc
+ capabilities SYS_NICE
onrestart restart zygote
writepid /dev/stune/foreground/tasks
socket pdx/system/vr/display/client stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index a4d9ff3..0403237 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -53,11 +53,15 @@
"libgui",
"liblayers_proto",
"liblog",
+ "libnativewindow",
"libprotobuf-cpp-full",
"libtimestats_proto",
"libui",
"libutils",
- ]
+ ],
+ header_libs: [
+ "libnativewindow_headers",
+ ],
}
cc_defaults {
diff --git a/services/surfaceflinger/tests/CommonTypes_test.cpp b/services/surfaceflinger/tests/CommonTypes_test.cpp
index ab4af09..25b4615 100644
--- a/services/surfaceflinger/tests/CommonTypes_test.cpp
+++ b/services/surfaceflinger/tests/CommonTypes_test.cpp
@@ -16,6 +16,7 @@
#include <aidl/android/hardware/graphics/common/BlendMode.h>
#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <android/data_space.h>
#include <android/hardware/graphics/common/1.2/types.h>
#include <android/hardware/graphics/composer/2.1/IComposerClient.h>
@@ -34,6 +35,51 @@
static_assert(static_cast<uint32_t>(AidlBlendMode::COVERAGE) ==
static_cast<uint32_t>(HidlBlendMode::COVERAGE));
+static_assert(static_cast<uint32_t>(ADATASPACE_UNKNOWN) ==
+ static_cast<uint32_t>(AidlDataspace::UNKNOWN));
+static_assert(static_cast<uint32_t>(ADATASPACE_SCRGB_LINEAR) ==
+ static_cast<uint32_t>(AidlDataspace::SCRGB_LINEAR));
+static_assert(static_cast<uint32_t>(ADATASPACE_SRGB) == static_cast<uint32_t>(AidlDataspace::SRGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_SCRGB) ==
+ static_cast<uint32_t>(AidlDataspace::SCRGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_DISPLAY_P3) ==
+ static_cast<uint32_t>(AidlDataspace::DISPLAY_P3));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT2020_PQ) ==
+ static_cast<uint32_t>(AidlDataspace::BT2020_PQ));
+static_assert(static_cast<uint32_t>(ADATASPACE_ADOBE_RGB) ==
+ static_cast<uint32_t>(AidlDataspace::ADOBE_RGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT2020) ==
+ static_cast<uint32_t>(AidlDataspace::BT2020));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT709) ==
+ static_cast<uint32_t>(AidlDataspace::BT709));
+static_assert(static_cast<uint32_t>(ADATASPACE_DCI_P3) ==
+ static_cast<uint32_t>(AidlDataspace::DCI_P3));
+static_assert(static_cast<uint32_t>(ADATASPACE_SRGB_LINEAR) ==
+ static_cast<uint32_t>(AidlDataspace::SRGB_LINEAR));
+
+static_assert(static_cast<uint32_t>(ADATASPACE_UNKNOWN) ==
+ static_cast<uint32_t>(HidlDataspace::UNKNOWN));
+static_assert(static_cast<uint32_t>(ADATASPACE_SCRGB_LINEAR) ==
+ static_cast<uint32_t>(HidlDataspace::V0_SCRGB_LINEAR));
+static_assert(static_cast<uint32_t>(ADATASPACE_SRGB) ==
+ static_cast<uint32_t>(HidlDataspace::V0_SRGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_SCRGB) ==
+ static_cast<uint32_t>(HidlDataspace::V0_SCRGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_DISPLAY_P3) ==
+ static_cast<uint32_t>(HidlDataspace::DISPLAY_P3));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT2020_PQ) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_PQ));
+static_assert(static_cast<uint32_t>(ADATASPACE_ADOBE_RGB) ==
+ static_cast<uint32_t>(HidlDataspace::ADOBE_RGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT2020) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT709) ==
+ static_cast<uint32_t>(HidlDataspace::V0_BT709));
+static_assert(static_cast<uint32_t>(ADATASPACE_DCI_P3) ==
+ static_cast<uint32_t>(HidlDataspace::DCI_P3));
+static_assert(static_cast<uint32_t>(ADATASPACE_SRGB_LINEAR) ==
+ static_cast<uint32_t>(HidlDataspace::V0_SRGB_LINEAR));
+
static_assert(static_cast<uint32_t>(AidlDataspace::UNKNOWN) ==
static_cast<uint32_t>(HidlDataspace::UNKNOWN));
static_assert(static_cast<uint32_t>(AidlDataspace::ARBITRARY) ==
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index 7786638..2bedd7d 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
{
"presubmit": {
- "filter": "*:-RefreshRateRangeTest.*:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/3:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/3"
+ "filter": "*:-LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/3:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/3"
}
}
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index d95252b..f055fe7 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -29,8 +29,8 @@
LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
- LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
- const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
+ impl::LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
+ const impl::LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
size_t layerCount() const { return mScheduler->layerHistorySize(); }
size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index ed620ef..cc3c985 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -39,6 +39,7 @@
static inline const HwcConfigGroupType HWC_GROUP_ID_0 = HwcConfigGroupType(0);
static inline const HwcConfigGroupType HWC_GROUP_ID_1 = HwcConfigGroupType(1);
static constexpr int64_t VSYNC_60 = 16666667;
+ static constexpr int64_t VSYNC_60_POINT_4 = 16666665;
static constexpr int64_t VSYNC_90 = 11111111;
RefreshRateConfigsTest();
@@ -238,6 +239,16 @@
ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
}
+TEST_F(RefreshRateConfigsTest, testInPolicy) {
+ RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60_POINT_4, HWC_GROUP_ID_0,
+ "60fps", 60};
+ ASSERT_TRUE(expectedDefaultConfig.inPolicy(60.000004, 60.000004));
+ ASSERT_TRUE(expectedDefaultConfig.inPolicy(59.0f, 60.1f));
+ ASSERT_FALSE(expectedDefaultConfig.inPolicy(75.0, 90.0));
+ ASSERT_FALSE(expectedDefaultConfig.inPolicy(60.0011, 90.0));
+ ASSERT_FALSE(expectedDefaultConfig.inPolicy(50.0, 59.998));
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index fa095aa..a67c24c 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -30,14 +30,14 @@
public:
explicit TestableScheduler(const scheduler::RefreshRateConfigs& configs)
: Scheduler([](bool) {}, configs, *this) {
- mLayerHistory.emplace();
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
}
TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs)
: Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this) {
- mLayerHistory.emplace();
+ mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
}
// Used to inject mock event thread.
@@ -46,7 +46,7 @@
}
size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
- return mLayerHistory->mLayerInfos.size();
+ return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())->mLayerInfos.size();
}
/* ------------------------------------------------------------------------
@@ -57,7 +57,9 @@
auto& mutableEventControlThread() { return mEventControlThread; }
auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
- auto& mutableLayerHistory() { return mLayerHistory; }
+ auto mutableLayerHistory() {
+ return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
+ }
~TestableScheduler() {
// All these pointer and container clears help ensure that GMock does
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 68e4c58..3e808c0 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -562,6 +562,16 @@
ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+
+ using namespace std::chrono_literals;
+ mTimeStats
+ ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+ .count());
+ mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+ .count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+ .count());
ASSERT_NO_FATAL_FAILURE(
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
ASSERT_NO_FATAL_FAILURE(
@@ -578,6 +588,8 @@
EXPECT_EQ(0, globalProto.missed_frames());
EXPECT_EQ(0, globalProto.client_composition_frames());
EXPECT_EQ(0, globalProto.present_to_present_size());
+ EXPECT_EQ(0, globalProto.frame_duration_size());
+ EXPECT_EQ(0, globalProto.render_engine_timing_size());
EXPECT_EQ(0, globalProto.stats_size());
}
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp
index 36f6b32..dd1603d 100644
--- a/services/vr/hardware_composer/impl/vr_composer_client.cpp
+++ b/services/vr/hardware_composer/impl/vr_composer_client.cpp
@@ -73,7 +73,7 @@
auto err = mVrHal.setLayerInfo(mCurrentDisplay, mCurrentLayer, read(), read());
if (err != Error::NONE) {
- mWriter.setError(getCommandLoc(), err);
+ mWriter->setError(getCommandLoc(), err);
}
return true;
@@ -86,7 +86,7 @@
auto err = mVrHal.setClientTargetMetadata(mCurrentDisplay, readBufferMetadata());
if (err != Error::NONE)
- mWriter.setError(getCommandLoc(), err);
+ mWriter->setError(getCommandLoc(), err);
return true;
}
@@ -99,7 +99,7 @@
auto err = mVrHal.setLayerBufferMetadata(mCurrentDisplay, mCurrentLayer,
readBufferMetadata());
if (err != Error::NONE)
- mWriter.setError(getCommandLoc(), err);
+ mWriter->setError(getCommandLoc(), err);
return true;
}