Merge "Implement initial version of tone mapping in Skia RenderEngine."
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index a686dfb..560459b 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -9,6 +9,7 @@
include/powermanager/
libs/binder/fuzzer/
libs/binder/ndk/
+ libs/binder/tests/fuzzers/
libs/binderthreadstate/
libs/graphicsenv/
libs/gui/
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl
index 7026ca8..491c629 100644
--- a/aidl/gui/android/view/LayerMetadataKey.aidl
+++ b/aidl/gui/android/view/LayerMetadataKey.aidl
@@ -23,4 +23,6 @@
METADATA_WINDOW_TYPE = 2,
METADATA_TASK_ID = 3,
METADATA_MOUSE_CURSOR = 4,
+ METADATA_ACCESSIBILITY_ID = 5,
+ METADATA_OWNER_PID = 6,
}
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 994375b..d95d04a 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -131,6 +131,34 @@
chmod 0666 /sys/kernel/tracing/events/task/task_newtask/enable
chmod 0666 /sys/kernel/debug/tracing/events/gpu_mem/gpu_mem_total/enable
chmod 0666 /sys/kernel/tracing/events/gpu_mem/gpu_mem_total/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/irq/enable
+ chmod 0666 /sys/kernel/tracing/events/irq/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/irq/irq_handler_entry/enable
+ chmod 0666 /sys/kernel/tracing/events/irq/irq_handler_entry/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/irq/irq_handler_exit/enable
+ chmod 0666 /sys/kernel/tracing/events/irq/irq_handler_exit/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/irq/softirq_entry/enable
+ chmod 0666 /sys/kernel/tracing/events/irq/softirq_entry/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/irq/softirq_exit/enable
+ chmod 0666 /sys/kernel/tracing/events/irq/softirq_exit/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/irq/softirq_raise/enable
+ chmod 0666 /sys/kernel/tracing/events/irq/softirq_raise/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/irq/tasklet_entry/enable
+ chmod 0666 /sys/kernel/tracing/events/irq/tasklet_entry/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/irq/tasklet_exit/enable
+ chmod 0666 /sys/kernel/tracing/events/irq/tasklet_exit/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/irq/tasklet_hi_entry/enable
+ chmod 0666 /sys/kernel/tracing/events/irq/tasklet_hi_entry/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/irq/tasklet_hi_exit/enable
+ chmod 0666 /sys/kernel/tracing/events/irq/tasklet_hi_exit/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/ipi/enable
+ chmod 0666 /sys/kernel/tracing/events/ipi/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/ipi/ipi_entry/enable
+ chmod 0666 /sys/kernel/tracing/events/ipi/ipi_entry/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/ipi/ipi_exit/enable
+ chmod 0666 /sys/kernel/tracing/events/ipi/ipi_exit/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/ipi/ipi_raise/enable
+ chmod 0666 /sys/kernel/tracing/events/ipi/ipi_raise/enable
# disk
chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp
index bbc724c..3091f6b 100644
--- a/cmds/dumpstate/DumpstateInternal.cpp
+++ b/cmds/dumpstate/DumpstateInternal.cpp
@@ -69,7 +69,7 @@
static const std::vector<std::string> group_names{
"log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats",
- "readproc", "bluetooth", "wakelock"};
+ "readproc", "bluetooth", "wakelock", "nfc"};
std::vector<gid_t> groups(group_names.size(), 0);
for (size_t i = 0; i < group_names.size(); ++i) {
grp = getgrnam(group_names[i].c_str());
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 91d5524..a81cdaf 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1636,6 +1636,8 @@
/* Dump Bluetooth HCI logs */
ds.AddDir("/data/misc/bluetooth/logs", true);
+ /* Dump Nfc NCI logs */
+ ds.AddDir("/data/misc/nfc/logs", true);
if (ds.options_->do_screenshot && !ds.do_early_screenshot_) {
MYLOGI("taking late screenshot\n");
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index bb0e5ad..1c6583e 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -239,10 +239,10 @@
EXPECT_EQ(access(getZipFilePath().c_str(), F_OK), 0);
}
-TEST_F(ZippedBugreportGenerationTest, Is3MBMBinSize) {
+TEST_F(ZippedBugreportGenerationTest, Is1MBMBinSize) {
struct stat st;
EXPECT_EQ(stat(getZipFilePath().c_str(), &st), 0);
- EXPECT_GE(st.st_size, 3000000 /* 3MB */);
+ EXPECT_GE(st.st_size, 1000000 /* 1MB */);
}
TEST_F(ZippedBugreportGenerationTest, TakesBetween30And300Seconds) {
@@ -401,7 +401,7 @@
SectionExists("batterystats", /* bytes= */ 1000);
}
-TEST_F(BugreportSectionTest, WifiSectionGenerated) {
+TEST_F(BugreportSectionTest, DISABLED_WifiSectionGenerated) {
SectionExists("wifi", /* bytes= */ 100000);
}
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index bd45005..7c9e3b2 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -41,6 +41,21 @@
"liblogwrap",
],
test_config: "installd_cache_test.xml",
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "QuotaUtils.cpp",
+ ],
+ static_libs: [
+ "libarcdiskquota",
+ "arc_services_aidl",
+ ],
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ },
+ },
}
cc_test {
@@ -66,6 +81,21 @@
"liblogwrap",
],
test_config: "installd_service_test.xml",
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "QuotaUtils.cpp",
+ ],
+ static_libs: [
+ "libarcdiskquota",
+ "arc_services_aidl",
+ ],
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ },
+ },
}
cc_test {
@@ -93,6 +123,21 @@
"libz",
],
test_config: "installd_dexopt_test.xml",
+
+ product_variables: {
+ arc: {
+ exclude_srcs: [
+ "QuotaUtils.cpp",
+ ],
+ static_libs: [
+ "libarcdiskquota",
+ "arc_services_aidl",
+ ],
+ cflags: [
+ "-DUSE_ARC",
+ ],
+ },
+ },
}
cc_test {
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index 9964888..ba6cdf1 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -479,8 +479,7 @@
}
TEST_F(ListTest, DumpVintf) {
- const std::string expected = "<manifest version=\"2.0\" type=\"device\">\n"
- " <hal format=\"hidl\">\n"
+ const std::string expected = " <hal format=\"hidl\">\n"
" <name>a.h.foo1</name>\n"
" <transport>hwbinder</transport>\n"
" <fqname>@1.0::IFoo/1</fqname>\n"
@@ -499,8 +498,7 @@
" <name>a.h.foo4</name>\n"
" <transport arch=\"32\">passthrough</transport>\n"
" <fqname>@4.0::IFoo/4</fqname>\n"
- " </hal>\n"
- "</manifest>";
+ " </hal>\n";
optind = 1; // mimic Lshal::parseArg()
EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"})));
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 6c3b79b..c6f656b 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -41,7 +41,6 @@
LayerChange layer = 5;
CropChange crop = 6;
MatrixChange matrix = 8;
- OverrideScalingModeChange override_scaling_mode = 9;
TransparentRegionHintChange transparent_region_hint = 10;
LayerStackChange layer_stack = 11;
HiddenFlagChange hidden_flag = 12;
@@ -55,6 +54,7 @@
ReparentChildrenChange reparent_children = 20;
BackgroundBlurRadiusChange background_blur_radius = 21;
ShadowRadiusChange shadow_radius = 22;
+ BlurRegionsChange blur_regions = 23;
}
}
@@ -95,10 +95,6 @@
required float dtdy = 4;
}
-message OverrideScalingModeChange {
- required int32 override_scaling_mode = 1;
-}
-
message TransparentRegionHintChange {
repeated Rectangle region = 1;
}
@@ -212,6 +208,23 @@
required float radius = 1;
}
+message BlurRegionsChange {
+ repeated BlurRegionChange blur_regions = 1;
+}
+
+message BlurRegionChange {
+ required uint32 blur_radius = 1;
+ required float corner_radius_tl = 2;
+ required float corner_radius_tr = 3;
+ required float corner_radius_bl = 4;
+ required float corner_radius_br = 5;
+ required float alpha = 6;
+ required int32 left = 7;
+ required int32 top = 8;
+ required int32 right = 9;
+ required int32 bottom = 10;
+}
+
message Origin {
required int32 pid = 1;
required int32 uid = 2;
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 86e4f5d..5849212 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -387,10 +387,6 @@
case SurfaceChange::SurfaceChangeCase::kMatrix:
setMatrix(transaction, change.id(), change.matrix());
break;
- case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
- setOverrideScalingMode(transaction, change.id(),
- change.override_scaling_mode());
- break;
case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
setTransparentRegionHint(transaction, change.id(),
change.transparent_region_hint());
@@ -427,6 +423,9 @@
case SurfaceChange::SurfaceChangeCase::kShadowRadius:
setShadowRadiusChange(transaction, change.id(), change.shadow_radius());
break;
+ case SurfaceChange::SurfaceChangeCase::kBlurRegions:
+ setBlurRegionsChange(transaction, change.id(), change.blur_regions());
+ break;
default:
status = 1;
break;
@@ -525,12 +524,6 @@
t.setMatrix(mLayers[id], mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy());
}
-void Replayer::setOverrideScalingMode(SurfaceComposerClient::Transaction& t,
- layer_id id, const OverrideScalingModeChange& osmc) {
- ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode());
- t.setOverrideScalingMode(mLayers[id], osmc.override_scaling_mode());
-}
-
void Replayer::setTransparentRegionHint(SurfaceComposerClient::Transaction& t,
layer_id id, const TransparentRegionHintChange& trhc) {
ALOGV("Setting Transparent Region Hint");
@@ -738,3 +731,24 @@
layer_id id, const ShadowRadiusChange& c) {
t.setShadowRadius(mLayers[id], c.radius());
}
+
+void Replayer::setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
+ layer_id id, const BlurRegionsChange& c) {
+ std::vector<BlurRegion> regions;
+ for(size_t i=0; i < c.blur_regions_size(); i++) {
+ auto protoRegion = c.blur_regions(i);
+ regions.push_back(BlurRegion{
+ .blurRadius = protoRegion.blur_radius(),
+ .alpha = protoRegion.alpha(),
+ .cornerRadiusTL = protoRegion.corner_radius_tl(),
+ .cornerRadiusTR = protoRegion.corner_radius_tr(),
+ .cornerRadiusBL = protoRegion.corner_radius_bl(),
+ .cornerRadiusBR = protoRegion.corner_radius_br(),
+ .left = protoRegion.left(),
+ .top = protoRegion.top(),
+ .right = protoRegion.right(),
+ .bottom = protoRegion.bottom()
+ });
+ }
+ t.setBlurRegions(mLayers[id], regions);
+}
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index 95857e1..a22262a 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -96,10 +96,10 @@
layer_id id, const CornerRadiusChange& cc);
void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t,
layer_id id, const BackgroundBlurRadiusChange& cc);
+ void setBlurRegions(SurfaceComposerClient::Transaction& t,
+ layer_id id, const BlurRegionsChange& cc);
void setMatrix(SurfaceComposerClient::Transaction& t,
layer_id id, const MatrixChange& mc);
- void setOverrideScalingMode(SurfaceComposerClient::Transaction& t,
- layer_id id, const OverrideScalingModeChange& osmc);
void setTransparentRegionHint(SurfaceComposerClient::Transaction& t,
layer_id id, const TransparentRegionHintChange& trgc);
void setLayerStack(SurfaceComposerClient::Transaction& t,
@@ -122,6 +122,8 @@
layer_id id, const ReparentChildrenChange& c);
void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
layer_id id, const ShadowRadiusChange& c);
+ void setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
+ layer_id id, const BlurRegionsChange& c);
void setDisplaySurface(SurfaceComposerClient::Transaction& t,
display_id id, const DispSurfaceChange& dsc);
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
index d63d97f..58bfbf3 100644
--- a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
+++ b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
@@ -69,7 +69,6 @@
print ("5. Crop Change")
print ("6. Final Crop Change")
print ("7. Matrix Change")
- print ("8. Override Scaling Mode Change")
print ("9. Transparent Region Hint Change")
print ("10. Layer Stack Change")
print ("11. Hidden Flag Change")
@@ -128,9 +127,6 @@
change.matrix.dtdx,\
change.matrix.dsdy,\
change.matrix.dtdy = layer()
- elif option == 8:
- change.override_scaling_mode.override_scaling_mode \
- = override_scaling_mode()
elif option == 9:
for rect in transparent_region_hint():
new = increment.transparent_region_hint.region.add()
@@ -227,11 +223,6 @@
return float(dsdx)
-def override_scaling_mode():
- mode = input("Enter override scaling mode: ")
-
- return int(mode)
-
def transparent_region_hint():
num = input("Enter number of rectangles in region: ")
diff --git a/data/etc/cec_config.xml b/data/etc/cec_config.xml
new file mode 100644
index 0000000..8e78ad7
--- /dev/null
+++ b/data/etc/cec_config.xml
@@ -0,0 +1,40 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<cec-settings>
+ <setting name="hdmi_cec_enabled"
+ value-type="int"
+ user-configurable="true">
+ <allowed-values>
+ <value int-value="0" />
+ <value int-value="1" />
+ </allowed-values>
+ <default-value int-value="1" />
+ </setting>
+ <setting name="send_standby_on_sleep"
+ value-type="string"
+ user-configurable="true">
+ <allowed-values>
+ <value string-value="to_tv" />
+ <value string-value="broadcast" />
+ <value string-value="none" />
+ </allowed-values>
+ <default-value string-value="to_tv" />
+ </setting>
+ <setting name="power_state_change_on_active_source_lost"
+ value-type="string"
+ user-configurable="false">
+ <allowed-values>
+ <value string-value="none" />
+ <value string-value="standby_now" />
+ </allowed-values>
+ <default-value string-value="none" />
+ </setting>
+ <setting name="system_audio_mode_muting"
+ value-type="int"
+ user-configurable="false">
+ <allowed-values>
+ <value int-value="0" />
+ <value int-value="1" />
+ </allowed-values>
+ <default-value int-value="1" />
+ </setting>
+</cec-settings>
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index d7e6e41..3dd1534 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -158,7 +158,8 @@
* - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
* supported.
*/
-int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDecoder)
+int AImageDecoder_createFromAAsset(struct AAsset* _Nonnull asset,
+ AImageDecoder* _Nonnull * _Nonnull outDecoder)
__INTRODUCED_IN(30);
/**
@@ -189,7 +190,8 @@
* - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
* supported.
*/
-int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+int AImageDecoder_createFromFd(int fd, AImageDecoder* _Nonnull * _Nonnull outDecoder)
+ __INTRODUCED_IN(30);
/**
* Create a new AImageDecoder from a buffer.
@@ -218,15 +220,16 @@
* - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
* supported.
*/
-int AImageDecoder_createFromBuffer(const void* buffer, size_t length,
- AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+int AImageDecoder_createFromBuffer(const void* _Nonnull buffer, size_t length,
+ AImageDecoder* _Nonnull * _Nonnull outDecoder)
+ __INTRODUCED_IN(30);
/**
* Delete the AImageDecoder.
*
* Available since API level 30.
*/
-void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30);
+void AImageDecoder_delete(AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30);
/**
* Choose the desired output format.
@@ -247,7 +250,7 @@
* - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}: The
* {@link AndroidBitmapFormat} is incompatible with the image.
*/
-int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*,
+int AImageDecoder_setAndroidBitmapFormat(AImageDecoder* _Nonnull decoder,
int32_t format) __INTRODUCED_IN(30);
/**
@@ -270,7 +273,7 @@
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
* {@link AImageDecoder} is null.
*/
-int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*,
+int AImageDecoder_setUnpremultipliedRequired(AImageDecoder* _Nonnull decoder,
bool unpremultipliedRequired) __INTRODUCED_IN(30);
/**
@@ -295,7 +298,8 @@
* {@link AImageDecoder} is null or |dataspace| does not correspond to an
* {@link ADataSpace} value.
*/
-int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_IN(30);
+int AImageDecoder_setDataSpace(AImageDecoder* _Nonnull decoder, int32_t dataspace)
+ __INTRODUCED_IN(30);
/**
* Specify the output size for a decoded image.
@@ -324,7 +328,8 @@
* or the scale is incompatible with a previous call to
* {@link AImageDecoder_setUnpremultipliedRequired}(true).
*/
-int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) __INTRODUCED_IN(30);
+int AImageDecoder_setTargetSize(AImageDecoder* _Nonnull decoder, int32_t width,
+ int32_t height) __INTRODUCED_IN(30);
/**
@@ -353,8 +358,9 @@
* - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
* {@link AImageDecoder}, |width| or |height| is null or |sampleSize| is < 1.
*/
-int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize,
- int32_t* width, int32_t* height) __INTRODUCED_IN(30);
+int AImageDecoder_computeSampledSize(const AImageDecoder* _Nonnull decoder, int sampleSize,
+ int32_t* _Nonnull width, int32_t* _Nonnull height)
+ __INTRODUCED_IN(30);
/**
* Specify how to crop the output after scaling (if any).
*
@@ -380,7 +386,7 @@
* {@link AImageDecoder} is null or the crop is not contained by the
* (possibly scaled) image dimensions.
*/
-int AImageDecoder_setCrop(AImageDecoder*, ARect crop) __INTRODUCED_IN(30);
+int AImageDecoder_setCrop(AImageDecoder* _Nonnull decoder, ARect crop) __INTRODUCED_IN(30);
struct AImageDecoderHeaderInfo;
/**
@@ -399,8 +405,8 @@
*
* Available since API level 30.
*/
-const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(
- const AImageDecoder*) __INTRODUCED_IN(30);
+const AImageDecoderHeaderInfo* _Nonnull AImageDecoder_getHeaderInfo(
+ const AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30);
/**
* Report the native width of the encoded image. This is also the logical
@@ -410,7 +416,8 @@
*
* Available since API level 30.
*/
-int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo* _Nonnull)
+ __INTRODUCED_IN(30);
/**
* Report the native height of the encoded image. This is also the logical
@@ -420,7 +427,8 @@
*
* Available since API level 30.
*/
-int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo* _Nonnull)
+ __INTRODUCED_IN(30);
/**
* Report the mimeType of the encoded image.
@@ -429,8 +437,8 @@
*
* @return a string literal describing the mime type.
*/
-const char* AImageDecoderHeaderInfo_getMimeType(
- const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+const char* _Nonnull AImageDecoderHeaderInfo_getMimeType(
+ const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
/**
* Report the {@link AndroidBitmapFormat} the AImageDecoder will decode to
@@ -441,7 +449,7 @@
* Available since API level 30.
*/
int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(
- const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+ const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
/**
* Report how the {@link AImageDecoder} will handle alpha by default. If the image
@@ -453,7 +461,7 @@
* Available since API level 30.
*/
int AImageDecoderHeaderInfo_getAlphaFlags(
- const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+ const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
/**
* Report the dataspace the AImageDecoder will decode to by default.
@@ -474,7 +482,7 @@
* no corresponding {@link ADataSpace}.
*/
int32_t AImageDecoderHeaderInfo_getDataSpace(
- const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+ const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30);
/**
* Return the minimum stride that can be used in
@@ -489,13 +497,17 @@
*
* Available since API level 30.
*/
-size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30);
+size_t AImageDecoder_getMinimumStride(AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30);
/**
* Decode the image into pixels, using the settings of the {@link AImageDecoder}.
*
* Available since API level 30.
*
+ * Starting in API level 31, it can be used to decode all of the frames of an
+ * animated image (i.e. GIF, WebP, HEIF) using new APIs (TODO (scroggo): list
+ * and describe here).
+ *
* @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 hold |size| bytes.
@@ -523,12 +535,64 @@
* - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a
* failure to allocate memory.
*/
-int AImageDecoder_decodeImage(AImageDecoder* decoder,
- void* pixels, size_t stride,
+int AImageDecoder_decodeImage(AImageDecoder* _Nonnull decoder,
+ void* _Nonnull pixels, size_t stride,
size_t size) __INTRODUCED_IN(30);
#endif // __ANDROID_API__ >= 30
+#if __ANDROID_API__ >= 31
+
+/**
+ * Return true iff the image is animated - i.e. has multiple frames.
+ *
+ * Introduced in API 31.
+ *
+ * This may require seeking past the first frame to verify whether
+ * there is a following frame (e.g. for GIF).
+ *
+ * Errors:
+ * - returns false if |decoder| is null.
+ */
+bool AImageDecoder_isAnimated(AImageDecoder* _Nonnull decoder)
+ __INTRODUCED_IN(31);
+
+enum {
+ /*
+ * Reported by {@link AImageDecoder_getRepeatCount} if the
+ * animation should repeat forever.
+ */
+ ANDROID_IMAGE_DECODER_INFINITE = INT32_MAX,
+};
+
+/**
+ * Report how many times the animation should repeat.
+ *
+ * Introduced in API 31.
+ *
+ * This does not include the first play through. e.g. a repeat
+ * count of 4 means that each frame is played 5 times.
+ *
+ * {@link ANDROID_IMAGE_DECODER_INFINITE} means to repeat forever.
+ *
+ * This may require seeking.
+ *
+ * For non-animated formats, this returns 0. It may return non-zero for
+ * an image with only one frame (i.e. {@link AImageDecoder_isAnimated} returns
+ * false) if the encoded image contains a repeat count.
+ *
+ * @return Number of times to repeat on success or a value
+ * indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder
+ * is null.
+ */
+int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder);
+ __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
#ifdef __cplusplus
}
#endif
diff --git a/include/android/permission_manager.h b/include/android/permission_manager.h
new file mode 100644
index 0000000..7817126
--- /dev/null
+++ b/include/android/permission_manager.h
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_PERMISSION_MANAGER_H
+#define ANDROID_PERMISSION_MANAGER_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/**
+ * Permission check results.
+ *
+ * Introduced in API 31.
+ */
+enum {
+ /**
+ * This is returned by APermissionManager_checkPermission()
+ * if the permission has been granted to the given package.
+ */
+ PERMISSION_MANAGER_PERMISSION_GRANTED = 0,
+ /**
+ * This is returned by APermissionManager_checkPermission()
+ * if the permission has not been granted to the given package.
+ */
+ PERMISSION_MANAGER_PERMISSION_DENIED = -1,
+};
+
+/**
+ * Permission check return status values.
+ *
+ * Introduced in API 31.
+ */
+enum {
+ /**
+ * This is returned if the permission check completed without errors.
+ * The output result is valid and contains one of {PERMISSION_MANAGER_PERMISSION_GRANTED,
+ * PERMISSION_MANAGER_PERMISSION_DENIED}.
+ */
+ PERMISSION_MANAGER_STATUS_OK = 0,
+ /**
+ * This is returned if the permission check encountered an unspecified error.
+ * The output result is unmodified.
+ */
+ PERMISSION_MANAGER_STATUS_ERROR_UNKNOWN = -1,
+ /**
+ * This is returned if the permission check failed because the service is
+ * unavailable. The output result is unmodified.
+ */
+ PERMISSION_MANAGER_STATUS_SERVICE_UNAVAILABLE = -2,
+};
+
+#if __ANDROID_API__ >= 31
+
+/**
+ * Checks whether the package with the given pid/uid has been granted a permission.
+ *
+ * Note that the Java API of Context#checkPermission() is usually faster due to caching,
+ * thus is preferred over this API wherever possible.
+ *
+ * @param permission the permission to be checked.
+ * @param pid the process id of the package to be checked.
+ * @param uid the uid of the package to be checked.
+ * @param outResult output of the permission check result.
+ *
+ * @return error codes if any error happened during the check.
+ */
+int32_t APermissionManager_checkPermission(const char* permission,
+ pid_t pid,
+ uid_t uid,
+ int32_t* outResult) __INTRODUCED_IN(31);
+
+#endif // __ANDROID_API__ >= 31
+
+__END_DECLS
+
+#endif // ANDROID_PERMISSION_MANAGER_H
diff --git a/include/ftl/ArrayTraits.h b/include/ftl/ArrayTraits.h
new file mode 100644
index 0000000..28f717a
--- /dev/null
+++ b/include/ftl/ArrayTraits.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright 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 <algorithm>
+#include <iterator>
+#include <new>
+
+#define FTL_ARRAY_TRAIT(T, U) using U = typename ArrayTraits<T>::U
+
+namespace android::ftl {
+
+template <typename T>
+struct ArrayTraits {
+ using value_type = T;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ using pointer = value_type*;
+ using reference = value_type&;
+ using iterator = pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+
+ using const_pointer = const value_type*;
+ using const_reference = const value_type&;
+ using const_iterator = const_pointer;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ // TODO: Replace with std::construct_at in C++20.
+ template <typename... Args>
+ static pointer construct_at(const_iterator it, Args&&... args) {
+ void* const ptr = const_cast<void*>(static_cast<const void*>(it));
+ return new (ptr) value_type{std::forward<Args>(args)...};
+ }
+};
+
+// CRTP mixin to define iterator functions in terms of non-const Self::begin and Self::end.
+template <typename Self, typename T>
+class ArrayIterators {
+ FTL_ARRAY_TRAIT(T, size_type);
+
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ Self& self() const { return *const_cast<Self*>(static_cast<const Self*>(this)); }
+
+public:
+ const_iterator begin() const { return cbegin(); }
+ const_iterator cbegin() const { return self().begin(); }
+
+ const_iterator end() const { return cend(); }
+ const_iterator cend() const { return self().end(); }
+
+ reverse_iterator rbegin() { return std::make_reverse_iterator(self().end()); }
+ const_reverse_iterator rbegin() const { return crbegin(); }
+ const_reverse_iterator crbegin() const { return self().rbegin(); }
+
+ reverse_iterator rend() { return std::make_reverse_iterator(self().begin()); }
+ const_reverse_iterator rend() const { return crend(); }
+ const_reverse_iterator crend() const { return self().rend(); }
+
+ iterator last() { return self().end() - 1; }
+ const_iterator last() const { return self().last(); }
+
+ reference front() { return *self().begin(); }
+ const_reference front() const { return self().front(); }
+
+ reference back() { return *last(); }
+ const_reference back() const { return self().back(); }
+
+ reference operator[](size_type i) { return *(self().begin() + i); }
+ const_reference operator[](size_type i) const { return self()[i]; }
+};
+
+// Mixin to define comparison operators for an array-like template.
+// TODO: Replace with operator<=> in C++20.
+template <template <typename, size_t> class Array>
+struct ArrayComparators {
+ template <typename T, size_t N, size_t M>
+ friend bool operator==(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator<(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator>(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return rhs < lhs;
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator!=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return !(lhs == rhs);
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator>=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return !(lhs < rhs);
+ }
+
+ template <typename T, size_t N, size_t M>
+ friend bool operator<=(const Array<T, N>& lhs, const Array<T, M>& rhs) {
+ return !(lhs > rhs);
+ }
+};
+
+} // namespace android::ftl
diff --git a/include/ftl/SmallVector.h b/include/ftl/SmallVector.h
new file mode 100644
index 0000000..cecec7f
--- /dev/null
+++ b/include/ftl/SmallVector.h
@@ -0,0 +1,380 @@
+/*
+ * Copyright 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 <ftl/ArrayTraits.h>
+#include <ftl/StaticVector.h>
+
+#include <algorithm>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <variant>
+#include <vector>
+
+namespace android::ftl {
+
+template <typename>
+struct IsSmallVector;
+
+// ftl::StaticVector that promotes to std::vector when full. SmallVector is a drop-in replacement
+// for std::vector with statically allocated storage for N elements, whose goal is to improve run
+// time by avoiding heap allocation and increasing probability of cache hits. The standard API is
+// augmented by an unstable_erase operation that does not preserve order, and a replace operation
+// that destructively emplaces.
+//
+// SmallVector<T, 0> is a specialization that thinly wraps std::vector.
+//
+// Example usage:
+//
+// ftl::SmallVector<char, 3> vector;
+// assert(vector.empty());
+// assert(!vector.dynamic());
+//
+// vector = {'a', 'b', 'c'};
+// assert(vector.size() == 3u);
+// assert(!vector.dynamic());
+//
+// vector.push_back('d');
+// assert(vector.dynamic());
+//
+// vector.unstable_erase(vector.begin());
+// assert(vector == (ftl::SmallVector{'d', 'b', 'c'}));
+//
+// vector.pop_back();
+// assert(vector.back() == 'b');
+// assert(vector.dynamic());
+//
+// const char array[] = "hi";
+// vector = ftl::SmallVector(array);
+// assert(vector == (ftl::SmallVector{'h', 'i', '\0'}));
+// assert(!vector.dynamic());
+//
+template <typename T, size_t N>
+class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> {
+ using Static = StaticVector<T, N>;
+ using Dynamic = SmallVector<T, 0>;
+
+ // TODO: Replace with std::remove_cvref_t in C++20.
+ template <typename U>
+ using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>;
+
+public:
+ FTL_ARRAY_TRAIT(T, value_type);
+ FTL_ARRAY_TRAIT(T, size_type);
+ FTL_ARRAY_TRAIT(T, difference_type);
+
+ FTL_ARRAY_TRAIT(T, pointer);
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_pointer);
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ // Creates an empty vector.
+ SmallVector() = default;
+
+ // Constructs at most N elements. See StaticVector for underlying constructors.
+ template <typename Arg, typename... Args,
+ typename = std::enable_if_t<!IsSmallVector<remove_cvref_t<Arg>>{}>>
+ SmallVector(Arg&& arg, Args&&... args)
+ : mVector(std::in_place_type<Static>, std::forward<Arg>(arg),
+ std::forward<Args>(args)...) {}
+
+ // Copies at most N elements from a smaller convertible vector.
+ template <typename U, size_t M, typename = std::enable_if_t<M <= N>>
+ SmallVector(const SmallVector<U, M>& other)
+ : SmallVector(IteratorRange, other.begin(), other.end()) {}
+
+ void swap(SmallVector& other) { mVector.swap(other.mVector); }
+
+ // Returns whether the vector is backed by static or dynamic storage.
+ bool dynamic() const { return std::holds_alternative<Dynamic>(mVector); }
+
+ // Avoid std::visit as it generates a dispatch table.
+#define DISPATCH(T, F, ...) \
+ T F() __VA_ARGS__ { \
+ return dynamic() ? std::get<Dynamic>(mVector).F() : std::get<Static>(mVector).F(); \
+ }
+
+ DISPATCH(size_type, max_size, const)
+ DISPATCH(size_type, size, const)
+ DISPATCH(bool, empty, const)
+
+ // noexcept to suppress warning about zero variadic macro arguments.
+ DISPATCH(iterator, begin, noexcept)
+ DISPATCH(const_iterator, begin, const)
+ DISPATCH(const_iterator, cbegin, const)
+
+ DISPATCH(iterator, end, noexcept)
+ DISPATCH(const_iterator, end, const)
+ DISPATCH(const_iterator, cend, const)
+
+ DISPATCH(reverse_iterator, rbegin, noexcept)
+ DISPATCH(const_reverse_iterator, rbegin, const)
+ DISPATCH(const_reverse_iterator, crbegin, const)
+
+ DISPATCH(reverse_iterator, rend, noexcept)
+ DISPATCH(const_reverse_iterator, rend, const)
+ DISPATCH(const_reverse_iterator, crend, const)
+
+ DISPATCH(iterator, last, noexcept)
+ DISPATCH(const_iterator, last, const)
+
+ DISPATCH(reference, front, noexcept)
+ DISPATCH(const_reference, front, const)
+
+ DISPATCH(reference, back, noexcept)
+ DISPATCH(const_reference, back, const)
+
+#undef DISPATCH
+
+ reference operator[](size_type i) {
+ return dynamic() ? std::get<Dynamic>(mVector)[i] : std::get<Static>(mVector)[i];
+ }
+
+ const_reference operator[](size_type i) const { return const_cast<SmallVector&>(*this)[i]; }
+
+ // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
+ // replacing at end() is erroneous.
+ //
+ // The element is emplaced via move constructor, so type T does not need to define copy/move
+ // assignment, e.g. its data members may be const.
+ //
+ // The arguments may directly or indirectly refer to the element being replaced.
+ //
+ // Iterators to the replaced element point to its replacement, and others remain valid.
+ //
+ template <typename... Args>
+ reference replace(const_iterator it, Args&&... args) {
+ if (dynamic()) {
+ return std::get<Dynamic>(mVector).replace(it, std::forward<Args>(args)...);
+ } else {
+ return std::get<Static>(mVector).replace(it, std::forward<Args>(args)...);
+ }
+ }
+
+ // Appends an element, and returns a reference to it.
+ //
+ // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
+ // Otherwise, only the end() iterator is invalidated.
+ //
+ template <typename... Args>
+ reference emplace_back(Args&&... args) {
+ constexpr auto insertStatic = &Static::template emplace_back<Args...>;
+ constexpr auto insertDynamic = &Dynamic::template emplace_back<Args...>;
+ return *insert<insertStatic, insertDynamic>(std::forward<Args>(args)...);
+ }
+
+ // Appends an element.
+ //
+ // If the vector reaches its static or dynamic capacity, then all iterators are invalidated.
+ // Otherwise, only the end() iterator is invalidated.
+ //
+ void push_back(const value_type& v) {
+ constexpr auto insertStatic =
+ static_cast<bool (Static::*)(const value_type&)>(&Static::push_back);
+ constexpr auto insertDynamic =
+ static_cast<bool (Dynamic::*)(const value_type&)>(&Dynamic::push_back);
+ insert<insertStatic, insertDynamic>(v);
+ }
+
+ void push_back(value_type&& v) {
+ constexpr auto insertStatic =
+ static_cast<bool (Static::*)(value_type&&)>(&Static::push_back);
+ constexpr auto insertDynamic =
+ static_cast<bool (Dynamic::*)(value_type&&)>(&Dynamic::push_back);
+ insert<insertStatic, insertDynamic>(std::move(v));
+ }
+
+ // Removes the last element. The vector must not be empty, or the call is erroneous.
+ //
+ // The last() and end() iterators are invalidated.
+ //
+ void pop_back() {
+ if (dynamic()) {
+ std::get<Dynamic>(mVector).pop_back();
+ } else {
+ std::get<Static>(mVector).pop_back();
+ }
+ }
+
+ // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
+ // this moves the last element to the slot of the erased element.
+ //
+ // The last() and end() iterators, as well as those to the erased element, are invalidated.
+ //
+ void unstable_erase(iterator it) {
+ if (dynamic()) {
+ std::get<Dynamic>(mVector).unstable_erase(it);
+ } else {
+ std::get<Static>(mVector).unstable_erase(it);
+ }
+ }
+
+private:
+ template <auto insertStatic, auto insertDynamic, typename... Args>
+ auto insert(Args&&... args) {
+ if (Dynamic* const vector = std::get_if<Dynamic>(&mVector)) {
+ return (vector->*insertDynamic)(std::forward<Args>(args)...);
+ }
+
+ auto& vector = std::get<Static>(mVector);
+ if (vector.full()) {
+ return (promote(vector).*insertDynamic)(std::forward<Args>(args)...);
+ } else {
+ return (vector.*insertStatic)(std::forward<Args>(args)...);
+ }
+ }
+
+ Dynamic& promote(Static& staticVector) {
+ assert(staticVector.full());
+
+ // Allocate double capacity to reduce probability of reallocation.
+ Dynamic vector;
+ vector.reserve(Static::max_size() * 2);
+ std::move(staticVector.begin(), staticVector.end(), std::back_inserter(vector));
+
+ return mVector.template emplace<Dynamic>(std::move(vector));
+ }
+
+ std::variant<Static, Dynamic> mVector;
+};
+
+// Partial specialization without static storage.
+template <typename T>
+class SmallVector<T, 0> final : ArrayTraits<T>,
+ ArrayIterators<SmallVector<T, 0>, T>,
+ std::vector<T> {
+ using ArrayTraits<T>::construct_at;
+
+ using Iter = ArrayIterators<SmallVector, T>;
+ using Impl = std::vector<T>;
+
+ friend Iter;
+
+public:
+ FTL_ARRAY_TRAIT(T, value_type);
+ FTL_ARRAY_TRAIT(T, size_type);
+ FTL_ARRAY_TRAIT(T, difference_type);
+
+ FTL_ARRAY_TRAIT(T, pointer);
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_pointer);
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ using Impl::Impl;
+
+ using Impl::empty;
+ using Impl::max_size;
+ using Impl::size;
+
+ using Impl::reserve;
+
+ // std::vector iterators are not necessarily raw pointers.
+ iterator begin() { return Impl::data(); }
+ iterator end() { return Impl::data() + size(); }
+
+ using Iter::begin;
+ using Iter::end;
+
+ using Iter::cbegin;
+ using Iter::cend;
+
+ using Iter::rbegin;
+ using Iter::rend;
+
+ using Iter::crbegin;
+ using Iter::crend;
+
+ using Iter::last;
+
+ using Iter::back;
+ using Iter::front;
+
+ using Iter::operator[];
+
+ template <typename... Args>
+ reference replace(const_iterator it, Args&&... args) {
+ value_type element{std::forward<Args>(args)...};
+ std::destroy_at(it);
+ // This is only safe because exceptions are disabled.
+ return *construct_at(it, std::move(element));
+ }
+
+ template <typename... Args>
+ iterator emplace_back(Args&&... args) {
+ return &Impl::emplace_back(std::forward<Args>(args)...);
+ }
+
+ bool push_back(const value_type& v) {
+ Impl::push_back(v);
+ return true;
+ }
+
+ bool push_back(value_type&& v) {
+ Impl::push_back(std::move(v));
+ return true;
+ }
+
+ using Impl::pop_back;
+
+ void unstable_erase(iterator it) {
+ if (it != last()) std::iter_swap(it, last());
+ pop_back();
+ }
+
+ void swap(SmallVector& other) { Impl::swap(other); }
+};
+
+template <typename>
+struct IsSmallVector : std::false_type {};
+
+template <typename T, size_t N>
+struct IsSmallVector<SmallVector<T, N>> : std::true_type {};
+
+// Deduction guide for array constructor.
+template <typename T, size_t N>
+SmallVector(T (&)[N]) -> SmallVector<std::remove_cv_t<T>, N>;
+
+// Deduction guide for variadic constructor.
+template <typename T, typename... Us, typename V = std::decay_t<T>,
+ typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
+SmallVector(T&&, Us&&...) -> SmallVector<V, 1 + sizeof...(Us)>;
+
+// Deduction guide for in-place constructor.
+template <typename T, typename... Us>
+SmallVector(std::in_place_type_t<T>, Us&&...) -> SmallVector<T, sizeof...(Us)>;
+
+// Deduction guide for StaticVector conversion.
+template <typename T, size_t N>
+SmallVector(StaticVector<T, N>&&) -> SmallVector<T, N>;
+
+template <typename T, size_t N>
+inline void swap(SmallVector<T, N>& lhs, SmallVector<T, N>& rhs) {
+ lhs.swap(rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/StaticVector.h b/include/ftl/StaticVector.h
new file mode 100644
index 0000000..457095d
--- /dev/null
+++ b/include/ftl/StaticVector.h
@@ -0,0 +1,350 @@
+/*
+ * Copyright 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 <ftl/ArrayTraits.h>
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace android::ftl {
+
+constexpr struct IteratorRangeTag {} IteratorRange;
+
+// Fixed-capacity, statically allocated counterpart of std::vector. Akin to std::array, StaticVector
+// allocates contiguous storage for N elements of type T at compile time, but stores at most (rather
+// than exactly) N elements. Unlike std::array, its default constructor does not require T to have a
+// default constructor, since elements are constructed in-place as the vector grows. Operations that
+// insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise
+// adheres to standard containers, except the unstable_erase operation that does not preserve order,
+// and the replace operation that destructively emplaces.
+//
+// StaticVector<T, 1> is analogous to an iterable std::optional, but StaticVector<T, 0> is an error.
+//
+// Example usage:
+//
+// ftl::StaticVector<char, 3> vector;
+// assert(vector.empty());
+//
+// vector = {'a', 'b'};
+// assert(vector.size() == 2u);
+//
+// vector.push_back('c');
+// assert(vector.full());
+//
+// assert(!vector.push_back('d'));
+// assert(vector.size() == 3u);
+//
+// vector.unstable_erase(vector.begin());
+// assert(vector == (ftl::StaticVector{'c', 'b'}));
+//
+// vector.pop_back();
+// assert(vector.back() == 'c');
+//
+// const char array[] = "hi";
+// vector = ftl::StaticVector(array);
+// assert(vector == (ftl::StaticVector{'h', 'i', '\0'}));
+//
+template <typename T, size_t N>
+class StaticVector final : ArrayTraits<T>,
+ ArrayIterators<StaticVector<T, N>, T>,
+ ArrayComparators<StaticVector> {
+ static_assert(N > 0);
+
+ using ArrayTraits<T>::construct_at;
+
+ using Iter = ArrayIterators<StaticVector, T>;
+ friend Iter;
+
+ // There is ambiguity when constructing from two iterator-like elements like pointers:
+ // they could be an iterator range, or arguments for in-place construction. Assume the
+ // latter unless they are input iterators and cannot be used to construct elements. If
+ // the former is intended, the caller can pass an IteratorRangeTag to disambiguate.
+ template <typename I, typename Traits = std::iterator_traits<I>>
+ using IsInputIterator = std::conjunction<
+ std::is_base_of<std::input_iterator_tag, typename Traits::iterator_category>,
+ std::negation<std::is_constructible<T, I>>>;
+
+public:
+ FTL_ARRAY_TRAIT(T, value_type);
+ FTL_ARRAY_TRAIT(T, size_type);
+ FTL_ARRAY_TRAIT(T, difference_type);
+
+ FTL_ARRAY_TRAIT(T, pointer);
+ FTL_ARRAY_TRAIT(T, reference);
+ FTL_ARRAY_TRAIT(T, iterator);
+ FTL_ARRAY_TRAIT(T, reverse_iterator);
+
+ FTL_ARRAY_TRAIT(T, const_pointer);
+ FTL_ARRAY_TRAIT(T, const_reference);
+ FTL_ARRAY_TRAIT(T, const_iterator);
+ FTL_ARRAY_TRAIT(T, const_reverse_iterator);
+
+ // Creates an empty vector.
+ StaticVector() = default;
+
+ // Copies and moves a vector, respectively.
+ StaticVector(const StaticVector& other)
+ : StaticVector(IteratorRange, other.begin(), other.end()) {}
+ StaticVector(StaticVector&& other) { swap<Empty>(other); }
+
+ // Copies at most N elements from a smaller convertible vector.
+ template <typename U, size_t M, typename = std::enable_if_t<M <= N>>
+ StaticVector(const StaticVector<U, M>& other)
+ : StaticVector(IteratorRange, other.begin(), other.end()) {}
+
+ // Copies at most N elements from an array.
+ template <typename U, size_t M>
+ explicit StaticVector(U (&array)[M])
+ : StaticVector(IteratorRange, std::begin(array), std::end(array)) {}
+
+ // Copies at most N elements from the range [first, last).
+ //
+ // IteratorRangeTag disambiguates with initialization from two iterator-like elements.
+ //
+ template <typename Iterator, typename = std::enable_if_t<IsInputIterator<Iterator>{}>>
+ StaticVector(Iterator first, Iterator last) : StaticVector(IteratorRange, first, last) {
+ using V = typename std::iterator_traits<Iterator>::value_type;
+ static_assert(std::is_constructible_v<value_type, V>, "Incompatible iterator range");
+ }
+
+ template <typename Iterator>
+ StaticVector(IteratorRangeTag, Iterator first, Iterator last)
+ : mSize(std::min(max_size(), static_cast<size_type>(std::distance(first, last)))) {
+ std::uninitialized_copy(first, first + mSize, begin());
+ }
+
+ // Constructs at most N elements. The template arguments T and N are inferred using the
+ // deduction guide defined below. Note that T is determined from the first element, and
+ // subsequent elements must have convertible types:
+ //
+ // ftl::StaticVector vector = {1, 2, 3};
+ // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<int, 3>>);
+ //
+ // const auto copy = "quince"s;
+ // auto move = "tart"s;
+ // ftl::StaticVector vector = {copy, std::move(move)};
+ //
+ // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 2>>);
+ //
+ template <typename E, typename... Es,
+ typename = std::enable_if_t<std::is_constructible_v<value_type, E>>>
+ StaticVector(E&& element, Es&&... elements)
+ : StaticVector(std::index_sequence<0>{}, std::forward<E>(element),
+ std::forward<Es>(elements)...) {
+ static_assert(sizeof...(elements) < N, "Too many elements");
+ }
+
+ // Constructs at most N elements. The template arguments T and N are inferred using the
+ // deduction guide defined below. Element types must be convertible to the specified T:
+ //
+ // ftl::StaticVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+ // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 3>>);
+ //
+ template <typename... Es>
+ explicit StaticVector(std::in_place_type_t<T>, Es... elements)
+ : StaticVector(std::forward<Es>(elements)...) {}
+
+ ~StaticVector() { std::destroy(begin(), end()); }
+
+ StaticVector& operator=(const StaticVector& other) {
+ StaticVector copy(other);
+ swap(copy);
+ return *this;
+ }
+
+ StaticVector& operator=(StaticVector&& other) {
+ std::destroy(begin(), end());
+ mSize = 0;
+ swap<Empty>(other);
+ return *this;
+ }
+
+ template <typename = void>
+ void swap(StaticVector&);
+
+ static constexpr size_type max_size() { return N; }
+ size_type size() const { return mSize; }
+
+ bool empty() const { return size() == 0; }
+ bool full() const { return size() == max_size(); }
+
+ iterator begin() { return std::launder(reinterpret_cast<pointer>(mData)); }
+ iterator end() { return begin() + size(); }
+
+ using Iter::begin;
+ using Iter::end;
+
+ using Iter::cbegin;
+ using Iter::cend;
+
+ using Iter::rbegin;
+ using Iter::rend;
+
+ using Iter::crbegin;
+ using Iter::crend;
+
+ using Iter::last;
+
+ using Iter::back;
+ using Iter::front;
+
+ using Iter::operator[];
+
+ // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so
+ // replacing at end() is erroneous.
+ //
+ // The element is emplaced via move constructor, so type T does not need to define copy/move
+ // assignment, e.g. its data members may be const.
+ //
+ // The arguments may directly or indirectly refer to the element being replaced.
+ //
+ // Iterators to the replaced element point to its replacement, and others remain valid.
+ //
+ template <typename... Args>
+ reference replace(const_iterator it, Args&&... args) {
+ value_type element{std::forward<Args>(args)...};
+ std::destroy_at(it);
+ // This is only safe because exceptions are disabled.
+ return *construct_at(it, std::move(element));
+ }
+
+ // Appends an element, and returns an iterator to it. If the vector is full, the element is not
+ // inserted, and the end() iterator is returned.
+ //
+ // On success, the end() iterator is invalidated.
+ //
+ template <typename... Args>
+ iterator emplace_back(Args&&... args) {
+ if (full()) return end();
+ const iterator it = construct_at(end(), std::forward<Args>(args)...);
+ ++mSize;
+ return it;
+ }
+
+ // Appends an element unless the vector is full, and returns whether the element was inserted.
+ //
+ // On success, the end() iterator is invalidated.
+ //
+ bool push_back(const value_type& v) {
+ // Two statements for sequence point.
+ const iterator it = emplace_back(v);
+ return it != end();
+ }
+
+ bool push_back(value_type&& v) {
+ // Two statements for sequence point.
+ const iterator it = emplace_back(std::move(v));
+ return it != end();
+ }
+
+ // Removes the last element. The vector must not be empty, or the call is erroneous.
+ //
+ // The last() and end() iterators are invalidated.
+ //
+ void pop_back() { unstable_erase(last()); }
+
+ // Erases an element, but does not preserve order. Rather than shifting subsequent elements,
+ // this moves the last element to the slot of the erased element.
+ //
+ // The last() and end() iterators, as well as those to the erased element, are invalidated.
+ //
+ void unstable_erase(const_iterator it) {
+ std::destroy_at(it);
+ if (it != last()) {
+ // Move last element and destroy its source for destructor side effects. This is only
+ // safe because exceptions are disabled.
+ construct_at(it, std::move(back()));
+ std::destroy_at(last());
+ }
+ --mSize;
+ }
+
+private:
+ struct Empty {};
+
+ // Recursion for variadic constructor.
+ template <size_t I, typename E, typename... Es>
+ StaticVector(std::index_sequence<I>, E&& element, Es&&... elements)
+ : StaticVector(std::index_sequence<I + 1>{}, std::forward<Es>(elements)...) {
+ construct_at(begin() + I, std::forward<E>(element));
+ }
+
+ // Base case for variadic constructor.
+ template <size_t I>
+ explicit StaticVector(std::index_sequence<I>) : mSize(I) {}
+
+ size_type mSize = 0;
+ std::aligned_storage_t<sizeof(value_type), alignof(value_type)> mData[N];
+};
+
+// Deduction guide for array constructor.
+template <typename T, size_t N>
+StaticVector(T (&)[N]) -> StaticVector<std::remove_cv_t<T>, N>;
+
+// Deduction guide for variadic constructor.
+template <typename T, typename... Us, typename V = std::decay_t<T>,
+ typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>>
+StaticVector(T&&, Us&&...) -> StaticVector<V, 1 + sizeof...(Us)>;
+
+// Deduction guide for in-place constructor.
+template <typename T, typename... Us>
+StaticVector(std::in_place_type_t<T>, Us&&...) -> StaticVector<T, sizeof...(Us)>;
+
+template <typename T, size_t N>
+template <typename E>
+void StaticVector<T, N>::swap(StaticVector& other) {
+ auto [to, from] = std::make_pair(this, &other);
+ if (from == this) return;
+
+ // Assume this vector has fewer elements, so the excess of the other vector will be moved to it.
+ auto [min, max] = std::make_pair(size(), other.size());
+
+ // No elements to swap if moving into an empty vector.
+ if constexpr (std::is_same_v<E, Empty>) {
+ assert(min == 0);
+ } else {
+ if (min > max) {
+ std::swap(from, to);
+ std::swap(min, max);
+ }
+
+ // Swap elements [0, min).
+ std::swap_ranges(begin(), begin() + min, other.begin());
+
+ // No elements to move if sizes are equal.
+ if (min == max) return;
+ }
+
+ // Move elements [min, max) and destroy their source for destructor side effects.
+ const auto [first, last] = std::make_pair(from->begin() + min, from->begin() + max);
+ std::uninitialized_move(first, last, to->begin() + min);
+ std::destroy(first, last);
+
+ std::swap(mSize, other.mSize);
+}
+
+template <typename T, size_t N>
+inline void swap(StaticVector<T, N>& lhs, StaticVector<T, N>& rhs) {
+ lhs.swap(rhs);
+}
+
+} // namespace android::ftl
diff --git a/include/gfx/.clang-format b/include/gfx/.clang-format
deleted file mode 100644
index 86763a0..0000000
--- a/include/gfx/.clang-format
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-
-AccessModifierOffset: -2
-AllowShortFunctionsOnASingleLine: Inline
-BinPackParameters: false
-ColumnLimit: 100
-CommentPragmas: NOLINT:.*
-ConstructorInitializerAllOnOneLineOrOnePerLine: true
-ConstructorInitializerIndentWidth: 2
-ContinuationIndentWidth: 8
-IndentWidth: 4
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 334fe34..b90d57e 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -19,6 +19,7 @@
#include <android-base/stringprintf.h>
#include <input/Input.h>
+#include <input/NamedEnum.h>
#include <cinttypes>
#include <optional>
@@ -44,18 +45,6 @@
VIRTUAL = 3,
};
-static const char* viewportTypeToString(ViewportType type) {
- switch (type) {
- case ViewportType::INTERNAL:
- return "INTERNAL";
- case ViewportType::EXTERNAL:
- return "EXTERNAL";
- case ViewportType::VIRTUAL:
- return "VIRTUAL";
- }
- return "UNKNOWN";
-}
-
/*
* Describes how coordinates are mapped on a physical display.
* See com.android.server.display.DisplayViewport.
@@ -142,7 +131,7 @@
"physicalFrame=[%d, %d, %d, %d], "
"deviceSize=[%d, %d], "
"isActive=[%d]",
- viewportTypeToString(type), displayId, uniqueId.c_str(),
+ NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(),
physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str()
: "<none>",
orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
diff --git a/include/input/Input.h b/include/input/Input.h
index 7d81fed..3facfa5 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -177,7 +177,7 @@
namespace android {
-#ifdef __ANDROID__
+#ifdef __linux__
class Parcel;
#endif
@@ -346,7 +346,7 @@
return getAxisValue(AMOTION_EVENT_AXIS_Y);
}
-#ifdef __ANDROID__
+#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
#endif
@@ -708,7 +708,7 @@
// Matrix is in row-major form and compatible with SkMatrix.
void transform(const std::array<float, 9>& matrix);
-#ifdef __ANDROID__
+#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
#endif
@@ -730,7 +730,7 @@
static const char* getLabel(int32_t axis);
static int32_t getAxisFromLabel(const char* label);
- static const char* actionToString(int32_t action);
+ static std::string actionToString(int32_t action);
protected:
int32_t mAction;
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index d874347..23f8ddf 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -19,7 +19,7 @@
#include <stdint.h>
-#ifdef __ANDROID__
+#ifdef __linux__
#include <binder/IBinder.h>
#endif
@@ -44,27 +44,27 @@
*/
class KeyCharacterMap {
public:
- enum KeyboardType {
- KEYBOARD_TYPE_UNKNOWN = 0,
- KEYBOARD_TYPE_NUMERIC = 1,
- KEYBOARD_TYPE_PREDICTIVE = 2,
- KEYBOARD_TYPE_ALPHA = 3,
- KEYBOARD_TYPE_FULL = 4,
+ enum class KeyboardType : int32_t {
+ UNKNOWN = 0,
+ NUMERIC = 1,
+ PREDICTIVE = 2,
+ ALPHA = 3,
+ FULL = 4,
/**
* Deprecated. Set 'keyboard.specialFunction' to '1' in the device's IDC file instead.
*/
- KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
- KEYBOARD_TYPE_OVERLAY = 6,
+ SPECIAL_FUNCTION = 5,
+ OVERLAY = 6,
};
- enum Format {
+ enum class Format {
// Base keyboard layout, may contain device-specific options, such as "type" declaration.
- FORMAT_BASE = 0,
+ BASE = 0,
// Overlay keyboard layout, more restrictive, may be published by applications,
// cannot override device-specific options.
- FORMAT_OVERLAY = 1,
+ OVERLAY = 1,
// Either base or overlay layout ok.
- FORMAT_ANY = 2,
+ ANY = 2,
};
// Substitute key code and meta state for fallback action.
@@ -88,7 +88,7 @@
void combine(const KeyCharacterMap& overlay);
/* Gets the keyboard type. */
- int32_t getKeyboardType() const;
+ KeyboardType getKeyboardType() const;
/* Gets the primary character for this key as in the label physically printed on it.
* Returns 0 if none (eg. for non-printing keys). */
@@ -134,7 +134,7 @@
void tryRemapKey(int32_t scanCode, int32_t metaState,
int32_t* outKeyCode, int32_t* outMetaState) const;
-#ifdef __ANDROID__
+#ifdef __linux__
/* Reads a key map from a parcel. */
static std::shared_ptr<KeyCharacterMap> readFromParcel(Parcel* parcel);
@@ -226,7 +226,7 @@
};
KeyedVector<int32_t, Key*> mKeys;
- int mType;
+ KeyboardType mType;
std::string mLoadFileName;
KeyedVector<int32_t, int32_t> mKeysByScanCode;
diff --git a/include/input/NamedEnum.h b/include/input/NamedEnum.h
index 42cfb12..1d987fe 100644
--- a/include/input/NamedEnum.h
+++ b/include/input/NamedEnum.h
@@ -115,10 +115,10 @@
// Do not specialize it to a large number to avoid performance issues.
// The recommended maximum enum number to specialize is 64.
template <typename E>
- static const std::string string(E val) {
+ static const std::string string(E val, const char* fallbackFormat = "0x%08x") {
std::string result;
std::optional<std::string_view> enumString = enum_name(val);
- result += enumString ? enumString.value() : base::StringPrintf("0x%08x", val);
+ result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val);
return result;
}
};
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp
index 09a5f39..cdd7764 100644
--- a/libs/android_runtime_lazy/Android.bp
+++ b/libs/android_runtime_lazy/Android.bp
@@ -35,6 +35,11 @@
vendor_available: true,
double_loadable: true,
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
cflags: [
"-Wall",
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 5e4c98f..727ea60 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -17,6 +17,7 @@
#include <mutex>
#include <unistd.h>
+#include <android/permission_manager.h>
#include <binder/ActivityManager.h>
#include <binder/Binder.h>
#include <binder/IServiceManager.h>
@@ -98,6 +99,18 @@
return PROCESS_STATE_UNKNOWN;
}
+status_t ActivityManager::checkPermission(const String16& permission,
+ const pid_t pid,
+ const uid_t uid,
+ int32_t* outResult) {
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->checkPermission(permission, pid, uid, outResult);
+ }
+ // ActivityManagerService appears dead. Return usual error code for dead service.
+ return DEAD_OBJECT;
+}
+
status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
sp<IActivityManager> service = getService();
if (service != nullptr) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index d363ee9..e6071a0 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -31,6 +31,11 @@
"libutils_headers",
],
min_sdk_version: "29",
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
// These interfaces are android-specific implementation unrelated to binder
@@ -122,6 +127,9 @@
vendor: {
exclude_srcs: libbinder_device_interface_sources,
},
+ darwin: {
+ enabled: false,
+ },
},
aidl: {
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 1eb5363..e9f5aae 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -17,9 +17,11 @@
#include <unistd.h>
#include <fcntl.h>
+#include <android/permission_manager.h>
#include <binder/ActivityManager.h>
#include <binder/IActivityManager.h>
#include <binder/Parcel.h>
+#include <utils/Errors.h>
namespace android {
@@ -104,6 +106,23 @@
}
return reply.readInt32();
}
+
+ virtual status_t checkPermission(const String16& permission,
+ const pid_t pid,
+ const uid_t uid,
+ int32_t* outResult) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeString16(permission);
+ data.writeInt32(pid);
+ data.writeInt32(uid);
+ status_t err = remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply);
+ if (err != NO_ERROR || ((err = reply.readExceptionCode()) != NO_ERROR)) {
+ return err;
+ }
+ *outResult = reply.readInt32();
+ return NO_ERROR;
+ }
};
// ------------------------------------------------------------------------------------
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 325e204..2e15e50 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -29,16 +29,12 @@
using AidlServiceManager = android::os::IServiceManager;
-class ClientCounterCallback : public ::android::os::BnClientCallback {
+class ClientCounterCallbackImpl : public ::android::os::BnClientCallback {
public:
- ClientCounterCallback() : mNumConnectedServices(0), mForcePersist(false) {}
+ ClientCounterCallbackImpl() : mNumConnectedServices(0), mForcePersist(false) {}
bool registerService(const sp<IBinder>& service, const std::string& name,
bool allowIsolated, int dumpFlags);
-
- /**
- * Set a flag to prevent services from automatically shutting down
- */
void forcePersist(bool persist);
protected:
@@ -75,7 +71,23 @@
bool mForcePersist;
};
-bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
+class ClientCounterCallback {
+public:
+ ClientCounterCallback();
+
+ bool registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags);
+
+ /**
+ * Set a flag to prevent services from automatically shutting down
+ */
+ void forcePersist(bool persist);
+
+private:
+ sp<ClientCounterCallbackImpl> mImpl;
+};
+
+bool ClientCounterCallbackImpl::registerService(const sp<IBinder>& service, const std::string& name,
bool allowIsolated, int dumpFlags) {
auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager()));
@@ -89,7 +101,7 @@
}
if (!reRegister) {
- if (!manager->registerClientCallback(name, service, this).isOk()) {
+ if(!manager->registerClientCallback(name, service, this).isOk()) {
ALOGE("Failed to add client callback for service %s", name.c_str());
return false;
}
@@ -105,7 +117,7 @@
return true;
}
-std::map<std::string, ClientCounterCallback::Service>::iterator ClientCounterCallback::assertRegisteredService(const sp<IBinder>& service) {
+std::map<std::string, ClientCounterCallbackImpl::Service>::iterator ClientCounterCallbackImpl::assertRegisteredService(const sp<IBinder>& service) {
LOG_ALWAYS_FATAL_IF(service == nullptr, "Got onClients callback for null service");
for (auto it = mRegisteredServices.begin(); it != mRegisteredServices.end(); ++it) {
auto const& [name, registered] = *it;
@@ -117,7 +129,7 @@
__builtin_unreachable();
}
-void ClientCounterCallback::forcePersist(bool persist) {
+void ClientCounterCallbackImpl::forcePersist(bool persist) {
mForcePersist = persist;
if(!mForcePersist) {
// Attempt a shutdown in case the number of clients hit 0 while the flag was on
@@ -129,7 +141,7 @@
* onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
* invocations could occur on different threads however.
*/
-Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) {
+Status ClientCounterCallbackImpl::onClients(const sp<IBinder>& service, bool clients) {
auto & [name, registered] = *assertRegisteredService(service);
if (registered.clients == clients) {
LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has "
@@ -154,7 +166,7 @@
return Status::ok();
}
-void ClientCounterCallback::tryShutdown() {
+void ClientCounterCallbackImpl::tryShutdown() {
if(mNumConnectedServices > 0) {
// Should only shut down if there are no clients
return;
@@ -175,7 +187,6 @@
bool success = manager->tryUnregisterService(entry.first, entry.second.service).isOk();
-
if (!success) {
ALOGI("Failed to unregister service %s", entry.first.c_str());
break;
@@ -200,6 +211,19 @@
}
}
+ClientCounterCallback::ClientCounterCallback() {
+ mImpl = sp<ClientCounterCallbackImpl>::make();
+}
+
+bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags) {
+ return mImpl->registerService(service, name, allowIsolated, dumpFlags);
+}
+
+void ClientCounterCallback::forcePersist(bool persist) {
+ mImpl->forcePersist(persist);
+}
+
} // namespace internal
LazyServiceRegistrar::LazyServiceRegistrar() {
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
index e4ea60f..e1cbc19 100644
--- a/libs/binder/MemoryHeapBase.cpp
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -49,7 +49,7 @@
int fd = ashmem_create_region(name == nullptr ? "MemoryHeapBase" : name, size);
ALOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
if (fd >= 0) {
- if (mapfd(fd, size) == NO_ERROR) {
+ if (mapfd(fd, true, size) == NO_ERROR) {
if (flags & READ_ONLY) {
ashmem_set_prot_region(fd, PROT_READ);
}
@@ -70,7 +70,7 @@
if (fd >= 0) {
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
- if (mapfd(fd, size) == NO_ERROR) {
+ if (mapfd(fd, false, size) == NO_ERROR) {
mDevice = device;
}
}
@@ -82,7 +82,7 @@
{
const size_t pagesize = getpagesize();
size = ((size + pagesize-1) & ~(pagesize-1));
- mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset);
+ mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), false, size, offset);
}
status_t MemoryHeapBase::init(int fd, void *base, size_t size, int flags, const char* device)
@@ -98,7 +98,7 @@
return NO_ERROR;
}
-status_t MemoryHeapBase::mapfd(int fd, size_t size, off_t offset)
+status_t MemoryHeapBase::mapfd(int fd, bool writeableByCaller, size_t size, off_t offset)
{
if (size == 0) {
// try to figure out the size automatically
@@ -116,8 +116,12 @@
}
if ((mFlags & DONT_MAP_LOCALLY) == 0) {
+ int prot = PROT_READ;
+ if (writeableByCaller || (mFlags & READ_ONLY) == 0) {
+ prot |= PROT_WRITE;
+ }
void* base = (uint8_t*)mmap(nullptr, size,
- PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
+ prot, MAP_SHARED, fd, offset);
if (base == MAP_FAILED) {
ALOGE("mmap(fd=%d, size=%zu) failed (%s)",
fd, size, strerror(errno));
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index ddd9f9b..4381386 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1068,6 +1068,7 @@
{
if (str == nullptr) return writeInt32(-1);
+ // NOTE: Keep this logic in sync with android_os_Parcel.cpp
status_t err = writeInt32(len);
if (err == NO_ERROR) {
uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char));
@@ -1108,6 +1109,7 @@
{
if (str == nullptr) return writeInt32(-1);
+ // NOTE: Keep this logic in sync with android_os_Parcel.cpp
status_t err = writeInt32(len);
if (err == NO_ERROR) {
len *= sizeof(char16_t);
diff --git a/libs/binder/ParcelableHolder.cpp b/libs/binder/ParcelableHolder.cpp
index e9df279..b2b8671 100644
--- a/libs/binder/ParcelableHolder.cpp
+++ b/libs/binder/ParcelableHolder.cpp
@@ -27,7 +27,6 @@
namespace android {
namespace os {
status_t ParcelableHolder::writeToParcel(Parcel* p) const {
- std::lock_guard<std::mutex> l(mMutex);
RETURN_ON_FAILURE(p->writeInt32(static_cast<int32_t>(this->getStability())));
if (this->mParcelPtr) {
RETURN_ON_FAILURE(p->writeInt32(this->mParcelPtr->dataSize()));
@@ -53,7 +52,6 @@
}
status_t ParcelableHolder::readFromParcel(const Parcel* p) {
- std::lock_guard<std::mutex> l(mMutex);
this->mStability = static_cast<Stability>(p->readInt32());
this->mParcelable = nullptr;
this->mParcelableName = std::nullopt;
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index c232283..61a611d 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -16,6 +16,13 @@
"name": "binderTextOutputTest"
},
{
+ "name": "binderParcelTest"
+ },
+ {
+ "name": "binderParcelTest",
+ "host": true
+ },
+ {
"name": "binderLibTest"
},
{
diff --git a/libs/binder/fuzzer/main.cpp b/libs/binder/fuzzer/main.cpp
deleted file mode 100644
index 6657edb..0000000
--- a/libs/binder/fuzzer/main.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.
- */
-#define FUZZ_LOG_TAG "main"
-
-#include "binder.h"
-#include "binder_ndk.h"
-#include "hwbinder.h"
-#include "util.h"
-
-#include <android-base/logging.h>
-
-#include <cstdlib>
-#include <ctime>
-
-template <typename P>
-void doFuzz(
- const std::vector<ParcelRead<P>>& reads,
- const std::vector<uint8_t>& input,
- const std::vector<uint8_t>& instructions) {
-
- P p;
- p.setData(input.data(), input.size());
-
- // since we are only using a byte to index
- CHECK(reads.size() <= 255) << reads.size();
-
- for (size_t i = 0; i < instructions.size() - 1; i += 2) {
- uint8_t a = instructions[i];
- uint8_t readIdx = a % reads.size();
-
- uint8_t b = instructions[i + 1];
-
- FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
- << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx)
- << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize()
- << " avail: " << p.dataAvail() << " pos: " << p.dataPosition()
- << " cap: " << p.dataCapacity();
-
- reads[readIdx](p, b);
- }
-}
-
-void fuzz(uint8_t options, const std::vector<uint8_t>& input, const std::vector<uint8_t>& instructions) {
- uint8_t parcelType = options & 0x3;
-
- switch (parcelType) {
- case 0x0:
- doFuzz<::android::hardware::Parcel>(HWBINDER_PARCEL_READ_FUNCTIONS, input,
- instructions);
- break;
- case 0x1:
- doFuzz<::android::Parcel>(BINDER_PARCEL_READ_FUNCTIONS, input, instructions);
- break;
- case 0x2:
- doFuzz<NdkParcelAdapter>(BINDER_NDK_PARCEL_READ_FUNCTIONS, input, instructions);
- break;
- case 0x3:
- /*reserved for future use*/
- break;
- default:
- LOG_ALWAYS_FATAL("unknown parcel type %d", static_cast<int>(parcelType));
- }
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- if (size <= 1) return 0; // no use
-
- // avoid timeouts, see b/142617274, b/142473153
- if (size > 50000) return 0;
-
- uint8_t options = *data;
- data++;
- size--;
-
- // TODO: generate 'objects' data
-
- // data to fill out parcel
- size_t inputLen = size / 2;
- std::vector<uint8_t> input(data, data + inputLen);
- data += inputLen;
- size -= inputLen;
-
- // data to use to determine what to do
- size_t instructionLen = size;
- std::vector<uint8_t> instructions(data, data + instructionLen);
- data += instructionLen;
- size -= instructionLen;
-
- CHECK(size == 0) << "size: " << size;
-
- FUZZ_LOG() << "options: " << (int)options << " inputLen: " << inputLen << " instructionLen: " << instructionLen;
- FUZZ_LOG() << "input: " << hexString(input);
- FUZZ_LOG() << "instructions: " << hexString(instructions);
-
- fuzz(options, input, instructions);
- return 0;
-}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 9108e31..6796723 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -77,9 +77,9 @@
void unregisterUidObserver(const sp<IUidObserver>& observer);
bool isUidActive(const uid_t uid, const String16& callingPackage);
int getUidProcessState(const uid_t uid, const String16& callingPackage);
+ status_t checkPermission(const String16& permission, const pid_t pid, const uid_t uid, int32_t* outResult);
-
- status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
+ status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient);
private:
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index e0248f6..4573347 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -39,13 +39,18 @@
virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
+ virtual status_t checkPermission(const String16& permission,
+ const pid_t pid,
+ const uid_t uid,
+ int32_t* outResult) = 0;
enum {
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
IS_UID_ACTIVE_TRANSACTION,
- GET_UID_PROCESS_STATE_TRANSACTION
+ GET_UID_PROCESS_STATE_TRANSACTION,
+ CHECK_PERMISSION_TRANSACTION,
};
};
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h
index 52bd5de..0ece121 100644
--- a/libs/binder/include/binder/MemoryHeapBase.h
+++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -51,6 +51,8 @@
/*
* maps memory from ashmem, with the given name for debugging
+ * if the READ_ONLY flag is set, the memory will be writeable by the calling process,
+ * but not by others. this is NOT the case with the other ctors.
*/
explicit MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = nullptr);
@@ -78,7 +80,7 @@
int flags = 0, const char* device = nullptr);
private:
- status_t mapfd(int fd, size_t size, off_t offset = 0);
+ status_t mapfd(int fd, bool writeableByCaller, size_t size, off_t offset = 0);
int mFD;
size_t mSize;
diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h
index b6814aa..4ea3dd3 100644
--- a/libs/binder/include/binder/ParcelableHolder.h
+++ b/libs/binder/include/binder/ParcelableHolder.h
@@ -59,7 +59,6 @@
template <typename T>
bool setParcelable(std::shared_ptr<T> p) {
- std::lock_guard<std::mutex> l(mMutex);
static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
if (p && this->getStability() > p->getStability()) {
return false;
@@ -73,7 +72,6 @@
template <typename T>
std::shared_ptr<T> getParcelable() const {
static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable");
- std::lock_guard<std::mutex> l(mMutex);
const std::string& parcelableDesc = T::getParcelableDescriptor();
if (!this->mParcelPtr) {
if (!this->mParcelable || !this->mParcelableName) {
@@ -103,7 +101,7 @@
return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get()));
}
- Stability getStability() const override { return mStability; };
+ Stability getStability() const override { return mStability; }
inline bool operator!=(const ParcelableHolder& rhs) const {
return std::tie(mParcelable, mParcelPtr, mStability) !=
@@ -135,7 +133,6 @@
mutable std::optional<std::string> mParcelableName;
mutable std::unique_ptr<Parcel> mParcelPtr;
Stability mStability;
- mutable std::mutex mMutex;
};
} // namespace os
} // namespace android
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 4fd0657..cecc759 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -26,6 +26,9 @@
"-D__ANDROID_API__=10000",
],
},
+ darwin: {
+ enabled: false,
+ },
},
}
@@ -35,6 +38,8 @@
defaults: ["libbinder_ndk_host_user"],
host_supported: true,
+ llndk_stubs: "libbinder_ndk.llndk",
+
export_include_dirs: [
"include_cpp",
"include_ndk",
@@ -82,6 +87,9 @@
linux: {
version_script: "libbinder_ndk.map.txt",
},
+ darwin: {
+ enabled: false,
+ },
},
stubs: {
symbol_file: "libbinder_ndk.map.txt",
@@ -120,7 +128,7 @@
}
llndk_library {
- name: "libbinder_ndk",
+ name: "libbinder_ndk.llndk",
symbol_file: "libbinder_ndk.map.txt",
export_include_dirs: [
"include_cpp",
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 4610ba9..d35debc 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#include <android/binder_context.h>
#include <android/binder_ibinder.h>
#include <android/binder_ibinder_platform.h>
+#include <android/binder_libbinder.h>
#include "ibinder_internal.h"
#include <android/binder_stability.h>
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 18877af..8d60226 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -313,7 +313,8 @@
/**
* Takes ownership of a.
*/
- explicit ScopedFileDescriptor(int a = -1) : ScopedAResource(a) {}
+ ScopedFileDescriptor() : ScopedFileDescriptor(-1) {}
+ explicit ScopedFileDescriptor(int a) : ScopedAResource(a) {}
~ScopedFileDescriptor() {}
ScopedFileDescriptor(ScopedFileDescriptor&&) = default;
ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default;
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 09949ea..054aebe 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -45,7 +45,7 @@
std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
if (static_cast<size_t>(length) > vec->max_size()) return false;
- vec->resize(length);
+ vec->resize(static_cast<size_t>(length));
*outBuffer = vec->data();
return true;
}
@@ -66,7 +66,7 @@
*vec = std::optional<std::vector<T>>(std::vector<T>{});
if (static_cast<size_t>(length) > (*vec)->max_size()) return false;
- (*vec)->resize(length);
+ (*vec)->resize(static_cast<size_t>(length));
*outBuffer = (*vec)->data();
return true;
@@ -90,7 +90,7 @@
std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData);
if (static_cast<size_t>(length) > vec->max_size()) return false;
- vec->resize(length);
+ vec->resize(static_cast<size_t>(length));
return true;
}
@@ -117,7 +117,7 @@
*vec = std::optional<std::vector<T>>(std::vector<T>{});
if (static_cast<size_t>(length) > (*vec)->max_size()) return false;
- (*vec)->resize(length);
+ (*vec)->resize(static_cast<size_t>(length));
return true;
}
@@ -257,7 +257,7 @@
if (length <= 0) return false;
std::string* str = static_cast<std::string*>(stringData);
- str->resize(length - 1);
+ str->resize(static_cast<size_t>(length) - 1);
*buffer = &(*str)[0];
return true;
}
@@ -279,7 +279,7 @@
}
*str = std::optional<std::string>(std::string{});
- (*str)->resize(length - 1);
+ (*str)->resize(static_cast<size_t>(length) - 1);
*buffer = &(**str)[0];
return true;
}
@@ -303,7 +303,7 @@
const std::vector<std::string>* vec = static_cast<const std::vector<std::string>*>(vectorData);
const std::string& element = vec->at(index);
- *outLength = element.size();
+ *outLength = static_cast<int32_t>(element.size());
return element.c_str();
}
@@ -337,7 +337,7 @@
return nullptr;
}
- *outLength = element->size();
+ *outLength = static_cast<int32_t>(element->size());
return element->c_str();
}
@@ -345,7 +345,7 @@
* Convenience API for writing a std::string.
*/
static inline binder_status_t AParcel_writeString(AParcel* parcel, const std::string& str) {
- return AParcel_writeString(parcel, str.c_str(), str.size());
+ return AParcel_writeString(parcel, str.c_str(), static_cast<int32_t>(str.size()));
}
/**
@@ -365,7 +365,7 @@
return AParcel_writeString(parcel, nullptr, -1);
}
- return AParcel_writeString(parcel, str->c_str(), str->size());
+ return AParcel_writeString(parcel, str->c_str(), static_cast<int32_t>(str->size()));
}
/**
@@ -383,7 +383,7 @@
static inline binder_status_t AParcel_writeVector(AParcel* parcel,
const std::vector<std::string>& vec) {
const void* vectorData = static_cast<const void*>(&vec);
- return AParcel_writeStringArray(parcel, vectorData, vec.size(),
+ return AParcel_writeStringArray(parcel, vectorData, static_cast<int32_t>(vec.size()),
AParcel_stdVectorStringElementGetter);
}
@@ -404,7 +404,8 @@
static inline binder_status_t AParcel_writeVector(
AParcel* parcel, const std::optional<std::vector<std::optional<std::string>>>& vec) {
const void* vectorData = static_cast<const void*>(&vec);
- return AParcel_writeStringArray(parcel, vectorData, (vec ? vec->size() : -1),
+ return AParcel_writeStringArray(parcel, vectorData,
+ (vec ? static_cast<int32_t>(vec->size()) : -1),
AParcel_nullableStdVectorStringElementGetter);
}
@@ -545,7 +546,7 @@
template <typename P>
static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<P>& vec) {
const void* vectorData = static_cast<const void*>(&vec);
- return AParcel_writeParcelableArray(parcel, vectorData, vec.size(),
+ return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec.size()),
AParcel_writeStdVectorParcelableElement<P>);
}
@@ -564,7 +565,7 @@
* Writes a vector of int32_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int32_t>& vec) {
- return AParcel_writeInt32Array(parcel, vec.data(), vec.size());
+ return AParcel_writeInt32Array(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -597,7 +598,7 @@
* Writes a vector of uint32_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint32_t>& vec) {
- return AParcel_writeUint32Array(parcel, vec.data(), vec.size());
+ return AParcel_writeUint32Array(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -631,7 +632,7 @@
* Writes a vector of int64_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int64_t>& vec) {
- return AParcel_writeInt64Array(parcel, vec.data(), vec.size());
+ return AParcel_writeInt64Array(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -664,7 +665,7 @@
* Writes a vector of uint64_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint64_t>& vec) {
- return AParcel_writeUint64Array(parcel, vec.data(), vec.size());
+ return AParcel_writeUint64Array(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -698,7 +699,7 @@
* Writes a vector of float to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<float>& vec) {
- return AParcel_writeFloatArray(parcel, vec.data(), vec.size());
+ return AParcel_writeFloatArray(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -731,7 +732,7 @@
* Writes a vector of double to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<double>& vec) {
- return AParcel_writeDoubleArray(parcel, vec.data(), vec.size());
+ return AParcel_writeDoubleArray(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -764,8 +765,8 @@
* Writes a vector of bool to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<bool>& vec) {
- return AParcel_writeBoolArray(parcel, static_cast<const void*>(&vec), vec.size(),
- AParcel_stdVectorGetter<bool>);
+ return AParcel_writeBoolArray(parcel, static_cast<const void*>(&vec),
+ static_cast<int32_t>(vec.size()), AParcel_stdVectorGetter<bool>);
}
/**
@@ -801,7 +802,7 @@
* Writes a vector of char16_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<char16_t>& vec) {
- return AParcel_writeCharArray(parcel, vec.data(), vec.size());
+ return AParcel_writeCharArray(parcel, vec.data(), static_cast<int32_t>(vec.size()));
}
/**
@@ -834,7 +835,8 @@
* Writes a vector of uint8_t to the next location in a non-null parcel.
*/
inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint8_t>& vec) {
- return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()), vec.size());
+ return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()),
+ static_cast<int32_t>(vec.size()));
}
/**
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index 6701518..dfcf4dc 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -25,6 +25,8 @@
*/
#pragma once
+#include <android/binder_parcel_utils.h>
+#include <optional>
namespace ndk {
// Also see Parcelable.h in libbinder.
@@ -33,6 +35,89 @@
STABILITY_LOCAL,
STABILITY_VINTF, // corresponds to @VintfStability
};
+#define RETURN_ON_FAILURE(expr) \
+ do { \
+ binder_status_t _status = (expr); \
+ if (_status != STATUS_OK) return _status; \
+ } while (false)
+
+class AParcelableHolder {
+ public:
+ AParcelableHolder() = delete;
+ explicit AParcelableHolder(parcelable_stability_t stability)
+ : mParcel(AParcel_create()), mStability(stability) {}
+
+ virtual ~AParcelableHolder() = default;
+
+ binder_status_t writeToParcel(AParcel* parcel) const {
+ RETURN_ON_FAILURE(AParcel_writeInt32(parcel, static_cast<int32_t>(this->mStability)));
+ RETURN_ON_FAILURE(AParcel_writeInt32(parcel, AParcel_getDataSize(this->mParcel.get())));
+ RETURN_ON_FAILURE(AParcel_appendFrom(this->mParcel.get(), parcel, 0,
+ AParcel_getDataSize(this->mParcel.get())));
+ return STATUS_OK;
+ }
+
+ binder_status_t readFromParcel(const AParcel* parcel) {
+ AParcel_reset(mParcel.get());
+
+ RETURN_ON_FAILURE(AParcel_readInt32(parcel, &this->mStability));
+ int32_t dataSize;
+ binder_status_t status = AParcel_readInt32(parcel, &dataSize);
+
+ if (status != STATUS_OK || dataSize < 0) {
+ return status != STATUS_OK ? status : STATUS_BAD_VALUE;
+ }
+
+ int32_t dataStartPos = AParcel_getDataPosition(parcel);
+
+ if (dataStartPos > INT32_MAX - dataSize) {
+ return STATUS_BAD_VALUE;
+ }
+
+ status = AParcel_appendFrom(parcel, mParcel.get(), dataStartPos, dataSize);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return AParcel_setDataPosition(parcel, dataStartPos + dataSize);
+ }
+
+ template <typename T>
+ bool setParcelable(T* p) {
+ if (p && this->mStability > T::_aidl_stability) {
+ return false;
+ }
+ AParcel_reset(mParcel.get());
+ AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor));
+ p->writeToParcel(mParcel.get());
+ return true;
+ }
+
+ template <typename T>
+ std::unique_ptr<T> getParcelable() const {
+ const std::string parcelableDesc(T::descriptor);
+ AParcel_setDataPosition(mParcel.get(), 0);
+ if (AParcel_getDataSize(mParcel.get()) == 0) {
+ return nullptr;
+ }
+ std::string parcelableDescInParcel;
+ binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel);
+ if (status != STATUS_OK || parcelableDesc != parcelableDescInParcel) {
+ return nullptr;
+ }
+ std::unique_ptr<T> ret = std::make_unique<T>();
+ status = ret->readFromParcel(this->mParcel.get());
+ if (status != STATUS_OK) {
+ return nullptr;
+ }
+ return std::move(ret);
+ }
+
+ private:
+ mutable ndk::ScopedAParcel mParcel;
+ parcelable_stability_t mStability;
+};
+
+#undef RETURN_ON_FAILURE
} // namespace ndk
/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h
index a031e29..93c3f32 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -1120,6 +1120,53 @@
// @END-PRIMITIVE-READ-WRITE
#endif //__ANDROID_API__ >= 29
+#if __ANDROID_API__ >= 31
+/**
+ * Reset the parcel to the initial status.
+ *
+ * Available since API level 31.
+ *
+ * \param parcel The parcel of which to be reset.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AParcel_reset(AParcel* parcel) __INTRODUCED_IN(31);
+
+/**
+ * Gets the size of the parcel.
+ *
+ * Available since API level 31.
+ *
+ * \param parcel The parcel of which to get the size.
+ *
+ * \return The size of the parcel.
+ */
+int32_t AParcel_getDataSize(const AParcel* parcel) __INTRODUCED_IN(31);
+
+/**
+ * Copy the data of a parcel to other parcel.
+ *
+ * Available since API level 31.
+ *
+ * \param from The source
+ * \param to The detination
+ * \param start The position where the copied data starts.
+ * \param size The amount of data which will be copied.
+ *
+ * \return STATUS_OK on success.
+ */
+binder_status_t AParcel_appendFrom(const AParcel* from, AParcel* to, int32_t start, int32_t size)
+ __INTRODUCED_IN(31);
+
+/**
+ * Creates a parcel.
+ *
+ * Available since API level 31.
+ *
+ * \return A parcel which is not related to any IBinder objects.
+ */
+AParcel* AParcel_create() __INTRODUCED_IN(31);
+#endif //__ANDROID_API__ >= 31
__END_DECLS
/** @} */
diff --git a/libs/binder/ndk/include_platform/android/binder_context.h b/libs/binder/ndk/include_platform/android/binder_context.h
deleted file mode 100644
index a99d555..0000000
--- a/libs/binder/ndk/include_platform/android/binder_context.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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 <android/binder_ibinder.h>
-
-__BEGIN_DECLS
-
-/**
- * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
- * must be called on a local binder server before it is sent out to any othe
- * process. If this is a remote binder, it will abort. If the kernel doesn't
- * support this feature, you'll always get null from AIBinder_getCallingSid.
- *
- * \param binder local server binder to request security contexts on
- */
-__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid)
- __INTRODUCED_IN(31);
-
-/**
- * Returns the selinux context of the callee.
- *
- * In order for this to work, the following conditions must be met:
- * - The kernel must be new enough to support this feature.
- * - The server must have called AIBinder_setRequestingSid.
- * - The callee must be a remote process.
- *
- * \return security context or null if unavailable. The lifetime of this context
- * is the lifetime of the transaction.
- */
-__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
-
-__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index 2af65cf..a99d555 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -16,39 +16,32 @@
#pragma once
-// binder_context.h used to be part of this header and is included for backwards
-// compatibility.
-#include <android/binder_context.h>
-
-#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
-
#include <android/binder_ibinder.h>
-#include <binder/IBinder.h>
+
+__BEGIN_DECLS
/**
- * Get libbinder version of binder from AIBinder.
+ * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This
+ * must be called on a local binder server before it is sent out to any othe
+ * process. If this is a remote binder, it will abort. If the kernel doesn't
+ * support this feature, you'll always get null from AIBinder_getCallingSid.
*
- * WARNING: function calls to a local object on the other side of this function
- * will parcel. When converting between binders, keep in mind it is not as
- * efficient as a direct function call.
- *
- * \param binder binder with ownership retained by the client
- * \return platform binder object
+ * \param binder local server binder to request security contexts on
*/
-android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder);
+__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid)
+ __INTRODUCED_IN(31);
/**
- * Get libbinder_ndk version of binder from platform binder.
+ * Returns the selinux context of the callee.
*
- * WARNING: function calls to a local object on the other side of this function
- * will parcel. When converting between binders, keep in mind it is not as
- * efficient as a direct function call.
+ * In order for this to work, the following conditions must be met:
+ * - The kernel must be new enough to support this feature.
+ * - The server must have called AIBinder_setRequestingSid.
+ * - The callee must be a remote process.
*
- * \param binder platform binder which may be from anywhere (doesn't have to be
- * created with libbinder_ndK)
- * \return binder with one reference count of ownership given to the client. See
- * AIBinder_decStrong
+ * \return security context or null if unavailable. The lifetime of this context
+ * is the lifetime of the transaction.
*/
-AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder);
+__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
-#endif
+__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_libbinder.h b/libs/binder/ndk/include_platform/android/binder_libbinder.h
new file mode 100644
index 0000000..f0c00e8
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_libbinder.h
@@ -0,0 +1,50 @@
+/*
+ * 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
+
+#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)
+
+#include <android/binder_ibinder.h>
+#include <binder/IBinder.h>
+
+/**
+ * Get libbinder version of binder from AIBinder.
+ *
+ * WARNING: function calls to a local object on the other side of this function
+ * will parcel. When converting between binders, keep in mind it is not as
+ * efficient as a direct function call.
+ *
+ * \param binder binder with ownership retained by the client
+ * \return platform binder object
+ */
+android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder);
+
+/**
+ * Get libbinder_ndk version of binder from platform binder.
+ *
+ * WARNING: function calls to a local object on the other side of this function
+ * will parcel. When converting between binders, keep in mind it is not as
+ * efficient as a direct function call.
+ *
+ * \param binder platform binder which may be from anywhere (doesn't have to be
+ * created with libbinder_ndK)
+ * \return binder with one reference count of ownership given to the client. See
+ * AIBinder_decStrong
+ */
+AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder);
+
+#endif
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 1701fb5..947cc98 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -120,6 +120,11 @@
AServiceManager_isDeclared; # apex llndk
AServiceManager_registerLazyService; # llndk
AServiceManager_waitForService; # apex llndk
+
+ AParcel_reset;
+ AParcel_getDataSize;
+ AParcel_appendFrom;
+ AParcel_create;
};
LIBBINDER_NDK_PLATFORM {
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp
index 722ae23..2f95318 100644
--- a/libs/binder/ndk/parcel.cpp
+++ b/libs/binder/ndk/parcel.cpp
@@ -647,4 +647,22 @@
return parcel->get()->allowFds();
}
+binder_status_t AParcel_reset(AParcel* parcel) {
+ parcel->get()->freeData();
+ return STATUS_OK;
+}
+
+int32_t AParcel_getDataSize(const AParcel* parcel) {
+ return parcel->get()->dataSize();
+}
+
+binder_status_t AParcel_appendFrom(const AParcel* from, AParcel* to, int32_t start, int32_t size) {
+ status_t status = to->get()->appendFrom(from->get(), start, size);
+ return PruneStatusT(status);
+}
+
+AParcel* AParcel_create() {
+ return new AParcel(nullptr);
+}
+
// @END
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 160b9f2..f84d9d3 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -18,9 +18,9 @@
#include <aidl/BnBinderNdkUnitTest.h>
#include <aidl/BnEmpty.h>
#include <android-base/logging.h>
-#include <android/binder_context.h>
#include <android/binder_ibinder_jni.h>
#include <android/binder_ibinder_platform.h>
+#include <android/binder_libbinder.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <gtest/gtest.h>
diff --git a/libs/binder/fuzzer/Android.bp b/libs/binder/parcel_fuzzer/Android.bp
similarity index 67%
rename from libs/binder/fuzzer/Android.bp
rename to libs/binder/parcel_fuzzer/Android.bp
index d2b4d52..3e6fe99 100644
--- a/libs/binder/fuzzer/Android.bp
+++ b/libs/binder/parcel_fuzzer/Android.bp
@@ -12,10 +12,13 @@
"binder_ndk.cpp",
"hwbinder.cpp",
"main.cpp",
+ "random_fd.cpp",
+ "random_parcel.cpp",
"util.cpp",
],
static_libs: [
"libbase",
+ "libbinder_random_parcel",
"libcgrouprc",
"libcgrouprc_format",
"libcutils",
@@ -45,3 +48,25 @@
// produced, you may find uncommenting the below line very useful.
// cflags: ["-DENABLE_LOG_FUZZ"],
}
+
+cc_library_static {
+ name: "libbinder_random_parcel",
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
+ srcs: [
+ "random_fd.cpp",
+ "random_parcel.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libutils",
+ ],
+ local_include_dirs: ["include_random_parcel"],
+ export_include_dirs: ["include_random_parcel"],
+}
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/parcel_fuzzer/binder.cpp
similarity index 100%
rename from libs/binder/fuzzer/binder.cpp
rename to libs/binder/parcel_fuzzer/binder.cpp
diff --git a/libs/binder/fuzzer/binder.h b/libs/binder/parcel_fuzzer/binder.h
similarity index 98%
rename from libs/binder/fuzzer/binder.h
rename to libs/binder/parcel_fuzzer/binder.h
index b224ef4..0c51d68 100644
--- a/libs/binder/fuzzer/binder.h
+++ b/libs/binder/parcel_fuzzer/binder.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
#include <binder/Parcel.h>
#include <vector>
diff --git a/libs/binder/fuzzer/binder_ndk.cpp b/libs/binder/parcel_fuzzer/binder_ndk.cpp
similarity index 83%
rename from libs/binder/fuzzer/binder_ndk.cpp
rename to libs/binder/parcel_fuzzer/binder_ndk.cpp
index 29da8f7..008780c 100644
--- a/libs/binder/fuzzer/binder_ndk.cpp
+++ b/libs/binder/parcel_fuzzer/binder_ndk.cpp
@@ -18,6 +18,7 @@
#include "binder_ndk.h"
#include <android/binder_parcel_utils.h>
+#include <android/binder_parcelable_utils.h>
#include "util.h"
@@ -54,6 +55,25 @@
binder_status_t status = AParcel_readStatusHeader(p.aParcel(), t.getR());
FUZZ_LOG() << "read status header: " << status;
},
+ [](const NdkParcelAdapter& p, uint8_t /*data*/) {
+ FUZZ_LOG() << "about to getDataSize the parcel";
+ AParcel_getDataSize(p.aParcel());
+ FUZZ_LOG() << "getDataSize done";
+ },
+ [](const NdkParcelAdapter& p, uint8_t data) {
+ FUZZ_LOG() << "about to read a ParcelableHolder";
+ ndk::AParcelableHolder ph {(data % 2 == 1) ? ndk::STABILITY_LOCAL : ndk::STABILITY_VINTF};
+ binder_status_t status = AParcel_readParcelable(p.aParcel(), &ph);
+ FUZZ_LOG() << "read the ParcelableHolder: " << status;
+ },
+ [](const NdkParcelAdapter& p, uint8_t data) {
+ FUZZ_LOG() << "about to appendFrom";
+ AParcel* parcel = AParcel_create();
+ binder_status_t status = AParcel_appendFrom(p.aParcel(), parcel, 0, data);
+ AParcel_delete(parcel);
+ FUZZ_LOG() << "appendFrom: " << status;
+ },
+
PARCEL_READ(int32_t, AParcel_readInt32),
PARCEL_READ(uint32_t, AParcel_readUint32),
PARCEL_READ(int64_t, AParcel_readInt64),
diff --git a/libs/binder/fuzzer/binder_ndk.h b/libs/binder/parcel_fuzzer/binder_ndk.h
similarity index 91%
rename from libs/binder/fuzzer/binder_ndk.h
rename to libs/binder/parcel_fuzzer/binder_ndk.h
index 622cafc..e69d9c1 100644
--- a/libs/binder/fuzzer/binder_ndk.h
+++ b/libs/binder/parcel_fuzzer/binder_ndk.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
#include <android/binder_auto_utils.h>
#include <vector>
@@ -31,6 +32,9 @@
const AParcel* aParcel() const { return mParcel.get(); }
AParcel* aParcel() { return mParcel.get(); }
+ android::Parcel* parcel() { return aParcel()->get(); }
+
+ const uint8_t* data() const { return aParcel()->get()->data(); }
size_t dataSize() const { return aParcel()->get()->dataSize(); }
size_t dataAvail() const { return aParcel()->get()->dataAvail(); }
size_t dataPosition() const { return aParcel()->get()->dataPosition(); }
diff --git a/libs/binder/fuzzer/hwbinder.cpp b/libs/binder/parcel_fuzzer/hwbinder.cpp
similarity index 100%
rename from libs/binder/fuzzer/hwbinder.cpp
rename to libs/binder/parcel_fuzzer/hwbinder.cpp
diff --git a/libs/binder/fuzzer/hwbinder.h b/libs/binder/parcel_fuzzer/hwbinder.h
similarity index 98%
rename from libs/binder/fuzzer/hwbinder.h
rename to libs/binder/parcel_fuzzer/hwbinder.h
index a6c66be..1fa56d4 100644
--- a/libs/binder/fuzzer/hwbinder.h
+++ b/libs/binder/parcel_fuzzer/hwbinder.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
#include <hwbinder/Parcel.h>
#include <vector>
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
similarity index 63%
copy from libs/binder/fuzzer/parcel_fuzzer.h
copy to libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
index 10cf17c..0a083d7 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_fd.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,5 +14,14 @@
* limitations under the License.
*/
-template <typename P>
-using ParcelRead = std::function<void(const P& p, uint8_t data)>;
+#pragma once
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android {
+
+// ownership to callee, always valid or aborts
+// get a random FD for use in fuzzing, of a few different specific types
+int getRandomFd(FuzzedDataProvider* provider);
+
+} // namespace android
diff --git a/libs/binder/fuzzer/binder.h b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
similarity index 72%
copy from libs/binder/fuzzer/binder.h
copy to libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
index b224ef4..b92a6a9 100644
--- a/libs/binder/fuzzer/binder.h
+++ b/libs/binder/parcel_fuzzer/include_random_parcel/fuzzbinder/random_parcel.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,9 +14,11 @@
* limitations under the License.
*/
+#pragma once
+
#include <binder/Parcel.h>
-#include <vector>
+#include <fuzzer/FuzzedDataProvider.h>
-#include "parcel_fuzzer.h"
-
-extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
+namespace android {
+void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider);
+} // namespace android
diff --git a/libs/binder/parcel_fuzzer/main.cpp b/libs/binder/parcel_fuzzer/main.cpp
new file mode 100644
index 0000000..386c70b
--- /dev/null
+++ b/libs/binder/parcel_fuzzer/main.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+#define FUZZ_LOG_TAG "main"
+
+#include "binder.h"
+#include "binder_ndk.h"
+#include "hwbinder.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+#include <fuzzbinder/random_parcel.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <cstdlib>
+#include <ctime>
+
+using android::fillRandomParcel;
+
+void fillRandomParcel(::android::hardware::Parcel* p, FuzzedDataProvider&& provider) {
+ // TODO: functionality to create random parcels for libhwbinder parcels
+ std::vector<uint8_t> input = provider.ConsumeRemainingBytes<uint8_t>();
+ p->setData(input.data(), input.size());
+}
+static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider) {
+ // fill underlying parcel using functions to fill random libbinder parcel
+ fillRandomParcel(p->parcel(), std::move(provider));
+}
+
+template <typename P>
+void doFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
+ FuzzedDataProvider&& provider) {
+ // Allow some majority of the bytes to be dedicated to telling us what to
+ // do. The fixed value added here represents that we want to test doing a
+ // lot of 'instructions' even on really short parcels.
+ size_t maxInstructions = 20 + (provider.remaining_bytes() * 2 / 3);
+ // but don't always use that many instructions. We want to allow the fuzzer
+ // to explore large parcels with few instructions if it wants to.
+ std::vector<uint8_t> instructions = provider.ConsumeBytes<uint8_t>(
+ provider.ConsumeIntegralInRange<size_t>(0, maxInstructions));
+
+ P p;
+ fillRandomParcel(&p, std::move(provider));
+
+ // since we are only using a byte to index
+ CHECK(reads.size() <= 255) << reads.size();
+
+ FUZZ_LOG() << "backend: " << backend;
+ FUZZ_LOG() << "input: " << hexString(p.data(), p.dataSize());
+ FUZZ_LOG() << "instructions: " << hexString(instructions);
+
+ for (size_t i = 0; i + 1 < instructions.size(); i += 2) {
+ uint8_t a = instructions[i];
+ uint8_t readIdx = a % reads.size();
+
+ uint8_t b = instructions[i + 1];
+
+ FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
+ << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx)
+ << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize()
+ << " avail: " << p.dataAvail() << " pos: " << p.dataPosition()
+ << " cap: " << p.dataCapacity();
+
+ reads[readIdx](p, b);
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size <= 1) return 0; // no use
+
+ // avoid timeouts, see b/142617274, b/142473153
+ if (size > 50000) return 0;
+
+ FuzzedDataProvider provider = FuzzedDataProvider(data, size);
+
+ const std::function<void(FuzzedDataProvider &&)> fuzzBackend[3] = {
+ [](FuzzedDataProvider&& provider) {
+ doFuzz<::android::hardware::Parcel>("hwbinder", HWBINDER_PARCEL_READ_FUNCTIONS,
+ std::move(provider));
+ },
+ [](FuzzedDataProvider&& provider) {
+ doFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
+ std::move(provider));
+ },
+ [](FuzzedDataProvider&& provider) {
+ doFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
+ std::move(provider));
+ },
+ };
+
+ provider.PickValueInArray(fuzzBackend)(std::move(provider));
+
+ return 0;
+}
diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/libs/binder/parcel_fuzzer/parcel_fuzzer.h
similarity index 97%
rename from libs/binder/fuzzer/parcel_fuzzer.h
rename to libs/binder/parcel_fuzzer/parcel_fuzzer.h
index 10cf17c..b68a8a9 100644
--- a/libs/binder/fuzzer/parcel_fuzzer.h
+++ b/libs/binder/parcel_fuzzer/parcel_fuzzer.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
template <typename P>
using ParcelRead = std::function<void(const P& p, uint8_t data)>;
diff --git a/libs/binder/parcel_fuzzer/random_fd.cpp b/libs/binder/parcel_fuzzer/random_fd.cpp
new file mode 100644
index 0000000..cef6adb
--- /dev/null
+++ b/libs/binder/parcel_fuzzer/random_fd.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 <fuzzbinder/random_fd.h>
+
+#include <fcntl.h>
+
+#include <android-base/logging.h>
+#include <cutils/ashmem.h>
+
+namespace android {
+
+int getRandomFd(FuzzedDataProvider* provider) {
+ int fd = provider->PickValueInArray<std::function<int()>>({
+ []() { return ashmem_create_region("binder test region", 1024); },
+ []() { return open("/dev/null", O_RDWR); },
+ })();
+ CHECK(fd >= 0);
+ return fd;
+}
+
+} // namespace android
diff --git a/libs/binder/parcel_fuzzer/random_parcel.cpp b/libs/binder/parcel_fuzzer/random_parcel.cpp
new file mode 100644
index 0000000..9ca4c8a
--- /dev/null
+++ b/libs/binder/parcel_fuzzer/random_parcel.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 <fuzzbinder/random_parcel.h>
+
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+#include <fuzzbinder/random_fd.h>
+#include <utils/String16.h>
+
+namespace android {
+
+class NamedBinder : public BBinder {
+public:
+ NamedBinder(const String16& descriptor) : mDescriptor(descriptor) {}
+ const String16& getInterfaceDescriptor() const override { return mDescriptor; }
+
+private:
+ String16 mDescriptor;
+};
+
+void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider) {
+ while (provider.remaining_bytes() > 0) {
+ auto fillFunc = provider.PickValueInArray<const std::function<void()>>({
+ // write data
+ [&]() {
+ size_t toWrite =
+ provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes());
+ std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(toWrite);
+ CHECK(OK == p->write(data.data(), data.size()));
+ },
+ // write FD
+ [&]() {
+ int fd = getRandomFd(&provider);
+ CHECK(OK == p->writeFileDescriptor(fd, true /*takeOwnership*/));
+ },
+ // write binder
+ [&]() {
+ auto makeFunc = provider.PickValueInArray<const std::function<sp<IBinder>()>>({
+ [&]() {
+ // descriptor is the length of a class name, e.g.
+ // "some.package.Foo"
+ std::string str =
+ provider.ConsumeRandomLengthString(100 /*max length*/);
+ return new NamedBinder(String16(str.c_str()));
+ },
+ []() {
+ // this is the easiest remote binder to get ahold of, and it
+ // should be able to handle anything thrown at it, and
+ // essentially every process can talk to it, so it's a good
+ // candidate for checking usage of an actual BpBinder
+ return IInterface::asBinder(defaultServiceManager());
+ },
+ []() { return nullptr; },
+ });
+ sp<IBinder> binder = makeFunc();
+ CHECK(OK == p->writeStrongBinder(binder));
+ },
+ });
+
+ fillFunc();
+ }
+}
+
+} // namespace android
diff --git a/libs/binder/fuzzer/util.cpp b/libs/binder/parcel_fuzzer/util.cpp
similarity index 100%
rename from libs/binder/fuzzer/util.cpp
rename to libs/binder/parcel_fuzzer/util.cpp
diff --git a/libs/binder/fuzzer/util.h b/libs/binder/parcel_fuzzer/util.h
similarity index 98%
rename from libs/binder/fuzzer/util.h
rename to libs/binder/parcel_fuzzer/util.h
index aa504d2..45e8c57 100644
--- a/libs/binder/fuzzer/util.h
+++ b/libs/binder/parcel_fuzzer/util.h
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
#include <iostream>
#include <sstream>
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index dc8270e..fd5f2f5 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -10,6 +10,11 @@
"libbinder_ndk_sys",
],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ }
}
rust_library {
@@ -23,6 +28,11 @@
"libbinder_ndk",
],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ }
}
rust_bindgen {
@@ -64,6 +74,9 @@
"-D__ANDROID_API__=10000",
],
},
+ darwin: {
+ enabled: false,
+ },
},
}
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index 303f4a5..3f20a4f 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#include <android/binder_context.h>
#include <android/binder_ibinder.h>
+#include <android/binder_ibinder_platform.h>
#include <android/binder_manager.h>
#include <android/binder_parcel.h>
#include <android/binder_process.h>
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index a03835b..87f1d45 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -60,6 +60,23 @@
require_root: true,
}
+// unit test only, which can run on host and doesn't use /dev/binder
+cc_test {
+ name: "binderParcelTest",
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ srcs: ["binderParcelTest.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+ test_suites: ["general-tests"],
+}
+
cc_test {
name: "binderLibTest",
defaults: ["binder_test_defaults"],
diff --git a/libs/binder/tests/binderParcelTest.cpp b/libs/binder/tests/binderParcelTest.cpp
new file mode 100644
index 0000000..1764228
--- /dev/null
+++ b/libs/binder/tests/binderParcelTest.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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 <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <gtest/gtest.h>
+
+using android::IPCThreadState;
+using android::OK;
+using android::Parcel;
+using android::String16;
+using android::String8;
+using android::status_t;
+
+// Tests a second operation results in a parcel at the same location as it
+// started.
+void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
+ Parcel p;
+ a(&p);
+ size_t end = p.dataPosition();
+ p.setDataPosition(0);
+ b(&p);
+ EXPECT_EQ(end, p.dataPosition());
+}
+
+TEST(Parcel, InverseInterfaceToken) {
+ const String16 token = String16("asdf");
+ parcelOpSameLength([&] (Parcel* p) {
+ p->writeInterfaceToken(token);
+ }, [&] (Parcel* p) {
+ EXPECT_TRUE(p->enforceInterface(token, IPCThreadState::self()));
+ });
+}
+
+TEST(Parcel, Utf8FromUtf16Read) {
+ const char* token = "asdf";
+ parcelOpSameLength([&] (Parcel* p) {
+ p->writeString16(String16(token));
+ }, [&] (Parcel* p) {
+ std::string s;
+ EXPECT_EQ(OK, p->readUtf8FromUtf16(&s));
+ EXPECT_EQ(token, s);
+ });
+}
+
+TEST(Parcel, Utf8AsUtf16Write) {
+ std::string token = "asdf";
+ parcelOpSameLength([&] (Parcel* p) {
+ p->writeUtf8AsUtf16(token);
+ }, [&] (Parcel* p) {
+ String16 s;
+ EXPECT_EQ(OK, p->readString16(&s));
+ EXPECT_EQ(s, String16(token.c_str()));
+ });
+}
+
+template <typename T>
+using readFunc = status_t (Parcel::*)(T* out) const;
+template <typename T>
+using writeFunc = status_t (Parcel::*)(const T& in);
+template <typename T>
+using copyWriteFunc = status_t (Parcel::*)(T in);
+
+template <typename T, typename WRITE_FUNC>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, WRITE_FUNC w) {
+ for (const T& value : ts) {
+ parcelOpSameLength([&] (Parcel* p) {
+ (*p.*w)(value);
+ }, [&] (Parcel* p) {
+ T outValue;
+ EXPECT_EQ(OK, (*p.*r)(&outValue));
+ EXPECT_EQ(value, outValue);
+ });
+ }
+}
+
+template <typename T>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, writeFunc<T> w) {
+ readWriteInverse<T, writeFunc<T>>(std::move(ts), r, w);
+}
+template <typename T>
+void readWriteInverse(std::vector<T>&& ts, readFunc<T> r, copyWriteFunc<T> w) {
+ readWriteInverse<T, copyWriteFunc<T>>(std::move(ts), r, w);
+}
+
+#define TEST_READ_WRITE_INVERSE(type, name, ...) \
+ TEST(Parcel, Inverse##name) { \
+ readWriteInverse<type>(__VA_ARGS__, &Parcel::read##name, &Parcel::write##name); \
+ }
+
+TEST_READ_WRITE_INVERSE(int32_t, Int32, {-2, -1, 0, 1, 2});
+TEST_READ_WRITE_INVERSE(uint32_t, Uint32, {0, 1, 2});
+TEST_READ_WRITE_INVERSE(int64_t, Int64, {-2, -1, 0, 1, 2});
+TEST_READ_WRITE_INVERSE(uint64_t, Uint64, {0, 1, 2});
+TEST_READ_WRITE_INVERSE(float, Float, {-1.0f, 0.0f, 3.14f});
+TEST_READ_WRITE_INVERSE(double, Double, {-1.0, 0.0, 3.14});
+TEST_READ_WRITE_INVERSE(bool, Bool, {true, false});
+TEST_READ_WRITE_INVERSE(char16_t, Char, {u'a', u'\0'});
+TEST_READ_WRITE_INVERSE(int8_t, Byte, {-1, 0, 1});
+TEST_READ_WRITE_INVERSE(String8, String8, {String8(), String8("a"), String8("asdf")});
+TEST_READ_WRITE_INVERSE(String16, String16, {String16(), String16("a"), String16("asdf")});
diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/fuzzers/Android.bp
new file mode 100644
index 0000000..c465bed
--- /dev/null
+++ b/libs/binder/tests/fuzzers/Android.bp
@@ -0,0 +1,71 @@
+//
+// 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.
+//
+
+cc_defaults {
+ name: "binder_fuzz_defaults",
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ "libbase",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ }
+}
+
+cc_fuzz {
+ name: "binder_binderFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["BinderFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_bpBinderFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ host_supported: false,
+ srcs: ["BpBinderFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_persistableBundleFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["PersistableBundleFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_stabilityFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["StabilityFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_statusFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["StatusFuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "binder_textOutputFuzz",
+ defaults: ["binder_fuzz_defaults"],
+ srcs: ["TextOutputFuzz.cpp"],
+}
diff --git a/libs/binder/tests/fuzzers/BinderFuzz.cpp b/libs/binder/tests/fuzzers/BinderFuzz.cpp
new file mode 100644
index 0000000..1e5d80a
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BinderFuzz.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 <BinderFuzzFunctions.h>
+#include <IBinderFuzzFunctions.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Binder.h>
+
+namespace android {
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ sp<BBinder> bbinder = new BBinder();
+
+ // To prevent memory from running out from calling too many add item operations.
+ const uint32_t MAX_RUNS = 2048;
+ uint32_t count = 0;
+
+ while (fdp.remaining_bytes() > 0 && count++ < MAX_RUNS) {
+ if (fdp.ConsumeBool()) {
+ callArbitraryFunction(&fdp, gBBinderOperations, bbinder);
+ } else {
+ callArbitraryFunction(&fdp, gIBinderOperations,
+ reinterpret_cast<IBinder *>(bbinder.get()));
+ }
+ }
+
+ return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/BinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BinderFuzzFunctions.h
new file mode 100644
index 0000000..9ac65bb
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BinderFuzzFunctions.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 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 <IBinderFuzzFunctions.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <stdint.h>
+#include <atomic>
+
+namespace android {
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BBinder>&)>>
+ gBBinderOperations = {[](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->isRequestingSid();
+ },
+ [](FuzzedDataProvider* fdp, const sp<BBinder>& bbinder) -> void {
+ bool request_sid = fdp->ConsumeBool();
+ bbinder->setRequestingSid(request_sid);
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->getExtension();
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ static IBinder* extension = nullptr;
+ bbinder->setExtension(extension);
+ },
+ [](FuzzedDataProvider* fdp, const sp<BBinder>& bbinder) -> void {
+ int priority;
+ int policy = fdp->ConsumeIntegralInRange<int>(0, 2);
+ if (policy == 0) {
+ priority = fdp->ConsumeIntegralInRange<int>(-20, 19);
+ } else {
+ priority = fdp->ConsumeIntegralInRange<int>(1, 99);
+ }
+ bbinder->setMinSchedulerPolicy(policy, priority);
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->getMinSchedulerPolicy();
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->getMinSchedulerPriority();
+ },
+ [](FuzzedDataProvider*, const sp<BBinder>& bbinder) -> void {
+ bbinder->getDebugPid();
+ }};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzz.cpp b/libs/binder/tests/fuzzers/BpBinderFuzz.cpp
new file mode 100644
index 0000000..c50279b
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BpBinderFuzz.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 <BpBinderFuzzFunctions.h>
+#include <IBinderFuzzFunctions.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/BpBinder.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ // TODO: In the future it would be more effective to fork a new process and then pass a BBinder
+ // to your process. Right now this is not implemented because it would involved fuzzing IPC on a
+ // forked process, and libfuzzer will not be able to handle code coverage. This would lead to
+ // crashes that are not easy to diagnose.
+ int32_t handle = fdp.ConsumeIntegralInRange<int32_t>(0, 1024);
+ sp<BpBinder> bpbinder = BpBinder::create(handle);
+ if (bpbinder == nullptr) return 0;
+
+ // To prevent memory from running out from calling too many add item operations.
+ const uint32_t MAX_RUNS = 2048;
+ uint32_t count = 0;
+ sp<IBinder::DeathRecipient> s_recipient = new FuzzDeathRecipient();
+
+ while (fdp.remaining_bytes() > 0 && count++ < MAX_RUNS) {
+ if (fdp.ConsumeBool()) {
+ callArbitraryFunction(&fdp, gBPBinderOperations, bpbinder, s_recipient);
+ } else {
+ callArbitraryFunction(&fdp, gIBinderOperations, bpbinder.get());
+ }
+ }
+
+ return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
new file mode 100644
index 0000000..c685b41
--- /dev/null
+++ b/libs/binder/tests/fuzzers/BpBinderFuzzFunctions.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 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 <IBinderFuzzFunctions.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/BpBinder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+
+#include <cutils/compiler.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/threads.h>
+
+#include <stdio.h>
+
+namespace android {
+
+// Static variable to reference so we don't consume a bunch of memory to link and
+// unlink DeathRecipients.
+static int8_t kBpBinderCookie = 0;
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, const sp<BpBinder>&,
+ const sp<IBinder::DeathRecipient>&)>>
+ gBPBinderOperations =
+ {[](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->handle(); },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>& s_recipient) -> void {
+ // Clean up possible leftover memory.
+ wp<IBinder::DeathRecipient> outRecipient(nullptr);
+ bpbinder->sendObituary();
+ bpbinder->unlinkToDeath(nullptr, reinterpret_cast<void*>(&kBpBinderCookie), 0,
+ &outRecipient);
+
+ uint32_t flags = fdp->ConsumeIntegral<uint32_t>();
+ kBpBinderCookie = fdp->ConsumeIntegral<int8_t>();
+ bpbinder->linkToDeath(s_recipient.get(),
+ reinterpret_cast<void*>(&kBpBinderCookie), flags);
+ },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ wp<IBinder::DeathRecipient> out_recipient(nullptr);
+ uint32_t flags = fdp->ConsumeIntegral<uint32_t>();
+ int8_t random_cookie = fdp->ConsumeIntegral<int8_t>();
+ bpbinder->unlinkToDeath(nullptr, reinterpret_cast<void*>(&random_cookie),
+ flags, &out_recipient);
+ },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->remoteBinder(); },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->sendObituary(); },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ uint32_t uid = fdp->ConsumeIntegral<uint32_t>();
+ bpbinder->getBinderProxyCount(uid);
+ },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->enableCountByUid(); },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void { bpbinder->disableCountByUid(); },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ Vector<uint32_t> uids;
+ Vector<uint32_t> counts;
+ bpbinder->getCountByUid(uids, counts);
+ },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ bool enable = fdp->ConsumeBool();
+ bpbinder->setCountByUidEnabled(enable);
+ },
+ [](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ binder_proxy_limit_callback cb = binder_proxy_limit_callback();
+ bpbinder->setLimitCallback(cb);
+ },
+ [](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
+ const sp<IBinder::DeathRecipient>&) -> void {
+ int high = fdp->ConsumeIntegral<int>();
+ int low = fdp->ConsumeIntegral<int>();
+ bpbinder->setBinderProxyCountWatermarks(high, low);
+ }};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h b/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
new file mode 100644
index 0000000..626b758
--- /dev/null
+++ b/libs/binder/tests/fuzzers/IBinderFuzzFunctions.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <binder/Parcel.h>
+#include <binder/Stability.h>
+#include <cutils/compiler.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class FuzzDeathRecipient : public IBinder::DeathRecipient {
+private:
+ virtual void binderDied(const wp<IBinder>& who) { (void)who; };
+};
+
+// Allow objects to be attached that aren't stack locals
+static uint32_t objectID = 0;
+static uint32_t object = 0;
+static uint32_t cleanup_cookie = 0;
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, IBinder*)>> gIBinderOperations =
+ {[](FuzzedDataProvider*, IBinder* ibinder) -> void { ibinder->getInterfaceDescriptor(); },
+ [](FuzzedDataProvider*, IBinder* ibinder) -> void { ibinder->isBinderAlive(); },
+ [](FuzzedDataProvider*, IBinder* ibinder) -> void { ibinder->pingBinder(); },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ int fd = STDOUT_FILENO;
+ std::string rand_str = fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ Vector<String16> args;
+ args.push(String16(rand_str.c_str()));
+ ibinder->dump(fd, args);
+ },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ objectID = fdp->ConsumeIntegral<uint32_t>();
+ object = fdp->ConsumeIntegral<uint32_t>();
+ cleanup_cookie = fdp->ConsumeIntegral<uint32_t>();
+ IBinder::object_cleanup_func func = IBinder::object_cleanup_func();
+ ibinder->attachObject(fdp->ConsumeBool() ? reinterpret_cast<void*>(&objectID)
+ : nullptr,
+ fdp->ConsumeBool() ? reinterpret_cast<void*>(&object) : nullptr,
+ fdp->ConsumeBool() ? reinterpret_cast<void*>(&cleanup_cookie)
+ : nullptr,
+ func);
+ },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ uint32_t id = fdp->ConsumeIntegral<uint32_t>();
+ ibinder->findObject(reinterpret_cast<void*>(&id));
+ },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ uint32_t id = fdp->ConsumeIntegral<uint32_t>();
+ ibinder->detachObject(reinterpret_cast<void*>(&id));
+ },
+ [](FuzzedDataProvider* fdp, IBinder* ibinder) -> void {
+ uint32_t code = fdp->ConsumeIntegral<uint32_t>();
+ Parcel p_data;
+ Parcel reply;
+ uint32_t flags = fdp->ConsumeIntegral<uint32_t>();
+ ibinder->transact(code, p_data, &reply, flags);
+ }};
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp b/libs/binder/tests/fuzzers/PersistableBundleFuzz.cpp
new file mode 100644
index 0000000..4843c46
--- /dev/null
+++ b/libs/binder/tests/fuzzers/PersistableBundleFuzz.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 <PersistableBundleFuzzFunctions.h>
+#include <binder/PersistableBundle.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String16.h>
+
+namespace android {
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ std::shared_ptr<os::PersistableBundle> p_bundle(new os::PersistableBundle());
+
+ while (fdp.remaining_bytes() > 0) {
+ String16 key(fdp.ConsumeRandomLengthString(fdp.remaining_bytes()).c_str());
+ callArbitraryFunction(&fdp, gPersistableBundleOperations, p_bundle, &key);
+ }
+
+ return 0;
+}
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h b/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
new file mode 100644
index 0000000..820e9e8
--- /dev/null
+++ b/libs/binder/tests/fuzzers/PersistableBundleFuzzFunctions.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright 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 <binder/Parcelable.h>
+#include <binder/PersistableBundle.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+#include <map>
+#include <set>
+#include <vector>
+
+namespace android {
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<
+ void(FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const&, String16*)>>
+ gPersistableBundleOperations =
+ {[](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void { p_bundle->empty(); },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void {
+ Parcel parcel;
+ p_bundle->writeToParcel(&parcel);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void {
+ Parcel parcel;
+ std::vector<uint8_t> buf = fdp->ConsumeBytes<uint8_t>(
+ fdp->ConsumeIntegralInRange<size_t>(0, fdp->remaining_bytes() - 1));
+ parcel.write(buf.data(), buf.size());
+ p_bundle->readFromParcel(&parcel);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void { p_bundle->size(); },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void { p_bundle->erase(*key); },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ bool value = fdp->ConsumeBool();
+ p_bundle->putBoolean(*key, value);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ int32_t value = fdp->ConsumeIntegral<int32_t>();
+ p_bundle->putInt(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ os::PersistableBundle value = os::PersistableBundle();
+ p_bundle->putPersistableBundle(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<String16> value;
+ p_bundle->putStringVector(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<double> value;
+ p_bundle->putDoubleVector(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<int64_t> value;
+ p_bundle->putLongVector(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<int32_t> value;
+ p_bundle->putIntVector(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<bool> value;
+ p_bundle->putBooleanVector(*key, value);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ String16 value(fdp->ConsumeRandomLengthString(fdp->remaining_bytes()).c_str());
+ p_bundle->putString(*key, value);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ int64_t value = fdp->ConsumeIntegral<int64_t>();
+ p_bundle->putLong(*key, value);
+ },
+ [](FuzzedDataProvider* fdp, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ double value = fdp->ConsumeFloatingPoint<double>();
+ p_bundle->putDouble(*key, value);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ bool out;
+ p_bundle->getBoolean(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ os::PersistableBundle out;
+ p_bundle->getPersistableBundle(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<String16> out;
+ p_bundle->getStringVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<double> out;
+ p_bundle->getDoubleVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<int64_t> out;
+ p_bundle->getLongVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<int32_t> out;
+ p_bundle->getIntVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ std::vector<bool> out;
+ p_bundle->getBooleanVector(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ String16 out;
+ p_bundle->getString(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ double out;
+ p_bundle->getDouble(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ int64_t out;
+ p_bundle->getLong(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16* key) -> void {
+ int32_t out;
+ p_bundle->getInt(*key, &out);
+ },
+ [](FuzzedDataProvider*, std::shared_ptr<os::PersistableBundle> const& p_bundle,
+ String16*) -> void {
+ p_bundle->getBooleanKeys();
+ p_bundle->getIntKeys();
+ p_bundle->getLongKeys();
+ p_bundle->getDoubleKeys();
+ p_bundle->getStringKeys();
+ p_bundle->getBooleanVectorKeys();
+ p_bundle->getIntVectorKeys();
+ p_bundle->getLongVectorKeys();
+ p_bundle->getDoubleVectorKeys();
+ p_bundle->getStringVectorKeys();
+ p_bundle->getPersistableBundleKeys();
+ }};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/StabilityFuzz.cpp b/libs/binder/tests/fuzzers/StabilityFuzz.cpp
new file mode 100644
index 0000000..8ad9b44
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StabilityFuzz.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 <StabilityFuzzFunctions.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ // Init our wrapper
+ FuzzedDataProvider dataProvider(data, size);
+ android::sp<android::IBinder> bbinder = new android::BBinder();
+
+ // Call some functions
+ while (dataProvider.remaining_bytes() > 0) {
+ callArbitraryFunction(&dataProvider, gStabilityOperations, bbinder);
+ }
+
+ return 0;
+}
diff --git a/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h b/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
new file mode 100644
index 0000000..8b4ed70
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StabilityFuzzFunctions.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 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/Binder.h>
+#include <binder/Stability.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#define STABILITY_MAX_TAG_LENGTH 2048
+static bool marked = false;
+
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<
+ std::function<void(FuzzedDataProvider*, android::sp<android::IBinder> const&)>>
+ gStabilityOperations = {
+ // markCompilationUnit(IBinder* binder)
+ [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+ if (!marked) {
+ android::internal::Stability::markCompilationUnit(bbinder.get());
+ marked = true;
+ }
+ },
+
+ // markVintf(IBinder* binder)
+ [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+ if (!marked) {
+ android::internal::Stability::markVintf(bbinder.get());
+ marked = true;
+ }
+ },
+
+ // debugLogStability(const std::string& tag, const sp<IBinder>& binder)
+ [](FuzzedDataProvider* fdp, android::sp<android::IBinder> const& bbinder) -> void {
+ std::string tag = fdp->ConsumeRandomLengthString(STABILITY_MAX_TAG_LENGTH);
+ android::internal::Stability::debugLogStability(tag, bbinder);
+ },
+
+ // markVndk(IBinder* binder)
+ [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+ if (!marked) {
+ android::internal::Stability::markVndk(bbinder.get());
+ marked = true;
+ }
+ },
+
+ // requiresVintfDeclaration(const sp<IBinder>& binder)
+ [](FuzzedDataProvider*, android::sp<android::IBinder> const& bbinder) -> void {
+ android::internal::Stability::requiresVintfDeclaration(bbinder);
+ }};
diff --git a/libs/binder/tests/fuzzers/StatusFuzz.cpp b/libs/binder/tests/fuzzers/StatusFuzz.cpp
new file mode 100644
index 0000000..4f6ad6f
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StatusFuzz.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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 <StatusFuzzFunctions.h>
+#include <binder/Parcel.h>
+#include <binder/Status.h>
+#include <commonFuzzHelpers.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <utils/String8.h>
+#include <cstdint>
+#include <sstream>
+#include <string>
+
+namespace android {
+// Fuzzer entry point.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+
+ int32_t exceptionCode = fdp.ConsumeIntegral<int32_t>();
+ std::string message_str = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ String8 message(message_str.c_str());
+
+ Parcel parcel;
+ std::vector<uint8_t> buf = fdp.ConsumeBytes<uint8_t>(
+ fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes() - 1));
+ parcel.write(buf.data(), buf.size());
+ binder::Status status = binder::Status::fromExceptionCode(exceptionCode, message);
+
+ while (fdp.remaining_bytes() > 0) {
+ callArbitraryFunction(&fdp, gStatusOperations, &status, &parcel);
+ }
+ return 0;
+}
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/StatusFuzzFunctions.h b/libs/binder/tests/fuzzers/StatusFuzzFunctions.h
new file mode 100644
index 0000000..bc8d17a
--- /dev/null
+++ b/libs/binder/tests/fuzzers/StatusFuzzFunctions.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Parcel.h>
+#include <binder/Status.h>
+#include <stdio.h>
+#include <utils/String8.h>
+#include <cstdint>
+#include <sstream>
+#include <string>
+
+namespace android {
+/* This is a vector of lambda functions the fuzzer will pull from.
+ * This is done so new functions can be added to the fuzzer easily
+ * without requiring modifications to the main fuzzer file. This also
+ * allows multiple fuzzers to include this file, if functionality is needed.
+ */
+static const std::vector<std::function<void(FuzzedDataProvider*, binder::Status*, Parcel*)>>
+ gStatusOperations = {
+ [](FuzzedDataProvider*, binder::Status* status, Parcel* parcel) -> void {
+ parcel->setDataPosition(0);
+ status->readFromParcel(*parcel);
+ },
+ [](FuzzedDataProvider*, binder::Status* status, Parcel* parcel) -> void {
+ status->writeToParcel(parcel);
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ std::string message_str =
+ fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ String8 message(message_str.c_str());
+ status->setServiceSpecificError(fdp->ConsumeIntegral<int32_t>(), message);
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ std::string message_str =
+ fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ String8 message(message_str.c_str());
+ status->setException(fdp->ConsumeIntegral<int32_t>(), message);
+ },
+ [](FuzzedDataProvider*, binder::Status* status, Parcel*) -> void { status->ok(); },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ std::string message_str =
+ fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ String8 message(message_str.c_str());
+ *status = binder::Status::fromExceptionCode(fdp->ConsumeIntegral<int32_t>(),
+ message);
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ *status = binder::Status::fromServiceSpecificError(
+ fdp->ConsumeIntegral<int32_t>());
+ },
+ [](FuzzedDataProvider* fdp, binder::Status*, Parcel*) -> void {
+ binder::Status::exceptionToString(fdp->ConsumeIntegral<int32_t>());
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ std::string message_str =
+ fdp->ConsumeRandomLengthString(fdp->remaining_bytes());
+ String8 message(message_str.c_str());
+ *status = binder::Status::fromServiceSpecificError(fdp->ConsumeIntegral<
+ int32_t>(),
+ message);
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ *status = binder::Status::fromStatusT(fdp->ConsumeIntegral<status_t>());
+ },
+ [](FuzzedDataProvider* fdp, binder::Status* status, Parcel*) -> void {
+ status->setFromStatusT(fdp->ConsumeIntegral<status_t>());
+ },
+ [](FuzzedDataProvider*, binder::Status* status, Parcel*) -> void {
+ std::stringstream ss;
+ ss << *status;
+ },
+};
+
+} // namespace android
diff --git a/libs/binder/tests/fuzzers/TextOutputFuzz.cpp b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
new file mode 100644
index 0000000..c950020
--- /dev/null
+++ b/libs/binder/tests/fuzzers/TextOutputFuzz.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 <fuzzer/FuzzedDataProvider.h>
+
+#include <binder/Debug.h>
+#include <binder/Parcel.h>
+#include <binder/TextOutput.h>
+#include "android-base/file.h"
+#include "android-base/test_utils.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <cstddef>
+#include <limits>
+
+// Fuzzer for the TextOutput class. These were lifted from the existing
+// test suite.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ CapturedStderr cap;
+
+ while (fdp.remaining_bytes() > 1) {
+ switch (fdp.ConsumeIntegral<uint8_t>() % 3) {
+ case 0: {
+ std::string input = fdp.ConsumeBytesAsString(
+ fdp.ConsumeIntegralInRange<size_t>(0, fdp.remaining_bytes()));
+ android::aerr << input << android::endl;
+ break;
+ }
+ case 1: {
+ std::string str = fdp.ConsumeRandomLengthString(fdp.remaining_bytes());
+ android::HexDump input(str.c_str(), sizeof(str.c_str()));
+ android::aerr << input << android::endl;
+ break;
+ }
+ case 2: {
+ android::TypeCode input(fdp.ConsumeIntegral<uint32_t>());
+ android::aerr << input << android::endl;
+ }
+ }
+ }
+ cap.Stop();
+
+ return 0;
+}
diff --git a/libs/binder/tests/fuzzers/commonFuzzHelpers.h b/libs/binder/tests/fuzzers/commonFuzzHelpers.h
new file mode 100644
index 0000000..d58d9b6
--- /dev/null
+++ b/libs/binder/tests/fuzzers/commonFuzzHelpers.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 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 <fuzzer/FuzzedDataProvider.h>
+#include <vector>
+
+// Calls a function from the ops_vector
+template <class F, class T, class... Types>
+void callArbitraryFunction(F* fdp, T const& ops_vector, Types... args) {
+ // Choose which function we'll be calling
+ uint8_t function_id = fdp->template ConsumeIntegralInRange<uint8_t>(0, ops_vector.size() - 1);
+
+ // Call the function we've chosen
+ ops_vector[function_id](fdp, args...);
+}
+
+template <class T>
+T getArbitraryVectorElement(FuzzedDataProvider* fdp, std::vector<T> const& vect, bool allow_null) {
+ // If we're allowing null, give it a 50:50 shot at returning a nullptr
+ if (vect.empty() || (allow_null && fdp->ConsumeBool())) {
+ return nullptr;
+ }
+
+ // Otherwise, return an element from our vector
+ return vect.at(fdp->ConsumeIntegralInRange<size_t>(0, vect.size() - 1));
+}
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index 88752ee..08c62df 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -19,6 +19,11 @@
double_loadable: true,
vendor_available: true,
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
shared_libs: [
"libbinder",
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 5e785b6..6058430 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -425,6 +425,7 @@
uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0;
do {
+ if (key.bucket > (gNCpus - 1) / CPUS_PER_ENTRY) return {};
if (lastUpdate) {
auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate);
if (!uidUpdated.has_value()) return {};
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index ea2a200..0d5f412 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -387,6 +387,28 @@
}
}
+TEST(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) {
+ uint32_t uid = 0;
+ {
+ // Find an unused UID
+ auto map = getUidsConcurrentTimes();
+ ASSERT_TRUE(map.has_value());
+ ASSERT_FALSE(map->empty());
+ for (const auto &kv : *map) uid = std::max(uid, kv.first);
+ ++uid;
+ }
+ android::base::unique_fd fd{
+ bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
+ ASSERT_GE(fd, 0);
+ uint32_t nCpus = get_nprocs_conf();
+ uint32_t maxBucket = (nCpus - 1) / CPUS_PER_ENTRY;
+ time_key_t key = {.uid = uid, .bucket = maxBucket + 1};
+ std::vector<concurrent_val_t> vals(nCpus);
+ ASSERT_FALSE(writeToMapEntry(fd, &key, vals.data(), BPF_NOEXIST));
+ EXPECT_FALSE(getUidsConcurrentTimes().has_value());
+ ASSERT_FALSE(deleteMapEntry(fd, &key));
+}
+
TEST(TimeInStateTest, AllUidTimesConsistent) {
auto tisMap = getUidsCpuFreqTimes();
ASSERT_TRUE(tisMap.has_value());
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 8f092f6..143fa13 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -33,6 +33,7 @@
"/system/bin/mediaextractor", // media.extractor
"/system/bin/mediametrics", // media.metrics
"/system/bin/mediaserver",
+ "/system/bin/mediatranscoding", // media.transcoding
"/system/bin/netd",
"/system/bin/sdcard",
"/apex/com.android.os.statsd/bin/statsd",
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
index 6909637..76518c1 100644
--- a/libs/fakeservicemanager/Android.bp
+++ b/libs/fakeservicemanager/Android.bp
@@ -9,6 +9,11 @@
"libbinder",
"libutils",
],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
cc_library {
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
new file mode 100644
index 0000000..fb32382
--- /dev/null
+++ b/libs/ftl/Android.bp
@@ -0,0 +1,17 @@
+cc_test {
+ name: "ftl_test",
+ test_suites: ["device-tests"],
+ sanitize: {
+ address: true,
+ },
+ srcs: [
+ "SmallVector_test.cpp",
+ "StaticVector_test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ "-Wpedantic",
+ ],
+}
diff --git a/libs/ftl/SmallVector_test.cpp b/libs/ftl/SmallVector_test.cpp
new file mode 100644
index 0000000..c41e622
--- /dev/null
+++ b/libs/ftl/SmallVector_test.cpp
@@ -0,0 +1,455 @@
+/*
+ * Copyright 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 <ftl/SmallVector.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+
+using namespace std::string_literals;
+
+namespace android::test {
+
+using ftl::SmallVector;
+
+// Keep in sync with example usage in header file.
+TEST(SmallVector, Example) {
+ ftl::SmallVector<char, 3> vector;
+ EXPECT_TRUE(vector.empty());
+ EXPECT_FALSE(vector.dynamic());
+
+ vector = {'a', 'b', 'c'};
+ EXPECT_EQ(vector.size(), 3u);
+ EXPECT_FALSE(vector.dynamic());
+
+ vector.push_back('d');
+ EXPECT_TRUE(vector.dynamic());
+
+ vector.unstable_erase(vector.begin());
+ EXPECT_EQ(vector, (ftl::SmallVector{'d', 'b', 'c'}));
+
+ vector.pop_back();
+ EXPECT_EQ(vector.back(), 'b');
+ EXPECT_TRUE(vector.dynamic());
+
+ const char array[] = "hi";
+ vector = ftl::SmallVector(array);
+ EXPECT_EQ(vector, (ftl::SmallVector{'h', 'i', '\0'}));
+ EXPECT_FALSE(vector.dynamic());
+}
+
+TEST(SmallVector, Construct) {
+ {
+ // Default constructor.
+ SmallVector<std::string, 2> vector;
+
+ EXPECT_TRUE(vector.empty());
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Array constructor.
+ const float kFloats[] = {.1f, .2f, .3f};
+ SmallVector vector(kFloats);
+
+ EXPECT_EQ(vector, (SmallVector{.1f, .2f, .3f}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Iterator constructor.
+ const char chars[] = "abcdef";
+ std::string string(chars);
+ SmallVector<char, sizeof(chars)> vector(string.begin(), string.end());
+
+ EXPECT_STREQ(vector.begin(), chars);
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Variadic constructor with same types.
+ SmallVector vector = {1, 2, 3};
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<int, 3>>);
+ EXPECT_EQ(vector, (SmallVector{1, 2, 3}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Variadic constructor with different types.
+ const auto copy = "quince"s;
+ auto move = "tart"s;
+ SmallVector vector = {copy, std::move(move)};
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 2>>);
+ EXPECT_EQ(vector, (SmallVector{"quince"s, "tart"s}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // In-place constructor with same types.
+ SmallVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
+ EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // In-place constructor with different types.
+ const auto copy = "red"s;
+ auto move = "velvet"s;
+ std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
+ SmallVector vector(std::in_place_type<std::string>, copy.c_str(), std::move(move), list);
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>);
+ EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+ {
+ // Conversion from StaticVector.
+ ftl::StaticVector doubles = {.1, .2, .3};
+ SmallVector vector = std::move(doubles);
+ EXPECT_TRUE(doubles.empty());
+
+ static_assert(std::is_same_v<decltype(vector), SmallVector<double, 3>>);
+ EXPECT_EQ(vector, (SmallVector{.1, .2, .3}));
+ EXPECT_FALSE(vector.dynamic());
+ }
+}
+
+TEST(SmallVector, String) {
+ SmallVector<char, 10> chars;
+ char c = 'a';
+ std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; });
+ chars.push_back('\0');
+
+ EXPECT_TRUE(chars.dynamic());
+ EXPECT_EQ(chars.size(), 11u);
+ EXPECT_STREQ(chars.begin(), "abcdefghij");
+
+ // Constructor takes iterator range.
+ const char kString[] = "123456";
+ SmallVector<char, 10> string(std::begin(kString), std::end(kString));
+
+ EXPECT_FALSE(string.dynamic());
+ EXPECT_STREQ(string.begin(), "123456");
+ EXPECT_EQ(string.size(), 7u);
+
+ // Similar to emplace, but replaces rather than inserts.
+ string.replace(string.begin() + 5, '\0');
+ EXPECT_STREQ(string.begin(), "12345");
+
+ swap(chars, string);
+
+ EXPECT_STREQ(chars.begin(), "12345");
+ EXPECT_STREQ(string.begin(), "abcdefghij");
+
+ EXPECT_FALSE(chars.dynamic());
+ EXPECT_TRUE(string.dynamic());
+}
+
+TEST(SmallVector, CopyableElement) {
+ struct Pair {
+ // Needed because std::vector emplace does not use uniform initialization.
+ Pair(int a, int b) : a(a), b(b) {}
+
+ const int a, b;
+ bool operator==(Pair p) const { return p.a == a && p.b == b; }
+ };
+
+ SmallVector<Pair, 5> pairs;
+
+ EXPECT_TRUE(pairs.empty());
+ EXPECT_EQ(pairs.max_size(), 5u);
+
+ for (size_t i = 0; i < pairs.max_size(); ++i) {
+ EXPECT_EQ(pairs.size(), i);
+
+ const int a = static_cast<int>(i) * 2;
+ EXPECT_EQ(pairs.emplace_back(a, a + 1), Pair(a, a + 1));
+ }
+
+ EXPECT_EQ(pairs.size(), 5u);
+ EXPECT_FALSE(pairs.dynamic());
+
+ // The vector is promoted when full.
+ EXPECT_EQ(pairs.emplace_back(10, 11), Pair(10, 11));
+ EXPECT_TRUE(pairs.dynamic());
+
+ EXPECT_EQ(pairs,
+ (SmallVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9},
+ Pair{10, 11}}));
+
+ // Constructor takes at most N elements.
+ SmallVector<int, 6> sums = {0, 0, 0, 0, 0, 0};
+ EXPECT_FALSE(sums.dynamic());
+
+ // Random-access iterators comply with standard.
+ std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; });
+ EXPECT_EQ(sums, (SmallVector{1, 5, 9, 13, 17, 21}));
+
+ sums.pop_back();
+ std::reverse(sums.begin(), sums.end());
+
+ EXPECT_EQ(sums, (SmallVector{17, 13, 9, 5, 1}));
+}
+
+TEST(SmallVector, MovableElement) {
+ // Construct std::string elements in-place from C-style strings. Without std::in_place_type, the
+ // element type would be deduced from the first element, i.e. const char*.
+ SmallVector strings(std::in_place_type<std::string>, "", "", "", "cake", "velvet", "red", "");
+ strings.pop_back();
+
+ EXPECT_EQ(strings.max_size(), 7u);
+ EXPECT_EQ(strings.size(), 6u);
+
+ // Erase "cake" and append a substring copy.
+ {
+ const auto it = std::find_if(strings.begin(), strings.end(),
+ [](const auto& s) { return !s.empty(); });
+ ASSERT_FALSE(it == strings.end());
+ EXPECT_EQ(*it, "cake");
+
+ strings.unstable_erase(it);
+ EXPECT_EQ(strings.emplace_back("cakewalk", 4u), "cake"s);
+ }
+
+ strings[1] = "quince"s;
+
+ // Replace last empty string with "tart".
+ {
+ const auto rit = std::find(strings.rbegin(), strings.rend(), std::string());
+ ASSERT_FALSE(rit == strings.rend());
+
+ std::initializer_list<char> list = {'t', 'a', 'r', 't'};
+ strings.replace(rit.base() - 1, list);
+ }
+
+ strings.front().assign("pie");
+
+ EXPECT_EQ(strings, (SmallVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
+
+ strings.push_back("nougat");
+ strings.push_back("oreo");
+ EXPECT_TRUE(strings.dynamic());
+
+ std::rotate(strings.begin(), strings.end() - 2, strings.end());
+
+ EXPECT_EQ(strings,
+ (SmallVector{"nougat"s, "oreo"s, "pie"s, "quince"s, "tart"s, "red"s, "velvet"s,
+ "cake"s}));
+}
+
+TEST(SmallVector, Replace) {
+ // Replacing does not require a copy/move assignment operator.
+ struct Word {
+ explicit Word(std::string str) : str(std::move(str)) {}
+ const std::string str;
+
+ bool operator==(const Word& other) const { return other.str == str; }
+ };
+
+ SmallVector words(std::in_place_type<Word>, "colored", "velour");
+
+ // The replaced element can be referenced by the replacement.
+ {
+ const Word& word = words.replace(words.last(), words.back().str.substr(0, 3) + "vet");
+ EXPECT_EQ(word, Word("velvet"));
+ }
+
+ // The vector is not promoted if replacing while full.
+ EXPECT_FALSE(words.dynamic());
+
+ words.emplace_back("cake");
+ EXPECT_TRUE(words.dynamic());
+
+ {
+ const Word& word = words.replace(words.begin(), words.front().str.substr(4));
+ EXPECT_EQ(word, Word("red"));
+ }
+
+ EXPECT_EQ(words, (SmallVector{Word("red"), Word("velvet"), Word("cake")}));
+}
+
+TEST(SmallVector, ReverseAppend) {
+ SmallVector strings = {"red"s, "velvet"s, "cake"s};
+ EXPECT_FALSE(strings.dynamic());
+
+ auto rit = strings.rbegin();
+ while (rit != strings.rend()) {
+ // Iterator and reference are invalidated on insertion.
+ const auto i = std::distance(strings.begin(), rit.base());
+ std::string s = *rit;
+
+ strings.push_back(std::move(s));
+ rit = std::make_reverse_iterator(strings.begin() + i) + 1;
+ }
+
+ EXPECT_EQ(strings, (SmallVector{"red"s, "velvet"s, "cake"s, "cake"s, "velvet"s, "red"s}));
+ EXPECT_TRUE(strings.dynamic());
+}
+
+TEST(SmallVector, Sort) {
+ SmallVector strings(std::in_place_type<std::string>, "pie", "quince", "tart", "red", "velvet");
+ strings.push_back("cake"s);
+
+ auto sorted = std::move(strings);
+ EXPECT_TRUE(strings.empty());
+
+ EXPECT_TRUE(sorted.dynamic());
+ EXPECT_TRUE(strings.dynamic());
+
+ std::sort(sorted.begin(), sorted.end());
+ EXPECT_EQ(sorted, (SmallVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s}));
+
+ // Constructor takes array reference.
+ {
+ const char* kStrings[] = {"cake", "lie"};
+ strings = SmallVector(kStrings);
+ EXPECT_FALSE(strings.dynamic());
+ }
+
+ EXPECT_GT(sorted, strings);
+ swap(sorted, strings);
+ EXPECT_LT(sorted, strings);
+
+ EXPECT_FALSE(sorted.dynamic());
+ EXPECT_TRUE(strings.dynamic());
+
+ // Append remaining elements, such that "pie" is the only difference.
+ for (const char* str : {"quince", "red", "tart", "velvet"}) {
+ sorted.emplace_back(str);
+ }
+ EXPECT_TRUE(sorted.dynamic());
+
+ EXPECT_NE(sorted, strings);
+
+ // Replace second element with "pie".
+ const auto it = sorted.begin() + 1;
+ EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
+
+ EXPECT_EQ(sorted, strings);
+}
+
+namespace {
+
+struct DestroyCounts {
+ DestroyCounts(int& live, int& dead) : counts{live, dead} {}
+ DestroyCounts(const DestroyCounts& other) : counts(other.counts) {}
+ DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; }
+ ~DestroyCounts() { ++(alive ? counts.live : counts.dead); }
+
+ struct {
+ int& live;
+ int& dead;
+ } counts;
+
+ bool alive = true;
+};
+
+void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
+ std::swap(lhs.alive, rhs.alive);
+}
+
+} // namespace
+
+TEST(SmallVector, Destroy) {
+ int live = 0;
+ int dead = 0;
+
+ { SmallVector<DestroyCounts, 3> counts; }
+ EXPECT_EQ(0, live);
+ EXPECT_EQ(0, dead);
+
+ {
+ SmallVector<DestroyCounts, 3> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ EXPECT_FALSE(counts.dynamic());
+ }
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(0, dead);
+
+ live = 0;
+ {
+ SmallVector<DestroyCounts, 3> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ EXPECT_TRUE(counts.dynamic());
+ }
+ EXPECT_EQ(4, live);
+ EXPECT_EQ(3, dead);
+
+ live = dead = 0;
+ {
+ SmallVector<DestroyCounts, 2> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ auto copy = counts;
+ EXPECT_TRUE(copy.dynamic());
+ }
+ EXPECT_EQ(6, live);
+ EXPECT_EQ(2, dead);
+
+ live = dead = 0;
+ {
+ SmallVector<DestroyCounts, 2> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ auto move = std::move(counts);
+ EXPECT_TRUE(move.dynamic());
+ }
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(2, dead);
+
+ live = dead = 0;
+ {
+ SmallVector<DestroyCounts, 2> counts1;
+ counts1.emplace_back(live, dead);
+ counts1.emplace_back(live, dead);
+ counts1.emplace_back(live, dead);
+
+ EXPECT_TRUE(counts1.dynamic());
+ EXPECT_EQ(2, dead);
+ dead = 0;
+
+ SmallVector<DestroyCounts, 2> counts2;
+ counts2.emplace_back(live, dead);
+
+ EXPECT_FALSE(counts2.dynamic());
+
+ swap(counts1, counts2);
+
+ EXPECT_FALSE(counts1.dynamic());
+ EXPECT_TRUE(counts2.dynamic());
+
+ EXPECT_EQ(0, live);
+ EXPECT_EQ(1, dead);
+
+ dead = 0;
+ }
+ EXPECT_EQ(4, live);
+ EXPECT_EQ(0, dead);
+}
+
+} // namespace android::test
diff --git a/libs/ftl/StaticVector_test.cpp b/libs/ftl/StaticVector_test.cpp
new file mode 100644
index 0000000..dd5ce35
--- /dev/null
+++ b/libs/ftl/StaticVector_test.cpp
@@ -0,0 +1,391 @@
+/*
+ * Copyright 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 <ftl/StaticVector.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <utility>
+
+using namespace std::string_literals;
+
+namespace android::test {
+
+using ftl::StaticVector;
+
+// Keep in sync with example usage in header file.
+TEST(StaticVector, Example) {
+ ftl::StaticVector<char, 3> vector;
+ EXPECT_TRUE(vector.empty());
+
+ vector = {'a', 'b'};
+ EXPECT_EQ(vector.size(), 2u);
+
+ vector.push_back('c');
+ EXPECT_TRUE(vector.full());
+
+ EXPECT_FALSE(vector.push_back('d'));
+ EXPECT_EQ(vector.size(), 3u);
+
+ vector.unstable_erase(vector.begin());
+ EXPECT_EQ(vector, (ftl::StaticVector{'c', 'b'}));
+
+ vector.pop_back();
+ EXPECT_EQ(vector.back(), 'c');
+
+ const char array[] = "hi";
+ vector = ftl::StaticVector(array);
+ EXPECT_EQ(vector, (ftl::StaticVector{'h', 'i', '\0'}));
+}
+
+TEST(StaticVector, Construct) {
+ {
+ // Default constructor.
+ StaticVector<std::string, 2> vector;
+ EXPECT_TRUE(vector.empty());
+ }
+ {
+ // Array constructor.
+ const float kFloats[] = {.1f, .2f, .3f};
+ StaticVector vector(kFloats);
+ EXPECT_EQ(vector, (StaticVector{.1f, .2f, .3f}));
+ }
+ {
+ // Iterator constructor.
+ const char chars[] = "abcdef";
+ std::string string(chars);
+ StaticVector<char, sizeof(chars)> vector(string.begin(), string.end());
+
+ EXPECT_STREQ(vector.begin(), chars);
+ }
+ {
+ // Variadic constructor with same types.
+ StaticVector vector = {1, 2, 3};
+
+ static_assert(std::is_same_v<decltype(vector), StaticVector<int, 3>>);
+ EXPECT_EQ(vector, (StaticVector{1, 2, 3}));
+ }
+ {
+ // Variadic constructor with different types.
+ const auto copy = "quince"s;
+ auto move = "tart"s;
+ StaticVector vector = {copy, std::move(move)};
+
+ static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 2>>);
+ EXPECT_EQ(vector, (StaticVector{"quince"s, "tart"s}));
+ }
+ {
+ // In-place constructor with same types.
+ StaticVector vector(std::in_place_type<std::string>, "red", "velvet", "cake");
+
+ static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
+ EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
+ }
+ {
+ // In-place constructor with different types.
+ const auto copy = "red"s;
+ auto move = "velvet"s;
+ std::initializer_list<char> list = {'c', 'a', 'k', 'e'};
+ StaticVector vector(std::in_place_type<std::string>, copy.c_str(), std::move(move), list);
+
+ static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>);
+ EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s}));
+ }
+ {
+ struct String {
+ explicit String(const char* str) : str(str) {}
+ explicit String(const char** ptr) : str(*ptr) {}
+ const char* str;
+ };
+
+ const char* kStrings[] = {"a", "b", "c", "d"};
+
+ {
+ // Two iterator-like elements.
+ StaticVector<String, 3> vector(kStrings, kStrings + 3);
+ ASSERT_EQ(vector.size(), 2u);
+
+ EXPECT_STREQ(vector[0].str, "a");
+ EXPECT_STREQ(vector[1].str, "d");
+ }
+ {
+ // Disambiguating iterator constructor.
+ StaticVector<String, 3> vector(ftl::IteratorRange, kStrings, kStrings + 3);
+ ASSERT_EQ(vector.size(), 3u);
+
+ EXPECT_STREQ(vector[0].str, "a");
+ EXPECT_STREQ(vector[1].str, "b");
+ EXPECT_STREQ(vector[2].str, "c");
+ }
+ }
+}
+
+TEST(StaticVector, String) {
+ StaticVector<char, 10> chars;
+ char c = 'a';
+ std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; });
+ chars.back() = '\0';
+
+ EXPECT_STREQ(chars.begin(), "abcdefghi");
+
+ // Constructor takes iterator range.
+ const char kString[] = "123456";
+ StaticVector<char, 10> string(std::begin(kString), std::end(kString));
+
+ EXPECT_STREQ(string.begin(), "123456");
+ EXPECT_EQ(string.size(), 7u);
+
+ // Similar to emplace, but replaces rather than inserts.
+ string.replace(string.begin() + 5, '\0');
+ EXPECT_STREQ(string.begin(), "12345");
+
+ swap(chars, string);
+
+ EXPECT_STREQ(chars.begin(), "12345");
+ EXPECT_STREQ(string.begin(), "abcdefghi");
+}
+
+TEST(StaticVector, CopyableElement) {
+ struct Pair {
+ const int a, b;
+ bool operator==(Pair p) const { return p.a == a && p.b == b; }
+ };
+
+ StaticVector<Pair, 5> pairs;
+
+ EXPECT_TRUE(pairs.empty());
+ EXPECT_EQ(pairs.max_size(), 5u);
+
+ for (size_t i = 0; i < pairs.max_size(); ++i) {
+ EXPECT_EQ(pairs.size(), i);
+
+ const int a = static_cast<int>(i) * 2;
+ const auto it = pairs.emplace_back(a, a + 1);
+ ASSERT_NE(it, pairs.end());
+ EXPECT_EQ(*it, (Pair{a, a + 1}));
+ }
+
+ EXPECT_TRUE(pairs.full());
+ EXPECT_EQ(pairs.size(), 5u);
+
+ // Insertion fails if the vector is full.
+ const auto it = pairs.emplace_back(10, 11);
+ EXPECT_EQ(it, pairs.end());
+
+ EXPECT_EQ(pairs, (StaticVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9}}));
+
+ // Constructor takes at most N elements.
+ StaticVector<int, 6> sums = {0, 0, 0, 0, 0, -1};
+ EXPECT_TRUE(sums.full());
+
+ // Random-access iterators comply with standard.
+ std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; });
+ EXPECT_EQ(sums, (StaticVector{1, 5, 9, 13, 17, -1}));
+
+ sums.pop_back();
+ std::reverse(sums.begin(), sums.end());
+
+ EXPECT_EQ(sums, (StaticVector{17, 13, 9, 5, 1}));
+}
+
+TEST(StaticVector, MovableElement) {
+ // Construct std::string elements in-place from C-style strings. Without std::in_place_type, the
+ // element type would be deduced from the first element, i.e. const char*.
+ StaticVector strings(std::in_place_type<std::string>, "", "", "", "cake", "velvet", "red", "");
+ strings.pop_back();
+
+ EXPECT_EQ(strings.max_size(), 7u);
+ EXPECT_EQ(strings.size(), 6u);
+
+ // Erase "cake" and append a substring copy.
+ {
+ auto it = std::find_if(strings.begin(), strings.end(),
+ [](const auto& s) { return !s.empty(); });
+ ASSERT_FALSE(it == strings.end());
+ EXPECT_EQ(*it, "cake");
+
+ strings.unstable_erase(it);
+
+ // Construct std::string from first 4 characters of C-style string.
+ it = strings.emplace_back("cakewalk", 4u);
+ ASSERT_NE(it, strings.end());
+ EXPECT_EQ(*it, "cake"s);
+ }
+
+ strings[1] = "quince"s;
+
+ // Replace last empty string with "tart".
+ {
+ const auto rit = std::find(strings.rbegin(), strings.rend(), std::string());
+ ASSERT_FALSE(rit == strings.rend());
+
+ std::initializer_list<char> list = {'t', 'a', 'r', 't'};
+ strings.replace(rit.base() - 1, list);
+ }
+
+ strings.front().assign("pie");
+
+ EXPECT_EQ(strings, (StaticVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s}));
+}
+
+TEST(StaticVector, Replace) {
+ // Replacing does not require a copy/move assignment operator.
+ struct Word {
+ explicit Word(std::string str) : str(std::move(str)) {}
+ const std::string str;
+ };
+
+ StaticVector words(std::in_place_type<Word>, "red", "velour", "cake");
+
+ // The replaced element can be referenced by the replacement.
+ const auto it = words.begin() + 1;
+ const Word& word = words.replace(it, it->str.substr(0, 3) + "vet");
+ EXPECT_EQ(word.str, "velvet");
+}
+
+TEST(StaticVector, ReverseTruncate) {
+ StaticVector<std::string, 10> strings("pie", "quince", "tart", "red", "velvet", "cake");
+ EXPECT_FALSE(strings.full());
+
+ for (auto it = strings.begin(); it != strings.end(); ++it) {
+ strings.replace(it, strings.back());
+ strings.pop_back();
+ }
+
+ EXPECT_EQ(strings, (StaticVector{"cake"s, "velvet"s, "red"s}));
+}
+
+TEST(StaticVector, Sort) {
+ StaticVector<std::string, 7> strings("pie", "quince", "tart", "red", "velvet", "cake");
+ EXPECT_FALSE(strings.full());
+
+ auto sorted = std::move(strings);
+ EXPECT_TRUE(strings.empty());
+
+ std::sort(sorted.begin(), sorted.end());
+ EXPECT_EQ(sorted, (StaticVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s}));
+
+ // Constructor takes array reference.
+ {
+ const char* kStrings[] = {"cake", "lie"};
+ strings = StaticVector(kStrings);
+ }
+
+ EXPECT_GT(sorted, strings);
+ swap(sorted, strings);
+ EXPECT_LT(sorted, strings);
+
+ // Append remaining elements, such that "pie" is the only difference.
+ for (const char* str : {"quince", "red", "tart", "velvet"}) {
+ sorted.emplace_back(str);
+ }
+
+ EXPECT_NE(sorted, strings);
+
+ // Replace second element with "pie".
+ const auto it = sorted.begin() + 1;
+ EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie");
+
+ EXPECT_EQ(sorted, strings);
+}
+
+namespace {
+
+struct DestroyCounts {
+ DestroyCounts(int& live, int& dead) : counts{live, dead} {}
+ DestroyCounts(const DestroyCounts& other) : counts(other.counts) {}
+ DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; }
+ ~DestroyCounts() { ++(alive ? counts.live : counts.dead); }
+
+ struct {
+ int& live;
+ int& dead;
+ } counts;
+
+ bool alive = true;
+};
+
+void swap(DestroyCounts& lhs, DestroyCounts& rhs) {
+ std::swap(lhs.alive, rhs.alive);
+}
+
+} // namespace
+
+TEST(StaticVector, Destroy) {
+ int live = 0;
+ int dead = 0;
+
+ { StaticVector<DestroyCounts, 5> counts; }
+ EXPECT_EQ(0, live);
+ EXPECT_EQ(0, dead);
+
+ {
+ StaticVector<DestroyCounts, 5> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ }
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(0, dead);
+
+ live = 0;
+ {
+ StaticVector<DestroyCounts, 5> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ auto copy = counts;
+ }
+ EXPECT_EQ(6, live);
+ EXPECT_EQ(0, dead);
+
+ live = 0;
+ {
+ StaticVector<DestroyCounts, 5> counts;
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+ counts.emplace_back(live, dead);
+
+ auto move = std::move(counts);
+ }
+ EXPECT_EQ(3, live);
+ EXPECT_EQ(3, dead);
+
+ live = dead = 0;
+ {
+ StaticVector<DestroyCounts, 5> counts1;
+ counts1.emplace_back(live, dead);
+ counts1.emplace_back(live, dead);
+ counts1.emplace_back(live, dead);
+
+ StaticVector<DestroyCounts, 5> counts2;
+ counts2.emplace_back(live, dead);
+
+ swap(counts1, counts2);
+
+ EXPECT_EQ(0, live);
+ EXPECT_EQ(2, dead);
+
+ dead = 0;
+ }
+ EXPECT_EQ(4, live);
+ EXPECT_EQ(0, dead);
+}
+
+} // namespace android::test
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index 66fb295..243d7f1 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -21,6 +21,11 @@
"-Wno-enum-compare",
],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
vendor_available: true,
vndk: {
@@ -38,14 +43,14 @@
],
shared_libs: [
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
"liblog",
],
export_shared_lib_headers: [
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.mapper@4.0",
"libhidlbase",
],
diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp
index 8444883..8933dc3 100644
--- a/libs/gralloc/types/fuzzer/Android.bp
+++ b/libs/gralloc/types/fuzzer/Android.bp
@@ -2,6 +2,11 @@
name: "libgralloctypes_fuzzer",
defaults: ["libbinder_ndk_host_user"],
host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ }
+ },
fuzz_config: {
cc: ["marissaw@google.com"],
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 87f7972..678613b 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -23,6 +23,7 @@
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
#include <gui/GLConsumer.h>
+#include <gui/Surface.h>
#include <utils/Trace.h>
@@ -158,7 +159,7 @@
if (context == nullptr) {
return;
}
- BLASTBufferQueue* bq = static_cast<BLASTBufferQueue*>(context);
+ sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context);
bq->transactionCallback(latchTime, presentFence, stats);
}
@@ -213,12 +214,9 @@
ATRACE_CALL();
BQA_LOGV("processNextBufferLocked useNextTransaction=%s", toString(useNextTransaction));
- // Wait to acquire a buffer if there are no frames available or we have acquired the maximum
+ // Wait to acquire a buffer if there are no frames available or we have acquired the max
// number of buffers.
- // As a special case, we wait for the first callback before acquiring the second buffer so we
- // can ensure the first buffer is presented if multiple buffers are queued in succession.
- if (mNumFrameAvailable == 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1 ||
- (!mInitialCallbackReceived && mNumAcquired == 1)) {
+ if (mNumFrameAvailable == 0 || maxBuffersAcquired()) {
BQA_LOGV("processNextBufferLocked waiting for frame available or callback");
return;
}
@@ -241,6 +239,7 @@
status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, -1, false);
if (status != OK) {
+ BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str());
return;
}
auto buffer = bufferItem.mGraphicBuffer;
@@ -248,14 +247,15 @@
if (buffer == nullptr) {
mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+ BQA_LOGE("Buffer was empty");
return;
}
if (rejectBuffer(bufferItem)) {
BQA_LOGE("rejecting buffer:configured size=%dx%d, buffer{size=%dx%d transform=%d}", mWidth,
mHeight, buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
- mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
- return;
+ // TODO(b/168917217) temporarily don't reject buffers until we can synchronize buffer size
+ // changes from ViewRootImpl.
}
mNumAcquired++;
@@ -302,23 +302,31 @@
return item.mCrop;
}
-void BLASTBufferQueue::onFrameAvailable(const BufferItem& /*item*/) {
+void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
ATRACE_CALL();
std::unique_lock _lock{mMutex};
const bool nextTransactionSet = mNextTransaction != nullptr;
- BQA_LOGV("onFrameAvailable nextTransactionSet=%s", toString(nextTransactionSet));
+ BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s mFlushShadowQueue=%s",
+ item.mFrameNumber, toString(nextTransactionSet), toString(mFlushShadowQueue));
- if (nextTransactionSet) {
- while (mNumFrameAvailable > 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) {
+ if (nextTransactionSet || mFlushShadowQueue) {
+ while (mNumFrameAvailable > 0 || maxBuffersAcquired()) {
+ BQA_LOGV("waiting in onFrameAvailable...");
mCallbackCV.wait(_lock);
}
}
+ mFlushShadowQueue = false;
// add to shadow queue
mNumFrameAvailable++;
processNextBufferLocked(true);
}
+void BLASTBufferQueue::onFrameReplaced(const BufferItem& item) {
+ BQA_LOGV("onFrameReplaced framenumber=%" PRIu64, item.mFrameNumber);
+ // Do nothing since we are not storing unacquired buffer items locally.
+}
+
void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
std::lock_guard _lock{mMutex};
mNextTransaction = t;
@@ -341,4 +349,72 @@
// reject buffers if the buffer size doesn't match.
return bufWidth != mWidth || bufHeight != mHeight;
}
+
+// Check if we have acquired the maximum number of buffers.
+// As a special case, we wait for the first callback before acquiring the second buffer so we
+// can ensure the first buffer is presented if multiple buffers are queued in succession.
+bool BLASTBufferQueue::maxBuffersAcquired() const {
+ return mNumAcquired == MAX_ACQUIRED_BUFFERS + 1 ||
+ (!mInitialCallbackReceived && mNumAcquired == 1);
+}
+
+class BBQSurface : public Surface {
+private:
+ sp<BLASTBufferQueue> mBbq;
+public:
+ BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp,
+ const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq)
+ : Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {}
+
+ void allocateBuffers() override {
+ uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth;
+ uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight;
+ auto gbp = getIGraphicBufferProducer();
+ std::thread ([reqWidth, reqHeight, gbp=getIGraphicBufferProducer(),
+ reqFormat=mReqFormat, reqUsage=mReqUsage] () {
+ gbp->allocateBuffers(reqWidth, reqHeight,
+ reqFormat, reqUsage);
+
+ }).detach();
+ }
+
+ status_t setFrameRate(float frameRate, int8_t compatibility) override {
+ if (!ValidateFrameRate(frameRate, compatibility, "BBQSurface::setFrameRate")) {
+ return BAD_VALUE;
+ }
+ return mBbq->setFrameRate(frameRate, compatibility);
+ }
+
+ status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId) override {
+ return mBbq->setFrameTimelineVsync(frameTimelineVsyncId);
+ }
+};
+
+// TODO: Can we coalesce this with frame updates? Need to confirm
+// no timing issues.
+status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility) {
+ std::unique_lock _lock{mMutex};
+ SurfaceComposerClient::Transaction t;
+
+ return t.setFrameRate(mSurfaceControl, frameRate, compatibility)
+ .apply();
+}
+
+status_t BLASTBufferQueue::setFrameTimelineVsync(int64_t frameTimelineVsyncId) {
+ std::unique_lock _lock{mMutex};
+ SurfaceComposerClient::Transaction t;
+
+ return t.setFrameTimelineVsync(mSurfaceControl, frameTimelineVsyncId)
+ .apply();
+}
+
+sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) {
+ std::unique_lock _lock{mMutex};
+ sp<IBinder> scHandle = nullptr;
+ if (includeSurfaceControlHandle && mSurfaceControl) {
+ scHandle = mSurfaceControl->getHandle();
+ }
+ return new BBQSurface(mProducer, true, scHandle, this);
+}
+
} // namespace android
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 7e894b4..abfee61 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -73,8 +73,8 @@
nsecs_t vsyncTimestamp;
PhysicalDisplayId vsyncDisplayId;
uint32_t vsyncCount;
- int64_t vsyncId;
- if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncId)) {
+ VsyncEventData vsyncEventData;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) {
ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this,
ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
}
@@ -117,13 +117,14 @@
nsecs_t vsyncTimestamp;
PhysicalDisplayId vsyncDisplayId;
uint32_t vsyncCount;
- int64_t vsyncId;
- if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncId)) {
+ VsyncEventData vsyncEventData;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) {
ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
", displayId=%s, count=%d, vsyncId=%" PRId64,
- this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount, vsyncId);
+ this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount,
+ vsyncEventData.id);
mWaitingForVsync = false;
- dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncId);
+ dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
}
return 1; // keep the callback
@@ -131,11 +132,11 @@
bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
PhysicalDisplayId* outDisplayId,
- uint32_t* outCount, int64_t* outVsyncId) {
+ uint32_t* outCount,
+ VsyncEventData* outVsyncEventData) {
bool gotVsync = false;
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
ssize_t n;
- *outVsyncId = 0;
while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
for (ssize_t i = 0; i < n; i++) {
@@ -148,7 +149,8 @@
*outTimestamp = ev.header.timestamp;
*outDisplayId = ev.header.displayId;
*outCount = ev.vsync.count;
- *outVsyncId = ev.vsync.vsyncId;
+ outVsyncEventData->id = ev.vsync.vsyncId;
+ outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
break;
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 964195d..6f92233 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -888,7 +888,7 @@
}
virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultConfig,
+ int32_t defaultConfig, bool allowGroupSwitching,
float primaryRefreshRateMin,
float primaryRefreshRateMax,
float appRequestRefreshRateMin,
@@ -909,6 +909,11 @@
ALOGE("setDesiredDisplayConfigSpecs failed to write defaultConfig: %d", result);
return result;
}
+ result = data.writeBool(allowGroupSwitching);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to write allowGroupSwitching: %d", result);
+ return result;
+ }
result = data.writeFloat(primaryRefreshRateMin);
if (result != NO_ERROR) {
ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMin: %d", result);
@@ -943,12 +948,14 @@
virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
int32_t* outDefaultConfig,
+ bool* outAllowGroupSwitching,
float* outPrimaryRefreshRateMin,
float* outPrimaryRefreshRateMax,
float* outAppRequestRefreshRateMin,
float* outAppRequestRefreshRateMax) {
- if (!outDefaultConfig || !outPrimaryRefreshRateMin || !outPrimaryRefreshRateMax ||
- !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
+ if (!outDefaultConfig || !outAllowGroupSwitching || !outPrimaryRefreshRateMin ||
+ !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin ||
+ !outAppRequestRefreshRateMax) {
return BAD_VALUE;
}
Parcel data, reply;
@@ -973,6 +980,11 @@
ALOGE("getDesiredDisplayConfigSpecs failed to read defaultConfig: %d", result);
return result;
}
+ result = reply.readBool(outAllowGroupSwitching);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to read allowGroupSwitching: %d", result);
+ return result;
+ }
result = reply.readFloat(outPrimaryRefreshRateMin);
if (result != NO_ERROR) {
ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMin: %d", result);
@@ -1829,6 +1841,13 @@
ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultConfig: %d", result);
return result;
}
+ bool allowGroupSwitching;
+ result = data.readBool(&allowGroupSwitching);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read allowGroupSwitching: %d",
+ result);
+ return result;
+ }
float primaryRefreshRateMin;
result = data.readFloat(&primaryRefreshRateMin);
if (result != NO_ERROR) {
@@ -1857,10 +1876,10 @@
result);
return result;
}
- result =
- setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin,
- primaryRefreshRateMax, appRequestRefreshRateMin,
- appRequestRefreshRateMax);
+ result = setDesiredDisplayConfigSpecs(displayToken, defaultConfig, allowGroupSwitching,
+ primaryRefreshRateMin, primaryRefreshRateMax,
+ appRequestRefreshRateMin,
+ appRequestRefreshRateMax);
if (result != NO_ERROR) {
ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: "
"%d",
@@ -1874,13 +1893,14 @@
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> displayToken = data.readStrongBinder();
int32_t defaultConfig;
+ bool allowGroupSwitching;
float primaryRefreshRateMin;
float primaryRefreshRateMax;
float appRequestRefreshRateMin;
float appRequestRefreshRateMax;
status_t result =
- getDesiredDisplayConfigSpecs(displayToken, &defaultConfig,
+ getDesiredDisplayConfigSpecs(displayToken, &defaultConfig, &allowGroupSwitching,
&primaryRefreshRateMin, &primaryRefreshRateMax,
&appRequestRefreshRateMin,
&appRequestRefreshRateMax);
@@ -1896,6 +1916,12 @@
ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultConfig: %d", result);
return result;
}
+ result = reply->writeBool(allowGroupSwitching);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write allowGroupSwitching: %d",
+ result);
+ return result;
+ }
result = reply->writeFloat(primaryRefreshRateMin);
if (result != NO_ERROR) {
ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMin: %d",
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index b3eb994..30c9b37 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -122,6 +122,8 @@
return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
case view::LayerMetadataKey::METADATA_TASK_ID:
return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
+ case view::LayerMetadataKey::METADATA_OWNER_PID:
+ return StringPrintf("ownerPID%s%d", separator, getInt32(key, 0));
default:
return StringPrintf("%d%s%dbytes", key, separator,
static_cast<int>(mMap.at(key).size()));
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 8594ab3..9722f36 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -44,7 +44,6 @@
cornerRadius(0.0f),
backgroundBlurRadius(0),
barrierFrameNumber(0),
- overrideScalingMode(-1),
transform(0),
transformToDisplayInverse(false),
crop(Rect::INVALID_RECT),
@@ -86,7 +85,6 @@
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, barrierSurfaceControl_legacy);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl);
SAFE_PARCEL(output.writeUint64, barrierFrameNumber);
- SAFE_PARCEL(output.writeInt32, overrideScalingMode);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild);
SAFE_PARCEL(output.writeFloat, color.r);
@@ -148,6 +146,21 @@
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
SAFE_PARCEL(output.writeUint32, fixedTransformHint);
SAFE_PARCEL(output.writeUint64, frameNumber);
+ SAFE_PARCEL(output.writeInt64, frameTimelineVsyncId);
+
+ SAFE_PARCEL(output.writeUint32, blurRegions.size());
+ for (auto region : blurRegions) {
+ SAFE_PARCEL(output.writeUint32, region.blurRadius);
+ SAFE_PARCEL(output.writeFloat, region.cornerRadiusTL);
+ SAFE_PARCEL(output.writeFloat, region.cornerRadiusTR);
+ SAFE_PARCEL(output.writeFloat, region.cornerRadiusBL);
+ SAFE_PARCEL(output.writeFloat, region.cornerRadiusBR);
+ SAFE_PARCEL(output.writeFloat, region.alpha);
+ SAFE_PARCEL(output.writeInt32, region.left);
+ SAFE_PARCEL(output.writeInt32, region.top);
+ SAFE_PARCEL(output.writeInt32, region.right);
+ SAFE_PARCEL(output.writeInt32, region.bottom);
+ }
return NO_ERROR;
}
@@ -176,7 +189,6 @@
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &barrierSurfaceControl_legacy);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl);
SAFE_PARCEL(input.readUint64, &barrierFrameNumber);
- SAFE_PARCEL(input.readInt32, &overrideScalingMode);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild);
@@ -253,6 +265,25 @@
SAFE_PARCEL(input.readUint32, &tmpUint32);
fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32);
SAFE_PARCEL(input.readUint64, &frameNumber);
+ SAFE_PARCEL(input.readInt64, &frameTimelineVsyncId);
+
+ uint32_t numRegions = 0;
+ SAFE_PARCEL(input.readUint32, &numRegions);
+ blurRegions.clear();
+ for (uint32_t i = 0; i < numRegions; i++) {
+ BlurRegion region;
+ SAFE_PARCEL(input.readUint32, ®ion.blurRadius);
+ SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusTL);
+ SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusTR);
+ SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusBL);
+ SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusBR);
+ SAFE_PARCEL(input.readFloat, ®ion.alpha);
+ SAFE_PARCEL(input.readInt32, ®ion.left);
+ SAFE_PARCEL(input.readInt32, ®ion.top);
+ SAFE_PARCEL(input.readInt32, ®ion.right);
+ SAFE_PARCEL(input.readInt32, ®ion.bottom);
+ blurRegions.push_back(region);
+ }
return NO_ERROR;
}
@@ -376,15 +407,15 @@
what |= eBackgroundBlurRadiusChanged;
backgroundBlurRadius = other.backgroundBlurRadius;
}
+ if (other.what & eBlurRegionsChanged) {
+ what |= eBlurRegionsChanged;
+ blurRegions = other.blurRegions;
+ }
if (other.what & eDeferTransaction_legacy) {
what |= eDeferTransaction_legacy;
barrierSurfaceControl_legacy = other.barrierSurfaceControl_legacy;
barrierFrameNumber = other.barrierFrameNumber;
}
- if (other.what & eOverrideScalingModeChanged) {
- what |= eOverrideScalingModeChanged;
- overrideScalingMode = other.overrideScalingMode;
- }
if (other.what & eReparentChildren) {
what |= eReparentChildren;
reparentSurfaceControl = other.reparentSurfaceControl;
@@ -593,6 +624,7 @@
SAFE_PARCEL(output.writeBool, captureSecureLayers);
SAFE_PARCEL(output.writeInt32, uid);
SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(dataspace));
+ SAFE_PARCEL(output.writeBool, allowProtected);
return NO_ERROR;
}
@@ -606,6 +638,7 @@
SAFE_PARCEL(input.readInt32, &uid);
SAFE_PARCEL(input.readInt32, &value);
dataspace = static_cast<ui::Dataspace>(value);
+ SAFE_PARCEL(input.readBool, &allowProtected);
return NO_ERROR;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 9ce8442..c1155ab 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -63,7 +63,8 @@
} // namespace
-Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp)
+Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp,
+ const sp<IBinder>& surfaceControlHandle)
: mGraphicBufferProducer(bufferProducer),
mCrop(Rect::EMPTY_RECT),
mBufferAge(0),
@@ -111,6 +112,7 @@
mProducerControlledByApp = controlledByApp;
mSwapIntervalZero = false;
mMaxBufferCount = NUM_BUFFER_SLOTS;
+ mSurfaceControlHandle = surfaceControlHandle;
}
Surface::~Surface() {
@@ -1521,7 +1523,7 @@
auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
ALOGV("Surface::dispatchSetFrameTimelineVsync");
- return composerService()->setFrameTimelineVsync(mGraphicBufferProducer, frameTimelineVsyncId);
+ return setFrameTimelineVsync(frameTimelineVsyncId);
}
bool Surface::transformToDisplayInverse() {
@@ -2288,4 +2290,9 @@
return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility);
}
+status_t Surface::setFrameTimelineVsync(int64_t frameTimelineVsyncId) {
+ return composerService()->setFrameTimelineVsync(mGraphicBufferProducer,
+ frameTimelineVsyncId);
+}
+
}; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0068ccf..039e900 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -695,8 +695,6 @@
}
}
- mListenerCallbacks.clear();
-
cacheBuffers();
Vector<ComposerState> composerStates;
@@ -709,10 +707,7 @@
composerStates.add(kv.second);
}
- mComposerStates.clear();
-
- displayStates = mDisplayStates;
- mDisplayStates.clear();
+ displayStates = std::move(mDisplayStates);
if (mForceSynchronous) {
flags |= ISurfaceComposer::eSynchronous;
@@ -733,21 +728,16 @@
flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
}
- mForceSynchronous = false;
- mAnimation = false;
- mEarlyWakeup = false;
- mExplicitEarlyWakeupStart = false;
- mExplicitEarlyWakeupEnd = false;
-
- uint64_t transactionId = mId;
- mId = generateId();
-
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
sf->setTransactionState(mFrameTimelineVsyncId, composerStates, displayStates, flags, applyToken,
mInputWindowCommands, mDesiredPresentTime,
{} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
- hasListenerCallbacks, listenerCallbacks, transactionId);
- mInputWindowCommands.clear();
+ hasListenerCallbacks, listenerCallbacks, mId);
+ mId = generateId();
+
+ // Clear the current states and flags
+ clear();
+
mStatus = NO_ERROR;
return NO_ERROR;
}
@@ -1027,6 +1017,18 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBlurRegions(
+ const sp<SurfaceControl>& sc, const std::vector<BlurRegion>& blurRegions) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ s->what |= layer_state_t::eBlurRegionsChanged;
+ s->blurRegions = blurRegions;
+ return *this;
+}
+
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(
const sp<SurfaceControl>& sc, const sp<SurfaceControl>& barrierSurfaceControl,
@@ -1353,35 +1355,6 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setOverrideScalingMode(
- const sp<SurfaceControl>& sc, int32_t overrideScalingMode) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
-
- switch (overrideScalingMode) {
- case NATIVE_WINDOW_SCALING_MODE_FREEZE:
- case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
- case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
- case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
- case -1:
- break;
- default:
- ALOGE("unknown scaling mode: %d",
- overrideScalingMode);
- mStatus = BAD_VALUE;
- return *this;
- }
-
- s->what |= layer_state_t::eOverrideScalingModeChanged;
- s->overrideScalingMode = overrideScalingMode;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
#ifndef NO_INPUT
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
const sp<SurfaceControl>& sc,
@@ -1539,6 +1512,19 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineVsync(
+ const sp<SurfaceControl>& sc, int64_t frameTimelineVsyncId) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eFrameTimelineVsyncChanged;
+ s->frameTimelineVsyncId = frameTimelineVsyncId;
+ return *this;
+}
+
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1654,11 +1640,11 @@
sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags,
- SurfaceControl* parent,
+ const sp<IBinder>& parentHandle,
LayerMetadata metadata,
uint32_t* outTransformHint) {
sp<SurfaceControl> s;
- createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata),
+ createSurfaceChecked(name, w, h, format, &s, flags, parentHandle, std::move(metadata),
outTransformHint);
return s;
}
@@ -1695,20 +1681,16 @@
status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
PixelFormat format,
sp<SurfaceControl>* outSurface, uint32_t flags,
- SurfaceControl* parent, LayerMetadata metadata,
+ const sp<IBinder>& parentHandle,
+ LayerMetadata metadata,
uint32_t* outTransformHint) {
sp<SurfaceControl> sur;
status_t err = mStatus;
if (mStatus == NO_ERROR) {
sp<IBinder> handle;
- sp<IBinder> parentHandle;
sp<IGraphicBufferProducer> gbp;
- if (parent != nullptr) {
- parentHandle = parent->getHandle();
- }
-
uint32_t transformHint = 0;
int32_t id = -1;
err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
@@ -1803,27 +1785,24 @@
return ComposerService::getComposerService()->getActiveConfig(display);
}
-status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultConfig,
- float primaryRefreshRateMin,
- float primaryRefreshRateMax,
- float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) {
+status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(
+ const sp<IBinder>& displayToken, int32_t defaultConfig, bool allowGroupSwitching,
+ float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
+ float appRequestRefreshRateMax) {
return ComposerService::getComposerService()
- ->setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin,
- primaryRefreshRateMax, appRequestRefreshRateMin,
- appRequestRefreshRateMax);
+ ->setDesiredDisplayConfigSpecs(displayToken, defaultConfig, allowGroupSwitching,
+ primaryRefreshRateMin, primaryRefreshRateMax,
+ appRequestRefreshRateMin, appRequestRefreshRateMax);
}
-status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultConfig,
- float* outPrimaryRefreshRateMin,
- float* outPrimaryRefreshRateMax,
- float* outAppRequestRefreshRateMin,
- float* outAppRequestRefreshRateMax) {
+status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(
+ const sp<IBinder>& displayToken, int32_t* outDefaultConfig, bool* outAllowGroupSwitching,
+ float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax,
+ float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) {
return ComposerService::getComposerService()
- ->getDesiredDisplayConfigSpecs(displayToken, outDefaultConfig, outPrimaryRefreshRateMin,
- outPrimaryRefreshRateMax, outAppRequestRefreshRateMin,
+ ->getDesiredDisplayConfigSpecs(displayToken, outDefaultConfig, outAllowGroupSwitching,
+ outPrimaryRefreshRateMin, outPrimaryRefreshRateMax,
+ outAppRequestRefreshRateMin,
outAppRequestRefreshRateMax);
}
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 5b1a018..2300e81 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -72,9 +72,10 @@
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
return mProducer;
}
+ sp<Surface> getSurface(bool includeSurfaceControlHandle);
void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ }
- void onFrameReplaced(const BufferItem& item) override {onFrameAvailable(item);}
+ void onFrameReplaced(const BufferItem& item) override;
void onFrameAvailable(const BufferItem& item) override;
void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
@@ -82,6 +83,10 @@
void setNextTransaction(SurfaceComposerClient::Transaction *t);
void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height);
+ void flushShadowQueue() { mFlushShadowQueue = true; }
+
+ status_t setFrameRate(float frameRate, int8_t compatibility);
+ status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId);
virtual ~BLASTBufferQueue() = default;
@@ -93,9 +98,10 @@
BLASTBufferQueue(const BLASTBufferQueue& rhs);
void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex);
- Rect computeCrop(const BufferItem& item);
+ Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
// Return true if we need to reject the buffer based on the scaling mode and the buffer size.
- bool rejectBuffer(const BufferItem& item) const;
+ bool rejectBuffer(const BufferItem& item) const REQUIRES(mMutex);
+ bool maxBuffersAcquired() const REQUIRES(mMutex);
std::string mName;
sp<SurfaceControl> mSurfaceControl;
@@ -130,6 +136,9 @@
sp<BLASTBufferItemConsumer> mBufferItemConsumer;
SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
+ // If set to true, the next queue buffer will wait until the shadow queue has been processed by
+ // the adapter.
+ bool mFlushShadowQueue = false;
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index cf598ea..c65618b 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -20,6 +20,17 @@
namespace android {
+struct VsyncEventData {
+ // The Vsync Id corresponsing to this vsync event. This will be used to
+ // populate ISurfaceComposer::setFrameTimelineVsync and
+ // SurfaceComposerClient::setFrameTimelineVsync
+ int64_t id = ISurfaceComposer::INVALID_VSYNC_ID;
+
+ // The deadline in CLOCK_MONOTONIC that the app needs to complete its
+ // frame by (both on the CPU and the GPU)
+ int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max();
+};
+
class DisplayEventDispatcher : public LooperCallback {
public:
explicit DisplayEventDispatcher(
@@ -44,7 +55,7 @@
bool mWaitingForVsync;
virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
- int64_t vsyncId) = 0;
+ VsyncEventData vsyncEventData) = 0;
virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
bool connected) = 0;
virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
@@ -54,6 +65,6 @@
virtual void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) = 0;
bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
- uint32_t* outCount, int64_t* outVsyncId);
+ uint32_t* outCount, VsyncEventData* outVsyncEventData);
};
} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index a416147..5cd9356 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -395,7 +395,7 @@
* returned from getDisplayConfigs().
*/
virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultConfig,
+ int32_t defaultConfig, bool allowGroupSwitching,
float primaryRefreshRateMin,
float primaryRefreshRateMax,
float appRequestRefreshRateMin,
@@ -403,6 +403,7 @@
virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
int32_t* outDefaultConfig,
+ bool* outAllowGroupSwitching,
float* outPrimaryRefreshRateMin,
float* outPrimaryRefreshRateMax,
float* outAppRequestRefreshRateMin,
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 4a92f53..9e9e191 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -52,6 +52,7 @@
eFXSurfaceMask = 0x000F0000,
};
+ // TODO(b/172002646): Clean up the Surface Creation Arguments
/*
* Requires ACCESS_SURFACE_FLINGER permission
*/
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index d58e019..ac48aef 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -27,6 +27,8 @@
METADATA_WINDOW_TYPE = 2,
METADATA_TASK_ID = 3,
METADATA_MOUSE_CURSOR = 4,
+ METADATA_ACCESSIBILITY_ID = 5,
+ METADATA_OWNER_PID = 6,
};
struct LayerMetadata : public Parcelable {
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index ff395ec..a73d9a6 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -51,6 +51,7 @@
#include <gui/LayerMetadata.h>
#include <gui/SurfaceControl.h>
#include <math/vec3.h>
+#include <ui/BlurRegion.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -94,7 +95,7 @@
eLayerStackChanged = 0x00000080,
eCropChanged_legacy = 0x00000100,
eDeferTransaction_legacy = 0x00000200,
- eOverrideScalingModeChanged = 0x00000400,
+ /* was ScalingModeChanged, now available 0x00000400, */
eShadowRadiusChanged = 0x00000800,
eReparentChildren = 0x00001000,
eDetachChildren = 0x00002000,
@@ -127,6 +128,8 @@
eProducerDisconnect = 0x100'00000000,
eFixedTransformHintChanged = 0x200'00000000,
eFrameNumberChanged = 0x400'00000000,
+ eFrameTimelineVsyncChanged = 0x800'00000000,
+ eBlurRegionsChanged = 0x1000'00000000,
};
layer_state_t();
@@ -163,7 +166,6 @@
sp<SurfaceControl> barrierSurfaceControl_legacy;
sp<SurfaceControl> reparentSurfaceControl;
uint64_t barrierFrameNumber;
- int32_t overrideScalingMode;
sp<SurfaceControl> relativeLayerSurfaceControl;
@@ -186,6 +188,7 @@
int32_t api;
sp<NativeHandle> sidebandStream;
mat4 colorTransform;
+ std::vector<BlurRegion> blurRegions;
#ifndef NO_INPUT
sp<InputWindowHandle> inputHandle = new InputWindowHandle();
@@ -228,6 +231,8 @@
// Used by BlastBufferQueue to forward the framenumber generated by the
// graphics producer.
uint64_t frameNumber;
+
+ int64_t frameTimelineVsyncId;
};
struct ComposerState {
@@ -319,6 +324,14 @@
// NOTE: In normal cases, we want the screen to be captured in display's colorspace.
ui::Dataspace dataspace = ui::Dataspace::UNKNOWN;
+ // The receiver of the capture can handle protected buffer. A protected buffer has
+ // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour.
+ // Any read/write access from unprotected context will result in undefined behaviour.
+ // Protected contents are typically DRM contents. This has no direct implication to the
+ // secure property of the surface, which is specified by the application explicitly to avoid
+ // the contents being accessed/captured by screenshot or unsecure display.
+ bool allowProtected = false;
+
virtual status_t write(Parcel& output) const;
virtual status_t read(const Parcel& input);
};
@@ -346,7 +359,7 @@
sp<GraphicBuffer> buffer;
bool capturedSecureLayers{false};
ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB};
- status_t result = NO_ERROR;
+ status_t result = OK;
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index a68f2e7..4aa076e 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -68,7 +68,6 @@
: public ANativeObjectBase<ANativeWindow, Surface, RefBase>
{
public:
-
/*
* creates a Surface from the given IGraphicBufferProducer (which concrete
* implementation is a BufferQueue).
@@ -83,9 +82,15 @@
*
* the controlledByApp flag indicates that this Surface (producer) is
* controlled by the application. This flag is used at connect time.
+ *
+ * Pass in the SurfaceControlHandle to store a weak reference to the layer
+ * that the Surface was created from. This handle can be used to create a
+ * child surface without using the IGBP to identify the layer. This is used
+ * for surfaces created by the BlastBufferQueue whose IGBP is created on the
+ * client and cannot be verified in SF.
*/
- explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer,
- bool controlledByApp = false);
+ explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false,
+ const sp<IBinder>& surfaceControlHandle = nullptr);
/* getIGraphicBufferProducer() returns the IGraphicBufferProducer this
* Surface was created with. Usually it's an error to use the
@@ -93,6 +98,8 @@
*/
sp<IGraphicBufferProducer> getIGraphicBufferProducer() const;
+ sp<IBinder> getSurfaceControlHandle() const { return mSurfaceControlHandle; }
+
/* convenience function to check that the given surface is non NULL as
* well as its IGraphicBufferProducer */
static bool isValid(const sp<Surface>& surface) {
@@ -120,7 +127,7 @@
* delay during dequeueBuffer. If there are already the maximum number of
* buffers allocated, this function has no effect.
*/
- void allocateBuffers();
+ virtual void allocateBuffers();
/* Sets the generation number on the IGraphicBufferProducer and updates the
* generation number on any buffers attached to the Surface after this call.
@@ -179,7 +186,8 @@
status_t getUniqueId(uint64_t* outId) const;
status_t getConsumerUsage(uint64_t* outUsage) const;
- status_t setFrameRate(float frameRate, int8_t compatibility);
+ virtual status_t setFrameRate(float frameRate, int8_t compatibility);
+ virtual status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId);
protected:
virtual ~Surface();
@@ -540,6 +548,11 @@
bool mEnableFrameTimestamps = false;
std::unique_ptr<ProducerFrameEventHistory> mFrameEventHistory;
+ // Reference to the SurfaceFlinger layer that was used to create this
+ // surface. This is only populated when the Surface is created from
+ // a BlastBufferQueue.
+ sp<IBinder> mSurfaceControlHandle;
+
bool mReportRemovedBuffers = false;
std::vector<sp<GraphicBuffer>> mRemovedBuffers;
int mMaxBufferCount;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 277060f..73909a3 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -29,6 +29,7 @@
#include <utils/SortedVector.h>
#include <utils/threads.h>
+#include <ui/BlurRegion.h>
#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
@@ -120,13 +121,15 @@
// Sets the refresh rate boundaries for the display.
static status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultConfig, float primaryRefreshRateMin,
+ int32_t defaultConfig, bool allowGroupSwitching,
+ float primaryRefreshRateMin,
float primaryRefreshRateMax,
float appRequestRefreshRateMin,
float appRequestRefreshRateMax);
// Gets the refresh rate boundaries for the display.
static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
int32_t* outDefaultConfig,
+ bool* outAllowGroupSwitching,
float* outPrimaryRefreshRateMin,
float* outPrimaryRefreshRateMax,
float* outAppRequestRefreshRateMin,
@@ -253,13 +256,13 @@
static sp<SurfaceComposerClient> getDefault();
//! Create a surface
- sp<SurfaceControl> createSurface(const String8& name, // name of the surface
- uint32_t w, // width in pixel
- uint32_t h, // height in pixel
- PixelFormat format, // pixel-format desired
- uint32_t flags = 0, // usage flags
- SurfaceControl* parent = nullptr, // parent
- LayerMetadata metadata = LayerMetadata(), // metadata
+ sp<SurfaceControl> createSurface(const String8& name, // name of the surface
+ uint32_t w, // width in pixel
+ uint32_t h, // height in pixel
+ PixelFormat format, // pixel-format desired
+ uint32_t flags = 0, // usage flags
+ const sp<IBinder>& parentHandle = nullptr, // parentHandle
+ LayerMetadata metadata = LayerMetadata(), // metadata
uint32_t* outTransformHint = nullptr);
status_t createSurfaceChecked(const String8& name, // name of the surface
@@ -267,9 +270,9 @@
uint32_t h, // height in pixel
PixelFormat format, // pixel-format desired
sp<SurfaceControl>* outSurface,
- uint32_t flags = 0, // usage flags
- SurfaceControl* parent = nullptr, // parent
- LayerMetadata metadata = LayerMetadata(), // metadata
+ uint32_t flags = 0, // usage flags
+ const sp<IBinder>& parentHandle = nullptr, // parentHandle
+ LayerMetadata metadata = LayerMetadata(), // metadata
uint32_t* outTransformHint = nullptr);
//! Create a surface
@@ -437,6 +440,8 @@
Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
int backgroundBlurRadius);
+ Transaction& setBlurRegions(const sp<SurfaceControl>& sc,
+ const std::vector<BlurRegion>& regions);
Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p);
// Defers applying any changes made in this transaction until the Layer
@@ -501,11 +506,6 @@
// Sometimes the WindowManager needs to extend their lifetime slightly
// in order to perform an exit animation or prevent flicker.
Transaction& detachChildren(const sp<SurfaceControl>& sc);
- // Set an override scaling mode as documented in <system/window.h>
- // the override scaling mode will take precedence over any client
- // specified scaling mode. -1 will clear the override scaling mode.
- Transaction& setOverrideScalingMode(const sp<SurfaceControl>& sc,
- int32_t overrideScalingMode);
#ifndef NO_INPUT
Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info);
@@ -537,6 +537,9 @@
// Sets the frame timeline vsync id received from choreographer that corresponds
// to the transaction.
Transaction& setFrameTimelineVsync(int64_t frameTimelineVsyncId);
+ // Variant that only applies to a specific SurfaceControl.
+ Transaction& setFrameTimelineVsync(const sp<SurfaceControl>& sc,
+ int64_t frameTimelineVsyncId);
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h
index cc64fd4..f7dcbc6 100644
--- a/libs/gui/include/gui/view/Surface.h
+++ b/libs/gui/include/gui/view/Surface.h
@@ -21,6 +21,7 @@
#include <utils/StrongPointer.h>
#include <utils/String16.h>
+#include <binder/IBinder.h>
#include <binder/Parcelable.h>
namespace android {
@@ -43,6 +44,7 @@
String16 name;
sp<IGraphicBufferProducer> graphicBufferProducer;
+ sp<IBinder> surfaceControlHandle;
virtual status_t writeToParcel(Parcel* parcel) const override;
virtual status_t readFromParcel(const Parcel* parcel) override;
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 9299721..f3559fa 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -123,6 +123,7 @@
.apply();
mCaptureArgs.displayToken = mDisplayToken;
+ mCaptureArgs.dataspace = ui::Dataspace::V0_SRGB;
}
void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
@@ -181,6 +182,7 @@
for (uint32_t row = 0; row < height; row++) {
for (uint32_t col = 0; col < width; col++) {
uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
+ ASSERT_NE(nullptr, pixel);
bool inRegion;
if (!outsideRegion) {
inRegion = row >= region.top + border && row < region.bottom - border &&
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 483f171..c39b0b5 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -153,6 +153,24 @@
EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
}
+ void expectTapWithFlag(int x, int y, int32_t flags) {
+ InputEvent *ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
+ MotionEvent *mev = static_cast<MotionEvent *>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
+ EXPECT_EQ(x, mev->getX(0));
+ EXPECT_EQ(y, mev->getY(0));
+ EXPECT_EQ(flags, mev->getFlags() & flags);
+
+ ev = consumeEvent();
+ ASSERT_NE(ev, nullptr);
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
+ mev = static_cast<MotionEvent *>(ev);
+ EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
+ EXPECT_EQ(flags, mev->getFlags() & flags);
+ }
+
~InputSurface() { mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken()); }
void doTransaction(std::function<void(SurfaceComposerClient::Transaction&,
@@ -602,4 +620,68 @@
surface->expectTap(5, 10);
}
+TEST_F(InputSurfacesTest, touch_flag_obscured) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ // Add non touchable window to fully cover touchable window. Window behind gets touch, but
+ // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED
+ std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+ nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ nonTouchableSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->showAt(100, 100);
+
+ injectTap(190, 199);
+ surface->expectTapWithFlag(90, 99, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED);
+}
+
+TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ // Add non touchable window to cover touchable window, but parent is cropped to not cover area
+ // that will be tapped. Window behind gets touch, but with flag
+ // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+ nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ nonTouchableSurface->mInputInfo.ownerUid = 22222;
+ parentSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->showAt(0, 0);
+ parentSurface->showAt(100, 100);
+
+ nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
+ t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+ t.reparent(sc, parentSurface->mSurfaceControl);
+ });
+
+ injectTap(190, 199);
+ surface->expectTapWithFlag(90, 99, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+}
+
+TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+ surface->showAt(100, 100);
+
+ // Add non touchable window to cover touchable window, but parent is cropped to avoid covering
+ // the touchable window. Window behind gets touch with no obscured flags.
+ std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
+ std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100);
+ nonTouchableSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ parentSurface->mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCHABLE;
+ nonTouchableSurface->mInputInfo.ownerUid = 22222;
+ parentSurface->mInputInfo.ownerUid = 22222;
+ nonTouchableSurface->showAt(0, 0);
+ parentSurface->showAt(50, 50);
+
+ nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
+ t.setCrop_legacy(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
+ t.reparent(sc, parentSurface->mSurfaceControl);
+ });
+
+ injectTap(101, 110);
+ surface->expectTap(1, 10);
+}
+
} // namespace android::test
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 5a376da..0cd3962 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -844,7 +844,7 @@
return NO_ERROR;
}
status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
- int32_t /*defaultConfig*/,
+ int32_t /*defaultConfig*/, bool /*allowGroupSwitching*/,
float /*primaryRefreshRateMin*/,
float /*primaryRefreshRateMax*/,
float /*appRequestRefreshRateMin*/,
@@ -853,6 +853,7 @@
}
status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
int32_t* /*outDefaultConfig*/,
+ bool* /*outAllowGroupSwitching*/,
float* /*outPrimaryRefreshRateMin*/,
float* /*outPrimaryRefreshRateMax*/,
float* /*outAppRequestRefreshRateMin*/,
diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp
index d64dfd5..1bfe462 100644
--- a/libs/gui/view/Surface.cpp
+++ b/libs/gui/view/Surface.cpp
@@ -45,7 +45,9 @@
if (res != OK) return res;
}
- return IGraphicBufferProducer::exportToParcel(graphicBufferProducer, parcel);
+ res = IGraphicBufferProducer::exportToParcel(graphicBufferProducer, parcel);
+ if (res != OK) return res;
+ return parcel->writeStrongBinder(surfaceControlHandle);
}
status_t Surface::readFromParcel(const Parcel* parcel) {
@@ -68,17 +70,14 @@
}
graphicBufferProducer = IGraphicBufferProducer::createFromParcel(parcel);
+ surfaceControlHandle = parcel->readStrongBinder();
return OK;
}
String16 Surface::readMaybeEmptyString16(const Parcel* parcel) {
- size_t len;
- const char16_t* str = parcel->readString16Inplace(&len);
- if (str != nullptr) {
- return String16(str, len);
- } else {
- return String16();
- }
+ std::optional<String16> str;
+ parcel->readString16(&str);
+ return str.value_or(String16());
}
} // namespace view
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index d8c3e6f..fce3000 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -40,8 +40,11 @@
"Keyboard.cpp",
"KeyCharacterMap.cpp",
"KeyLayoutMap.cpp",
+ "LatencyStatistics.cpp",
"PropertyMap.cpp",
"TouchVideoFrame.cpp",
+ "VelocityControl.cpp",
+ "VelocityTracker.cpp",
"VirtualKeyMap.cpp",
],
@@ -69,9 +72,6 @@
srcs: [
"InputTransport.cpp",
"InputWindow.cpp",
- "LatencyStatistics.cpp",
- "VelocityControl.cpp",
- "VelocityTracker.cpp",
"android/FocusRequest.aidl",
"android/InputApplicationInfo.aidl",
"android/os/IInputConstants.aidl",
@@ -99,12 +99,33 @@
shared: {
enabled: false,
},
+ include_dirs: [
+ "frameworks/native/libs/arect/include",
+ ],
+ },
+ linux_glibc: {
+ srcs: [
+ "InputTransport.cpp",
+ "InputWindow.cpp",
+ "android/FocusRequest.aidl",
+ "android/InputApplicationInfo.aidl",
+ "android/os/IInputConstants.aidl",
+ "android/os/IInputFlinger.aidl",
+ "android/os/ISetInputWindowsListener.aidl",
+ "android/os/TouchOcclusionMode.aidl",
+ ],
+ static_libs: [
+ "libhostgraphics",
+ ],
+ shared_libs: [
+ "libbinder",
+ ],
},
},
aidl: {
local_include_dirs: ["."],
- export_aidl_headers: true
+ export_aidl_headers: true,
},
}
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index fb2f186..0ea3889 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -19,18 +19,24 @@
#include <attestation/HmacKeyManager.h>
#include <cutils/compiler.h>
+#include <inttypes.h>
#include <limits.h>
#include <string.h>
+#include <android-base/stringprintf.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
-#ifdef __ANDROID__
+#ifdef __linux__
#include <binder/Parcel.h>
+#endif
+#ifdef __ANDROID__
#include <sys/random.h>
#endif
+using android::base::StringPrintf;
+
namespace android {
const char* motionClassificationToString(MotionClassification classification) {
@@ -250,7 +256,7 @@
setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset);
}
-#ifdef __ANDROID__
+#ifdef __linux__
status_t PointerCoords::readFromParcel(Parcel* parcel) {
bits = parcel->readInt64();
@@ -536,7 +542,7 @@
}
}
-#ifdef __ANDROID__
+#ifdef __linux__
static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) {
float dsdx, dtdx, tx, dtdy, dsdy, ty;
status_t status = parcel.readFloat(&dsdx);
@@ -700,23 +706,37 @@
return InputEventLookup::getAxisByLabel(label);
}
-const char* MotionEvent::actionToString(int32_t action) {
+std::string MotionEvent::actionToString(int32_t action) {
// Convert MotionEvent action to string
switch (action & AMOTION_EVENT_ACTION_MASK) {
case AMOTION_EVENT_ACTION_DOWN:
return "DOWN";
- case AMOTION_EVENT_ACTION_MOVE:
- return "MOVE";
case AMOTION_EVENT_ACTION_UP:
return "UP";
+ case AMOTION_EVENT_ACTION_MOVE:
+ return "MOVE";
case AMOTION_EVENT_ACTION_CANCEL:
return "CANCEL";
+ case AMOTION_EVENT_ACTION_OUTSIDE:
+ return "OUTSIDE";
case AMOTION_EVENT_ACTION_POINTER_DOWN:
return "POINTER_DOWN";
case AMOTION_EVENT_ACTION_POINTER_UP:
return "POINTER_UP";
+ case AMOTION_EVENT_ACTION_HOVER_MOVE:
+ return "HOVER_MOVE";
+ case AMOTION_EVENT_ACTION_SCROLL:
+ return "SCROLL";
+ case AMOTION_EVENT_ACTION_HOVER_ENTER:
+ return "HOVER_ENTER";
+ case AMOTION_EVENT_ACTION_HOVER_EXIT:
+ return "HOVER_EXIT";
+ case AMOTION_EVENT_ACTION_BUTTON_PRESS:
+ return "BUTTON_PRESS";
+ case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
+ return "BUTTON_RELEASE";
}
- return "UNKNOWN";
+ return android::base::StringPrintf("%" PRId32, action);
}
// --- FocusEvent ---
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 7ac8a2e..f5432ad 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -19,10 +19,9 @@
#include <stdlib.h>
#include <string.h>
-#ifdef __ANDROID__
+#ifdef __linux__
#include <binder/Parcel.h>
#endif
-
#include <android/keycodes.h>
#include <attestation/HmacKeyManager.h>
#include <input/InputEventLabels.h>
@@ -86,9 +85,7 @@
// --- KeyCharacterMap ---
-KeyCharacterMap::KeyCharacterMap() :
- mType(KEYBOARD_TYPE_UNKNOWN) {
-}
+KeyCharacterMap::KeyCharacterMap() : mType(KeyboardType::UNKNOWN) {}
KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other)
: mType(other.mType),
@@ -187,7 +184,7 @@
mLoadFileName = overlay.mLoadFileName;
}
-int32_t KeyCharacterMap::getKeyboardType() const {
+KeyCharacterMap::KeyboardType KeyCharacterMap::getKeyboardType() const {
return mType;
}
@@ -590,14 +587,14 @@
}
}
-#ifdef __ANDROID__
+#ifdef __linux__
std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
if (parcel == nullptr) {
ALOGE("%s: Null parcel", __func__);
return nullptr;
}
std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
- map->mType = parcel->readInt32();
+ map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32());
size_t numKeys = parcel->readInt32();
if (parcel->errorCheck()) {
return nullptr;
@@ -655,7 +652,7 @@
ALOGE("%s: Null parcel", __func__);
return;
}
- parcel->writeInt32(mType);
+ parcel->writeInt32(static_cast<int32_t>(mType));
size_t numKeys = mKeys.size();
parcel->writeInt32(numKeys);
@@ -676,8 +673,7 @@
parcel->writeInt32(0);
}
}
-#endif
-
+#endif // __linux__
// --- KeyCharacterMap::Key ---
@@ -781,20 +777,20 @@
return BAD_VALUE;
}
- if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
+ if (mMap->mType == KeyboardType::UNKNOWN) {
ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.",
mTokenizer->getLocation().string());
return BAD_VALUE;
}
- if (mFormat == FORMAT_BASE) {
- if (mMap->mType == KEYBOARD_TYPE_OVERLAY) {
+ if (mFormat == Format::BASE) {
+ if (mMap->mType == KeyboardType::OVERLAY) {
ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
mTokenizer->getLocation().string());
return BAD_VALUE;
}
- } else if (mFormat == FORMAT_OVERLAY) {
- if (mMap->mType != KEYBOARD_TYPE_OVERLAY) {
+ } else if (mFormat == Format::OVERLAY) {
+ if (mMap->mType != KeyboardType::OVERLAY) {
ALOGE("%s: Overlay keyboard layout missing required keyboard "
"'type OVERLAY' declaration.",
mTokenizer->getLocation().string());
@@ -806,7 +802,7 @@
}
status_t KeyCharacterMap::Parser::parseType() {
- if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) {
+ if (mMap->mType != KeyboardType::UNKNOWN) {
ALOGE("%s: Duplicate keyboard 'type' declaration.",
mTokenizer->getLocation().string());
return BAD_VALUE;
@@ -815,20 +811,20 @@
KeyboardType type;
String8 typeToken = mTokenizer->nextToken(WHITESPACE);
if (typeToken == "NUMERIC") {
- type = KEYBOARD_TYPE_NUMERIC;
+ type = KeyboardType::NUMERIC;
} else if (typeToken == "PREDICTIVE") {
- type = KEYBOARD_TYPE_PREDICTIVE;
+ type = KeyboardType::PREDICTIVE;
} else if (typeToken == "ALPHA") {
- type = KEYBOARD_TYPE_ALPHA;
+ type = KeyboardType::ALPHA;
} else if (typeToken == "FULL") {
- type = KEYBOARD_TYPE_FULL;
+ type = KeyboardType::FULL;
} else if (typeToken == "SPECIAL_FUNCTION") {
ALOGW("The SPECIAL_FUNCTION type is now declared in the device's IDC file, please set "
"the property 'keyboard.specialFunction' to '1' there instead.");
// TODO: return BAD_VALUE here in Q
- type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
+ type = KeyboardType::SPECIAL_FUNCTION;
} else if (typeToken == "OVERLAY") {
- type = KEYBOARD_TYPE_OVERLAY;
+ type = KeyboardType::OVERLAY;
} else {
ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
typeToken.string());
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 38a68b3..14dc9e5 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -128,7 +128,7 @@
}
base::Result<std::shared_ptr<KeyCharacterMap>> ret =
- KeyCharacterMap::load(path, KeyCharacterMap::FORMAT_BASE);
+ KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE);
if (!ret) {
return ret.error().code();
}
@@ -159,9 +159,9 @@
bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier,
const PropertyMap* deviceConfiguration, const KeyMap* keyMap) {
// TODO: remove the third OR statement (SPECIAL_FUNCTION) in Q
- if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration)
- || keyMap->keyCharacterMap->getKeyboardType()
- == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) {
+ if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration) ||
+ keyMap->keyCharacterMap->getKeyboardType() ==
+ KeyCharacterMap::KeyboardType::SPECIAL_FUNCTION) {
return false;
}
diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING
new file mode 100644
index 0000000..9626d8d
--- /dev/null
+++ b/libs/input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/native/services/inputflinger"
+ }
+ ]
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 44147a5..b23aade 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -30,7 +30,8 @@
"liblog",
"libui",
"libutils",
- ]
+ ],
+ test_suites: ["device-tests"],
}
// NOTE: This is a compile time test, and does not need to be
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 1452745..4f53dc9 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -54,8 +54,12 @@
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
+ ASSERT_NE(nullptr, mPublisher->getChannel());
+ ASSERT_NE(nullptr, mConsumer->getChannel());
EXPECT_EQ(mServerChannel.get(), mPublisher->getChannel().get());
EXPECT_EQ(mClientChannel.get(), mConsumer->getChannel().get());
+ ASSERT_EQ(mPublisher->getChannel()->getConnectionToken(),
+ mConsumer->getChannel()->getConnectionToken());
}
void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() {
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 470f28f..9e515cd 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -129,13 +129,14 @@
static Choreographer* getForThread();
virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
int64_t getVsyncId() const;
+ int64_t getFrameDeadline() const;
private:
Choreographer(const Choreographer&) = delete;
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
- int64_t vsyncId) override;
+ VsyncEventData vsyncEventData) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId,
nsecs_t vsyncPeriod) override;
@@ -149,7 +150,7 @@
std::vector<RefreshRateCallback> mRefreshRateCallbacks;
nsecs_t mLatestVsyncPeriod = -1;
- int64_t mLastVsyncId = -1;
+ VsyncEventData mLastVsyncEventData;
const sp<Looper> mLooper;
const std::thread::id mThreadId;
@@ -354,7 +355,8 @@
// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
// the internal display implicitly.
-void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, int64_t vsyncId) {
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
+ VsyncEventData vsyncEventData) {
std::vector<FrameCallback> callbacks{};
{
std::lock_guard<std::mutex> _l{mLock};
@@ -364,7 +366,7 @@
mFrameCallbacks.pop();
}
}
- mLastVsyncId = vsyncId;
+ mLastVsyncEventData = vsyncEventData;
for (const auto& cb : callbacks) {
if (cb.callback64 != nullptr) {
cb.callback64(timestamp, cb.data);
@@ -410,7 +412,11 @@
}
int64_t Choreographer::getVsyncId() const {
- return mLastVsyncId;
+ return mLastVsyncEventData.id;
+}
+
+int64_t Choreographer::getFrameDeadline() const {
+ return mLastVsyncEventData.deadlineTimestamp;
}
} // namespace android
@@ -492,6 +498,10 @@
return AChoreographer_to_Choreographer(choreographer)->getVsyncId();
}
+int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer) {
+ return AChoreographer_to_Choreographer(choreographer)->getFrameDeadline();
+}
+
} // namespace android
/* Glue for the NDK interface */
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
index 1d57c15..d3a4a66 100644
--- a/libs/nativedisplay/include-private/private/android/choreographer.h
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -35,6 +35,13 @@
// this function from anywhere else will return an undefined value.
int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer);
+// Returns the deadline timestamp (in CLOCK_MONOTONIC) of the last frame callback.
+// Client are expected to call this function from their frame callback function
+// to get the deadline and use it to know whether a frame is likely to miss
+// presentation. Calling this function from anywhere else will return an undefined
+// value.
+int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer);
+
// Trampoline functions allowing libandroid.so to define the NDK symbols without including
// the entirety of libnativedisplay as a whole static lib. As libnativedisplay
// maintains global state, libnativedisplay can never be directly statically
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 5ed2e49..fda6a20 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -30,6 +30,7 @@
android::AChoreographer_routeUnregisterRefreshRateCallback*;
android::AChoreographer_signalRefreshRateCallbacks*;
android::AChoreographer_getVsyncId*;
+ android::AChoreographer_getFrameDeadline*;
android::ADisplay_acquirePhysicalDisplays*;
android::ADisplay_release*;
android::ADisplay_getMaxSupportedFps*;
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 52d73e0..07e5d86 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -40,6 +40,7 @@
cc_library {
name: "libnativewindow",
+ llndk_stubs: "libnativewindow.llndk",
export_include_dirs: [
"include",
"include-private",
@@ -95,7 +96,7 @@
}
llndk_library {
- name: "libnativewindow",
+ name: "libnativewindow.llndk",
symbol_file: "libnativewindow.map.txt",
unversioned: true,
export_include_dirs: ["include"],
diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp
index b9cea10..245c9e1 100644
--- a/libs/renderengine/Description.cpp
+++ b/libs/renderengine/Description.cpp
@@ -52,5 +52,10 @@
return colorMatrix != identity;
}
+bool Description::hasDisplayColorMatrix() const {
+ const mat4 identity;
+ return displayColorMatrix != identity;
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 6adcbea..be83ebc 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -735,9 +735,9 @@
bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(),
buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
if (!created) {
- ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
- buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
- buffer->getPixelFormat());
+ ALOGE("Failed to create image. id=%" PRIx64 " size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+ buffer->getId(), buffer->getWidth(), buffer->getHeight(), buffer->getStride(),
+ buffer->getUsage(), buffer->getPixelFormat());
return NO_INIT;
}
@@ -1121,6 +1121,7 @@
setOutputDataSpace(display.outputDataspace);
setDisplayMaxLuminance(display.maxLuminance);
+ setDisplayColorTransform(display.colorTransform);
const mat4 projectionMatrix =
ui::Transform(display.orientation).asMatrix4() * mState.projectionMatrix;
@@ -1189,7 +1190,7 @@
position[3] = vec2(bounds.right, bounds.top);
setupLayerCropping(*layer, mesh);
- setColorTransform(display.colorTransform * layer->colorTransform);
+ setColorTransform(layer->colorTransform);
bool usePremultipliedAlpha = true;
bool disableTexture = true;
@@ -1218,6 +1219,11 @@
texCoords[2] = vec2(1.0, 1.0);
texCoords[3] = vec2(1.0, 0.0);
setupLayerTexturing(texture);
+
+ // Do not cache protected EGLImage, protected memory is limited.
+ if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) {
+ unbindExternalTextureBuffer(gBuf->getId());
+ }
}
const half3 solidColor = layer->source.solidColor;
@@ -1346,6 +1352,10 @@
mState.colorMatrix = colorTransform;
}
+void GLESRenderEngine::setDisplayColorTransform(const mat4& colorTransform) {
+ mState.displayColorMatrix = colorTransform;
+}
+
void GLESRenderEngine::disableTexturing() {
mState.textureEnabled = false;
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 1779994..c0449a1 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -168,6 +168,7 @@
void setupLayerTexturing(const Texture& texture);
void setupFillWithColor(float r, float g, float b, float a);
void setColorTransform(const mat4& colorTransform);
+ void setDisplayColorTransform(const mat4& colorTransform);
void disableTexturing();
void disableBlending();
void setupCornerRadiusCropSize(float width, float height);
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index f4fbf35..a172c56 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -66,6 +66,7 @@
mTextureMatrixLoc = glGetUniformLocation(programId, "texture");
mSamplerLoc = glGetUniformLocation(programId, "sampler");
mColorLoc = glGetUniformLocation(programId, "color");
+ mDisplayColorMatrixLoc = glGetUniformLocation(programId, "displayColorMatrix");
mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
@@ -129,6 +130,9 @@
const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a};
glUniform4fv(mColorLoc, 1, color);
}
+ if (mDisplayColorMatrixLoc >= 0) {
+ glUniformMatrix4fv(mDisplayColorMatrixLoc, 1, GL_FALSE, desc.displayColorMatrix.asArray());
+ }
if (mInputTransformMatrixLoc >= 0) {
mat4 inputTransformMatrix = desc.inputTransformMatrix;
glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index fc3755e..4292645 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -104,6 +104,7 @@
/* location of transform matrix */
GLint mInputTransformMatrixLoc;
GLint mOutputTransformMatrixLoc;
+ GLint mDisplayColorMatrixLoc;
/* location of corner radius uniform */
GLint mCornerRadiusLoc;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 3ae35ec..7fc0499 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -180,6 +180,9 @@
description.hasOutputTransformMatrix() || description.hasColorMatrix()
? Key::OUTPUT_TRANSFORM_MATRIX_ON
: Key::OUTPUT_TRANSFORM_MATRIX_OFF)
+ .set(Key::Key::DISPLAY_COLOR_TRANSFORM_MATRIX_MASK,
+ description.hasDisplayColorMatrix() ? Key::DISPLAY_COLOR_TRANSFORM_MATRIX_ON
+ : Key::DISPLAY_COLOR_TRANSFORM_MATRIX_OFF)
.set(Key::ROUNDED_CORNERS_MASK,
description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
.set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
@@ -661,7 +664,8 @@
)__SHADER__";
}
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
+ needs.hasDisplayColorMatrix()) {
if (needs.needsToneMapping()) {
fs << "uniform float displayMaxLuminance;";
fs << "uniform float maxMasteringLuminance;";
@@ -700,6 +704,21 @@
)__SHADER__";
}
+ if (needs.hasDisplayColorMatrix()) {
+ fs << "uniform mat4 displayColorMatrix;";
+ fs << R"__SHADER__(
+ highp vec3 DisplayColorMatrix(const highp vec3 color) {
+ return clamp(vec3(displayColorMatrix * vec4(color, 1.0)), 0.0, 1.0);
+ }
+ )__SHADER__";
+ } else {
+ fs << R"__SHADER__(
+ highp vec3 DisplayColorMatrix(const highp vec3 color) {
+ return color;
+ }
+ )__SHADER__";
+ }
+
generateEOTF(fs, needs);
generateOOTF(fs, needs);
generateOETF(fs, needs);
@@ -732,14 +751,17 @@
}
}
- if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF()) ||
+ needs.hasDisplayColorMatrix()) {
if (!needs.isOpaque() && needs.isPremultiplied()) {
// un-premultiply if needed before linearization
// avoid divide by 0 by adding 0.5/256 to the alpha channel
fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
}
fs << "gl_FragColor.rgb = "
- "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));";
+ "DisplayColorMatrix(OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))"
+ ")));";
+
if (!needs.isOpaque() && needs.isPremultiplied()) {
// and re-premultiply if needed after gamma correction
fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index 901e631..37bb651 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -117,6 +117,11 @@
SHADOW_MASK = 1 << SHADOW_SHIFT,
SHADOW_OFF = 0 << SHADOW_SHIFT,
SHADOW_ON = 1 << SHADOW_SHIFT,
+
+ DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT = 14,
+ DISPLAY_COLOR_TRANSFORM_MATRIX_MASK = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
+ DISPLAY_COLOR_TRANSFORM_MATRIX_OFF = 0 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
+ DISPLAY_COLOR_TRANSFORM_MATRIX_ON = 1 << DISPLAY_COLOR_TRANSFORM_MATRIX_SHIFT,
};
inline Key() : mKey(0) {}
@@ -143,6 +148,10 @@
inline bool hasOutputTransformMatrix() const {
return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON;
}
+ inline bool hasDisplayColorMatrix() const {
+ return (mKey & DISPLAY_COLOR_TRANSFORM_MATRIX_MASK) ==
+ DISPLAY_COLOR_TRANSFORM_MATRIX_ON;
+ }
inline bool hasTransformMatrix() const {
return hasInputTransformMatrix() || hasOutputTransformMatrix();
}
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 95e9367..d8d989e 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -21,6 +21,7 @@
#include <math/mat4.h>
#include <math/vec3.h>
#include <renderengine/Texture.h>
+#include <ui/BlurRegion.h>
#include <ui/Fence.h>
#include <ui/FloatRect.h>
#include <ui/GraphicBuffer.h>
@@ -151,6 +152,8 @@
ShadowSettings shadow;
int backgroundBlurRadius = 0;
+
+ std::vector<BlurRegion> blurRegions;
};
// Keep in sync with custom comparison function in
@@ -182,7 +185,29 @@
lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
}
+static inline bool operator==(const BlurRegion& lhs, const BlurRegion& rhs) {
+ return lhs.alpha == rhs.alpha && lhs.cornerRadiusTL == rhs.cornerRadiusTL &&
+ lhs.cornerRadiusTR == rhs.cornerRadiusTR && lhs.cornerRadiusBL == rhs.cornerRadiusBL &&
+ lhs.cornerRadiusBR == rhs.cornerRadiusBR && lhs.blurRadius == rhs.blurRadius &&
+ lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right &&
+ lhs.bottom == rhs.bottom;
+}
+
+static inline bool operator!=(const BlurRegion& lhs, const BlurRegion& rhs) {
+ return !(lhs == rhs);
+}
+
static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
+ if (lhs.blurRegions.size() != rhs.blurRegions.size()) {
+ return false;
+ }
+ const auto size = lhs.blurRegions.size();
+ for (size_t i = 0; i < size; i++) {
+ if (lhs.blurRegions[i] != rhs.blurRegions[i]) {
+ return false;
+ }
+ }
+
return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
lhs.sourceDataspace == rhs.sourceDataspace &&
lhs.colorTransform == rhs.colorTransform &&
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index a62161a..fa6ec10 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -44,6 +44,7 @@
bool hasInputTransformMatrix() const;
bool hasOutputTransformMatrix() const;
bool hasColorMatrix() const;
+ bool hasDisplayColorMatrix() const;
// whether textures are premultiplied
bool isPremultipliedAlpha = false;
@@ -79,6 +80,8 @@
// The color matrix will be applied in linear space right before OETF.
mat4 colorMatrix;
+ // The display color matrix will be applied in gamma space after OETF
+ mat4 displayColorMatrix;
mat4 inputTransformMatrix;
mat4 outputTransformMatrix;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 6bf14e2..902348b 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -26,6 +26,14 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
+#include <sync/sync.h>
+#include <ui/BlurRegion.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Trace.h>
+#include "../gl/GLExtensions.h"
+#include "SkiaGLRenderEngine.h"
+#include "filters/BlurFilter.h"
+
#include <GrContextOptions.h>
#include <SkCanvas.h>
#include <SkColorFilter.h>
@@ -530,10 +538,25 @@
SkPaint paint;
const auto& bounds = layer->geometry.boundaries;
const auto dest = getSkRect(bounds);
+ std::unordered_map<uint32_t, sk_sp<SkSurface>> cachedBlurs;
- if (layer->backgroundBlurRadius > 0) {
- ATRACE_NAME("BackgroundBlur");
- mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius);
+ if (mBlurFilter) {
+ if (layer->backgroundBlurRadius > 0) {
+ ATRACE_NAME("BackgroundBlur");
+ auto blurredSurface =
+ mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius);
+ cachedBlurs[layer->backgroundBlurRadius] = blurredSurface;
+ }
+ if (layer->blurRegions.size() > 0) {
+ for (auto region : layer->blurRegions) {
+ if (cachedBlurs[region.blurRadius]) {
+ continue;
+ }
+ ATRACE_NAME("BlurRegion");
+ auto blurredSurface = mBlurFilter->generate(canvas, surface, region.blurRadius);
+ cachedBlurs[region.blurRadius] = blurredSurface;
+ }
+ }
}
if (layer->source.buffer.buffer) {
@@ -573,6 +596,10 @@
}
auto texMatrix = getSkM44(item.textureTransform).asM33();
+
+ // b/171404534, scale to fix the layer
+ matrix.postScale(bounds.getWidth() / bufferWidth, bounds.getHeight() / bufferHeight);
+
// textureTansform was intended to be passed directly into a shader, so when
// building the total matrix with the textureTransform we need to first
// normalize it, then apply the textureTransform, then scale back up.
@@ -622,7 +649,11 @@
} else {
ATRACE_NAME("DrawColor");
const auto color = layer->source.solidColor;
- paint.setColor(SkColor4f{.fR = color.r, .fG = color.g, .fB = color.b, layer->alpha});
+ paint.setShader(SkShaders::Color(SkColor4f{.fR = color.r,
+ .fG = color.g,
+ .fB = color.b,
+ layer->alpha},
+ nullptr));
}
paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)));
@@ -631,6 +662,10 @@
canvas->save();
canvas->concat(getSkM44(layer->geometry.positionTransform));
+ for (const auto effectRegion : layer->blurRegions) {
+ drawBlurRegion(canvas, effectRegion, dest, cachedBlurs[effectRegion.blurRadius]);
+ }
+
if (layer->shadow.length > 0) {
const auto rect = layer->geometry.roundedCornersRadius > 0
? getSkRect(layer->geometry.roundedCornersCrop)
@@ -643,6 +678,7 @@
} else {
canvas->drawRect(dest, paint);
}
+
canvas->restore();
}
{
@@ -729,6 +765,34 @@
flags);
}
+void SkiaGLRenderEngine::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion,
+ const SkRect& layerBoundaries,
+ sk_sp<SkSurface> blurredSurface) {
+ ATRACE_CALL();
+ SkPaint paint;
+ paint.setAlpha(static_cast<int>(effectRegion.alpha * 255));
+ const auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right,
+ effectRegion.bottom);
+
+ const auto matrix = mBlurFilter->getShaderMatrix(
+ SkMatrix::MakeTrans(layerBoundaries.left(), layerBoundaries.top()));
+ paint.setShader(blurredSurface->makeImageSnapshot()->makeShader(matrix));
+
+ if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 ||
+ effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) {
+ const SkVector radii[4] =
+ {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL),
+ SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR),
+ SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL),
+ SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)};
+ SkRRect roundedRect;
+ roundedRect.setRectRadii(rect, radii);
+ canvas->drawRRect(roundedRect, paint);
+ } else {
+ canvas->drawRect(rect, paint);
+ }
+}
+
EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config,
EGLContext shareContext, bool useContextPriority,
Protection protection) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index ed4ba11..0143445 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -75,6 +75,8 @@
bool waitFence(base::unique_fd fenceFd);
void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
const ShadowSettings& shadowSettings);
+ void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerBounds,
+ sk_sp<SkSurface> blurrendSurface);
EGLDisplay mEGLDisplay;
EGLConfig mEGLConfig;
diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index eb791a7..f6a316f 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -55,8 +55,10 @@
mBlurEffect = std::move(blurEffect);
}
-void BlurFilter::draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t blurRadius) const {
+sk_sp<SkSurface> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
+ const uint32_t blurRadius) const {
ATRACE_CALL();
+
// Kawase is an approximation of Gaussian, but it behaves differently from it.
// A radius transformation is required for approximating them, and also to introduce
// non-integer steps, necessary to smoothly interpolate large radii.
@@ -72,8 +74,10 @@
const float stepY = radiusByPasses;
// start by drawing and downscaling and doing the first blur pass
+ SkFilterOptions linear = {SkSamplingMode::kLinear, SkMipmapMode::kNone};
SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
- blurBuilder.child("input") = input->makeImageSnapshot()->makeShader();
+ blurBuilder.child("input") =
+ input->makeImageSnapshot()->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
blurBuilder.uniform("in_inverseScale") = kInverseInputScale;
blurBuilder.uniform("in_blurOffset") =
SkV2{stepX * kInverseInputScale, stepY * kInverseInputScale};
@@ -97,7 +101,9 @@
for (auto i = 1; i < numberOfPasses; i++) {
const float stepScale = (float)i * kInputScale;
- blurBuilder.child("input") = readSurface->makeImageSnapshot()->makeShader();
+ blurBuilder.child("input") =
+ readSurface->makeImageSnapshot()->makeShader(SkTileMode::kClamp,
+ SkTileMode::kClamp, linear);
blurBuilder.uniform("in_inverseScale") = 1.0f;
blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
@@ -107,25 +113,35 @@
drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint);
// Swap buffers for next iteration
- auto tmp = drawSurface;
+ const auto tmp = drawSurface;
drawSurface = readSurface;
readSurface = tmp;
blurBuilder.child("input") = nullptr;
}
lastDrawTarget = readSurface;
}
+ lastDrawTarget->flushAndSubmit();
+ return lastDrawTarget;
+}
- drawSurface->flushAndSubmit();
+sk_sp<SkSurface> BlurFilter::draw(SkCanvas* canvas, const sk_sp<SkSurface> input,
+ const uint32_t blurRadius) const {
+ ATRACE_CALL();
+ auto surface = generate(canvas, input, blurRadius);
- // do the final composition, with alpha blending to hide downscaling artifacts.
- {
- SkPaint paint;
- paint.setShader(lastDrawTarget->makeImageSnapshot()->makeShader(
- SkMatrix::MakeScale(kInverseInputScale)));
- paint.setFilterQuality(kLow_SkFilterQuality);
- paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255);
- canvas->drawIRect(SkIRect::MakeWH(input->width(), input->height()), paint);
- }
+ SkPaint paint;
+ const auto image = surface->makeImageSnapshot();
+ paint.setShader(image->makeShader(SkMatrix::MakeScale(kInverseInputScale)));
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255);
+ canvas->drawIRect(SkIRect::MakeWH(input->width(), input->height()), paint);
+ return surface;
+}
+
+SkMatrix BlurFilter::getShaderMatrix(const SkMatrix& transformMatrix) const {
+ SkMatrix matrix;
+ matrix.setConcat(transformMatrix, SkMatrix::MakeScale(kInverseInputScale));
+ return matrix;
}
} // namespace skia
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 94b3673..6f973d7 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -47,8 +47,14 @@
explicit BlurFilter();
virtual ~BlurFilter(){};
- // Execute blur passes, rendering to a canvas.
- void draw(SkCanvas* canvas, sk_sp<SkSurface> input, const uint32_t radius) const;
+ // Execute blur, saving it to a texture
+ sk_sp<SkSurface> generate(SkCanvas* canvas, const sk_sp<SkSurface> input,
+ const uint32_t radius) const;
+ // Same as generate but also drawing to the screen
+ sk_sp<SkSurface> draw(SkCanvas* canvas, const sk_sp<SkSurface> input,
+ const uint32_t radius) const;
+ // Returns a matrix that should be applied to the blur shader
+ SkMatrix getShaderMatrix(const SkMatrix& transformMatrix) const;
private:
sk_sp<SkRuntimeEffect> mBlurEffect;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index d795616..d20fcc4 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -39,7 +39,7 @@
struct RenderEngineTest : public ::testing::Test {
static void SetUpTestSuite() {
- sRE = renderengine::gl::GLESRenderEngine::create(
+ renderengine::RenderEngineCreationArgs reCreationArgs =
renderengine::RenderEngineCreationArgs::Builder()
.setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
.setImageCacheSize(1)
@@ -49,13 +49,18 @@
.setSupportsBackgroundBlur(true)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
.setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
- .build());
+ .build();
+ sRE = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
+
+ reCreationArgs.useColorManagement = true;
+ sRECM = renderengine::gl::GLESRenderEngine::create(reCreationArgs);
}
static void TearDownTestSuite() {
// The ordering here is important - sCurrentBuffer must live longer
// than RenderEngine to avoid a null reference on tear-down.
sRE = nullptr;
+ sRECM = nullptr;
sCurrentBuffer = nullptr;
}
@@ -85,6 +90,9 @@
sRE->deleteTextures(1, &texName);
EXPECT_FALSE(sRE->isTextureNameKnownForTesting(texName));
}
+ for (uint32_t texName : mTexNamesCM) {
+ sRECM->deleteTextures(1, &texName);
+ }
}
void writeBufferToFile(const char* basename) {
@@ -253,10 +261,11 @@
void invokeDraw(renderengine::DisplaySettings settings,
std::vector<const renderengine::LayerSettings*> layers,
- sp<GraphicBuffer> buffer) {
+ sp<GraphicBuffer> buffer, bool useColorManagement = false) {
base::unique_fd fence;
- status_t status =
- sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
+ status_t status = useColorManagement
+ ? sRECM->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence)
+ : sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence);
sCurrentBuffer = buffer;
int fd = fence.release();
@@ -267,7 +276,11 @@
ASSERT_EQ(NO_ERROR, status);
if (layers.size() > 0) {
- ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+ if (useColorManagement) {
+ ASSERT_TRUE(sRECM->isFramebufferImageCachedForTesting(buffer->getId()));
+ } else {
+ ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+ }
}
}
@@ -322,12 +335,15 @@
void fillBufferLayerTransform();
template <typename SourceVariant>
- void fillBufferWithColorTransform();
+ void fillBufferWithColorTransform(bool useColorManagement = false);
template <typename SourceVariant>
void fillBufferColorTransform();
template <typename SourceVariant>
+ void fillBufferColorTransformCM();
+
+ template <typename SourceVariant>
void fillRedBufferWithRoundedCorners();
template <typename SourceVariant>
@@ -366,6 +382,8 @@
// For now, exercise the GL backend directly so that some caching specifics
// can be tested without changing the interface.
static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
+ // renderengine object with Color Management enabled
+ static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRECM;
// Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
// be freed *after* RenderEngine is destroyed, so that the EGL image is
// destroyed first.
@@ -374,14 +392,17 @@
sp<GraphicBuffer> mBuffer;
std::vector<uint32_t> mTexNames;
+ std::vector<uint32_t> mTexNamesCM;
};
std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
+std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRECM = nullptr;
+
sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;
struct ColorSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
- RenderEngineTest* /*fixture*/) {
+ RenderEngineTest* /*fixture*/, bool /*useColorManagement*/ = false) {
layer.source.solidColor = half3(r, g, b);
}
};
@@ -409,11 +430,16 @@
template <typename OpaquenessVariant>
struct BufferSourceVariant {
static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
- RenderEngineTest* fixture) {
+ RenderEngineTest* fixture, bool useColorManagement = false) {
sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
uint32_t texName;
- fixture->sRE->genTextures(1, &texName);
- fixture->mTexNames.push_back(texName);
+ if (useColorManagement) {
+ fixture->sRECM->genTextures(1, &texName);
+ fixture->mTexNamesCM.push_back(texName);
+ } else {
+ fixture->sRE->genTextures(1, &texName);
+ fixture->mTexNames.push_back(texName);
+ }
uint8_t* pixels;
buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -643,7 +669,7 @@
}
template <typename SourceVariant>
-void RenderEngineTest::fillBufferWithColorTransform() {
+void RenderEngineTest::fillBufferWithColorTransform(bool useColorManagement) {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
settings.clip = Rect(1, 1);
@@ -652,12 +678,12 @@
renderengine::LayerSettings layer;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
- SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this, useColorManagement);
layer.alpha = 1.0f;
// construct a fake color matrix
// annihilate green and blue channels
- settings.colorTransform = mat4::scale(vec4(1, 0, 0, 1));
+ settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
// set red channel to red + green
layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
@@ -666,13 +692,19 @@
layers.push_back(&layer);
- invokeDraw(settings, layers, mBuffer);
+ invokeDraw(settings, layers, mBuffer, useColorManagement);
}
template <typename SourceVariant>
void RenderEngineTest::fillBufferColorTransform() {
fillBufferWithColorTransform<SourceVariant>();
- expectBufferColor(fullscreenRect(), 191, 0, 0, 255);
+ expectBufferColor(fullscreenRect(), 172, 0, 0, 255, 1);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformCM() {
+ fillBufferWithColorTransform<SourceVariant>(true);
+ expectBufferColor(fullscreenRect(), 126, 0, 0, 255, 1);
}
template <typename SourceVariant>
@@ -1073,7 +1105,11 @@
}
TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) {
- fillBufferLayerTransform<ColorSourceVariant>();
+ fillBufferColorTransform<ColorSourceVariant>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_colorSource) {
+ fillBufferColorTransformCM<ColorSourceVariant>();
}
TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
@@ -1129,7 +1165,11 @@
}
TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) {
- fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+ fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_opaqueBufferSource) {
+ fillBufferColorTransformCM<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
@@ -1185,7 +1225,11 @@
}
TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) {
- fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+ fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransformCM_bufferSource) {
+ fillBufferColorTransformCM<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 3fa2e53..7c68aaa 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -140,7 +140,7 @@
"android.hardware.graphics.allocator@2.0",
"android.hardware.graphics.allocator@3.0",
"android.hardware.graphics.allocator@4.0",
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.common@1.2",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@2.1",
@@ -157,7 +157,7 @@
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.mapper@4.0",
"libgralloctypes",
],
@@ -243,6 +243,8 @@
name: "libui_host_common",
srcs: [
"Rect.cpp",
- "PixelFormat.cpp"
+ "Region.cpp",
+ "PixelFormat.cpp",
+ "Transform.cpp"
],
}
diff --git a/libs/binder/fuzzer/binder.h b/libs/ui/include/ui/BlurRegion.h
similarity index 61%
copy from libs/binder/fuzzer/binder.h
copy to libs/ui/include/ui/BlurRegion.h
index b224ef4..c5a5d47 100644
--- a/libs/binder/fuzzer/binder.h
+++ b/libs/ui/include/ui/BlurRegion.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 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.
@@ -14,9 +14,23 @@
* limitations under the License.
*/
-#include <binder/Parcel.h>
-#include <vector>
+#pragma once
-#include "parcel_fuzzer.h"
+#include <inttypes.h>
-extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
+namespace android {
+
+struct BlurRegion {
+ uint32_t blurRadius;
+ float cornerRadiusTL;
+ float cornerRadiusTR;
+ float cornerRadiusBL;
+ float cornerRadiusBR;
+ float alpha;
+ int left;
+ int top;
+ int right;
+ int bottom;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
index afde5f7..f5c5a5a 100644
--- a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
+++ b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp
@@ -52,7 +52,7 @@
// Fuzzer for Serialization operations, this is mostly just lifted from the
// existing test cases to use fuzzed values as inputs.
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+void FuzzSerializeDeserialize(const uint8_t* data, size_t size) {
FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
Payload result;
@@ -106,6 +106,183 @@
Deserialize(&vec_val, &result);
Serialize(t1_val, &result);
Deserialize(&t1_val, &result);
+}
+
+void FuzzDeserializeUint8(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_UINT8, fdp.ConsumeIntegral<uint8_t>()};
+ std::uint8_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint16(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_UINT16, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::uint16_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint32(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_UINT32, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::uint32_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeUint64(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {
+ ENCODING_TYPE_UINT64, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::uint64_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt8(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT8, fdp.ConsumeIntegral<uint8_t>()};
+ std::int8_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt16(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT16, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::int16_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt32(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT32, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::int32_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeInt64(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT64,
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ std::int64_t result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFloat32(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_FLOAT32, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ float floatResult;
+ Deserialize(&floatResult, &buffer);
+
+ buffer.Rewind();
+ double doubleResult;
+ Deserialize(&doubleResult, &buffer);
+}
+
+void FuzzDeserializeFloat64(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {
+ ENCODING_TYPE_FLOAT64, fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(), fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ double result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFixstr(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ std::string s_val = fdp.ConsumeRemainingBytesAsString();
+ Payload buffer = {ENCODING_TYPE_FIXSTR_MAX};
+ for (std::string::iterator iter = s_val.begin(); iter != s_val.end();
+ iter++) {
+ buffer.Append(1, *iter);
+ }
+ std::string result;
+ Deserialize(&result, &buffer);
+}
+
+void FuzzDeserializeFixmap(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_FIXMAP_MAX};
+ // Fill the map with the fuzzed data, not attempting to
+ // make a valid map
+ while (fdp.remaining_bytes() > 0) {
+ buffer.Append(1, fdp.ConsumeIntegral<uint8_t>());
+ }
+
+ std::map<std::uint32_t, std::uint32_t> result;
+ Deserialize(&result, &buffer);
+
+ buffer.Rewind();
+ std::unordered_map<std::uint32_t, std::uint32_t> unorderedResult;
+ Deserialize(&unorderedResult, &buffer);
+}
+
+void FuzzDeserializeVariant(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ Payload buffer = {ENCODING_TYPE_INT16,
+ ENCODING_TYPE_FLOAT32,
+ ENCODING_TYPE_FIXSTR_MAX,
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>(),
+ fdp.ConsumeIntegral<uint8_t>()};
+ // Add the rest of the data as a string
+ std::string s_val = fdp.ConsumeRemainingBytesAsString();
+ for (std::string::iterator iter = s_val.begin(); iter != s_val.end();
+ iter++) {
+ buffer.Append(1, *iter);
+ }
+ Variant<int, float, std::string> result;
+ Deserialize(&result, &buffer);
+}
+
+// Attempts to deserialize fuzzed data as various types
+void FuzzDeserialize(const uint8_t* data, size_t size) {
+ FuzzDeserializeUint8(data, size);
+ FuzzDeserializeUint16(data, size);
+ FuzzDeserializeUint32(data, size);
+ FuzzDeserializeUint64(data, size);
+ FuzzDeserializeInt8(data, size);
+ FuzzDeserializeInt16(data, size);
+ FuzzDeserializeInt32(data, size);
+ FuzzDeserializeInt64(data, size);
+ FuzzDeserializeFloat32(data, size);
+ FuzzDeserializeFloat64(data, size);
+ FuzzDeserializeFixstr(data, size);
+ FuzzDeserializeFixmap(data, size);
+ FuzzDeserializeVariant(data, size);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzSerializeDeserialize(data, size);
+ FuzzDeserialize(data, size);
return 0;
}
diff --git a/opengl/Android.bp b/opengl/Android.bp
index 9ca8b0b..393ced7 100644
--- a/opengl/Android.bp
+++ b/opengl/Android.bp
@@ -53,25 +53,25 @@
}
llndk_library {
- name: "libEGL",
+ name: "libEGL.llndk",
symbol_file: "libs/libEGL.map.txt",
export_include_dirs: ["include"],
}
llndk_library {
- name: "libGLESv1_CM",
+ name: "libGLESv1_CM.llndk",
symbol_file: "libs/libGLESv1_CM.map.txt",
export_include_dirs: ["include"],
}
llndk_library {
- name: "libGLESv2",
+ name: "libGLESv2.llndk",
symbol_file: "libs/libGLESv2.map.txt",
export_include_dirs: ["include"],
}
llndk_library {
- name: "libGLESv3",
+ name: "libGLESv3.llndk",
symbol_file: "libs/libGLESv3.map.txt",
export_include_dirs: ["include"],
}
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 3c76c62..77d887c 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -128,6 +128,7 @@
cc_library_shared {
name: "libEGL",
defaults: ["egl_libs_defaults"],
+ llndk_stubs: "libEGL.llndk",
srcs: [
"EGL/egl_tls.cpp",
"EGL/egl_cache.cpp",
@@ -193,6 +194,7 @@
cc_library_shared {
name: "libGLESv1_CM",
defaults: ["gles_libs_defaults"],
+ llndk_stubs: "libGLESv1_CM.llndk",
srcs: ["GLES_CM/gl.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv1\""],
version_script: "libGLESv1_CM.map.txt",
@@ -204,6 +206,7 @@
cc_library_shared {
name: "libGLESv2",
defaults: ["gles_libs_defaults"],
+ llndk_stubs: "libGLESv2.llndk",
srcs: ["GLES2/gl2.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv2\""],
@@ -218,6 +221,7 @@
cc_library_shared {
name: "libGLESv3",
defaults: ["gles_libs_defaults"],
+ llndk_stubs: "libGLESv3.llndk",
srcs: ["GLES2/gl2.cpp"],
cflags: ["-DLOG_TAG=\"libGLESv3\""],
}
diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java
index 9c80212..59ef3c8 100644
--- a/opengl/tools/glgen/src/JniCodeEmitter.java
+++ b/opengl/tools/glgen/src/JniCodeEmitter.java
@@ -15,6 +15,7 @@
*/
import java.io.PrintStream;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@@ -201,6 +202,33 @@
out.println(iii + ");");
}
+ // Function to automatically generate properly formatted function calls that
+ // comply with clang format rules
+ public static String formatFunctionCall(String indent, String functionCall) {
+ final int MAXLEN = 100;
+ String tokens[] = functionCall.split("\\(|\\)", 2);
+ String params[] = tokens[1].split(",\\s*");
+ String formatted = indent + tokens[0] + "(";
+ char[] chars = new char[indent.length() + tokens[0].length() + 1];
+ Arrays.fill(chars, ' ');
+ String multiIndent = new String(chars);
+ ArrayList<String> lines = new ArrayList<String>();
+ for(int i = 0; i < params.length; i++) {
+ String terminator = ((i == params.length - 1) ? "" : ",");
+ if(indent.length() + formatted.length() + params[i].length() > MAXLEN) {
+ lines.add(formatted);
+ if (!indent.equals(multiIndent)) {
+ indent = multiIndent;
+ }
+ formatted = indent + params[i] + terminator;
+ } else {
+ formatted += (i == 0 ? "" : " ") + params[i] + terminator;
+ }
+ }
+ lines.add(formatted);
+ return String.join("\n", lines);
+ }
+
void printIfcheckPostamble(PrintStream out, boolean isBuffer, boolean emitExceptionCheck,
String iii) {
printIfcheckPostamble(out, isBuffer, emitExceptionCheck,
@@ -1538,14 +1566,19 @@
"_exception ? JNI_ABORT : 0" : "0")) +
");");
} else {
- out.println(indent + indent +
+ String bufferOffset = numBufferArgs <= 1 ? "_bufferOffset" :
+ "_" + cfunc.getArgName(cIndex) + "BufferOffset";
+ String typeCast = "(char *)" + cfunc.getArgName(cIndex);
+ String withOffset = "(void *)(" + typeCast + " - " + bufferOffset + ")";
+ String releasePointerCall = (
"releasePointer(_env, " + array + ", " +
- cfunc.getArgName(cIndex) +
+ withOffset +
", " +
(cfunc.getArgType(cIndex).isConst() ?
"JNI_FALSE" : (emitExceptionCheck ?
"_exception ? JNI_FALSE : JNI_TRUE" : "JNI_TRUE")) +
");");
+ out.println(formatFunctionCall(indent + indent, releasePointerCall));
}
out.println(indent + "}");
}
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
index c78322e..3aa862f 100644
--- a/services/gpuservice/gpumem/GpuMem.cpp
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -43,18 +43,21 @@
// Make sure bpf programs are loaded
bpf::waitForProgsLoaded();
- int fd = bpf::bpfFdGet(kGpuMemTotalProgPath, BPF_F_RDONLY);
+ errno = 0;
+ int fd = bpf::retrieveProgram(kGpuMemTotalProgPath);
if (fd < 0) {
- ALOGE("Failed to retrieve pinned program from %s", kGpuMemTotalProgPath);
+ ALOGE("Failed to retrieve pinned program from %s [%d(%s)]", kGpuMemTotalProgPath, errno,
+ strerror(errno));
return;
}
// Attach the program to the tracepoint, and the tracepoint is automatically enabled here.
+ errno = 0;
int count = 0;
while (bpf_attach_tracepoint(fd, kGpuMemTraceGroup, kGpuMemTotalTracepoint) < 0) {
if (++count > kGpuWaitTimeout) {
- ALOGE("Failed to attach bpf program to %s/%s tracepoint", kGpuMemTraceGroup,
- kGpuMemTotalTracepoint);
+ ALOGE("Failed to attach bpf program to %s/%s tracepoint [%d(%s)]", kGpuMemTraceGroup,
+ kGpuMemTotalTracepoint, errno, strerror(errno));
return;
}
// Retry until GPU driver loaded or timeout.
@@ -62,9 +65,11 @@
}
// Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
+ errno = 0;
auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kGpuMemTotalMapPath);
if (!map.isValid()) {
- ALOGE("Failed to create bpf map from %s", kGpuMemTotalMapPath);
+ ALOGE("Failed to create bpf map from %s [%d(%s)]", kGpuMemTotalMapPath, errno,
+ strerror(errno));
return;
}
setGpuMemTotalMap(map);
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index db81f5d..940a26b 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -38,4 +38,5 @@
static_libs: [
"libgmock",
],
+ require_root: true,
}
diff --git a/services/gpuservice/tests/unittests/AndroidTest.xml b/services/gpuservice/tests/unittests/AndroidTest.xml
index 66f51c7..72976a8 100644
--- a/services/gpuservice/tests/unittests/AndroidTest.xml
+++ b/services/gpuservice/tests/unittests/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="cleanup" value="true" />
<option name="push" value="gpuservice_unittest->/data/local/tmp/gpuservice_unittest" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index b2dadf8..9cc777d 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -19,6 +19,9 @@
//#define LOG_NDEBUG 0
#include "InputReaderBase.h"
+#include "input/DisplayViewport.h"
+#include "input/Input.h"
+#include "input/NamedEnum.h"
#include <android/log.h>
#include <android-base/stringprintf.h>
@@ -99,17 +102,19 @@
size_t count = 0;
std::optional<DisplayViewport> result = std::nullopt;
for (const DisplayViewport& currentViewport : mDisplays) {
- // Return the first match
+ // Return the first match, or the default display if we're looking for the internal viewport
if (currentViewport.type == type) {
- if (!result) {
+ if (!result ||
+ (type == ViewportType::INTERNAL &&
+ currentViewport.displayId == ADISPLAY_ID_DEFAULT)) {
result = std::make_optional(currentViewport);
}
count++;
}
}
if (count > 1) {
- ALOGE("Found %zu viewports with type %s, but expected 1 at most",
- count, viewportTypeToString(type));
+ ALOGW("Found %zu viewports with type %s, but expected 1 at most", count,
+ NamedEnum::string(type).c_str());
}
return result;
}
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 824c01e..33520b2 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -7,6 +7,18 @@
"include-filter": "android.server.wm.WindowInputTests"
}
]
+ },
+ {
+ "name": "libinput_tests"
+ },
+ {
+ "name": "inputflinger_tests"
+ },
+ {
+ "name": "InputTests"
+ },
+ {
+ "name": "libinputservice_test"
}
]
}
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 34fa239..29df00b 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -59,7 +59,6 @@
EventEntry::EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags)
: id(id),
- refCount(1),
type(type),
eventTime(eventTime),
policyFlags(policyFlags),
@@ -70,15 +69,6 @@
releaseInjectionState();
}
-void EventEntry::release() {
- refCount -= 1;
- if (refCount == 0) {
- delete this;
- } else {
- ALOG_ASSERT(refCount > 0);
- }
-}
-
void EventEntry::releaseInjectionState() {
if (injectionState) {
injectionState->release();
@@ -151,11 +141,12 @@
if (!GetBoolProperty("ro.debuggable", false)) {
return "KeyEvent";
}
- return StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
+ return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64
+ ", source=0x%08x, displayId=%" PRId32 ", action=%s, "
"flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
"repeatCount=%d), policyFlags=0x%08x",
- deviceId, source, displayId, KeyEvent::actionToString(action), flags,
- keyCode, scanCode, metaState, repeatCount, policyFlags);
+ deviceId, eventTime, source, displayId, KeyEvent::actionToString(action),
+ flags, keyCode, scanCode, metaState, repeatCount, policyFlags);
}
void KeyEntry::recycle() {
@@ -210,15 +201,16 @@
return "MotionEvent";
}
std::string msg;
- msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32
+ msg += StringPrintf("MotionEvent(deviceId=%d, eventTime=%" PRIu64
+ ", source=0x%08x, displayId=%" PRId32
", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
"buttonState=0x%08x, "
"classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
"xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
- deviceId, source, displayId, MotionEvent::actionToString(action),
- actionButton, flags, metaState, buttonState,
- motionClassificationToString(classification), edgeFlags, xPrecision,
- yPrecision, xCursorPosition, yCursorPosition);
+ deviceId, eventTime, source, displayId,
+ MotionEvent::actionToString(action).c_str(), actionButton, flags, metaState,
+ buttonState, motionClassificationToString(classification), edgeFlags,
+ xPrecision, yPrecision, xCursorPosition, yCursorPosition);
for (uint32_t i = 0; i < pointerCount; i++) {
if (i) {
@@ -235,22 +227,16 @@
volatile int32_t DispatchEntry::sNextSeqAtomic;
-DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, ui::Transform transform,
- float globalScaleFactor)
+DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
+ ui::Transform transform, float globalScaleFactor)
: seq(nextSeq()),
- eventEntry(eventEntry),
+ eventEntry(std::move(eventEntry)),
targetFlags(targetFlags),
transform(transform),
globalScaleFactor(globalScaleFactor),
deliveryTime(0),
resolvedAction(0),
- resolvedFlags(0) {
- eventEntry->refCount += 1;
-}
-
-DispatchEntry::~DispatchEntry() {
- eventEntry->release();
-}
+ resolvedFlags(0) {}
uint32_t DispatchEntry::nextSeq() {
// Sequence number 0 is reserved and will never be returned.
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 47f75cbe..0661709 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -54,7 +54,6 @@
}
int32_t id;
- mutable int32_t refCount;
Type type;
nsecs_t eventTime;
uint32_t policyFlags;
@@ -79,13 +78,12 @@
return isInjected() || IdGenerator::getSource(id) != IdGenerator::Source::INPUT_READER;
}
- void release();
-
virtual std::string getDescription() const = 0;
-protected:
EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
virtual ~EventEntry();
+
+protected:
void releaseInjectionState();
};
@@ -93,7 +91,6 @@
explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime);
std::string getDescription() const override;
-protected:
virtual ~ConfigurationChangedEntry();
};
@@ -103,7 +100,6 @@
DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId);
std::string getDescription() const override;
-protected:
virtual ~DeviceResetEntry();
};
@@ -116,7 +112,6 @@
std::string_view reason);
std::string getDescription() const override;
-protected:
virtual ~FocusEntry();
};
@@ -149,7 +144,6 @@
std::string getDescription() const override;
void recycle();
-protected:
virtual ~KeyEntry();
};
@@ -182,7 +176,6 @@
float xOffset, float yOffset);
std::string getDescription() const override;
-protected:
virtual ~MotionEntry();
};
@@ -190,7 +183,7 @@
struct DispatchEntry {
const uint32_t seq; // unique sequence number, never 0
- EventEntry* eventEntry; // the event to dispatch
+ std::shared_ptr<EventEntry> eventEntry; // the event to dispatch
int32_t targetFlags;
ui::Transform transform;
float globalScaleFactor;
@@ -205,9 +198,8 @@
int32_t resolvedAction;
int32_t resolvedFlags;
- DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, ui::Transform transform,
- float globalScaleFactor);
- ~DispatchEntry();
+ DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags,
+ ui::Transform transform, float globalScaleFactor);
inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; }
@@ -252,13 +244,13 @@
// parameters for the command (usage varies by command)
sp<Connection> connection;
nsecs_t eventTime;
- KeyEntry* keyEntry;
+ std::shared_ptr<KeyEntry> keyEntry;
std::shared_ptr<InputApplicationHandle> inputApplicationHandle;
std::string reason;
int32_t userActivityEventType;
uint32_t seq;
bool handled;
- std::shared_ptr<InputChannel> inputChannel;
+ sp<IBinder> connectionToken;
sp<IBinder> oldToken;
sp<IBinder> newToken;
std::string obscuringPackage;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index d7aea4e..d6fa74d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -37,6 +37,10 @@
// Log debug messages about input focus tracking.
static constexpr bool DEBUG_FOCUS = false;
+// Log debug messages about touch occlusion
+// STOPSHIP(b/169067926): Set to false
+static constexpr bool DEBUG_TOUCH_OCCLUSION = true;
+
// Log debug messages about the app switch latency optimization.
#define DEBUG_APP_SWITCH 0
@@ -200,12 +204,12 @@
return true;
}
-static void dumpRegion(std::string& dump, const Region& region) {
+static std::string dumpRegion(const Region& region) {
if (region.isEmpty()) {
- dump += "<empty>";
- return;
+ return "<empty>";
}
+ std::string dump;
bool first = true;
Region::const_iterator cur = region.begin();
Region::const_iterator const tail = region.end();
@@ -218,6 +222,7 @@
dump += StringPrintf("[%d,%d][%d,%d]", cur->left, cur->top, cur->right, cur->bottom);
cur++;
}
+ return dump;
}
static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) {
@@ -311,19 +316,19 @@
}
static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
- EventEntry* eventEntry,
+ std::shared_ptr<EventEntry> eventEntry,
int32_t inputTargetFlags) {
if (inputTarget.useDefaultPointerTransform()) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
- return std::make_unique<DispatchEntry>(eventEntry, // increments ref
- inputTargetFlags, transform,
+ return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
inputTarget.globalScaleFactor);
}
ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
- PointerCoords pointerCoords[motionEntry.pointerCount];
+ std::vector<PointerCoords> pointerCoords;
+ pointerCoords.resize(motionEntry.pointerCount);
// 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
@@ -347,17 +352,18 @@
pointerCoords[pointerIndex].transform(inverseFirstTransform);
}
- MotionEntry* combinedMotionEntry =
- new MotionEntry(motionEntry.id, 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 */);
+ std::unique_ptr<MotionEntry> combinedMotionEntry =
+ std::make_unique<MotionEntry>(motionEntry.id, 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.data(), 0 /* xOffset */, 0 /* yOffset */);
if (motionEntry.injectionState) {
combinedMotionEntry->injectionState = motionEntry.injectionState;
@@ -365,10 +371,8 @@
}
std::unique_ptr<DispatchEntry> dispatchEntry =
- std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
- inputTargetFlags, firstPointerTransform,
- inputTarget.globalScaleFactor);
- combinedMotionEntry->release();
+ std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
+ firstPointerTransform, inputTarget.globalScaleFactor);
return dispatchEntry;
}
@@ -557,9 +561,7 @@
mNoFocusedWindowTimeoutTime = std::nullopt;
return LONG_LONG_MIN;
} else {
- // Keep waiting
- const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime);
- ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining);
+ // Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes.
nextAnrCheck = *mNoFocusedWindowTimeoutTime;
}
}
@@ -673,22 +675,24 @@
switch (mPendingEvent->type) {
case EventEntry::Type::CONFIGURATION_CHANGED: {
- ConfigurationChangedEntry* typedEntry =
- static_cast<ConfigurationChangedEntry*>(mPendingEvent);
+ const ConfigurationChangedEntry& typedEntry =
+ static_cast<const ConfigurationChangedEntry&>(*mPendingEvent);
done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped
break;
}
case EventEntry::Type::DEVICE_RESET: {
- DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent);
+ const DeviceResetEntry& typedEntry =
+ static_cast<const DeviceResetEntry&>(*mPendingEvent);
done = dispatchDeviceResetLocked(currentTime, typedEntry);
dropReason = DropReason::NOT_DROPPED; // device resets are never dropped
break;
}
case EventEntry::Type::FOCUS: {
- FocusEntry* typedEntry = static_cast<FocusEntry*>(mPendingEvent);
+ std::shared_ptr<FocusEntry> typedEntry =
+ std::static_pointer_cast<FocusEntry>(mPendingEvent);
dispatchFocusLocked(currentTime, typedEntry);
done = true;
dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
@@ -696,37 +700,38 @@
}
case EventEntry::Type::KEY: {
- KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
+ std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
if (isAppSwitchDue) {
- if (isAppSwitchKeyEvent(*typedEntry)) {
+ if (isAppSwitchKeyEvent(*keyEntry)) {
resetPendingAppSwitchLocked(true);
isAppSwitchDue = false;
} else if (dropReason == DropReason::NOT_DROPPED) {
dropReason = DropReason::APP_SWITCH;
}
}
- if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
+ if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) {
dropReason = DropReason::STALE;
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DropReason::BLOCKED;
}
- done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
+ done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
break;
}
case EventEntry::Type::MOTION: {
- MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
+ std::shared_ptr<MotionEntry> motionEntry =
+ std::static_pointer_cast<MotionEntry>(mPendingEvent);
if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
dropReason = DropReason::APP_SWITCH;
}
- if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
+ if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) {
dropReason = DropReason::STALE;
}
if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
dropReason = DropReason::BLOCKED;
}
- done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
+ done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
break;
}
}
@@ -802,17 +807,18 @@
return false;
}
-bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
+bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) {
bool needWake = mInboundQueue.empty();
- mInboundQueue.push_back(entry);
+ mInboundQueue.push_back(std::move(newEntry));
+ EventEntry& entry = *(mInboundQueue.back());
traceInboundQueueLengthLocked();
- switch (entry->type) {
+ switch (entry.type) {
case EventEntry::Type::KEY: {
// Optimize app switch latency.
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*entry);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
if (isAppSwitchKeyEvent(keyEntry)) {
if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
@@ -831,8 +837,8 @@
}
case EventEntry::Type::MOTION: {
- if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(*entry))) {
- mNextUnblockedEvent = entry;
+ if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) {
+ mNextUnblockedEvent = mInboundQueue.back();
needWake = true;
}
break;
@@ -851,11 +857,9 @@
return needWake;
}
-void InputDispatcher::addRecentEventLocked(EventEntry* entry) {
- entry->refCount += 1;
+void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) {
mRecentQueue.push_back(entry);
if (mRecentQueue.size() > RECENT_QUEUE_MAX_SIZE) {
- mRecentQueue.front()->release();
mRecentQueue.pop_front();
}
}
@@ -1035,7 +1039,7 @@
void InputDispatcher::drainInboundQueueLocked() {
while (!mInboundQueue.empty()) {
- EventEntry* entry = mInboundQueue.front();
+ std::shared_ptr<EventEntry> entry = mInboundQueue.front();
mInboundQueue.pop_front();
releaseInboundEventLocked(entry);
}
@@ -1049,66 +1053,48 @@
}
}
-void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) {
+void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) {
InjectionState* injectionState = entry->injectionState;
if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) {
#if DEBUG_DISPATCH_CYCLE
ALOGD("Injected inbound event was dropped.");
#endif
- setInjectionResult(entry, InputEventInjectionResult::FAILED);
+ setInjectionResult(*entry, InputEventInjectionResult::FAILED);
}
if (entry == mNextUnblockedEvent) {
mNextUnblockedEvent = nullptr;
}
addRecentEventLocked(entry);
- entry->release();
}
void InputDispatcher::resetKeyRepeatLocked() {
if (mKeyRepeatState.lastKeyEntry) {
- mKeyRepeatState.lastKeyEntry->release();
mKeyRepeatState.lastKeyEntry = nullptr;
}
}
-KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
- KeyEntry* entry = mKeyRepeatState.lastKeyEntry;
+std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) {
+ std::shared_ptr<KeyEntry> entry = mKeyRepeatState.lastKeyEntry;
- // Reuse the repeated key entry if it is otherwise unreferenced.
uint32_t policyFlags = entry->policyFlags &
(POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED);
- if (entry->refCount == 1) {
- entry->recycle();
- entry->id = mIdGenerator.nextId();
- entry->eventTime = currentTime;
- entry->policyFlags = policyFlags;
- entry->repeatCount += 1;
- } else {
- KeyEntry* newEntry =
- new KeyEntry(mIdGenerator.nextId(), currentTime, entry->deviceId, entry->source,
- entry->displayId, policyFlags, entry->action, entry->flags,
- entry->keyCode, entry->scanCode, entry->metaState,
- entry->repeatCount + 1, entry->downTime);
- mKeyRepeatState.lastKeyEntry = newEntry;
- entry->release();
+ std::shared_ptr<KeyEntry> newEntry =
+ std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, entry->deviceId,
+ entry->source, entry->displayId, policyFlags, entry->action,
+ entry->flags, entry->keyCode, entry->scanCode,
+ entry->metaState, entry->repeatCount + 1, entry->downTime);
- entry = newEntry;
- }
- entry->syntheticRepeat = true;
-
- // Increment reference count since we keep a reference to the event in
- // mKeyRepeatState.lastKeyEntry in addition to the one we return.
- entry->refCount += 1;
-
+ newEntry->syntheticRepeat = true;
+ mKeyRepeatState.lastKeyEntry = newEntry;
mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay;
- return entry;
+ return newEntry;
}
bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime,
- ConfigurationChangedEntry* entry) {
+ const ConfigurationChangedEntry& entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry->eventTime);
+ ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime);
#endif
// Reset key repeating in case a keyboard device was added or removed or something.
@@ -1117,19 +1103,20 @@
// Enqueue a command to run outside the lock to tell the policy that the configuration changed.
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
- commandEntry->eventTime = entry->eventTime;
+ commandEntry->eventTime = entry.eventTime;
postCommandLocked(std::move(commandEntry));
return true;
}
-bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) {
+bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime,
+ const DeviceResetEntry& entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
- ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry->eventTime,
- entry->deviceId);
+ ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime,
+ entry.deviceId);
#endif
CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset");
- options.deviceId = entry->deviceId;
+ options.deviceId = entry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
return true;
}
@@ -1143,20 +1130,23 @@
mPendingEvent = nullptr;
}
- FocusEntry* focusEntry =
- new FocusEntry(mIdGenerator.nextId(), now(), windowToken, hasFocus, reason);
+ std::unique_ptr<FocusEntry> focusEntry =
+ std::make_unique<FocusEntry>(mIdGenerator.nextId(), now(), windowToken, hasFocus,
+ reason);
// This event should go to the front of the queue, but behind all other focus events
// Find the last focus event, and insert right after it
- std::deque<EventEntry*>::reverse_iterator it =
+ std::deque<std::shared_ptr<EventEntry>>::reverse_iterator it =
std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
- [](EventEntry* event) { return event->type == EventEntry::Type::FOCUS; });
+ [](const std::shared_ptr<EventEntry>& event) {
+ return event->type == EventEntry::Type::FOCUS;
+ });
// Maintain the order of focus events. Insert the entry after all other focus events.
- mInboundQueue.insert(it.base(), focusEntry);
+ mInboundQueue.insert(it.base(), std::move(focusEntry));
}
-void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) {
+void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry) {
std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
if (channel == nullptr) {
return; // Window has gone away
@@ -1172,7 +1162,7 @@
dispatchEventLocked(currentTime, entry, {target});
}
-bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
+bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
// Preprocessing.
if (!entry->dispatchInProgress) {
@@ -1200,10 +1190,9 @@
mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;
}
mKeyRepeatState.lastKeyEntry = entry;
- entry->refCount += 1;
} else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry &&
mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) {
- // The stale device releases the key, reset staleDeviceId.
+ // The key on device 'deviceId' is still down, do not stop key repeat
#if DEBUG_INBOUND_EVENT_DETAILS
ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId);
#endif
@@ -1241,12 +1230,9 @@
&InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
sp<IBinder> focusedWindowToken =
getValueByKey(mFocusedWindowTokenByDisplay, getTargetDisplayId(*entry));
- if (focusedWindowToken != nullptr) {
- commandEntry->inputChannel = getInputChannelLocked(focusedWindowToken);
- }
+ commandEntry->connectionToken = focusedWindowToken;
commandEntry->keyEntry = entry;
postCommandLocked(std::move(commandEntry));
- entry->refCount += 1;
return false; // wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
@@ -1259,7 +1245,7 @@
// Clean up if dropping the event.
if (*dropReason != DropReason::NOT_DROPPED) {
- setInjectionResult(entry,
+ setInjectionResult(*entry,
*dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
: InputEventInjectionResult::FAILED);
mReporter->reportDroppedKey(entry->id);
@@ -1274,7 +1260,7 @@
return false;
}
- setInjectionResult(entry, injectionResult);
+ setInjectionResult(*entry, injectionResult);
if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
return true;
}
@@ -1298,7 +1284,7 @@
#endif
}
-bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry,
+bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
ATRACE_CALL();
// Preprocessing.
@@ -1310,7 +1296,7 @@
// Clean up if dropping the event.
if (*dropReason != DropReason::NOT_DROPPED) {
- setInjectionResult(entry,
+ setInjectionResult(*entry,
*dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
: InputEventInjectionResult::FAILED);
return true;
@@ -1337,7 +1323,7 @@
return false;
}
- setInjectionResult(entry, injectionResult);
+ setInjectionResult(*entry, injectionResult);
if (injectionResult == InputEventInjectionResult::PERMISSION_DENIED) {
ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent));
return true;
@@ -1411,7 +1397,8 @@
#endif
}
-void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry,
+void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
+ std::shared_ptr<EventEntry> eventEntry,
const std::vector<InputTarget>& inputTargets) {
ATRACE_CALL();
#if DEBUG_DISPATCH_CYCLE
@@ -1774,6 +1761,12 @@
TouchOcclusionInfo occlusionInfo =
computeTouchOcclusionInfoLocked(newTouchedWindowHandle, x, y);
if (!isTouchTrustedLocked(occlusionInfo)) {
+ if (DEBUG_TOUCH_OCCLUSION) {
+ ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
+ for (const auto& log : occlusionInfo.debugInfo) {
+ ALOGD("%s", log.c_str());
+ }
+ }
onUntrustedTouchLocked(occlusionInfo.obscuringPackage);
if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
ALOGW("Dropping untrusted touch event due to %s/%d",
@@ -2184,6 +2177,15 @@
auto otherInfo = otherHandle->getInfo();
if (!otherInfo->visible) {
return false;
+ } else if (otherInfo->alpha == 0 &&
+ otherInfo->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) {
+ // Those act as if they were invisible, so we don't need to flag them.
+ // We do want to potentially flag touchable windows even if they have 0
+ // opacity, since they can consume touches and alter the effects of the
+ // user interaction (eg. apps that rely on
+ // FLAG_WINDOW_IS_PARTIALLY_OBSCURED should still be told about those
+ // windows), hence we also check for FLAG_NOT_TOUCHABLE.
+ return false;
} else if (info->ownerUid == otherInfo->ownerUid) {
// If ownerUid is the same we don't generate occlusion events as there
// is no security boundary within an uid.
@@ -2215,7 +2217,8 @@
*/
InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const {
- int32_t displayId = windowHandle->getInfo()->displayId;
+ const InputWindowInfo* windowInfo = windowHandle->getInfo();
+ int32_t displayId = windowInfo->displayId;
const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
TouchOcclusionInfo info;
info.hasBlockingOcclusion = false;
@@ -2228,8 +2231,11 @@
}
const InputWindowInfo* otherInfo = otherHandle->getInfo();
if (canBeObscuredBy(windowHandle, otherHandle) &&
- windowHandle->getInfo()->ownerUid != otherInfo->ownerUid &&
- otherInfo->frameContainsPoint(x, y)) {
+ windowInfo->ownerUid != otherInfo->ownerUid && otherInfo->frameContainsPoint(x, y)) {
+ if (DEBUG_TOUCH_OCCLUSION) {
+ info.debugInfo.push_back(
+ dumpWindowForTouchOcclusion(otherInfo, /* isTouchedWindow */ false));
+ }
// canBeObscuredBy() has returned true above, which means this window is untrusted, so
// we perform the checks below to see if the touch can be propagated or not based on the
// window's touch occlusion mode
@@ -2255,9 +2261,29 @@
}
}
}
+ if (DEBUG_TOUCH_OCCLUSION) {
+ info.debugInfo.push_back(
+ dumpWindowForTouchOcclusion(windowInfo, /* isTouchedWindow */ true));
+ }
return info;
}
+std::string InputDispatcher::dumpWindowForTouchOcclusion(const InputWindowInfo* info,
+ bool isTouchedWindow) const {
+ return StringPrintf(INDENT2 "* %stype=%s, package=%s/%" PRId32 ", mode=%s, alpha=%.2f, "
+ "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
+ "], touchableRegion=%s, window={%s}, applicationInfo=%s, "
+ "flags={%s}, inputFeatures={%s}, hasToken=%s\n",
+ (isTouchedWindow) ? "[TOUCHED] " : "",
+ NamedEnum::string(info->type, "%" PRId32).c_str(),
+ info->packageName.c_str(), info->ownerUid,
+ toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
+ info->frameTop, info->frameRight, info->frameBottom,
+ dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
+ info->applicationInfo.name.c_str(), info->flags.string().c_str(),
+ info->inputFeatures.string().c_str(), toString(info->token != nullptr));
+}
+
bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
if (occlusionInfo.hasBlockingOcclusion) {
ALOGW("Untrusted touch due to occlusion by %s/%d", occlusionInfo.obscuringPackage.c_str(),
@@ -2380,7 +2406,7 @@
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection,
- EventEntry* eventEntry,
+ std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget) {
if (ATRACE_ENABLED()) {
std::string message =
@@ -2414,7 +2440,7 @@
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
- MotionEntry* splitMotionEntry =
+ std::unique_ptr<MotionEntry> splitMotionEntry =
splitMotionEvent(originalMotionEntry, inputTarget.pointerIds);
if (!splitMotionEntry) {
return; // split event was dropped
@@ -2424,8 +2450,8 @@
connection->getInputChannelName().c_str());
logOutboundMotionDetails(" ", *splitMotionEntry);
}
- enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);
- splitMotionEntry->release();
+ enqueueDispatchEntriesLocked(currentTime, connection, std::move(splitMotionEntry),
+ inputTarget);
return;
}
}
@@ -2436,7 +2462,7 @@
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection,
- EventEntry* eventEntry,
+ std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget) {
if (ATRACE_ENABLED()) {
std::string message =
@@ -2468,7 +2494,7 @@
}
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
- EventEntry* eventEntry,
+ std::shared_ptr<EventEntry> eventEntry,
const InputTarget& inputTarget,
int32_t dispatchMode) {
if (ATRACE_ENABLED()) {
@@ -2490,11 +2516,11 @@
// 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;
+ EventEntry& newEntry = *(dispatchEntry->eventEntry);
// Apply target flags and update the connection's input state.
- switch (newEntry->type) {
+ switch (newEntry.type) {
case EventEntry::Type::KEY: {
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(newEntry);
dispatchEntry->resolvedEventId = keyEntry.id;
dispatchEntry->resolvedAction = keyEntry.action;
dispatchEntry->resolvedFlags = keyEntry.flags;
@@ -2511,7 +2537,7 @@
}
case EventEntry::Type::MOTION: {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(newEntry);
// Assign a default value to dispatchEntry that will never be generated by InputReader,
// and assign a InputDispatcher value if it doesn't change in the if-else chain below.
constexpr int32_t DEFAULT_RESOLVED_EVENT_ID =
@@ -2582,7 +2608,7 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("%s events should not go to apps",
- EventEntry::typeToString(newEntry->type));
+ EventEntry::typeToString(newEntry.type));
break;
}
}
@@ -2706,39 +2732,38 @@
// Publish the event.
status_t status;
- EventEntry* eventEntry = dispatchEntry->eventEntry;
- switch (eventEntry->type) {
+ const EventEntry& eventEntry = *(dispatchEntry->eventEntry);
+ switch (eventEntry.type) {
case EventEntry::Type::KEY: {
- const KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
- std::array<uint8_t, 32> hmac = getSignature(*keyEntry, *dispatchEntry);
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
+ std::array<uint8_t, 32> hmac = getSignature(keyEntry, *dispatchEntry);
// Publish the key event.
- status =
- connection->inputPublisher
- .publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId,
- keyEntry->deviceId, keyEntry->source,
- keyEntry->displayId, std::move(hmac),
- dispatchEntry->resolvedAction,
- dispatchEntry->resolvedFlags, keyEntry->keyCode,
- keyEntry->scanCode, keyEntry->metaState,
- keyEntry->repeatCount, keyEntry->downTime,
- keyEntry->eventTime);
+ status = connection->inputPublisher
+ .publishKeyEvent(dispatchEntry->seq,
+ dispatchEntry->resolvedEventId, keyEntry.deviceId,
+ keyEntry.source, keyEntry.displayId,
+ std::move(hmac), dispatchEntry->resolvedAction,
+ dispatchEntry->resolvedFlags, keyEntry.keyCode,
+ keyEntry.scanCode, keyEntry.metaState,
+ keyEntry.repeatCount, keyEntry.downTime,
+ keyEntry.eventTime);
break;
}
case EventEntry::Type::MOTION: {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
PointerCoords scaledCoords[MAX_POINTERS];
- const PointerCoords* usingCoords = motionEntry->pointerCoords;
+ const PointerCoords* usingCoords = motionEntry.pointerCoords;
// Set the X and Y offset and X and Y scale depending on the input source.
- if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
+ if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&
!(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
float globalScaleFactor = dispatchEntry->globalScaleFactor;
if (globalScaleFactor != 1.0f) {
- for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
- scaledCoords[i] = motionEntry->pointerCoords[i];
+ for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
+ scaledCoords[i] = motionEntry.pointerCoords[i];
// Don't apply window scale here since we don't want scale to affect raw
// coordinates. The scale will be sent back to the client and applied
// later when requesting relative coordinates.
@@ -2750,43 +2775,42 @@
} else {
// We don't want the dispatch target to know.
if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
- for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
+ for (uint32_t i = 0; i < motionEntry.pointerCount; i++) {
scaledCoords[i].clear();
}
usingCoords = scaledCoords;
}
}
- std::array<uint8_t, 32> hmac = getSignature(*motionEntry, *dispatchEntry);
+ std::array<uint8_t, 32> hmac = getSignature(motionEntry, *dispatchEntry);
// Publish the motion event.
status = connection->inputPublisher
.publishMotionEvent(dispatchEntry->seq,
dispatchEntry->resolvedEventId,
- motionEntry->deviceId, motionEntry->source,
- motionEntry->displayId, std::move(hmac),
+ motionEntry.deviceId, motionEntry.source,
+ motionEntry.displayId, std::move(hmac),
dispatchEntry->resolvedAction,
- motionEntry->actionButton,
+ motionEntry.actionButton,
dispatchEntry->resolvedFlags,
- motionEntry->edgeFlags, motionEntry->metaState,
- motionEntry->buttonState,
- motionEntry->classification,
+ motionEntry.edgeFlags, motionEntry.metaState,
+ motionEntry.buttonState,
+ motionEntry.classification,
dispatchEntry->transform,
- motionEntry->xPrecision,
- motionEntry->yPrecision,
- motionEntry->xCursorPosition,
- motionEntry->yCursorPosition,
- motionEntry->downTime, motionEntry->eventTime,
- motionEntry->pointerCount,
- motionEntry->pointerProperties, usingCoords);
- reportTouchEventForStatistics(*motionEntry);
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition,
+ motionEntry.yCursorPosition,
+ motionEntry.downTime, motionEntry.eventTime,
+ motionEntry.pointerCount,
+ motionEntry.pointerProperties, usingCoords);
+ reportTouchEventForStatistics(motionEntry);
break;
}
case EventEntry::Type::FOCUS: {
- FocusEntry* focusEntry = static_cast<FocusEntry*>(eventEntry);
+ const FocusEntry& focusEntry = static_cast<const FocusEntry&>(eventEntry);
status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq,
- focusEntry->id,
- focusEntry->hasFocus,
+ focusEntry.id,
+ focusEntry.hasFocus,
mInTouchMode);
break;
}
@@ -2794,7 +2818,7 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET: {
LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
- EventEntry::typeToString(eventEntry->type));
+ EventEntry::typeToString(eventEntry.type));
return;
}
}
@@ -2932,7 +2956,7 @@
void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) {
if (dispatchEntry->hasForegroundTarget()) {
- decrementPendingForegroundDispatches(dispatchEntry->eventEntry);
+ decrementPendingForegroundDispatches(*(dispatchEntry->eventEntry));
}
delete dispatchEntry;
}
@@ -3048,7 +3072,7 @@
nsecs_t currentTime = now();
- std::vector<EventEntry*> cancelationEvents =
+ std::vector<std::unique_ptr<EventEntry>> cancelationEvents =
connection->inputState.synthesizeCancelationEvents(currentTime, options);
if (cancelationEvents.empty()) {
@@ -3073,7 +3097,7 @@
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
for (size_t i = 0; i < cancelationEvents.size(); i++) {
- EventEntry* cancelationEventEntry = cancelationEvents[i];
+ std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]);
switch (cancelationEventEntry->type) {
case EventEntry::Type::KEY: {
logOutboundKeyDetails("cancel - ",
@@ -3097,10 +3121,8 @@
}
}
- enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
- target, InputTarget::FLAG_DISPATCH_AS_IS);
-
- cancelationEventEntry->release();
+ enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), target,
+ InputTarget::FLAG_DISPATCH_AS_IS);
}
startDispatchCycleLocked(currentTime, connection);
@@ -3114,7 +3136,7 @@
nsecs_t currentTime = now();
- std::vector<EventEntry*> downEvents =
+ std::vector<std::unique_ptr<EventEntry>> downEvents =
connection->inputState.synthesizePointerDownEvents(currentTime);
if (downEvents.empty()) {
@@ -3137,7 +3159,7 @@
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- for (EventEntry* downEventEntry : downEvents) {
+ for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) {
switch (downEventEntry->type) {
case EventEntry::Type::MOTION: {
logOutboundMotionDetails("down - ",
@@ -3155,17 +3177,15 @@
}
}
- enqueueDispatchEntryLocked(connection, downEventEntry, // increments ref
- target, InputTarget::FLAG_DISPATCH_AS_IS);
-
- downEventEntry->release();
+ enqueueDispatchEntryLocked(connection, std::move(downEventEntry), target,
+ InputTarget::FLAG_DISPATCH_AS_IS);
}
startDispatchCycleLocked(currentTime, connection);
}
-MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry,
- BitSet32 pointerIds) {
+std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent(
+ const MotionEntry& originalMotionEntry, BitSet32 pointerIds) {
ALOG_ASSERT(pointerIds.value != 0);
uint32_t splitPointerIndexMap[MAX_POINTERS];
@@ -3238,17 +3258,22 @@
originalMotionEntry.id, newId);
ATRACE_NAME(message.c_str());
}
- MotionEntry* splitMotionEntry =
- new MotionEntry(newId, originalMotionEntry.eventTime, originalMotionEntry.deviceId,
- originalMotionEntry.source, originalMotionEntry.displayId,
- originalMotionEntry.policyFlags, action,
- originalMotionEntry.actionButton, originalMotionEntry.flags,
- originalMotionEntry.metaState, originalMotionEntry.buttonState,
- originalMotionEntry.classification, originalMotionEntry.edgeFlags,
- originalMotionEntry.xPrecision, originalMotionEntry.yPrecision,
- originalMotionEntry.xCursorPosition,
- originalMotionEntry.yCursorPosition, originalMotionEntry.downTime,
- splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0);
+ std::unique_ptr<MotionEntry> splitMotionEntry =
+ std::make_unique<MotionEntry>(newId, originalMotionEntry.eventTime,
+ originalMotionEntry.deviceId, originalMotionEntry.source,
+ originalMotionEntry.displayId,
+ originalMotionEntry.policyFlags, action,
+ originalMotionEntry.actionButton,
+ originalMotionEntry.flags, originalMotionEntry.metaState,
+ originalMotionEntry.buttonState,
+ originalMotionEntry.classification,
+ originalMotionEntry.edgeFlags,
+ originalMotionEntry.xPrecision,
+ originalMotionEntry.yPrecision,
+ originalMotionEntry.xCursorPosition,
+ originalMotionEntry.yCursorPosition,
+ originalMotionEntry.downTime, splitPointerCount,
+ splitPointerProperties, splitPointerCoords, 0, 0);
if (originalMotionEntry.injectionState) {
splitMotionEntry->injectionState = originalMotionEntry.injectionState;
@@ -3267,9 +3292,9 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- ConfigurationChangedEntry* newEntry =
- new ConfigurationChangedEntry(args->id, args->eventTime);
- needWake = enqueueInboundEventLocked(newEntry);
+ std::unique_ptr<ConfigurationChangedEntry> newEntry =
+ std::make_unique<ConfigurationChangedEntry>(args->id, args->eventTime);
+ needWake = enqueueInboundEventLocked(std::move(newEntry));
} // release lock
if (needWake) {
@@ -3373,12 +3398,13 @@
mLock.lock();
}
- KeyEntry* newEntry =
- new KeyEntry(args->id, args->eventTime, args->deviceId, args->source,
- args->displayId, policyFlags, args->action, flags, keyCode,
- args->scanCode, metaState, repeatCount, args->downTime);
+ std::unique_ptr<KeyEntry> newEntry =
+ std::make_unique<KeyEntry>(args->id, args->eventTime, args->deviceId, args->source,
+ args->displayId, policyFlags, args->action, flags,
+ keyCode, args->scanCode, metaState, repeatCount,
+ args->downTime);
- needWake = enqueueInboundEventLocked(newEntry);
+ needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
} // release lock
@@ -3459,16 +3485,18 @@
}
// Just enqueue a new motion event.
- MotionEntry* newEntry =
- new MotionEntry(args->id, args->eventTime, args->deviceId, args->source,
- args->displayId, policyFlags, args->action, args->actionButton,
- args->flags, args->metaState, args->buttonState,
- args->classification, args->edgeFlags, args->xPrecision,
- args->yPrecision, args->xCursorPosition, args->yCursorPosition,
- args->downTime, args->pointerCount, args->pointerProperties,
- args->pointerCoords, 0, 0);
+ std::unique_ptr<MotionEntry> newEntry =
+ std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId,
+ args->source, args->displayId, policyFlags,
+ args->action, args->actionButton, args->flags,
+ args->metaState, args->buttonState,
+ args->classification, args->edgeFlags,
+ args->xPrecision, args->yPrecision,
+ args->xCursorPosition, args->yCursorPosition,
+ args->downTime, args->pointerCount,
+ args->pointerProperties, args->pointerCoords, 0, 0);
- needWake = enqueueInboundEventLocked(newEntry);
+ needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
} // release lock
@@ -3503,9 +3531,9 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- DeviceResetEntry* newEntry =
- new DeviceResetEntry(args->id, args->eventTime, args->deviceId);
- needWake = enqueueInboundEventLocked(newEntry);
+ std::unique_ptr<DeviceResetEntry> newEntry =
+ std::make_unique<DeviceResetEntry>(args->id, args->eventTime, args->deviceId);
+ needWake = enqueueInboundEventLocked(std::move(newEntry));
} // release lock
if (needWake) {
@@ -3528,7 +3556,7 @@
policyFlags |= POLICY_FLAG_TRUSTED;
}
- std::queue<EventEntry*> injectedEntries;
+ std::queue<std::unique_ptr<EventEntry>> injectedEntries;
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
const KeyEvent& incomingKey = static_cast<const KeyEvent&>(*event);
@@ -3562,13 +3590,14 @@
}
mLock.lock();
- KeyEntry* injectedEntry =
- new KeyEntry(incomingKey.getId(), incomingKey.getEventTime(),
- VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
- incomingKey.getDisplayId(), policyFlags, action, flags, keyCode,
- incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(),
- incomingKey.getDownTime());
- injectedEntries.push(injectedEntry);
+ std::unique_ptr<KeyEntry> injectedEntry =
+ std::make_unique<KeyEntry>(incomingKey.getId(), incomingKey.getEventTime(),
+ VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+ incomingKey.getDisplayId(), policyFlags, action,
+ flags, keyCode, incomingKey.getScanCode(), metaState,
+ incomingKey.getRepeatCount(),
+ incomingKey.getDownTime());
+ injectedEntries.push(std::move(injectedEntry));
break;
}
@@ -3596,37 +3625,46 @@
mLock.lock();
const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
- MotionEntry* injectedEntry =
- new MotionEntry(motionEvent->getId(), *sampleEventTimes, VIRTUAL_KEYBOARD_ID,
- motionEvent->getSource(), motionEvent->getDisplayId(),
- policyFlags, action, actionButton, motionEvent->getFlags(),
- motionEvent->getMetaState(), motionEvent->getButtonState(),
- motionEvent->getClassification(), motionEvent->getEdgeFlags(),
- motionEvent->getXPrecision(), motionEvent->getYPrecision(),
- motionEvent->getRawXCursorPosition(),
- motionEvent->getRawYCursorPosition(),
- motionEvent->getDownTime(), uint32_t(pointerCount),
- pointerProperties, samplePointerCoords,
- motionEvent->getXOffset(), motionEvent->getYOffset());
- injectedEntries.push(injectedEntry);
+ std::unique_ptr<MotionEntry> injectedEntry =
+ std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes,
+ VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
+ motionEvent->getDisplayId(), policyFlags, action,
+ actionButton, motionEvent->getFlags(),
+ motionEvent->getMetaState(),
+ motionEvent->getButtonState(),
+ motionEvent->getClassification(),
+ motionEvent->getEdgeFlags(),
+ motionEvent->getXPrecision(),
+ motionEvent->getYPrecision(),
+ motionEvent->getRawXCursorPosition(),
+ motionEvent->getRawYCursorPosition(),
+ motionEvent->getDownTime(),
+ uint32_t(pointerCount), pointerProperties,
+ samplePointerCoords, motionEvent->getXOffset(),
+ motionEvent->getYOffset());
+ injectedEntries.push(std::move(injectedEntry));
for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
samplePointerCoords += pointerCount;
- MotionEntry* nextInjectedEntry =
- new MotionEntry(motionEvent->getId(), *sampleEventTimes,
- VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
- motionEvent->getDisplayId(), policyFlags, action,
- actionButton, motionEvent->getFlags(),
- motionEvent->getMetaState(), motionEvent->getButtonState(),
- motionEvent->getClassification(),
- motionEvent->getEdgeFlags(), motionEvent->getXPrecision(),
- motionEvent->getYPrecision(),
- motionEvent->getRawXCursorPosition(),
- motionEvent->getRawYCursorPosition(),
- motionEvent->getDownTime(), uint32_t(pointerCount),
- pointerProperties, samplePointerCoords,
- motionEvent->getXOffset(), motionEvent->getYOffset());
- injectedEntries.push(nextInjectedEntry);
+ std::unique_ptr<MotionEntry> nextInjectedEntry =
+ std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes,
+ VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
+ motionEvent->getDisplayId(), policyFlags,
+ action, actionButton, motionEvent->getFlags(),
+ motionEvent->getMetaState(),
+ motionEvent->getButtonState(),
+ motionEvent->getClassification(),
+ motionEvent->getEdgeFlags(),
+ motionEvent->getXPrecision(),
+ motionEvent->getYPrecision(),
+ motionEvent->getRawXCursorPosition(),
+ motionEvent->getRawYCursorPosition(),
+ motionEvent->getDownTime(),
+ uint32_t(pointerCount), pointerProperties,
+ samplePointerCoords,
+ motionEvent->getXOffset(),
+ motionEvent->getYOffset());
+ injectedEntries.push(std::move(nextInjectedEntry));
}
break;
}
@@ -3646,7 +3684,7 @@
bool needWake = false;
while (!injectedEntries.empty()) {
- needWake |= enqueueInboundEventLocked(injectedEntries.front());
+ needWake |= enqueueInboundEventLocked(std::move(injectedEntries.front()));
injectedEntries.pop();
}
@@ -3753,9 +3791,9 @@
mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
}
-void InputDispatcher::setInjectionResult(EventEntry* entry,
+void InputDispatcher::setInjectionResult(EventEntry& entry,
InputEventInjectionResult injectionResult) {
- InjectionState* injectionState = entry->injectionState;
+ InjectionState* injectionState = entry.injectionState;
if (injectionState) {
#if DEBUG_INJECTION
ALOGD("Setting input event injection result to %d. "
@@ -3763,7 +3801,7 @@
injectionResult, injectionState->injectorPid, injectionState->injectorUid);
#endif
- if (injectionState->injectionIsAsync && !(entry->policyFlags & POLICY_FLAG_FILTERED)) {
+ if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) {
// Log the outcome since the injector did not wait for the injection result.
switch (injectionResult) {
case InputEventInjectionResult::SUCCEEDED:
@@ -3789,15 +3827,15 @@
}
}
-void InputDispatcher::incrementPendingForegroundDispatches(EventEntry* entry) {
- InjectionState* injectionState = entry->injectionState;
+void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
+ InjectionState* injectionState = entry.injectionState;
if (injectionState) {
injectionState->pendingForegroundDispatches += 1;
}
}
-void InputDispatcher::decrementPendingForegroundDispatches(EventEntry* entry) {
- InjectionState* injectionState = entry->injectionState;
+void InputDispatcher::decrementPendingForegroundDispatches(EventEntry& entry) {
+ InjectionState* injectionState = entry.injectionState;
if (injectionState) {
injectionState->pendingForegroundDispatches -= 1;
@@ -4364,6 +4402,24 @@
return dump;
}
+std::string InputDispatcher::dumpPendingFocusRequestsLocked() {
+ if (mPendingFocusRequests.empty()) {
+ return INDENT "mPendingFocusRequests: <none>\n";
+ }
+
+ std::string dump;
+ dump += INDENT "mPendingFocusRequests:\n";
+ for (const auto& [displayId, focusRequest] : mPendingFocusRequests) {
+ // Rather than printing raw values for focusRequest.token and focusRequest.focusedToken,
+ // try to resolve them to actual windows.
+ std::string windowName = getConnectionNameLocked(focusRequest.token);
+ std::string focusedWindowName = getConnectionNameLocked(focusRequest.focusedToken);
+ dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", token->%s, focusedToken->%s\n",
+ displayId, windowName.c_str(), focusedWindowName.c_str());
+ }
+ return dump;
+}
+
void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
@@ -4386,6 +4442,7 @@
}
dump += dumpFocusedWindowsLocked();
+ dump += dumpPendingFocusRequestsLocked();
if (!mTouchStatesByDisplay.empty()) {
dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
@@ -4448,13 +4505,15 @@
windowInfo->frameRight, windowInfo->frameBottom,
windowInfo->globalScaleFactor,
windowInfo->applicationInfo.name.c_str());
- dumpRegion(dump, windowInfo->touchableRegion);
+ dump += dumpRegion(windowInfo->touchableRegion);
dump += StringPrintf(", inputFeatures=%s",
windowInfo->inputFeatures.string().c_str());
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
- "ms\n",
+ "ms, trustedOverlay=%s, hasToken=%s\n",
windowInfo->ownerPid, windowInfo->ownerUid,
- millis(windowInfo->dispatchingTimeout));
+ millis(windowInfo->dispatchingTimeout),
+ toString(windowInfo->trustedOverlay),
+ toString(windowInfo->token != nullptr));
windowInfo->transform.dump(dump, "transform", INDENT4);
}
} else {
@@ -4485,7 +4544,7 @@
// Dump recently dispatched or dropped events from oldest to newest.
if (!mRecentQueue.empty()) {
dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size());
- for (EventEntry* entry : mRecentQueue) {
+ for (std::shared_ptr<EventEntry>& entry : mRecentQueue) {
dump += INDENT2;
dump += entry->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
@@ -4508,7 +4567,7 @@
// Dump inbound events from oldest to newest.
if (!mInboundQueue.empty()) {
dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size());
- for (EventEntry* entry : mInboundQueue) {
+ for (std::shared_ptr<EventEntry>& entry : mInboundQueue) {
dump += INDENT2;
dump += entry->getDescription();
dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
@@ -4791,6 +4850,14 @@
return nullptr;
}
+std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connectionToken) const {
+ sp<Connection> connection = getConnectionLocked(connectionToken);
+ if (connection == nullptr) {
+ return "<nullptr>";
+ }
+ return connection->getInputChannelName();
+}
+
void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
removeByValue(mConnectionsByFd, connection);
@@ -4851,14 +4918,13 @@
connection.inputChannel->getName().c_str(),
ns2ms(currentWait),
oldestEntry->eventEntry->getDescription().c_str());
-
- updateLastAnrStateLocked(getWindowHandleLocked(connection.inputChannel->getConnectionToken()),
- reason);
+ sp<IBinder> connectionToken = connection.inputChannel->getConnectionToken();
+ updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason);
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
commandEntry->inputApplicationHandle = nullptr;
- commandEntry->inputChannel = connection.inputChannel;
+ commandEntry->connectionToken = connectionToken;
commandEntry->reason = std::move(reason);
postCommandLocked(std::move(commandEntry));
}
@@ -4872,7 +4938,6 @@
std::unique_ptr<CommandEntry> commandEntry =
std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
commandEntry->inputApplicationHandle = application;
- commandEntry->inputChannel = nullptr;
commandEntry->reason = std::move(reason);
postCommandLocked(std::move(commandEntry));
}
@@ -4941,10 +5006,8 @@
}
void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) {
- sp<IBinder> token =
- commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
mLock.unlock();
-
+ const sp<IBinder>& token = commandEntry->connectionToken;
const std::chrono::nanoseconds timeoutExtension =
mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
@@ -5001,16 +5064,14 @@
void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
CommandEntry* commandEntry) {
- KeyEntry* entry = commandEntry->keyEntry;
- KeyEvent event = createKeyEvent(*entry);
+ KeyEntry& entry = *(commandEntry->keyEntry);
+ KeyEvent event = createKeyEvent(entry);
mLock.unlock();
android::base::Timer t;
- sp<IBinder> token = commandEntry->inputChannel != nullptr
- ? commandEntry->inputChannel->getConnectionToken()
- : nullptr;
- nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags);
+ const sp<IBinder>& token = commandEntry->connectionToken;
+ nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry.policyFlags);
if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms",
std::to_string(t.duration().count()).c_str());
@@ -5019,14 +5080,13 @@
mLock.lock();
if (delay < 0) {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
+ entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
} else if (!delay) {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
+ entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
} else {
- entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
- entry->interceptKeyWakeupTime = now() + delay;
+ entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
+ entry.interceptKeyWakeupTime = now() + delay;
}
- entry->release();
}
void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) {
@@ -5070,11 +5130,11 @@
bool restartEvent;
if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
- KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+ KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry));
restartEvent =
afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
} else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
- MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+ MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry));
restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
handled);
} else {
@@ -5109,20 +5169,20 @@
bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
DispatchEntry* dispatchEntry,
- KeyEntry* keyEntry, bool handled) {
- if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) {
+ KeyEntry& keyEntry, bool handled) {
+ if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
if (!handled) {
// Report the key as unhandled, since the fallback was not handled.
- mReporter->reportUnhandledKey(keyEntry->id);
+ mReporter->reportUnhandledKey(keyEntry.id);
}
return false;
}
// Get the fallback key state.
// Clear it out after dispatching the UP.
- int32_t originalKeyCode = keyEntry->keyCode;
+ int32_t originalKeyCode = keyEntry.keyCode;
int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode);
- if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+ if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
connection->inputState.removeFallbackKey(originalKeyCode);
}
@@ -5135,16 +5195,15 @@
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Asking policy to cancel fallback action. "
"keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
- keyEntry->policyFlags);
+ keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
#endif
- KeyEvent event = createKeyEvent(*keyEntry);
+ KeyEvent event = createKeyEvent(keyEntry);
event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
mLock.unlock();
mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event,
- keyEntry->policyFlags, &event);
+ keyEntry.policyFlags, &event);
mLock.lock();
@@ -5163,13 +5222,13 @@
// If the application did not handle a non-fallback key, first check
// that we are in a good state to perform unhandled key event processing
// Then ask the policy what to do with it.
- bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN && keyEntry->repeatCount == 0;
+ bool initialDown = keyEntry.action == AKEY_EVENT_ACTION_DOWN && keyEntry.repeatCount == 0;
if (fallbackKeyCode == -1 && !initialDown) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Skipping unhandled key event processing "
"since this is not an initial down. "
"keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- originalKeyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
+ originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
#endif
return false;
}
@@ -5178,15 +5237,15 @@
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Asking policy to perform fallback action. "
"keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
- keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
+ keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags);
#endif
- KeyEvent event = createKeyEvent(*keyEntry);
+ KeyEvent event = createKeyEvent(keyEntry);
mLock.unlock();
bool fallback =
mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
- &event, keyEntry->policyFlags, &event);
+ &event, keyEntry.policyFlags, &event);
mLock.lock();
@@ -5234,7 +5293,7 @@
fallback = false;
fallbackKeyCode = AKEYCODE_UNKNOWN;
- if (keyEntry->action != AKEY_EVENT_ACTION_UP) {
+ if (keyEntry.action != AKEY_EVENT_ACTION_UP) {
connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
}
}
@@ -5254,22 +5313,22 @@
if (fallback) {
// Restart the dispatch cycle using the fallback key.
- keyEntry->eventTime = event.getEventTime();
- keyEntry->deviceId = event.getDeviceId();
- keyEntry->source = event.getSource();
- keyEntry->displayId = event.getDisplayId();
- keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
- keyEntry->keyCode = fallbackKeyCode;
- keyEntry->scanCode = event.getScanCode();
- keyEntry->metaState = event.getMetaState();
- keyEntry->repeatCount = event.getRepeatCount();
- keyEntry->downTime = event.getDownTime();
- keyEntry->syntheticRepeat = false;
+ keyEntry.eventTime = event.getEventTime();
+ keyEntry.deviceId = event.getDeviceId();
+ keyEntry.source = event.getSource();
+ keyEntry.displayId = event.getDisplayId();
+ keyEntry.flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK;
+ keyEntry.keyCode = fallbackKeyCode;
+ keyEntry.scanCode = event.getScanCode();
+ keyEntry.metaState = event.getMetaState();
+ keyEntry.repeatCount = event.getRepeatCount();
+ keyEntry.downTime = event.getDownTime();
+ keyEntry.syntheticRepeat = false;
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("Unhandled key event: Dispatching fallback key. "
"originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
- originalKeyCode, fallbackKeyCode, keyEntry->metaState);
+ originalKeyCode, fallbackKeyCode, keyEntry.metaState);
#endif
return true; // restart the event
} else {
@@ -5278,7 +5337,7 @@
#endif
// Report the key as unhandled, since there is no fallback key.
- mReporter->reportUnhandledKey(keyEntry->id);
+ mReporter->reportUnhandledKey(keyEntry.id);
}
}
return false;
@@ -5286,7 +5345,7 @@
bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection,
DispatchEntry* dispatchEntry,
- MotionEntry* motionEntry, bool handled) {
+ MotionEntry& motionEntry, bool handled) {
return false;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 235a8d3..5387c40 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -158,9 +158,9 @@
sp<Looper> mLooper;
- EventEntry* mPendingEvent GUARDED_BY(mLock);
- std::deque<EventEntry*> mInboundQueue GUARDED_BY(mLock);
- std::deque<EventEntry*> mRecentQueue GUARDED_BY(mLock);
+ std::shared_ptr<EventEntry> mPendingEvent GUARDED_BY(mLock);
+ std::deque<std::shared_ptr<EventEntry>> mInboundQueue GUARDED_BY(mLock);
+ std::deque<std::shared_ptr<EventEntry>> mRecentQueue GUARDED_BY(mLock);
std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
DropReason mLastDropReason GUARDED_BY(mLock);
@@ -175,7 +175,7 @@
void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
// Enqueues an inbound event. Returns true if mLooper->wake() should be called.
- bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
+ bool enqueueInboundEventLocked(std::unique_ptr<EventEntry> entry) REQUIRES(mLock);
// Cleans up input state when dropping an inbound event.
void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock);
@@ -185,7 +185,7 @@
std::string_view reason) REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
- void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
+ void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
// App switch latency optimization.
bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
@@ -197,7 +197,7 @@
// Blocked event latency optimization. Drops old events when the user intends
// to transfer focus to a new application.
- EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
+ std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
TouchState* touchState,
@@ -210,6 +210,8 @@
sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
REQUIRES(mLock);
+ std::string getConnectionNameLocked(const sp<IBinder>& connectionToken) const REQUIRES(mLock);
+
void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
struct IBinderHash {
@@ -242,21 +244,21 @@
// Event injection and synchronization.
std::condition_variable mInjectionResultAvailable;
bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
- void setInjectionResult(EventEntry* entry,
+ void setInjectionResult(EventEntry& entry,
android::os::InputEventInjectionResult injectionResult);
std::condition_variable mInjectionSyncFinished;
- void incrementPendingForegroundDispatches(EventEntry* entry);
- void decrementPendingForegroundDispatches(EventEntry* entry);
+ void incrementPendingForegroundDispatches(EventEntry& entry);
+ void decrementPendingForegroundDispatches(EventEntry& entry);
// Key repeat tracking.
struct KeyRepeatState {
- KeyEntry* lastKeyEntry; // or null if no repeat
+ std::shared_ptr<KeyEntry> lastKeyEntry; // or null if no repeat
nsecs_t nextRepeatTime;
} mKeyRepeatState GUARDED_BY(mLock);
void resetKeyRepeatLocked() REQUIRES(mLock);
- KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock);
+ std::shared_ptr<KeyEntry> synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock);
// Key replacement tracking
struct KeyReplacement {
@@ -292,7 +294,7 @@
// Inbound event processing.
void drainInboundQueueLocked() REQUIRES(mLock);
void releasePendingEventLocked() REQUIRES(mLock);
- void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
+ void releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
// Dispatch state.
bool mDispatchEnabled GUARDED_BY(mLock);
@@ -356,15 +358,17 @@
const std::vector<InputTarget>& targets) REQUIRES(mLock);
// Dispatch inbound events.
- bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry)
+ bool dispatchConfigurationChangedLocked(nsecs_t currentTime,
+ const ConfigurationChangedEntry& entry) REQUIRES(mLock);
+ bool dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry)
REQUIRES(mLock);
- bool dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock);
- bool dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason,
- nsecs_t* nextWakeupTime) REQUIRES(mLock);
- bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason,
- nsecs_t* nextWakeupTime) REQUIRES(mLock);
- void dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) REQUIRES(mLock);
- void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
+ bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry,
+ DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ bool dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
+ DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry)
+ REQUIRES(mLock);
+ void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry,
const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
@@ -461,6 +465,7 @@
float obscuringOpacity;
std::string obscuringPackage;
int32_t obscuringUid;
+ std::vector<std::string> debugInfo;
};
TouchOcclusionInfo computeTouchOcclusionInfoLocked(const sp<InputWindowHandle>& windowHandle,
@@ -469,6 +474,7 @@
bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
int32_t y) const REQUIRES(mLock);
bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
+ std::string dumpWindowForTouchOcclusion(const InputWindowInfo* info, bool isTouchWindow) const;
std::string getApplicationWindowLabel(
const std::shared_ptr<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle);
@@ -478,12 +484,12 @@
// 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)
+ std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
REQUIRES(mLock);
void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget& inputTarget)
+ std::shared_ptr<EventEntry>, const InputTarget& inputTarget)
REQUIRES(mLock);
- void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry,
+ void enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry>,
const InputTarget& inputTarget, int32_t dispatchMode)
REQUIRES(mLock);
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
@@ -517,7 +523,8 @@
REQUIRES(mLock);
// Splitting motion events across windows.
- MotionEntry* splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds);
+ std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry,
+ BitSet32 pointerIds);
// Reset and drop everything the dispatcher is doing.
void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
@@ -527,6 +534,7 @@
void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
void logDispatchStateLocked() REQUIRES(mLock);
std::string dumpFocusedWindowsLocked() REQUIRES(mLock);
+ std::string dumpPendingFocusRequestsLocked() REQUIRES(mLock);
// Registration.
void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock);
@@ -566,10 +574,10 @@
REQUIRES(mLock);
void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, KeyEntry* keyEntry,
+ DispatchEntry* dispatchEntry, KeyEntry& keyEntry,
bool handled) REQUIRES(mLock);
bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, MotionEntry* motionEntry,
+ DispatchEntry* dispatchEntry, MotionEntry& motionEntry,
bool handled) REQUIRES(mLock);
void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
KeyEvent createKeyEvent(const KeyEntry& entry);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 386056d..1656a21 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -265,17 +265,18 @@
}
}
-std::vector<EventEntry*> InputState::synthesizeCancelationEvents(
+std::vector<std::unique_ptr<EventEntry>> InputState::synthesizeCancelationEvents(
nsecs_t currentTime, const CancelationOptions& options) {
- std::vector<EventEntry*> events;
+ std::vector<std::unique_ptr<EventEntry>> events;
for (KeyMemento& memento : mKeyMementos) {
if (shouldCancelKey(memento, options)) {
- events.push_back(new KeyEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
- memento.source, memento.displayId, memento.policyFlags,
- AKEY_EVENT_ACTION_UP,
- memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode,
- memento.scanCode, memento.metaState, 0 /*repeatCount*/,
- memento.downTime));
+ events.push_back(
+ std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId,
+ memento.source, memento.displayId,
+ memento.policyFlags, AKEY_EVENT_ACTION_UP,
+ memento.flags | AKEY_EVENT_FLAG_CANCELED,
+ memento.keyCode, memento.scanCode, memento.metaState,
+ 0 /*repeatCount*/, memento.downTime));
}
}
@@ -283,22 +284,26 @@
if (shouldCancelMotion(memento, options)) {
const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
: AMOTION_EVENT_ACTION_CANCEL;
- events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
- memento.source, memento.displayId, memento.policyFlags,
- action, 0 /*actionButton*/, memento.flags, AMETA_NONE,
- 0 /*buttonState*/, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
- memento.yPrecision, memento.xCursorPosition,
- memento.yCursorPosition, memento.downTime,
- memento.pointerCount, memento.pointerProperties,
- memento.pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/));
+ events.push_back(
+ std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
+ memento.deviceId, memento.source,
+ memento.displayId, memento.policyFlags, action,
+ 0 /*actionButton*/, memento.flags, AMETA_NONE,
+ 0 /*buttonState*/, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+ memento.yPrecision, memento.xCursorPosition,
+ memento.yCursorPosition, memento.downTime,
+ memento.pointerCount, memento.pointerProperties,
+ memento.pointerCoords, 0 /*xOffset*/,
+ 0 /*yOffset*/));
}
}
return events;
}
-std::vector<EventEntry*> InputState::synthesizePointerDownEvents(nsecs_t currentTime) {
- std::vector<EventEntry*> events;
+std::vector<std::unique_ptr<EventEntry>> InputState::synthesizePointerDownEvents(
+ nsecs_t currentTime) {
+ std::vector<std::unique_ptr<EventEntry>> events;
for (MotionMemento& memento : mMotionMementos) {
if (!(memento.source & AINPUT_SOURCE_CLASS_POINTER)) {
continue;
@@ -333,15 +338,17 @@
: AMOTION_EVENT_ACTION_POINTER_DOWN
| (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
- memento.source, memento.displayId, memento.policyFlags,
- action, 0 /*actionButton*/, memento.flags, AMETA_NONE,
- 0 /*buttonState*/, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
- memento.yPrecision, memento.xCursorPosition,
- memento.yCursorPosition, memento.downTime,
- pointerCount, pointerProperties, pointerCoords,
- 0 /*xOffset*/, 0 /*yOffset*/));
+ events.push_back(
+ std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime,
+ memento.deviceId, memento.source,
+ memento.displayId, memento.policyFlags, action,
+ 0 /*actionButton*/, memento.flags, AMETA_NONE,
+ 0 /*buttonState*/, MotionClassification::NONE,
+ AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+ memento.yPrecision, memento.xCursorPosition,
+ memento.yCursorPosition, memento.downTime,
+ pointerCount, pointerProperties, pointerCoords,
+ 0 /*xOffset*/, 0 /*yOffset*/));
}
memento.firstNewPointerIdx = INVALID_POINTER_INDEX;
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index d97a664..74ae21f 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -51,11 +51,11 @@
bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags);
// Synthesizes cancelation events for the current state and resets the tracked state.
- std::vector<EventEntry*> synthesizeCancelationEvents(nsecs_t currentTime,
- const CancelationOptions& options);
+ std::vector<std::unique_ptr<EventEntry>> synthesizeCancelationEvents(
+ nsecs_t currentTime, const CancelationOptions& options);
// Synthesizes down events for the current state.
- std::vector<EventEntry*> synthesizePointerDownEvents(nsecs_t currentTime);
+ std::vector<std::unique_ptr<EventEntry>> synthesizePointerDownEvents(nsecs_t currentTime);
// Clears the current state.
void clear();
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 6465cc9..8cb7194 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -48,4 +48,5 @@
"libc++fs"
],
require_root: true,
+ test_suites: ["device-tests"],
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index dc32003..40471b2 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -198,7 +198,7 @@
std::condition_variable mNotifyAnr;
std::chrono::nanoseconds mAnrTimeout = 0ms;
- virtual void notifyConfigurationChanged(nsecs_t when) override {
+ void notifyConfigurationChanged(nsecs_t when) override {
std::scoped_lock lock(mLock);
mConfigurationChangedTime = when;
}
@@ -213,17 +213,17 @@
return mAnrTimeout;
}
- virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
+ void notifyInputChannelBroken(const sp<IBinder>&) override {}
- virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+ void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
- virtual void notifyUntrustedTouch(const std::string& obscuringPackage) override {}
+ void notifyUntrustedTouch(const std::string& obscuringPackage) override {}
- virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
+ void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
*outConfig = mConfig;
}
- virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+ bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
std::scoped_lock lock(mLock);
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY: {
@@ -241,22 +241,20 @@
return true;
}
- virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+ void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
- virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+ void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
- virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
- uint32_t) override {
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override {
return 0;
}
- virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
- KeyEvent*) override {
+ bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override {
return false;
}
- virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
- uint32_t policyFlags) override {
+ void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+ uint32_t policyFlags) override {
std::scoped_lock lock(mLock);
/** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
* essentially a passthrough for notifySwitch.
@@ -264,13 +262,11 @@
mLastNotifySwitch = NotifySwitchArgs(1 /*id*/, when, policyFlags, switchValues, switchMask);
}
- virtual void pokeUserActivity(nsecs_t, int32_t) override {}
+ void pokeUserActivity(nsecs_t, int32_t) override {}
- virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
- return false;
- }
+ bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
- virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
+ void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
std::scoped_lock lock(mLock);
mOnPointerDownToken = newToken;
}
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 211b49e..a72d5c3 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -33,6 +33,8 @@
#include <math.h>
#include <memory>
+#include "input/DisplayViewport.h"
+#include "input/Input.h"
namespace android {
@@ -1233,6 +1235,47 @@
}
/**
+ * When we have multiple internal displays make sure we always return the default display when
+ * querying by type.
+ */
+TEST_F(InputReaderPolicyTest, Viewports_ByTypeReturnsDefaultForInternal) {
+ const std::string uniqueId1 = "uniqueId1";
+ const std::string uniqueId2 = "uniqueId2";
+ constexpr int32_t nonDefaultDisplayId = 2;
+ static_assert(nonDefaultDisplayId != ADISPLAY_ID_DEFAULT,
+ "Test display ID should not be ADISPLAY_ID_DEFAULT");
+
+ // Add the default display first and ensure it gets returned.
+ mFakePolicy->clearViewports();
+ mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT,
+ ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT,
+ ViewportType::INTERNAL);
+
+ std::optional<DisplayViewport> viewport =
+ mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ ASSERT_TRUE(viewport);
+ ASSERT_EQ(ADISPLAY_ID_DEFAULT, viewport->displayId);
+ ASSERT_EQ(ViewportType::INTERNAL, viewport->type);
+
+ // Add the default display second to make sure order doesn't matter.
+ mFakePolicy->clearViewports();
+ mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT,
+ ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT,
+ ViewportType::INTERNAL);
+
+ viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ ASSERT_TRUE(viewport);
+ ASSERT_EQ(ADISPLAY_ID_DEFAULT, viewport->displayId);
+ ASSERT_EQ(ViewportType::INTERNAL, viewport->type);
+}
+
+/**
* Check getDisplayViewportByPort
*/
TEST_F(InputReaderPolicyTest, Viewports_GetByPort) {
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 3cccaf9..6810c1b7 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -161,7 +161,7 @@
Mutex::Autolock _l(mConnectionLock);
sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
if (si == nullptr ||
- !canAccessSensor(si->getSensor(), "Tried adding", mOpPackageName) ||
+ !canAccessSensor(si->getSensor(), "Add to SensorEventConnection: ", mOpPackageName) ||
mSensorInfo.count(handle) > 0) {
return false;
}
@@ -460,8 +460,12 @@
mTargetSdk > 0 && mTargetSdk <= __ANDROID_API_P__) {
success = true;
} else {
+ int32_t sensorHandle = event.sensor;
+ String16 noteMsg("Sensor event (");
+ noteMsg.append(String16(mService->getSensorStringType(sensorHandle)));
+ noteMsg.append(String16(")"));
int32_t appOpMode = mService->sAppOpsManager.noteOp(iter->second, mUid,
- mOpPackageName);
+ mOpPackageName, {}, noteMsg);
success = (appOpMode == AppOpsManager::MODE_ALLOWED);
}
}
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index 0ce32cc..85ce0f0 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -57,6 +57,12 @@
mNonSensor.getName());
}
+String8 SensorList::getStringType(int handle) const {
+ return getOne<String8>(
+ handle, [] (const Entry& e) -> String8 {return e.si->getSensor().getStringType();},
+ mNonSensor.getStringType());
+}
+
sp<SensorInterface> SensorList::getInterface(int handle) const {
return getOne<sp<SensorInterface>>(
handle, [] (const Entry& e) -> sp<SensorInterface> {return e.si;}, nullptr);
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 8424b22..617ceef 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -53,6 +53,8 @@
const Vector<Sensor> getVirtualSensors() const;
String8 getName(int handle) const;
+ String8 getStringType(int handle) const;
+
sp<SensorInterface> getInterface(int handle) const;
bool isNewHandle(int handle) const;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 2969839..8f25bdb 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1112,6 +1112,10 @@
return mSensors.getName(handle);
}
+String8 SensorService::getSensorStringType(int handle) const {
+ return mSensors.getStringType(handle);
+}
+
bool SensorService::isVirtualSensor(int handle) const {
sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
return sensor != nullptr && sensor->isVirtual();
@@ -1807,9 +1811,6 @@
}
const int32_t opCode = sensor.getRequiredAppOp();
- const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
- IPCThreadState::self()->getCallingUid(), opPackageName);
- bool appOpAllowed = appOpMode == AppOpsManager::MODE_ALLOWED;
int targetSdkVersion = getTargetSdkVersion(opPackageName);
bool canAccess = false;
@@ -1822,14 +1823,16 @@
canAccess = true;
} else if (hasPermissionForSensor(sensor)) {
// Ensure that the AppOp is allowed, or that there is no necessary app op for the sensor
- if (opCode < 0 || appOpAllowed) {
+ if (opCode >= 0) {
+ const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
+ IPCThreadState::self()->getCallingUid(), opPackageName);
+ canAccess = (appOpMode == AppOpsManager::MODE_ALLOWED);
+ } else {
canAccess = true;
}
}
- if (canAccess) {
- sAppOpsManager.noteOp(opCode, IPCThreadState::self()->getCallingUid(), opPackageName);
- } else {
+ if (!canAccess) {
ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).string(),
operation, sensor.getName().string(), sensor.getRequiredPermission().string());
}
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 052cbfe..50c7c2f 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -295,6 +295,7 @@
virtual status_t dump(int fd, const Vector<String16>& args);
status_t dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock) const;
String8 getSensorName(int handle) const;
+ String8 getSensorStringType(int handle) const;
bool isVirtualSensor(int handle) const;
sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
bool isWakeUpSensor(int type) const;
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index c77298e..fa75ffa 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -204,8 +204,8 @@
layer.frameNumber = mCurrentFrameNumber;
layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
- // TODO: we could be more subtle with isFixedSize()
- const bool useFiltering = targetSettings.needsFiltering || mNeedsFiltering || isFixedSize();
+ const bool useFiltering =
+ targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering();
// Query the texture matrix given our current filtering mode.
float textureMatrix[16];
@@ -393,6 +393,15 @@
nsecs_t expectedPresentTime) {
ATRACE_CALL();
+ // If this is not a valid vsync for the layer's uid, return and try again later
+ const bool isVsyncValidForUid =
+ mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid);
+ if (!isVsyncValidForUid) {
+ ATRACE_NAME("!isVsyncValidForUid");
+ mFlinger->setTransactionFlags(eTraversalNeeded);
+ return false;
+ }
+
bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
if (refreshRequired) {
@@ -527,10 +536,6 @@
}
uint32_t BufferLayer::getEffectiveScalingMode() const {
- if (mOverrideScalingMode >= 0) {
- return mOverrideScalingMode;
- }
-
return mBufferInfo.mScaleMode;
}
@@ -826,6 +831,10 @@
}
}
+bool BufferLayer::bufferNeedsFiltering() const {
+ return isFixedSize();
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 1cd753b..deaf846 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -94,8 +94,7 @@
bool hasReadyFrame() const override;
- // Returns the current scaling mode, unless mOverrideScalingMode
- // is set, in which case, it returns mOverrideScalingMode
+ // Returns the current scaling mode
uint32_t getEffectiveScalingMode() const override;
// Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
@@ -116,6 +115,10 @@
ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
+ // Returns true if the transformed buffer size does not match the layer size and we need
+ // to apply filtering.
+ virtual bool bufferNeedsFiltering() const;
+
protected:
struct BufferInfo {
nsecs_t mDesiredPresentTime;
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 7c73df2..69d2d11 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -503,6 +503,9 @@
BufferLayerConsumer::Image::Image(const sp<GraphicBuffer>& graphicBuffer,
renderengine::RenderEngine& engine)
: mGraphicBuffer(graphicBuffer), mRE(engine) {
+ if (graphicBuffer != nullptr && (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED)) {
+ return;
+ }
mRE.cacheExternalTextureBuffer(mGraphicBuffer);
}
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 0863a22..71b05fd 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -239,7 +239,7 @@
bool queuedBuffer = false;
const int32_t layerId = getSequence();
LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
- getProducerStickyTransform() != 0, mName, mOverrideScalingMode,
+ getProducerStickyTransform() != 0, mName,
getTransformToDisplayInverse());
if (isRemovedFromCurrentState()) {
@@ -442,7 +442,8 @@
}
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
+ mName, mFrameTimelineVsyncId);
surfaceFrame->setActualQueueTime(systemTime());
mQueueItems.push_back({item, std::move(surfaceFrame)});
@@ -480,7 +481,8 @@
}
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(mName, mFrameTimelineVsyncId);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName,
+ mName, mFrameTimelineVsyncId);
surfaceFrame->setActualQueueTime(systemTime());
mQueueItems[mQueueItems.size() - 1].item = item;
mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 361c1f3..a64b243 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -48,7 +48,6 @@
BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args)
: BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) {
- mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
}
@@ -263,6 +262,8 @@
bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
nsecs_t postTime, nsecs_t desiredPresentTime,
const client_cache_t& clientCacheId, uint64_t frameNumber) {
+ ATRACE_CALL();
+
if (mCurrentState.buffer) {
mReleasePreviousBuffer = true;
}
@@ -276,7 +277,7 @@
const int32_t layerId = getSequence();
mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
- postTime);
+ mOwnerUid, postTime);
desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
mCurrentState.desiredPresentTime = desiredPresentTime;
@@ -681,6 +682,10 @@
mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
}
+uint32_t BufferStateLayer::getEffectiveScalingMode() const {
+ return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+}
+
Rect BufferStateLayer::computeCrop(const State& s) {
if (s.crop.isEmpty() && s.buffer) {
return s.buffer->getBounds();
@@ -739,6 +744,31 @@
static_cast<float>(s.active.transform.ty() + s.active.h)),
radius);
}
+
+bool BufferStateLayer::bufferNeedsFiltering() const {
+ const State& s(getDrawingState());
+ if (!s.buffer) {
+ return false;
+ }
+
+ uint32_t bufferWidth = s.buffer->width;
+ uint32_t bufferHeight = s.buffer->height;
+
+ // Undo any transformations on the buffer and return the result.
+ if (s.transform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+
+ if (s.transformToDisplayInverse) {
+ uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
+ if (invTransform & ui::Transform::ROT_90) {
+ std::swap(bufferWidth, bufferHeight);
+ }
+ }
+
+ const Rect layerSize{getBounds()};
+ return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight;
+}
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index c13f5e8..104a13b 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -92,7 +92,6 @@
return false;
}
bool setCrop_legacy(const Rect& /*crop*/) override { return false; }
- bool setOverrideScalingMode(int32_t /*overrideScalingMode*/) override { return false; }
void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
uint64_t /*frameNumber*/) override {}
void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/,
@@ -110,6 +109,7 @@
bool fenceHasSignaled() const override;
bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
bool onPreComposition(nsecs_t refreshStartTime) override;
+ uint32_t getEffectiveScalingMode() const override;
protected:
void gatherBufferInfo() override;
@@ -143,6 +143,8 @@
bool willPresentCurrentTransaction() const;
+ bool bufferNeedsFiltering() const override;
+
static const std::array<float, 16> IDENTITY_MATRIX;
std::unique_ptr<renderengine::Image> mTextureImage;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 77400eb..5a3b9ac 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -20,6 +20,7 @@
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
+#include <ui/BlurRegion.h>
#include <ui/FloatRect.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -118,6 +119,9 @@
// length of the shadow in screen space
float shadowRadius{0.f};
+ // List of regions that require blur
+ std::vector<BlurRegion> blurRegions;
+
/*
* Geometry state
*/
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index d590f23..3be1cc4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -167,6 +167,11 @@
const Rect& orientedDisplaySpaceRect) = 0;
// Sets the bounds to use
virtual void setDisplaySize(const ui::Size&) = 0;
+ // Gets the transform hint used in layers that belong to this output. Used to guide
+ // composition orientation so that HW overlay can be used when display isn't in its natural
+ // orientation on some devices. Therefore usually we only use transform hint from display
+ // output.
+ virtual ui::Transform::RotationFlags getTransformHint() const = 0;
// Sets the layer stack filtering settings for this output. See
// belongsInOutput for full details.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 5b832a5..651230c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -42,6 +42,7 @@
const Rect& orientedDisplaySpaceRect) override;
void setDisplaySize(const ui::Size&) override;
void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
+ ui::Transform::RotationFlags getTransformHint() const override;
void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
void setColorProfile(const ColorProfile&) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index d6fbd7f..95db4da 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -39,6 +39,7 @@
MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&));
MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
+ MOCK_CONST_METHOD0(getTransformHint, ui::Transform::RotationFlags());
MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 04dceae..3852f45 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -192,6 +192,10 @@
dirtyEntireOutput();
}
+ui::Transform::RotationFlags Output::getTransformHint() const {
+ return static_cast<ui::Transform::RotationFlags>(getState().transform.getOrientation());
+}
+
void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
auto& outputState = editState();
outputState.layerStackId = layerStackId;
@@ -665,7 +669,8 @@
compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const {
compositionengine::OutputLayer* layerRequestingBgComposition = nullptr;
for (auto* layer : getOutputLayersOrderedByZ()) {
- if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0) {
+ if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0 ||
+ layer->getLayerFE().getCompositionState()->blurRegions.size() > 0) {
layerRequestingBgComposition = layer;
}
}
@@ -897,6 +902,8 @@
needsProtected == renderEngine.isProtected()) {
mRenderSurface->setProtected(needsProtected);
}
+ } else if (!outputState.isSecure && renderEngine.isProtected()) {
+ renderEngine.useProtectedContext(false);
}
base::unique_fd fd;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 6d01bf1..1befbf8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -160,6 +160,7 @@
EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
}
DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index b5cda68..84c027b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -81,6 +81,7 @@
MOCK_METHOD2(onHotplug,
std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection));
+ MOCK_CONST_METHOD0(updatesDeviceProductInfoOnHotplugReconnect, bool());
MOCK_METHOD2(onVsync, bool(hal::HWDisplayId, int64_t));
MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync));
MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(PhysicalDisplayId));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 1e10365..9badb99 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -268,6 +268,8 @@
EXPECT_EQ(orientation, state.framebufferSpace.orientation);
EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content));
+
+ EXPECT_EQ(ui::Transform::ROT_90, mOutput->getTransformHint());
}
TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) {
@@ -2950,6 +2952,7 @@
mOutput.mState.usesClientComposition = false;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
@@ -2962,6 +2965,7 @@
mOutput.mState.flipClientTarget = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
@@ -2971,6 +2975,7 @@
TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFailsForClientComposition) {
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
@@ -2983,6 +2988,7 @@
mOutput.mState.flipClientTarget = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
@@ -2993,6 +2999,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3015,6 +3022,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3042,6 +3050,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3070,6 +3079,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3098,6 +3108,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3129,6 +3140,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r2}))
.WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r3}));
@@ -3151,6 +3163,7 @@
struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComposeSurfacesTest {
OutputComposeSurfacesTest_UsesExpectedDisplaySettings() {
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
.WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
@@ -3298,6 +3311,8 @@
mOutput.mState.isSecure = false;
mLayer2.mLayerFEState.hasProtectedContent = true;
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
+ EXPECT_CALL(mRenderEngine, useProtectedContext(false)).WillOnce(Return(true));
mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
}
@@ -3390,6 +3405,7 @@
EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
.WillRepeatedly(Return());
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
@@ -3998,6 +4014,34 @@
mOutput->updateAndWriteCompositionState(args);
}
+TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) {
+ InjectedLayer layer1;
+ InjectedLayer layer2;
+ InjectedLayer layer3;
+
+ // Layer requesting blur, or below, should request client composition.
+ EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+ EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+ EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+ EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+ EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+
+ BlurRegion region;
+ layer2.layerFEState.blurRegions.push_back(region);
+
+ injectOutputLayer(layer1);
+ injectOutputLayer(layer2);
+ injectOutputLayer(layer3);
+
+ mOutput->editState().isEnabled = true;
+
+ CompositionRefreshArgs args;
+ args.updatingGeometryThisFrame = false;
+ args.devOptForceClientComposition = false;
+ mOutput->updateAndWriteCompositionState(args);
+}
+
TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
// In split-screen landscape mode, the screen is rotated 90 degrees, with
// one layer on the left covering the left side of the output, and one layer
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 5bd7a1f..cbc201f 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -248,6 +248,10 @@
return mCompositionDisplay->getState().layerStackId;
}
+ui::Transform::RotationFlags DisplayDevice::getTransformHint() const {
+ return mCompositionDisplay->getTransformHint();
+}
+
const ui::Transform& DisplayDevice::getTransform() const {
return mCompositionDisplay->getState().transform;
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index fa684c0..cc38ab0 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -95,10 +95,7 @@
static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
- ui::Transform::RotationFlags getTransformHint() const {
- return static_cast<ui::Transform::RotationFlags>(getTransform().getOrientation());
- }
-
+ ui::Transform::RotationFlags getTransformHint() const;
const ui::Transform& getTransform() const;
const Rect& getLayerStackSpaceRect() const;
const Rect& getOrientedDisplaySpaceRect() const;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 2fdbd3a..1548d18 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -26,6 +26,7 @@
#include "HWComposer.h"
+#include <android-base/properties.h>
#include <compositionengine/Output.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -38,6 +39,7 @@
#include "../Layer.h" // needed only for debugging
#include "../Promise.h"
#include "../SurfaceFlinger.h"
+#include "../SurfaceFlingerProperties.h"
#include "ComposerHal.h"
#include "HWC2.h"
@@ -143,12 +145,13 @@
namespace impl {
-HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) : mComposer(std::move(composer)) {
-}
+HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer)
+ : mComposer(std::move(composer)),
+ mUpdateDeviceProductInfoOnHotplugReconnect(
+ android::sysprop::update_device_product_info_on_hotplug_reconnect(false)) {}
HWComposer::HWComposer(const std::string& composerServiceName)
- : mComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {
-}
+ : HWComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {}
HWComposer::~HWComposer() {
mDisplayData.clear();
@@ -204,6 +207,10 @@
}
}
+bool HWComposer::updatesDeviceProductInfoOnHotplugReconnect() const {
+ return mUpdateDeviceProductInfoOnHotplugReconnect;
+}
+
bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) {
const auto displayId = toPhysicalDisplayId(hwcDisplayId);
if (!displayId) {
@@ -888,6 +895,16 @@
info = DisplayIdentificationInfo{.id = *displayId,
.name = std::string(),
.deviceProductInfo = std::nullopt};
+ if (mUpdateDeviceProductInfoOnHotplugReconnect) {
+ uint8_t port;
+ DisplayIdentificationData data;
+ getDisplayIdentificationData(hwcDisplayId, &port, &data);
+ if (auto newInfo = parseDisplayIdentificationData(port, data)) {
+ info->deviceProductInfo = std::move(newInfo->deviceProductInfo);
+ } else {
+ ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId);
+ }
+ }
} else {
uint8_t port;
DisplayIdentificationData data;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 028a9a4..d8af5bf 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -173,6 +173,10 @@
virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId,
hal::Connection) = 0;
+ // If true we'll update the DeviceProductInfo on subsequent hotplug connected events.
+ // TODO(b/157555476): Remove when the framework has proper support for headless mode
+ virtual bool updatesDeviceProductInfoOnHotplugReconnect() const = 0;
+
virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0;
virtual void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) = 0;
@@ -303,10 +307,12 @@
// Events handling ---------------------------------------------------------
- // Returns stable display ID (and display name on connection of new or previously disconnected
+ // Returns PhysicalDisplayId (and display name on connection of new or previously disconnected
// display), or std::nullopt if hotplug event was ignored.
std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, hal::Connection) override;
+ bool updatesDeviceProductInfoOnHotplugReconnect() const override;
+
bool onVsync(hal::HWDisplayId, int64_t timestamp) override;
void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) override;
@@ -400,6 +406,8 @@
bool mHasMultiDisplaySupport = false;
RandomDisplayIdGenerator<HalVirtualDisplayId> mVirtualIdGenerator{getMaxVirtualDisplayCount()};
+
+ const bool mUpdateDeviceProductInfoOnHotplugReconnect;
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
index 9a6b328..20486e0 100644
--- a/services/surfaceflinger/DisplayRenderArea.cpp
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -26,8 +26,7 @@
return RenderArea::RotationFlags::ROT_0;
}
- const ui::Rotation orientation = display.getPhysicalOrientation() + display.getOrientation();
- return ui::Transform::toRotationFlags(orientation);
+ return ui::Transform::toRotationFlags(display.getOrientation());
}
} // namespace
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 6ba4c43..e075d3e 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -5,10 +5,12 @@
"FrameTimeline.cpp",
],
shared_libs: [
+ "android.hardware.graphics.composer@2.4",
"libbase",
"libcutils",
"liblog",
"libgui",
+ "libtimestats",
"libui",
"libutils",
],
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 43176a3..bd87482 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -93,19 +93,19 @@
}
}
-std::string toString(JankType jankType) {
+std::string toString(TimeStats::JankType jankType) {
switch (jankType) {
- case JankType::None:
+ case TimeStats::JankType::None:
return "None";
- case JankType::Display:
+ case TimeStats::JankType::Display:
return "Composer/Display - outside SF and App";
- case JankType::SurfaceFlingerDeadlineMissed:
+ case TimeStats::JankType::SurfaceFlingerDeadlineMissed:
return "SurfaceFlinger Deadline Missed";
- case JankType::AppDeadlineMissed:
+ case TimeStats::JankType::AppDeadlineMissed:
return "App Deadline Missed";
- case JankType::PredictionExpired:
+ case TimeStats::JankType::PredictionExpired:
return "Prediction Expired";
- case JankType::SurfaceFlingerEarlyLatch:
+ case TimeStats::JankType::SurfaceFlingerEarlyLatch:
return "SurfaceFlinger Early Latch";
default:
return "Unclassified";
@@ -177,15 +177,19 @@
}
}
-SurfaceFrame::SurfaceFrame(const std::string& layerName, PredictionState predictionState,
+SurfaceFrame::SurfaceFrame(pid_t ownerPid, uid_t ownerUid, std::string layerName,
+ std::string debugName, PredictionState predictionState,
frametimeline::TimelineItem&& predictions)
- : mLayerName(layerName),
+ : mOwnerPid(ownerPid),
+ mOwnerUid(ownerUid),
+ mLayerName(std::move(layerName)),
+ mDebugName(std::move(debugName)),
mPresentState(PresentState::Unknown),
mPredictionState(predictionState),
mPredictions(predictions),
mActuals({0, 0, 0}),
mActualQueueTime(0),
- mJankType(JankType::None),
+ mJankType(TimeStats::JankType::None),
mJankMetadata(0) {}
void SurfaceFrame::setPresentState(PresentState state) {
@@ -227,13 +231,13 @@
mActuals.presentTime = presentTime;
}
-void SurfaceFrame::setJankInfo(JankType jankType, int32_t jankMetadata) {
+void SurfaceFrame::setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata) {
std::lock_guard<std::mutex> lock(mMutex);
mJankType = jankType;
mJankMetadata = jankMetadata;
}
-JankType SurfaceFrame::getJankType() const {
+TimeStats::JankType SurfaceFrame::getJankType() const {
std::lock_guard<std::mutex> lock(mMutex);
return mJankType;
}
@@ -267,13 +271,15 @@
void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) {
std::lock_guard<std::mutex> lock(mMutex);
StringAppendF(&result, "%s", indent.c_str());
- StringAppendF(&result, "Layer - %s", mLayerName.c_str());
- if (mJankType != JankType::None) {
+ StringAppendF(&result, "Layer - %s", mDebugName.c_str());
+ if (mJankType != TimeStats::JankType::None) {
// Easily identify a janky Surface Frame in the dump
StringAppendF(&result, " [*] ");
}
StringAppendF(&result, "\n");
StringAppendF(&result, "%s", indent.c_str());
+ StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid);
+ StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Present State : %s\n", presentStateToString(mPresentState).c_str());
StringAppendF(&result, "%s", indent.c_str());
StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str());
@@ -285,32 +291,37 @@
dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime);
}
-FrameTimeline::FrameTimeline()
+FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats)
: mCurrentDisplayFrame(std::make_shared<DisplayFrame>()),
- mMaxDisplayFrames(kDefaultMaxDisplayFrames) {}
+ mMaxDisplayFrames(kDefaultMaxDisplayFrames),
+ mTimeStats(std::move(timeStats)) {}
FrameTimeline::DisplayFrame::DisplayFrame()
: surfaceFlingerPredictions(TimelineItem()),
surfaceFlingerActuals(TimelineItem()),
predictionState(PredictionState::None),
- jankType(JankType::None),
+ jankType(TimeStats::JankType::None),
jankMetadata(0) {
this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
}
std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
- const std::string& layerName, std::optional<int64_t> token) {
+ pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
+ std::optional<int64_t> token) {
ATRACE_CALL();
if (!token) {
- return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::None,
+ return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
+ std::move(debugName), PredictionState::None,
TimelineItem());
}
std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
if (predictions) {
- return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Valid,
+ return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
+ std::move(debugName), PredictionState::Valid,
std::move(*predictions));
}
- return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Expired,
+ return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName),
+ std::move(debugName), PredictionState::Expired,
TimelineItem());
}
@@ -359,6 +370,7 @@
}
}
if (signalTime != Fence::SIGNAL_TIME_INVALID) {
+ int32_t totalJankReasons = TimeStats::JankType::None;
auto& displayFrame = pendingPresentFence.second;
displayFrame->surfaceFlingerActuals.presentTime = signalTime;
@@ -377,21 +389,26 @@
displayFrame->jankMetadata |= EarlyFinish;
}
- if (displayFrame->jankMetadata & EarlyFinish & EarlyPresent) {
- displayFrame->jankType = JankType::SurfaceFlingerEarlyLatch;
- } else if (displayFrame->jankMetadata & LateFinish & LatePresent) {
- displayFrame->jankType = JankType::SurfaceFlingerDeadlineMissed;
+ if ((displayFrame->jankMetadata & EarlyFinish) &&
+ (displayFrame->jankMetadata & EarlyPresent)) {
+ displayFrame->jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch;
+ } else if ((displayFrame->jankMetadata & LateFinish) &&
+ (displayFrame->jankMetadata & LatePresent)) {
+ displayFrame->jankType = TimeStats::JankType::SurfaceFlingerDeadlineMissed;
} else if (displayFrame->jankMetadata & EarlyPresent ||
displayFrame->jankMetadata & LatePresent) {
// Cases where SF finished early but frame was presented late and vice versa
- displayFrame->jankType = JankType::Display;
+ displayFrame->jankType = TimeStats::JankType::Display;
}
}
+
if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) {
displayFrame->jankMetadata |=
sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart;
}
+ totalJankReasons |= displayFrame->jankType;
+
for (auto& surfaceFrame : displayFrame->surfaceFrames) {
if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
// Only presented SurfaceFrames need to be updated
@@ -401,13 +418,13 @@
const auto& predictionState = surfaceFrame->getPredictionState();
if (predictionState == PredictionState::Expired) {
// Jank analysis cannot be done on apps that don't use predictions
- surfaceFrame->setJankInfo(JankType::PredictionExpired, 0);
+ surfaceFrame->setJankInfo(TimeStats::JankType::PredictionExpired, 0);
continue;
} else if (predictionState == PredictionState::Valid) {
const auto& actuals = surfaceFrame->getActuals();
const auto& predictions = surfaceFrame->getPredictions();
int32_t jankMetadata = 0;
- JankType jankType = JankType::None;
+ TimeStats::JankType jankType = TimeStats::JankType::None;
if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) {
jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish
: EarlyFinish;
@@ -419,19 +436,26 @@
: EarlyPresent;
}
if (jankMetadata & EarlyPresent) {
- jankType = JankType::SurfaceFlingerEarlyLatch;
+ jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch;
} else if (jankMetadata & LatePresent) {
if (jankMetadata & EarlyFinish) {
// TODO(b/169890654): Classify this properly
- jankType = JankType::Display;
+ jankType = TimeStats::JankType::Display;
} else {
- jankType = JankType::AppDeadlineMissed;
+ jankType = TimeStats::JankType::AppDeadlineMissed;
}
}
+
+ totalJankReasons |= jankType;
+ mTimeStats->incrementJankyFrames(surfaceFrame->getOwnerUid(),
+ surfaceFrame->getName(),
+ jankType | displayFrame->jankType);
surfaceFrame->setJankInfo(jankType, jankMetadata);
}
}
}
+
+ mTimeStats->incrementJankyFrames(totalJankReasons);
}
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
@@ -467,7 +491,7 @@
void FrameTimeline::dumpDisplayFrame(std::string& result,
const std::shared_ptr<DisplayFrame>& displayFrame,
nsecs_t baseTime) {
- if (displayFrame->jankType != JankType::None) {
+ if (displayFrame->jankType != TimeStats::JankType::None) {
// Easily identify a janky Display Frame in the dump
StringAppendF(&result, " [*] ");
}
@@ -501,11 +525,11 @@
nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]);
for (size_t i = 0; i < mDisplayFrames.size(); i++) {
const auto& displayFrame = mDisplayFrames[i];
- if (displayFrame->jankType == JankType::None) {
+ if (displayFrame->jankType == TimeStats::JankType::None) {
// Check if any Surface Frame has been janky
bool isJanky = false;
for (const auto& surfaceFrame : displayFrame->surfaceFrames) {
- if (surfaceFrame->getJankType() != JankType::None) {
+ if (surfaceFrame->getJankType() != TimeStats::JankType::None) {
isJanky = true;
break;
}
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index bd637df..e61567e 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -16,9 +16,7 @@
#pragma once
-#include <deque>
-#include <mutex>
-
+#include <../TimeStats/TimeStats.h>
#include <gui/ISurfaceComposer.h>
#include <ui/FenceTime.h>
#include <utils/RefBase.h>
@@ -26,26 +24,10 @@
#include <utils/Timers.h>
#include <utils/Vector.h>
-namespace android::frametimeline {
+#include <deque>
+#include <mutex>
-/*
- * The type of jank that is associated with a Display/Surface frame
- */
-enum class JankType {
- // No Jank
- None,
- // Jank not related to SurfaceFlinger or the App
- Display,
- // SF took too long on the CPU
- SurfaceFlingerDeadlineMissed,
- // Either App or GPU took too long on the frame
- AppDeadlineMissed,
- // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a jank
- // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
- PredictionExpired,
- // Latching a buffer early might cause an early present of the frame
- SurfaceFlingerEarlyLatch,
-};
+namespace android::frametimeline {
enum JankMetadata {
// Frame was presented earlier than expected
@@ -125,6 +107,7 @@
virtual nsecs_t getActualQueueTime() const = 0;
virtual PresentState getPresentState() const = 0;
virtual PredictionState getPredictionState() const = 0;
+ virtual pid_t getOwnerPid() const = 0;
virtual void setPresentState(PresentState state) = 0;
@@ -147,8 +130,10 @@
// Create a new surface frame, set the predictions based on a token and return it to the caller.
// Sets the PredictionState of SurfaceFrame.
+ // Debug name is the human-readable debugging string for dumpsys.
virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken(
- const std::string& layerName, std::optional<int64_t> token) = 0;
+ pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
+ std::optional<int64_t> token) = 0;
// Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
// composited into one display frame.
@@ -206,8 +191,8 @@
class SurfaceFrame : public android::frametimeline::SurfaceFrame {
public:
- SurfaceFrame(const std::string& layerName, PredictionState predictionState,
- TimelineItem&& predictions);
+ SurfaceFrame(pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
+ PredictionState predictionState, TimelineItem&& predictions);
~SurfaceFrame() = default;
TimelineItem getPredictions() const override { return mPredictions; };
@@ -215,38 +200,46 @@
nsecs_t getActualQueueTime() const override;
PresentState getPresentState() const override;
PredictionState getPredictionState() const override { return mPredictionState; };
+ pid_t getOwnerPid() const override { return mOwnerPid; };
+ TimeStats::JankType getJankType() const;
+ nsecs_t getBaseTime() const;
+ uid_t getOwnerUid() const { return mOwnerUid; };
+ const std::string& getName() const { return mLayerName; };
void setActualStartTime(nsecs_t actualStartTime) override;
void setActualQueueTime(nsecs_t actualQueueTime) override;
void setAcquireFenceTime(nsecs_t acquireFenceTime) override;
void setPresentState(PresentState state) override;
void setActualPresentTime(nsecs_t presentTime);
- void setJankInfo(JankType jankType, int32_t jankMetadata);
- JankType getJankType() const;
- nsecs_t getBaseTime() const;
+ void setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata);
+
// All the timestamps are dumped relative to the baseTime
void dump(std::string& result, const std::string& indent, nsecs_t baseTime);
private:
+ const pid_t mOwnerPid;
+ const uid_t mOwnerUid;
const std::string mLayerName;
+ const std::string mDebugName;
PresentState mPresentState GUARDED_BY(mMutex);
const PredictionState mPredictionState;
const TimelineItem mPredictions;
TimelineItem mActuals GUARDED_BY(mMutex);
nsecs_t mActualQueueTime GUARDED_BY(mMutex);
mutable std::mutex mMutex;
- JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank
+ TimeStats::JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank
int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank
};
class FrameTimeline : public android::frametimeline::FrameTimeline {
public:
- FrameTimeline();
+ FrameTimeline(std::shared_ptr<TimeStats> timeStats);
~FrameTimeline() = default;
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken(
- const std::string& layerName, std::optional<int64_t> token) override;
+ pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName,
+ std::optional<int64_t> token) override;
void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame,
SurfaceFrame::PresentState state) override;
void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override;
@@ -278,7 +271,7 @@
std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames;
PredictionState predictionState;
- JankType jankType = JankType::None; // Enum for the type of jank
+ TimeStats::JankType jankType = TimeStats::JankType::None; // Enum for the type of jank
int32_t jankMetadata = 0x0; // Additional details about the jank
};
@@ -300,6 +293,7 @@
TokenManager mTokenManager;
std::mutex mMutex;
uint32_t mMaxDisplayFrames;
+ std::shared_ptr<TimeStats> mTimeStats;
static constexpr uint32_t kDefaultMaxDisplayFrames = 64;
// The initial container size for the vector<SurfaceFrames> inside display frame. Although this
// number doesn't represent any bounds on the number of surface frames that can go in a display
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d9faec4..9481966 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -150,9 +150,11 @@
if (mCallingUid == AID_GRAPHICS || mCallingUid == AID_SYSTEM) {
// If the system didn't send an ownerUid, use the callingUid for the ownerUid.
mOwnerUid = args.metadata.getInt32(METADATA_OWNER_UID, mCallingUid);
+ mOwnerPid = args.metadata.getInt32(METADATA_OWNER_PID, mCallingPid);
} else {
// A create layer request from a non system request cannot specify the owner uid
mOwnerUid = mCallingUid;
+ mOwnerPid = mCallingPid;
}
}
@@ -482,6 +484,7 @@
compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
compositionState->alpha = alpha;
compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+ compositionState->blurRegions = drawingState.blurRegions;
}
void Layer::prepareGeometryCompositionState() {
@@ -550,7 +553,8 @@
isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
// Force client composition for special cases known only to the front-end.
- if (isHdrY410() || usesRoundedCorners || drawShadows()) {
+ if (isHdrY410() || usesRoundedCorners || drawShadows() ||
+ getDrawingState().blurRegions.size() > 0) {
compositionState->forceClientComposition = true;
}
}
@@ -646,6 +650,7 @@
layerSettings.alpha = alpha;
layerSettings.sourceDataspace = getDataSpace();
layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+ layerSettings.blurRegions = getBlurRegions();
return layerSettings;
}
@@ -897,7 +902,9 @@
: std::make_optional(stateToCommit->frameTimelineVsyncId);
auto surfaceFrame =
- mFlinger->mFrameTimeline->createSurfaceFrameForToken(mTransactionName, vsyncId);
+ mFlinger->mFrameTimeline->createSurfaceFrameForToken(getOwnerPid(), getOwnerUid(),
+ mName, mTransactionName,
+ vsyncId);
surfaceFrame->setActualQueueTime(stateToCommit->postTime);
// For transactions we set the acquire fence time to the post time as we
// don't have a buffer. For BufferStateLayer it is overridden in
@@ -1034,6 +1041,12 @@
mCurrentState.inputInfoChanged = false;
}
+ // Add the callbacks from the drawing state into the current state. This is so when the current
+ // state gets copied to drawing, we don't lose the callback handles that are still in drawing.
+ for (auto& handle : s.callbackHandles) {
+ c.callbackHandles.push_back(handle);
+ }
+
// Commit the transaction
commitTransaction(c);
mPendingStatesSnapshot = mPendingStates;
@@ -1279,6 +1292,14 @@
return true;
}
+bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) {
+ mCurrentState.sequence++;
+ mCurrentState.blurRegions = blurRegions;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
+
bool Layer::setFlags(uint8_t flags, uint8_t mask) {
const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask);
if (mCurrentState.flags == newFlags) return false;
@@ -1300,13 +1321,6 @@
return true;
}
-bool Layer::setOverrideScalingMode(int32_t scalingMode) {
- if (scalingMode == mOverrideScalingMode) return false;
- mOverrideScalingMode = scalingMode;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
bool Layer::setMetadata(const LayerMetadata& data) {
if (!mCurrentState.metadata.merge(data, true /* eraseEmpty */)) return false;
mCurrentState.sequence++;
@@ -1462,7 +1476,6 @@
}
void Layer::setFrameTimelineVsyncForTransaction(int64_t frameTimelineVsyncId, nsecs_t postTime) {
- mCurrentState.sequence++;
mCurrentState.frameTimelineVsyncId = frameTimelineVsyncId;
mCurrentState.postTime = postTime;
mCurrentState.modified = true;
@@ -1730,7 +1743,7 @@
FrameEventHistoryDelta* outDelta) {
if (newTimestamps) {
mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
- getName().c_str(), newTimestamps->postedTime);
+ getName().c_str(), mOwnerUid, newTimestamps->postedTime);
mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber,
newTimestamps->acquireFence);
}
@@ -2174,6 +2187,10 @@
return getDrawingState().backgroundBlurRadius;
}
+const std::vector<BlurRegion>& Layer::getBlurRegions() const {
+ return getDrawingState().blurRegions;
+}
+
Layer::RoundedCornerState Layer::getRoundedCornerState() const {
const auto& p = mDrawingParent.promote();
if (p != nullptr) {
@@ -2453,6 +2470,7 @@
ySurfaceInset = std::round(ySurfaceInset * yScale);
}
+ // Transform the layer bounds from layer coordinate space to display coordinate space.
Rect transformedLayerBounds = t.transform(layerBounds);
// clamp inset to layer bounds
@@ -2477,12 +2495,6 @@
transformedLayerBounds.bottom = tmp;
}
- // Input coordinate should match the layer bounds.
- info.frameLeft = transformedLayerBounds.left;
- info.frameTop = transformedLayerBounds.top;
- info.frameRight = transformedLayerBounds.right;
- info.frameBottom = transformedLayerBounds.bottom;
-
// Compute the correct transform to send to input. This will allow it to transform the
// input coordinates from display space into window space. Therefore, it needs to use the
// final layer frame to create the inverse transform. Since surface insets are added later,
@@ -2490,7 +2502,7 @@
// the final frame calculated.
// 1. Take the original transform set on the window and get the inverse transform. This is
// used to get the final bounds in display space (ignorning the transform). Apply the
- // inverse transform on the layerBounds to get the untransformed frame (in display space)
+ // inverse transform on the layerBounds to get the untransformed frame (in layer space)
// 2. Take the top and left of the untransformed frame to get the real position on screen.
// Apply the layer transform on top/left so it includes any scale or rotation. These will
// be the new translation values for the transform.
@@ -2504,6 +2516,16 @@
inputTransform.set(translation.x, translation.y);
info.transform = inputTransform.inverse();
+ // We need to send the layer bounds cropped to the screenbounds since the layer can be cropped.
+ // The frame should be the area the user sees on screen since it's used for occlusion
+ // detection.
+ Rect screenBounds = Rect{mScreenBounds};
+ transformedLayerBounds.intersect(screenBounds, &transformedLayerBounds);
+ info.frameLeft = transformedLayerBounds.left;
+ info.frameTop = transformedLayerBounds.top;
+ info.frameRight = transformedLayerBounds.right;
+ info.frameBottom = transformedLayerBounds.bottom;
+
// Position the touchable region relative to frame screen location and restrict it to frame
// bounds.
info.touchableRegion = inputTransform.transform(info.touchableRegion);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 02593d5..b1ab9ec 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -26,6 +26,7 @@
#include <renderengine/Mesh.h>
#include <renderengine/Texture.h>
#include <sys/types.h>
+#include <ui/BlurRegion.h>
#include <ui/FloatRect.h>
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
@@ -255,6 +256,9 @@
// be rendered around the layer.
float shadowRadius;
+ // Layer regions that are made of custom materials, like frosted glass
+ std::vector<BlurRegion> blurRegions;
+
// Priority of the layer assigned by Window Manager.
int32_t frameRateSelectionPriority;
@@ -341,7 +345,6 @@
//
// The first set of geometry functions are controlled by the scaling mode, described
// in window.h. The scaling mode may be set by the client, as it submits buffers.
- // This value may be overriden through SurfaceControl, with setOverrideScalingMode.
//
// Put simply, if our scaling mode is SCALING_MODE_FREEZE, then
// matrix updates will not be applied while a resize is pending
@@ -392,6 +395,7 @@
// When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which
// is specified in pixels.
virtual bool setBackgroundBlurRadius(int backgroundBlurRadius);
+ virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions);
virtual bool setTransparentRegionHint(const Region& transparent);
virtual bool setFlags(uint8_t flags, uint8_t mask);
virtual bool setLayerStack(uint32_t layerStack);
@@ -399,7 +403,6 @@
virtual void deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle,
uint64_t frameNumber);
virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
- virtual bool setOverrideScalingMode(int32_t overrideScalingMode);
virtual bool setMetadata(const LayerMetadata& data);
virtual void setChildrenDrawingParent(const sp<Layer>&);
virtual bool reparent(const sp<IBinder>& newParentHandle);
@@ -467,7 +470,7 @@
virtual bool canReceiveInput() const;
/*
- * isProtected - true if the layer may contain protected content in the
+ * isProtected - true if the layer may contain protected contents in the
* GRALLOC_USAGE_PROTECTED sense.
*/
virtual bool isProtected() const { return false; }
@@ -679,7 +682,8 @@
/*
* isSecure - true if this surface is secure, that is if it prevents
- * screenshots or VNC servers.
+ * screenshots or VNC servers. A surface can be set to be secure by the
+ * application, being secure doesn't mean the surface has DRM contents.
*/
bool isSecure() const;
@@ -851,6 +855,8 @@
uid_t getOwnerUid() { return mOwnerUid; }
+ pid_t getOwnerPid() { return mOwnerPid; }
+
// This layer is not a clone, but it's the parent to the cloned hierarchy. The
// variable mClonedChild represents the top layer that will be cloned so this
// layer will be the parent of mClonedChild.
@@ -1003,7 +1009,6 @@
bool mIsActiveBufferUpdatedForGpu = true;
// We encode unset as -1.
- int32_t mOverrideScalingMode{-1};
std::atomic<uint64_t> mCurrentFrameNumber{0};
// Whether filtering is needed b/c of the drawingstate
bool mNeedsFiltering{false};
@@ -1040,6 +1045,14 @@
// Can only be accessed with the SF state lock held.
std::unique_ptr<frametimeline::SurfaceFrame> mSurfaceFrame;
+ // The owner of the layer. If created from a non system process, it will be the calling uid.
+ // If created from a system process, the value can be passed in.
+ uid_t mOwnerUid;
+
+ // The owner pid of the layer. If created from a non system process, it will be the calling pid.
+ // If created from a system process, the value can be passed in.
+ pid_t mOwnerPid;
+
private:
virtual void setTransformHint(ui::Transform::RotationFlags) {}
@@ -1076,7 +1089,8 @@
sp<Layer> getRootLayer();
// Cached properties computed from drawing state
- // Effective transform taking into account parent transforms and any parent scaling.
+ // Effective transform taking into account parent transforms and any parent scaling, which is
+ // a transform from the current layer coordinate space to display(screen) coordinate space.
ui::Transform mEffectiveTransform;
// Bounds of the layer before any transformation is applied and before it has been cropped
@@ -1097,10 +1111,6 @@
pid_t mCallingPid;
uid_t mCallingUid;
- // The owner of the layer. If created from a non system process, it will be the calling uid.
- // If created from a system process, the value can be passed in.
- uid_t mOwnerUid;
-
// The current layer is a clone of mClonedFrom. This means that this layer will update it's
// properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
// this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
@@ -1111,6 +1121,9 @@
// final shadow radius for this layer. If a shadow is specified for a layer, then effective
// shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
float mEffectiveShadowRadius = 0.f;
+
+ // A list of regions on this layer that should have blurs.
+ const std::vector<BlurRegion>& getBlurRegions() const;
};
} // namespace android
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index e6c8654..053b7f7 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -29,13 +29,12 @@
LayerRejecter::LayerRejecter(Layer::State& front, Layer::State& current,
bool& recomputeVisibleRegions, bool stickySet, const std::string& name,
- int32_t overrideScalingMode, bool transformToDisplayInverse)
+ bool transformToDisplayInverse)
: mFront(front),
mCurrent(current),
mRecomputeVisibleRegions(recomputeVisibleRegions),
mStickyTransformSet(stickySet),
mName(name),
- mOverrideScalingMode(overrideScalingMode),
mTransformToDisplayInverse(transformToDisplayInverse) {}
bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
@@ -59,7 +58,7 @@
}
}
- int actualScalingMode = mOverrideScalingMode >= 0 ? mOverrideScalingMode : item.mScalingMode;
+ int actualScalingMode = item.mScalingMode;
bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE;
if (mFront.active_legacy != mFront.requested_legacy) {
if (isFixedSize ||
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
index fb5c750..4981f45 100644
--- a/services/surfaceflinger/LayerRejecter.h
+++ b/services/surfaceflinger/LayerRejecter.h
@@ -24,7 +24,7 @@
class LayerRejecter : public BufferLayerConsumer::BufferRejecter {
public:
LayerRejecter(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions,
- bool stickySet, const std::string& name, int32_t overrideScalingMode,
+ bool stickySet, const std::string& name,
bool transformToDisplayInverse);
virtual bool reject(const sp<GraphicBuffer>&, const BufferItem&);
@@ -35,7 +35,6 @@
bool& mRecomputeVisibleRegions;
const bool mStickyTransformSet;
const std::string& mName;
- const int32_t mOverrideScalingMode;
const bool mTransformToDisplayInverse;
};
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index bf2a509..bf5be47 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -31,6 +31,8 @@
#include <android-base/stringprintf.h>
+#include <binder/IPCThreadState.h>
+
#include <cutils/compiler.h>
#include <cutils/sched_policy.h>
@@ -123,11 +125,12 @@
} // namespace
-EventThreadConnection::EventThreadConnection(EventThread* eventThread,
+EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid,
ResyncCallback resyncCallback,
ISurfaceComposer::ConfigChanged configChanged)
: resyncCallback(std::move(resyncCallback)),
mConfigChanged(configChanged),
+ mOwnerUid(callingUid),
mEventThread(eventThread),
mChannel(gui::BitTube::DefaultSize) {}
@@ -170,10 +173,12 @@
EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
android::frametimeline::TokenManager* tokenManager,
- InterceptVSyncsCallback interceptVSyncsCallback)
+ InterceptVSyncsCallback interceptVSyncsCallback,
+ ThrottleVsyncCallback throttleVsyncCallback)
: mVSyncSource(std::move(vsyncSource)),
mTokenManager(tokenManager),
mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
+ mThrottleVsyncCallback(std::move(throttleVsyncCallback)),
mThreadName(mVSyncSource->getName()) {
mVSyncSource->setCallback(this);
@@ -216,8 +221,9 @@
sp<EventThreadConnection> EventThread::createEventConnection(
ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const {
- return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback),
- configChanged);
+ return new EventThreadConnection(const_cast<EventThread*>(this),
+ IPCThreadState::self()->getCallingUid(),
+ std::move(resyncCallback), configChanged);
}
status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) {
@@ -443,6 +449,11 @@
bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event,
const sp<EventThreadConnection>& connection) const {
+ const auto throttleVsync = [&] {
+ return mThrottleVsyncCallback &&
+ mThrottleVsyncCallback(event.vsync.expectedVSyncTimestamp, connection->mOwnerUid);
+ };
+
switch (event.header.type) {
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
return true;
@@ -458,12 +469,22 @@
case VSyncRequest::SingleSuppressCallback:
connection->vsyncRequest = VSyncRequest::None;
return false;
- case VSyncRequest::Single:
+ case VSyncRequest::Single: {
+ if (throttleVsync()) {
+ return false;
+ }
connection->vsyncRequest = VSyncRequest::SingleSuppressCallback;
return true;
+ }
case VSyncRequest::Periodic:
+ if (throttleVsync()) {
+ return false;
+ }
return true;
default:
+ // We don't throttle vsync if the app set a vsync request rate
+ // since there is no easy way to do that and this is a very
+ // rare case
return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0;
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index e42ca05..2e2d989 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -81,7 +81,7 @@
class EventThreadConnection : public BnDisplayEventConnection {
public:
- EventThreadConnection(EventThread*, ResyncCallback,
+ EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
ISurfaceComposer::ConfigChanged configChanged);
virtual ~EventThreadConnection();
@@ -98,6 +98,8 @@
const ISurfaceComposer::ConfigChanged mConfigChanged =
ISurfaceComposer::ConfigChanged::eConfigChangedSuppress;
+ const uid_t mOwnerUid;
+
private:
virtual void onFirstRef();
EventThread* const mEventThread;
@@ -143,9 +145,10 @@
class EventThread : public android::EventThread, private VSyncSource::Callback {
public:
using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
+ using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>;
- EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*,
- InterceptVSyncsCallback);
+ EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback,
+ ThrottleVsyncCallback);
~EventThread();
sp<EventThreadConnection> createEventConnection(
@@ -196,6 +199,7 @@
frametimeline::TokenManager* const mTokenManager;
const InterceptVSyncsCallback mInterceptVSyncsCallback;
+ const ThrottleVsyncCallback mThrottleVsyncCallback;
const char* const mThreadName;
std::thread mThread;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 1343375..47a4f42 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -90,6 +90,7 @@
{
std::lock_guard lock(mVsync.mutex);
mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
+ mVsync.mScheduled = false;
}
mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions(
{targetWakeupTime, readyTime, vsyncTime}),
@@ -114,6 +115,10 @@
ATRACE_CALL();
std::lock_guard lock(mVsync.mutex);
mVsync.workDuration = workDuration;
+ if (mVsync.mScheduled) {
+ mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
+ mVsync.lastCallbackTime.count()});
+ }
}
void MessageQueue::waitMessage() {
@@ -147,13 +152,10 @@
if (mEvents) {
mEvents->requestNextVsync();
} else {
- const auto [workDuration, lastVsyncCallback] = [&] {
- std::lock_guard lock(mVsync.mutex);
- std::chrono::nanoseconds mWorkDurationNanos = mVsync.workDuration;
- return std::make_pair(mWorkDurationNanos.count(), mVsync.lastCallbackTime.count());
- }();
-
- mVsync.registration->schedule({workDuration, /*readyDuration=*/0, lastVsyncCallback});
+ std::lock_guard lock(mVsync.mutex);
+ mVsync.mScheduled = true;
+ mVsync.registration->schedule({mVsync.workDuration.get().count(), /*readyDuration=*/0,
+ mVsync.lastCallbackTime.count()});
}
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 139b38e..99ce3a6 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -109,6 +109,7 @@
TracedOrdinal<std::chrono::nanoseconds> workDuration
GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)};
std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0};
+ bool mScheduled GUARDED_BY(mutex) = false;
TracedOrdinal<int> value = {"VSYNC-sf", 0};
};
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index a90d05e..2783800 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -20,6 +20,21 @@
#include <sstream>
#include <thread>
+namespace {
+using namespace std::chrono_literals;
+
+constexpr int64_t kNsToSeconds = std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+
+// The syscall interface uses a pair of integers for the timestamp. The first
+// (tv_sec) is the whole count of seconds. The second (tv_nsec) is the
+// nanosecond part of the count. This function takes care of translation.
+void calculateTimeoutTime(std::chrono::nanoseconds timestamp, timespec* spec) {
+ clock_gettime(CLOCK_MONOTONIC, spec);
+ spec->tv_sec += static_cast<__kernel_time_t>(timestamp.count() / kNsToSeconds);
+ spec->tv_nsec += timestamp.count() % kNsToSeconds;
+}
+} // namespace
+
namespace android {
namespace scheduler {
@@ -32,81 +47,95 @@
}
void OneShotTimer::start() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mState = TimerState::RESET;
+ sem_init(&mSemaphore, 0, 0);
+
+ if (!mThread.joinable()) {
+ // Only create thread if it has not been created.
+ mThread = std::thread(&OneShotTimer::loop, this);
}
- mThread = std::thread(&OneShotTimer::loop, this);
}
void OneShotTimer::stop() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mState = TimerState::STOPPED;
- }
- mCondition.notify_all();
+ mStopTriggered = true;
+ sem_post(&mSemaphore);
+
if (mThread.joinable()) {
mThread.join();
+ sem_destroy(&mSemaphore);
}
}
void OneShotTimer::loop() {
+ TimerState state = TimerState::RESET;
while (true) {
bool triggerReset = false;
bool triggerTimeout = false;
- {
- std::lock_guard<std::mutex> lock(mMutex);
- if (mState == TimerState::STOPPED) {
- break;
- }
- if (mState == TimerState::IDLE) {
- mCondition.wait(mMutex);
- continue;
- }
-
- if (mState == TimerState::RESET) {
- triggerReset = true;
- }
+ state = checkForResetAndStop(state);
+ if (state == TimerState::STOPPED) {
+ break;
}
+
+ if (state == TimerState::IDLE) {
+ sem_wait(&mSemaphore);
+ continue;
+ }
+
+ if (state == TimerState::RESET) {
+ triggerReset = true;
+ }
+
if (triggerReset && mResetCallback) {
mResetCallback();
}
- { // lock the mutex again. someone might have called stop meanwhile
- std::lock_guard<std::mutex> lock(mMutex);
- if (mState == TimerState::STOPPED) {
- break;
- }
+ state = checkForResetAndStop(state);
+ if (state == TimerState::STOPPED) {
+ break;
+ }
- auto triggerTime = std::chrono::steady_clock::now() + mInterval;
- mState = TimerState::WAITING;
- while (mState == TimerState::WAITING) {
- constexpr auto zero = std::chrono::steady_clock::duration::zero();
- auto waitTime = triggerTime - std::chrono::steady_clock::now();
- if (waitTime > zero) mCondition.wait_for(mMutex, waitTime);
- if (mState == TimerState::RESET) {
- triggerTime = std::chrono::steady_clock::now() + mInterval;
- mState = TimerState::WAITING;
- } else if (mState == TimerState::WAITING &&
- (triggerTime - std::chrono::steady_clock::now()) <= zero) {
- triggerTimeout = true;
- mState = TimerState::IDLE;
- }
+ auto triggerTime = std::chrono::steady_clock::now() + mInterval;
+ state = TimerState::WAITING;
+ while (state == TimerState::WAITING) {
+ constexpr auto zero = std::chrono::steady_clock::duration::zero();
+ // Wait for mInterval time for semaphore signal.
+ struct timespec ts;
+ calculateTimeoutTime(std::chrono::nanoseconds(mInterval), &ts);
+ sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &ts);
+
+ state = checkForResetAndStop(state);
+ if (state == TimerState::RESET) {
+ triggerTime = std::chrono::steady_clock::now() + mInterval;
+ state = TimerState::WAITING;
+ } else if (state == TimerState::WAITING &&
+ (triggerTime - std::chrono::steady_clock::now()) <= zero) {
+ triggerTimeout = true;
+ state = TimerState::IDLE;
}
}
+
if (triggerTimeout && mTimeoutCallback) {
mTimeoutCallback();
}
}
}
-void OneShotTimer::reset() {
- {
- std::lock_guard<std::mutex> lock(mMutex);
- mState = TimerState::RESET;
+OneShotTimer::TimerState OneShotTimer::checkForResetAndStop(TimerState state) {
+ // Stop takes precedence of the reset.
+ if (mStopTriggered.exchange(false)) {
+ return TimerState::STOPPED;
}
- mCondition.notify_all();
+ // If the state was stopped, the thread was joined, and we cannot reset
+ // the timer anymore.
+ if (state != TimerState::STOPPED && mResetTriggered.exchange(false)) {
+ return TimerState::RESET;
+ }
+ return state;
+}
+
+void OneShotTimer::reset() {
+ mResetTriggered = true;
+ sem_post(&mSemaphore);
}
std::string OneShotTimer::dump() const {
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index b005754..8bbd4f5 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -16,6 +16,7 @@
#pragma once
+#include <semaphore.h>
#include <chrono>
#include <condition_variable>
#include <thread>
@@ -70,17 +71,15 @@
// Function that loops until the condition for stopping is met.
void loop();
+ // Checks whether mResetTriggered and mStopTriggered were set and updates
+ // mState if so.
+ TimerState checkForResetAndStop(TimerState state);
+
// Thread waiting for timer to expire.
std::thread mThread;
- // Condition used to notify mThread.
- std::condition_variable_any mCondition;
-
- // Lock used for synchronizing the waiting thread with the application thread.
- std::mutex mMutex;
-
- // Current timer state
- TimerState mState GUARDED_BY(mMutex) = TimerState::RESET;
+ // Semaphore to keep mThread synchronized.
+ sem_t mSemaphore;
// Interval after which timer expires.
const Interval mInterval;
@@ -90,6 +89,12 @@
// Callback that happens when timer expires.
const TimeoutCallback mTimeoutCallback;
+
+ // After removing lock guarding mState, the state can be now accessed at
+ // any time. Keep a bool if the reset or stop were requested, and occasionally
+ // check in the main loop if they were.
+ std::atomic<bool> mResetTriggered = false;
+ std::atomic<bool> mStopTriggered = false;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 8661b6e..150f925 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -48,6 +48,13 @@
}
}
+std::string RefreshRateConfigs::Policy::toString() const {
+ return base::StringPrintf("default config ID: %d, allowGroupSwitching = %d"
+ ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]",
+ defaultConfig.value(), allowGroupSwitching, primaryRange.min,
+ primaryRange.max, appRequestRange.min, appRequestRange.max);
+}
+
const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
const std::vector<LayerRequirement>& layers) const {
std::lock_guard lock(mLock);
@@ -426,10 +433,12 @@
// defaultConfig must be a valid config, and within the given refresh rate range.
auto iter = mRefreshRates.find(policy.defaultConfig);
if (iter == mRefreshRates.end()) {
+ ALOGE("Default config is not found.");
return false;
}
const RefreshRate& refreshRate = *iter->second;
if (!refreshRate.inPolicy(policy.primaryRange.min, policy.primaryRange.max)) {
+ ALOGE("Default config is not in the primary range.");
return false;
}
return policy.appRequestRange.min <= policy.primaryRange.min &&
@@ -439,6 +448,7 @@
status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
std::lock_guard lock(mLock);
if (!isPolicyValid(policy)) {
+ ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str());
return BAD_VALUE;
}
Policy previousPolicy = *getCurrentPolicyLocked();
@@ -618,4 +628,36 @@
return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
}
+void RefreshRateConfigs::setPreferredRefreshRateForUid(uid_t uid, float refreshRateHz) {
+ if (refreshRateHz > 0 && refreshRateHz < 1) {
+ return;
+ }
+
+ std::lock_guard lock(mLock);
+ if (refreshRateHz != 0) {
+ mPreferredRefreshRateForUid[uid] = refreshRateHz;
+ } else {
+ mPreferredRefreshRateForUid.erase(uid);
+ }
+}
+
+int RefreshRateConfigs::getRefreshRateDividerForUid(uid_t uid) const {
+ constexpr float kThreshold = 0.1f;
+ std::lock_guard lock(mLock);
+
+ const auto iter = mPreferredRefreshRateForUid.find(uid);
+ if (iter == mPreferredRefreshRateForUid.end()) {
+ return 1;
+ }
+
+ const auto refreshRateHz = iter->second;
+ const auto numPeriods = mCurrentRefreshRate->getFps() / refreshRateHz;
+ const auto numPeriodsRounded = std::round(numPeriods);
+ if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) {
+ return 1;
+ }
+
+ return static_cast<int>(numPeriods);
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index eed8486..8ff92a0 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -108,6 +108,10 @@
std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>;
struct Policy {
+ private:
+ static constexpr int kAllowGroupSwitchingDefault = false;
+
+ public:
struct Range {
float min = 0;
float max = std::numeric_limits<float>::max();
@@ -122,6 +126,8 @@
// The default config, used to ensure we only initiate display config switches within the
// same config group as defaultConfigId's group.
HwcConfigIndexType defaultConfig;
+ // Whether or not we switch config groups to get the best frame rate.
+ bool allowGroupSwitching = kAllowGroupSwitchingDefault;
// The primary refresh rate range represents display manager's general guidance on the
// display configs we'll consider when switching refresh rates. Unless we get an explicit
// signal from an app, we should stay within this range.
@@ -133,15 +139,23 @@
// app request range. The app request range will be greater than or equal to the primary
// refresh rate range, never smaller.
Range appRequestRange;
- // Whether or not we switch config groups to get the best frame rate. Only used by tests.
- bool allowGroupSwitching = false;
Policy() = default;
+
Policy(HwcConfigIndexType defaultConfig, const Range& range)
- : Policy(defaultConfig, range, range) {}
+ : Policy(defaultConfig, kAllowGroupSwitchingDefault, range, range) {}
+
+ Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, const Range& range)
+ : Policy(defaultConfig, allowGroupSwitching, range, range) {}
+
Policy(HwcConfigIndexType defaultConfig, const Range& primaryRange,
const Range& appRequestRange)
+ : Policy(defaultConfig, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {}
+
+ Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching,
+ const Range& primaryRange, const Range& appRequestRange)
: defaultConfig(defaultConfig),
+ allowGroupSwitching(allowGroupSwitching),
primaryRange(primaryRange),
appRequestRange(appRequestRange) {}
@@ -152,6 +166,7 @@
}
bool operator!=(const Policy& other) const { return !(*this == other); }
+ std::string toString() const;
};
// Return code set*Policy() to indicate the current policy is unchanged.
@@ -296,6 +311,13 @@
// refresh rates.
KernelIdleTimerAction getIdleTimerAction() const;
+ // Stores the preferred refresh rate that an app should run at.
+ // refreshRate == 0 means no preference.
+ void setPreferredRefreshRateForUid(uid_t, float refreshRateHz) EXCLUDES(mLock);
+
+ // Returns a divider for the current refresh rate
+ int getRefreshRateDividerForUid(uid_t) const EXCLUDES(mLock);
+
private:
friend class RefreshRateConfigsTest;
@@ -353,6 +375,8 @@
Policy mDisplayManagerPolicy GUARDED_BY(mLock);
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
+ std::unordered_map<uid_t, float> mPreferredRefreshRateForUid GUARDED_BY(mLock);
+
// The min and max refresh rates supported by the device.
// This will not change at runtime.
const RefreshRate* mMinSupportedRefreshRate;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 7b8448f..a14019e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -212,13 +212,26 @@
readyDuration, traceVsync, name);
}
+bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
+ const auto divider = mRefreshRateConfigs.getRefreshRateDividerForUid(uid);
+ if (divider <= 1) {
+ return true;
+ }
+
+ return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, divider);
+}
+
Scheduler::ConnectionHandle Scheduler::createConnection(
const char* connectionName, frametimeline::TokenManager* tokenManager,
std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration);
+ auto throttleVsync = [this](nsecs_t expectedVsyncTimestamp, uid_t uid) {
+ return !isVsyncValid(expectedVsyncTimestamp, uid);
+ };
auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager,
- std::move(interceptCallback));
+ std::move(interceptCallback),
+ std::move(throttleVsync));
return createConnection(std::move(eventThread));
}
@@ -379,7 +392,8 @@
auto eventThread =
std::make_unique<impl::EventThread>(std::move(vsyncSource),
/*tokenManager=*/nullptr,
- impl::EventThread::InterceptVSyncsCallback());
+ impl::EventThread::InterceptVSyncsCallback(),
+ impl::EventThread::ThrottleVsyncCallback());
mInjectorConnectionHandle = createConnection(std::move(eventThread));
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 47ce4a4..4c86d26 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -139,6 +139,10 @@
scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; }
+ // Returns true if a given vsync timestamp is considered valid vsync
+ // for a given uid
+ bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const;
+
void dump(std::string&) const;
void dump(ConnectionHandle, std::string&) const;
void dumpVsync(std::string&) const;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index e90edf7..75d1e6f 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -27,6 +27,9 @@
#include <chrono>
#include <sstream>
+#undef LOG_TAG
+#define LOG_TAG "VSyncPredictor"
+
namespace android::scheduler {
using base::StringAppendF;
@@ -66,7 +69,7 @@
nsecs_t VSyncPredictor::currentPeriod() const {
std::lock_guard lock(mMutex);
- return std::get<0>(mRateMap.find(mIdealPeriod)->second);
+ return mRateMap.find(mIdealPeriod)->second.slope;
}
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
@@ -118,7 +121,7 @@
// normalizing to the oldest timestamp cuts down on error in calculating the intercept.
auto const oldest_ts = *std::min_element(mTimestamps.begin(), mTimestamps.end());
auto it = mRateMap.find(mIdealPeriod);
- auto const currentPeriod = std::get<0>(it->second);
+ auto const currentPeriod = it->second.slope;
// TODO (b/144707443): its important that there's some precision in the mean of the ordinals
// for the intercept calculation, so scale the ordinals by 1000 to continue
// fixed point calculation. Explore expanding
@@ -172,10 +175,8 @@
return true;
}
-nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
- std::lock_guard lock(mMutex);
-
- auto const [slope, intercept] = getVSyncPredictionModel(lock);
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
+ auto const [slope, intercept] = getVSyncPredictionModelLocked();
if (mTimestamps.empty()) {
traceInt64If("VSP-mode", 1);
@@ -210,13 +211,71 @@
return prediction;
}
-std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel() const {
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
std::lock_guard lock(mMutex);
- return VSyncPredictor::getVSyncPredictionModel(lock);
+ return nextAnticipatedVSyncTimeFromLocked(timePoint);
}
-std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel(
- std::lock_guard<std::mutex> const&) const {
+/*
+ * Returns whether a given vsync timestamp is in phase with a vsync divider.
+ * For example, if the vsync timestamps are (0,16,32,48):
+ * isVSyncInPhase(0, 2) = true
+ * isVSyncInPhase(16, 2) = false
+ * isVSyncInPhase(32, 2) = true
+ */
+bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, int divider) const {
+ struct VsyncError {
+ nsecs_t vsyncTimestamp;
+ float error;
+
+ bool operator<(const VsyncError& other) const { return error < other.error; }
+ };
+
+ std::lock_guard lock(mMutex);
+ if (divider <= 1) {
+ return true;
+ }
+
+ const nsecs_t period = mRateMap[mIdealPeriod].slope;
+ const nsecs_t justBeforeTimePoint = timePoint - period / 2;
+ const nsecs_t dividedPeriod = mIdealPeriod / divider;
+
+ // If this is the first time we have asked about this divider with the
+ // current vsync period, it is considered in phase and we store the closest
+ // vsync timestamp
+ const auto knownTimestampIter = mRateDividerKnownTimestampMap.find(dividedPeriod);
+ if (knownTimestampIter == mRateDividerKnownTimestampMap.end()) {
+ const auto vsync = nextAnticipatedVSyncTimeFromLocked(justBeforeTimePoint);
+ mRateDividerKnownTimestampMap[dividedPeriod] = vsync;
+ return true;
+ }
+
+ // Find the next N vsync timestamp where N is the divider.
+ // One of these vsyncs will be in phase. We return the one which is
+ // the most aligned with the last known in phase vsync
+ std::vector<VsyncError> vsyncs(static_cast<size_t>(divider));
+ const nsecs_t knownVsync = knownTimestampIter->second;
+ nsecs_t point = justBeforeTimePoint;
+ for (size_t i = 0; i < divider; i++) {
+ const nsecs_t vsync = nextAnticipatedVSyncTimeFromLocked(point);
+ const auto numPeriods = static_cast<float>(vsync - knownVsync) / (period * divider);
+ const auto error = std::abs(std::round(numPeriods) - numPeriods);
+ vsyncs[i] = {vsync, error};
+ point = vsync + 1;
+ }
+
+ const auto minVsyncError = std::min_element(vsyncs.begin(), vsyncs.end());
+ mRateDividerKnownTimestampMap[dividedPeriod] = minVsyncError->vsyncTimestamp;
+ return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2;
+}
+
+VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const {
+ std::lock_guard lock(mMutex);
+ const auto model = VSyncPredictor::getVSyncPredictionModelLocked();
+ return {model.slope, model.intercept};
+}
+
+VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const {
return mRateMap.find(mIdealPeriod)->second;
}
@@ -269,8 +328,8 @@
for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
StringAppendF(&result,
"\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
- idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f,
- std::get<1>(periodInterceptTuple));
+ idealPeriod / 1e6f, periodInterceptTuple.slope / 1e6f,
+ periodInterceptTuple.intercept);
}
}
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 5f2ec49..381cf81 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -38,10 +38,10 @@
uint32_t outlierTolerancePercent);
~VSyncPredictor();
- bool addVsyncTimestamp(nsecs_t timestamp) final;
- nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
- nsecs_t currentPeriod() const final;
- void resetModel() final;
+ bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex);
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex);
+ nsecs_t currentPeriod() const final EXCLUDES(mMutex);
+ void resetModel() final EXCLUDES(mMutex);
/*
* Inform the model that the period is anticipated to change to a new value.
@@ -50,16 +50,23 @@
*
* \param [in] period The new period that should be used.
*/
- void setPeriod(nsecs_t period) final;
+ void setPeriod(nsecs_t period) final EXCLUDES(mMutex);
/* Query if the model is in need of more samples to make a prediction.
* \return True, if model would benefit from more samples, False if not.
*/
- bool needsMoreSamples() const final;
+ bool needsMoreSamples() const final EXCLUDES(mMutex);
- std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
+ struct Model {
+ nsecs_t slope;
+ nsecs_t intercept;
+ };
- void dump(std::string& result) const final;
+ VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex);
+
+ bool isVSyncInPhase(nsecs_t timePoint, int divider) const final EXCLUDES(mMutex);
+
+ void dump(std::string& result) const final EXCLUDES(mMutex);
private:
VSyncPredictor(VSyncPredictor const&) = delete;
@@ -76,13 +83,19 @@
std::mutex mutable mMutex;
size_t next(size_t i) const REQUIRES(mMutex);
bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
- std::tuple<nsecs_t, nsecs_t> getVSyncPredictionModel(std::lock_guard<std::mutex> const&) const
- REQUIRES(mMutex);
+
+ Model getVSyncPredictionModelLocked() const REQUIRES(mMutex);
+
+ nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex);
nsecs_t mIdealPeriod GUARDED_BY(mMutex);
std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
- std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex);
+ // Map between ideal vsync period and the calculated model
+ std::unordered_map<nsecs_t, Model> mutable mRateMap GUARDED_BY(mMutex);
+
+ // Map between the divided vsync period and the last known vsync timestamp
+ std::unordered_map<nsecs_t, nsecs_t> mutable mRateDividerKnownTimestampMap GUARDED_BY(mMutex);
size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0;
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 107c540..2cd9b3d 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -68,6 +68,14 @@
virtual bool needsMoreSamples() const = 0;
+ /*
+ * Checks if a vsync timestamp is in phase for a given divider.
+ *
+ * \param [in] timePoint A vsync timestamp
+ * \param [in] divider The divider to check for
+ */
+ virtual bool isVSyncInPhase(nsecs_t timePoint, int divider) const = 0;
+
virtual void dump(std::string& result) const = 0;
protected:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 575da26..57c4d52 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -331,7 +331,7 @@
mInterceptor(mFactory.createSurfaceInterceptor()),
mTimeStats(std::make_shared<impl::TimeStats>()),
mFrameTracer(std::make_unique<FrameTracer>()),
- mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>()),
+ mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>(mTimeStats)),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
@@ -357,7 +357,9 @@
hasWideColorDisplay = has_wide_color_display(false);
- useColorManagement = use_color_management(false);
+ // Android 12 and beyond, color management in display pipeline is turned on
+ // by default.
+ useColorManagement = use_color_management(true);
mDefaultCompositionDataspace =
static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB));
@@ -1041,7 +1043,12 @@
} else {
const HwcConfigIndexType config(mode);
const float fps = mRefreshRateConfigs->getRefreshRateFromConfigId(config).getFps();
- const scheduler::RefreshRateConfigs::Policy policy{config, {fps, fps}};
+ // Keep the old switching type.
+ const auto allowGroupSwitching =
+ mRefreshRateConfigs->getCurrentPolicy().allowGroupSwitching;
+ const scheduler::RefreshRateConfigs::Policy policy{config,
+ allowGroupSwitching,
+ {fps, fps}};
constexpr bool kOverridePolicy = false;
return setDesiredDisplayConfigSpecsInternal(display, policy, kOverridePolicy);
@@ -1848,16 +1855,18 @@
bool refreshNeeded;
{
- ConditionalLockGuard<std::mutex> lock(mTracingLock, mTracingEnabled);
+ mTracePostComposition = mTracing.flagIsSet(SurfaceTracing::TRACE_COMPOSITION) ||
+ mTracing.flagIsSet(SurfaceTracing::TRACE_SYNC) ||
+ mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS);
+ const bool tracePreComposition = mTracingEnabled && !mTracePostComposition;
+ ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition);
mFrameTimeline->setSfWakeUp(vsyncId, frameStart);
refreshNeeded = handleMessageTransaction();
refreshNeeded |= handleMessageInvalidate();
- if (mTracingEnabled) {
- mAddCompositionStateToTrace =
- mTracing.flagIsSetLocked(SurfaceTracing::TRACE_COMPOSITION);
- if (mVisibleRegionsDirty && !mAddCompositionStateToTrace) {
+ if (tracePreComposition) {
+ if (mVisibleRegionsDirty) {
mTracing.notifyLocked("visibleRegionsDirty");
}
}
@@ -2007,12 +2016,15 @@
modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
mLayersWithQueuedFrames.clear();
- if (mVisibleRegionsDirty) {
- mVisibleRegionsDirty = false;
- if (mTracingEnabled && mAddCompositionStateToTrace) {
+ if (mTracingEnabled && mTracePostComposition) {
+ // This may block if SurfaceTracing is running in sync mode.
+ if (mVisibleRegionsDirty) {
mTracing.notify("visibleRegionsDirty");
+ } else if (mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS)) {
+ mTracing.notify("bufferLatched");
}
}
+ mVisibleRegionsDirty = false;
if (mCompositionEngine->needsAnotherUpdate()) {
signalLayerUpdate();
@@ -2313,7 +2325,7 @@
void SurfaceFlinger::processDisplayHotplugEventsLocked() {
for (const auto& event : mPendingHotplugEvents) {
- const std::optional<DisplayIdentificationInfo> info =
+ std::optional<DisplayIdentificationInfo> info =
getHwComposer().onHotplug(event.hwcDisplayId, event.connection);
if (!info) {
@@ -2335,9 +2347,9 @@
state.physical = {.id = displayId,
.type = getHwComposer().getDisplayConnectionType(displayId),
.hwcDisplayId = event.hwcDisplayId,
- .deviceProductInfo = info->deviceProductInfo};
+ .deviceProductInfo = std::move(info->deviceProductInfo)};
state.isSecure = true; // All physical displays are currently considered secure.
- state.displayName = info->name;
+ state.displayName = std::move(info->name);
sp<IBinder> token = new BBinder();
mCurrentState.displays.add(token, state);
@@ -2349,7 +2361,10 @@
const auto token = it->second;
auto& state = mCurrentState.displays.editValueFor(token);
- state.sequenceId = DisplayDeviceState{}.sequenceId;
+ state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId
+ if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) {
+ state.physical->deviceProductInfo = std::move(info->deviceProductInfo);
+ }
}
} else {
ALOGV("Removing display %s", to_string(displayId).c_str());
@@ -3669,6 +3684,9 @@
if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) {
if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eBlurRegionsChanged) {
+ if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded;
+ }
if (what & layer_state_t::eLayerStackChanged) {
ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
// We only allow setting layer stacks for top level layers,
@@ -3703,11 +3721,6 @@
if (what & layer_state_t::eDetachChildren) {
layer->detachChildren();
}
- if (what & layer_state_t::eOverrideScalingModeChanged) {
- layer->setOverrideScalingMode(s.overrideScalingMode);
- // We don't trigger a traversal here because if no other state is
- // changed, we don't want this to cause any more work
- }
if (what & layer_state_t::eTransformChanged) {
if (layer->setTransform(s.transform)) flags |= eTraversalNeeded;
}
@@ -3772,6 +3785,11 @@
flags |= eTraversalNeeded;
}
}
+ if (what & layer_state_t::eFrameTimelineVsyncChanged) {
+ layer->setFrameTimelineVsyncForTransaction(s.frameTimelineVsyncId, postTime);
+ } else if (frameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID) {
+ layer->setFrameTimelineVsyncForTransaction(frameTimelineVsyncId, postTime);
+ }
if (what & layer_state_t::eFixedTransformHintChanged) {
if (layer->setFixedTransformHint(s.fixedTransformHint)) {
flags |= eTraversalNeeded | eTransformHintUpdateNeeded;
@@ -3831,8 +3849,6 @@
}
}
- layer->setFrameTimelineVsyncForTransaction(frameTimelineVsyncId, postTime);
-
if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded;
// Do not put anything that updates layer state or modifies flags after
// setTransactionCompletedListener
@@ -4274,7 +4290,7 @@
status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
if (asProto && mTracing.isEnabled()) {
- mTracing.writeToFileAsync();
+ mTracing.writeToFile();
}
return doDump(fd, DumpArgs(), asProto);
@@ -4358,21 +4374,14 @@
dispSyncPresentTimeOffset, getVsyncPeriodFromHWC());
scheduler::RefreshRateConfigs::Policy policy = mRefreshRateConfigs->getDisplayManagerPolicy();
- StringAppendF(&result,
- "DesiredDisplayConfigSpecs (DisplayManager): default config ID: %d"
- ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]\n\n",
- policy.defaultConfig.value(), policy.primaryRange.min, policy.primaryRange.max,
- policy.appRequestRange.min, policy.appRequestRange.max);
+ StringAppendF(&result, "DesiredDisplayConfigSpecs (DisplayManager): %s\n\n",
+ policy.toString().c_str());
StringAppendF(&result, "(config override by backdoor: %s)\n\n",
mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy();
if (currentPolicy != policy) {
- StringAppendF(&result,
- "DesiredDisplayConfigSpecs (Override): default config ID: %d"
- ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]\n\n",
- currentPolicy.defaultConfig.value(), currentPolicy.primaryRange.min,
- currentPolicy.primaryRange.max, currentPolicy.appRequestRange.min,
- currentPolicy.appRequestRange.max);
+ StringAppendF(&result, "DesiredDisplayConfigSpecs (Override): %s\n\n",
+ currentPolicy.toString().c_str());
}
mScheduler->dump(mAppConnectionHandle, result);
@@ -4904,7 +4913,7 @@
}
// Numbers from 1000 to 1038 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1038) {
+ if (code >= 1000 && code <= 1039) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -5098,19 +5107,19 @@
}
case 1025: { // Set layer tracing
n = data.readInt32();
+ bool tracingEnabledChanged;
if (n) {
ALOGD("LayerTracing enabled");
- mTracingEnabledChanged = mTracing.enable();
- reply->writeInt32(NO_ERROR);
+ tracingEnabledChanged = mTracing.enable();
+ if (tracingEnabledChanged) {
+ schedule([&]() MAIN_THREAD { mTracing.notify("start"); }).wait();
+ }
} else {
ALOGD("LayerTracing disabled");
- mTracingEnabledChanged = mTracing.disable();
- if (mTracingEnabledChanged) {
- reply->writeInt32(mTracing.writeToFile());
- } else {
- reply->writeInt32(NO_ERROR);
- }
+ tracingEnabledChanged = mTracing.disable();
}
+ mTracingEnabledChanged = tracingEnabledChanged;
+ reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
case 1026: { // Get layer tracing status
@@ -5269,6 +5278,13 @@
mFrameTimeline->setMaxDisplayFrames(n);
return NO_ERROR;
}
+ case 1039: {
+ // The first parameter is the uid
+ n = data.readInt32();
+ const float refreshRateHz = data.readFloat();
+ mRefreshRateConfigs->setPreferredRefreshRateForUid(n, refreshRateHz);
+ }
+ return NO_ERROR;
}
}
return err;
@@ -5383,6 +5399,45 @@
return PERMISSION_DENIED;
}
+status_t SurfaceFlinger::setSchedFifo(bool enabled) {
+ static constexpr int kFifoPriority = 2;
+ static constexpr int kOtherPriority = 0;
+
+ struct sched_param param = {0};
+ int sched_policy;
+ if (enabled) {
+ sched_policy = SCHED_FIFO;
+ param.sched_priority = kFifoPriority;
+ } else {
+ sched_policy = SCHED_OTHER;
+ param.sched_priority = kOtherPriority;
+ }
+
+ if (sched_setscheduler(0, sched_policy, ¶m) != 0) {
+ return -errno;
+ }
+ return NO_ERROR;
+}
+
+sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
+ if (const sp<IBinder> displayToken =
+ getPhysicalDisplayTokenLocked(PhysicalDisplayId{displayOrLayerStack})) {
+ return getDisplayDeviceLocked(displayToken);
+ }
+ // Couldn't find display by displayId. Try to get display by layerStack since virtual displays
+ // may not have a displayId.
+ return getDisplayByLayerStack(displayOrLayerStack);
+}
+
+sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
+ for (const auto& [token, display] : mDisplays) {
+ if (display->getLayerStack() == layerStack) {
+ return display;
+ }
+ }
+ return nullptr;
+}
+
status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args,
const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
@@ -5431,46 +5486,7 @@
};
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
- args.pixelFormat, captureListener);
-}
-
-status_t SurfaceFlinger::setSchedFifo(bool enabled) {
- static constexpr int kFifoPriority = 2;
- static constexpr int kOtherPriority = 0;
-
- struct sched_param param = {0};
- int sched_policy;
- if (enabled) {
- sched_policy = SCHED_FIFO;
- param.sched_priority = kFifoPriority;
- } else {
- sched_policy = SCHED_OTHER;
- param.sched_priority = kOtherPriority;
- }
-
- if (sched_setscheduler(0, sched_policy, ¶m) != 0) {
- return -errno;
- }
- return NO_ERROR;
-}
-
-sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
- if (const sp<IBinder> displayToken =
- getPhysicalDisplayTokenLocked(PhysicalDisplayId{displayOrLayerStack})) {
- return getDisplayDeviceLocked(displayToken);
- }
- // Couldn't find display by displayId. Try to get display by layerStack since virtual displays
- // may not have a displayId.
- return getDisplayByLayerStack(displayOrLayerStack);
-}
-
-sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
- for (const auto& [token, display] : mDisplays) {
- if (display->getLayerStack() == layerStack) {
- return display;
- }
- }
- return nullptr;
+ args.pixelFormat, args.allowProtected, captureListener);
}
status_t SurfaceFlinger::captureDisplay(uint64_t displayOrLayerStack,
@@ -5505,7 +5521,8 @@
};
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
- ui::PixelFormat::RGBA_8888, captureListener);
+ ui::PixelFormat::RGBA_8888, false /* allowProtected */,
+ captureListener);
}
status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
@@ -5626,18 +5643,32 @@
};
return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
- args.pixelFormat, captureListener);
+ args.pixelFormat, args.allowProtected, captureListener);
}
status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
+ const bool allowProtected,
const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
- // TODO(b/116112787) Make buffer usage a parameter.
- const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
- GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
+ // Loop over all visible layers to see whether there's any protected layer. A protected layer is
+ // typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer.
+ // A protected layer has no implication on whether it's secure, which is explicitly set by
+ // application to avoid being screenshot or drawn via unsecure display.
+ const bool supportsProtected = getRenderEngine().supportsProtectedContent();
+ bool hasProtectedLayer = false;
+ if (allowProtected && supportsProtected) {
+ traverseLayers([&](Layer* layer) {
+ hasProtectedLayer = hasProtectedLayer || (layer->isVisible() && layer->isProtected());
+ });
+ }
+
+ const uint32_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE |
+ (hasProtectedLayer && allowProtected && supportsProtected
+ ? GRALLOC_USAGE_PROTECTED
+ : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
sp<GraphicBuffer> buffer =
getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
static_cast<android_pixel_format>(reqPixelFormat),
@@ -5648,7 +5679,7 @@
status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
TraverseLayersFunction traverseLayers,
- sp<GraphicBuffer>& buffer, bool regionSampling,
+ sp<GraphicBuffer>& buffer, const bool regionSampling,
const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
@@ -5706,6 +5737,8 @@
captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure());
});
+ const bool useProtected = buffer->getUsage() & GRALLOC_USAGE_PROTECTED;
+
// We allow the system server to take screenshots of secure layers for
// use in situations like the Screen-rotation animation and place
// the impetus on WindowManager to not persist them.
@@ -5750,14 +5783,13 @@
std::vector<Layer*> renderedLayers;
Region clearRegion = Region::INVALID_REGION;
traverseLayers([&](Layer* layer) {
- const bool supportProtectedContent = false;
Region clip(renderArea.getBounds());
compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
clip,
layer->needsFilteringForScreenshots(display.get(), transform) ||
renderArea.needsFiltering(),
renderArea.isSecure(),
- supportProtectedContent,
+ useProtected,
clearRegion,
layerStackSpaceRect,
clientCompositionDisplay.outputDataspace,
@@ -5795,7 +5827,7 @@
// there is no need for synchronization with the GPU.
base::unique_fd bufferFence;
base::unique_fd drawFence;
- getRenderEngine().useProtectedContext(false);
+ getRenderEngine().useProtectedContext(useProtected);
getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
/*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
@@ -5807,6 +5839,8 @@
layer->onLayerDisplayed(releaseFence);
}
}
+ // Always switch back to unprotected context.
+ getRenderEngine().useProtectedContext(false);
return NO_ERROR;
}
@@ -5948,12 +5982,10 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultConfig,
- float primaryRefreshRateMin,
- float primaryRefreshRateMax,
- float appRequestRefreshRateMin,
- float appRequestRefreshRateMax) {
+status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(
+ const sp<IBinder>& displayToken, int32_t defaultConfig, bool allowGroupSwitching,
+ float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin,
+ float appRequestRefreshRateMax) {
ATRACE_CALL();
if (!displayToken) {
@@ -5972,6 +6004,7 @@
} else {
using Policy = scheduler::RefreshRateConfigs::Policy;
const Policy policy{HwcConfigIndexType(defaultConfig),
+ allowGroupSwitching,
{primaryRefreshRateMin, primaryRefreshRateMax},
{appRequestRefreshRateMin, appRequestRefreshRateMax}};
constexpr bool kOverridePolicy = false;
@@ -5983,12 +6016,10 @@
return future.get();
}
-status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultConfig,
- float* outPrimaryRefreshRateMin,
- float* outPrimaryRefreshRateMax,
- float* outAppRequestRefreshRateMin,
- float* outAppRequestRefreshRateMax) {
+status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(
+ const sp<IBinder>& displayToken, int32_t* outDefaultConfig, bool* outAllowGroupSwitching,
+ float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax,
+ float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) {
ATRACE_CALL();
if (!displayToken || !outDefaultConfig || !outPrimaryRefreshRateMin ||
@@ -6006,6 +6037,7 @@
scheduler::RefreshRateConfigs::Policy policy =
mRefreshRateConfigs->getDisplayManagerPolicy();
*outDefaultConfig = policy.defaultConfig.value();
+ *outAllowGroupSwitching = policy.allowGroupSwitching;
*outPrimaryRefreshRateMin = policy.primaryRange.min;
*outPrimaryRefreshRateMax = policy.primaryRange.max;
*outAppRequestRefreshRateMin = policy.appRequestRange.min;
@@ -6016,6 +6048,7 @@
} else {
const auto displayId = display->getPhysicalId();
*outDefaultConfig = getHwComposer().getActiveConfigIndex(displayId);
+ *outAllowGroupSwitching = false;
auto vsyncPeriod = getHwComposer().getActiveConfig(displayId)->getVsyncPeriod();
*outPrimaryRefreshRateMin = 1e9f / vsyncPeriod;
*outPrimaryRefreshRateMax = 1e9f / vsyncPeriod;
@@ -6111,6 +6144,11 @@
Mutex::Autolock lock(mStateLock);
if (authenticateSurfaceTextureLocked(surface)) {
sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+ if (layer == nullptr) {
+ ALOGE("Attempt to set frame rate on a layer that no longer exists");
+ return BAD_VALUE;
+ }
+
if (layer->setFrameRate(
Layer::FrameRate(frameRate,
Layer::FrameRate::convertCompatibility(compatibility)))) {
@@ -6141,8 +6179,8 @@
// This is a little racy, but not in a way that hurts anything. As we grab the
// defaultConfig from the display manager policy, we could be setting a new display
// manager policy, leaving us using a stale defaultConfig. The defaultConfig doesn't
- // matter for the override policy though, since we set allowGroupSwitching to true, so
- // it's not a problem.
+ // matter for the override policy though, since we set allowGroupSwitching to
+ // true, so it's not a problem.
scheduler::RefreshRateConfigs::Policy overridePolicy;
overridePolicy.defaultConfig =
mRefreshRateConfigs->getDisplayManagerPolicy().defaultConfig;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f55dd90..a821d44 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -583,11 +583,12 @@
const sp<IRegionSamplingListener>& listener) override;
status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t displayModeId,
- float primaryRefreshRateMin, float primaryRefreshRateMax,
+ bool allowGroupSwitching, float primaryRefreshRateMin,
+ float primaryRefreshRateMax,
float appRequestRefreshRateMin,
float appRequestRefreshRateMax) override;
status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultConfig,
+ int32_t* outDefaultConfig, bool* outAllowGroupSwitching,
float* outPrimaryRefreshRateMin,
float* outPrimaryRefreshRateMax,
float* outAppRequestRefreshRateMin,
@@ -797,13 +798,14 @@
// Boot animation, on/off animations and screen capture
void startBootAnim();
+ status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
+ ui::PixelFormat, const bool allowProtected,
+ const sp<IScreenCaptureListener>&);
+ status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, sp<GraphicBuffer>&,
+ bool regionSampling, const sp<IScreenCaptureListener>&);
status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
const sp<GraphicBuffer>&, bool forSystem, int* outSyncFd,
bool regionSampling, ScreenCaptureResults&);
- status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
- ui::PixelFormat, const sp<IScreenCaptureListener>&);
- status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, sp<GraphicBuffer>&,
- bool regionSampling, const sp<IScreenCaptureListener>&);
sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
@@ -1133,7 +1135,7 @@
SurfaceTracing mTracing{*this};
std::mutex mTracingLock;
bool mTracingEnabled = false;
- bool mAddCompositionStateToTrace = false;
+ bool mTracePostComposition = false;
std::atomic<bool> mTracingEnabledChanged = false;
const std::shared_ptr<TimeStats> mTimeStats;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 9d78702..97725ec 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -371,5 +371,10 @@
return primaries;
}
+bool update_device_product_info_on_hotplug_reconnect(bool defaultValue) {
+ return SurfaceFlingerProperties::update_device_product_info_on_hotplug_reconnect().value_or(
+ defaultValue);
+}
+
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index c63adfe..37a6b40 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -95,6 +95,9 @@
int32_t display_update_imminent_timeout_ms(int32_t defaultValue);
android::ui::DisplayPrimaries getDisplayNativePrimaries();
+
+bool update_device_product_info_on_hotplug_reconnect(bool defaultValue);
+
} // namespace sysprop
} // namespace android
#endif // SURFACEFLINGERPROPERTIES_H_
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index da58d4e..3548923 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -140,12 +140,12 @@
addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius);
+ addBlurRegionsLocked(transaction, layerId, layer->mCurrentState.blurRegions);
if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
addDeferTransactionLocked(transaction, layerId,
layer->mCurrentState.barrierLayer_legacy.promote(),
layer->mCurrentState.barrierFrameNumber);
}
- addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
addFlagsLocked(transaction, layerId, layer->mCurrentState.flags,
layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque |
layer_state_t::eLayerSecure);
@@ -362,6 +362,25 @@
blurRadiusChange->set_background_blur_radius(backgroundBlurRadius);
}
+void SurfaceInterceptor::addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
+ const std::vector<BlurRegion>& blurRegions) {
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ BlurRegionsChange* blurRegionsChange(change->mutable_blur_regions());
+ for (const auto blurRegion : blurRegions) {
+ const auto blurRegionChange = blurRegionsChange->add_blur_regions();
+ blurRegionChange->set_blur_radius(blurRegion.blurRadius);
+ blurRegionChange->set_corner_radius_tl(blurRegion.cornerRadiusTL);
+ blurRegionChange->set_corner_radius_tr(blurRegion.cornerRadiusTR);
+ blurRegionChange->set_corner_radius_bl(blurRegion.cornerRadiusBL);
+ blurRegionChange->set_corner_radius_br(blurRegion.cornerRadiusBR);
+ blurRegionChange->set_alpha(blurRegion.alpha);
+ blurRegionChange->set_left(blurRegion.left);
+ blurRegionChange->set_top(blurRegion.top);
+ blurRegionChange->set_right(blurRegion.right);
+ blurRegionChange->set_bottom(blurRegion.bottom);
+ }
+}
+
void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
const sp<const Layer>& layer, uint64_t frameNumber)
{
@@ -376,14 +395,6 @@
deferTransaction->set_frame_number(frameNumber);
}
-void SurfaceInterceptor::addOverrideScalingModeLocked(Transaction* transaction,
- int32_t layerId, int32_t overrideScalingMode)
-{
- SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
- OverrideScalingModeChange* overrideChange(change->mutable_override_scaling_mode());
- overrideChange->set_override_scaling_mode(overrideScalingMode);
-}
-
void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId,
int32_t parentId) {
SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
@@ -465,6 +476,9 @@
if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) {
addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius);
}
+ if (state.what & layer_state_t::eBlurRegionsChanged) {
+ addBlurRegionsLocked(transaction, layerId, state.blurRegions);
+ }
if (state.what & layer_state_t::eDeferTransaction_legacy) {
sp<Layer> otherLayer = nullptr;
if (state.barrierSurfaceControl_legacy != nullptr) {
@@ -474,9 +488,6 @@
}
addDeferTransactionLocked(transaction, layerId, otherLayer, state.barrierFrameNumber);
}
- if (state.what & layer_state_t::eOverrideScalingModeChanged) {
- addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
- }
if (state.what & layer_state_t::eReparent) {
auto parentHandle = (state.parentSurfaceControlForChild)
? state.parentSurfaceControlForChild->getHandle()
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 9ac189a..3df79c6 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -165,10 +165,10 @@
void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
int32_t backgroundBlurRadius);
+ void addBlurRegionsLocked(Transaction* transaction, int32_t layerId,
+ const std::vector<BlurRegion>& effectRegions);
void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
const sp<const Layer>& layer, uint64_t frameNumber);
- void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
- int32_t overrideScalingMode);
void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state);
void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays,
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index d84ce69..1d1f0c5 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
#undef LOG_TAG
#define LOG_TAG "SurfaceTracing"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -32,65 +29,65 @@
namespace android {
-SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger)
- : mFlinger(flinger), mSfLock(flinger.mTracingLock) {}
+SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {}
-void SurfaceTracing::mainLoop() {
- bool enabled = addFirstEntry();
- while (enabled) {
- LayersTraceProto entry = traceWhenNotified();
- enabled = addTraceToBuffer(entry);
- }
-}
-
-bool SurfaceTracing::addFirstEntry() {
- LayersTraceProto entry;
- {
- std::scoped_lock lock(mSfLock);
- entry = traceLayersLocked("tracing.enable");
- }
- return addTraceToBuffer(entry);
-}
-
-LayersTraceProto SurfaceTracing::traceWhenNotified() {
- std::unique_lock<std::mutex> lock(mSfLock);
- mCanStartTrace.wait(lock);
- android::base::ScopedLockAssertion assumeLock(mSfLock);
- LayersTraceProto entry = traceLayersLocked(mWhere);
- mTracingInProgress = false;
- mMissedTraceEntries = 0;
- lock.unlock();
- return entry;
-}
-
-bool SurfaceTracing::addTraceToBuffer(LayersTraceProto& entry) {
+bool SurfaceTracing::enable() {
std::scoped_lock lock(mTraceLock);
- mBuffer.emplace(std::move(entry));
- if (mWriteToFile) {
- writeProtoFileLocked();
- mWriteToFile = false;
+ if (mEnabled) {
+ return false;
}
+
+ if (flagIsSet(TRACE_SYNC)) {
+ runner = std::make_unique<SurfaceTracing::Runner>(mFlinger, mConfig);
+ } else {
+ runner = std::make_unique<SurfaceTracing::AsyncRunner>(mFlinger, mConfig,
+ mFlinger.mTracingLock);
+ }
+ mEnabled = true;
+ return true;
+}
+
+bool SurfaceTracing::disable() {
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return false;
+ }
+ mEnabled = false;
+ runner->stop();
+ return true;
+}
+
+bool SurfaceTracing::isEnabled() const {
+ std::scoped_lock lock(mTraceLock);
return mEnabled;
}
+status_t SurfaceTracing::writeToFile() {
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return STATUS_OK;
+ }
+ return runner->writeToFile();
+}
+
void SurfaceTracing::notify(const char* where) {
- std::scoped_lock lock(mSfLock);
- notifyLocked(where);
+ if (mEnabled) {
+ runner->notify(where);
+ }
}
void SurfaceTracing::notifyLocked(const char* where) {
- mWhere = where;
- if (mTracingInProgress) {
- mMissedTraceEntries++;
+ if (mEnabled) {
+ runner->notifyLocked(where);
}
- mTracingInProgress = true;
- mCanStartTrace.notify_one();
}
-void SurfaceTracing::writeToFileAsync() {
+void SurfaceTracing::dump(std::string& result) const {
std::scoped_lock lock(mTraceLock);
- mWriteToFile = true;
- mCanStartTrace.notify_one();
+ base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
+ if (mEnabled) {
+ runner->dump(result);
+ }
}
void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) {
@@ -101,12 +98,12 @@
}
void SurfaceTracing::LayersTraceBuffer::emplace(LayersTraceProto&& proto) {
- auto protoSize = proto.ByteSize();
+ size_t protoSize = static_cast<size_t>(proto.ByteSize());
while (mUsedInBytes + protoSize > mSizeInBytes) {
if (mStorage.empty()) {
return;
}
- mUsedInBytes -= mStorage.front().ByteSize();
+ mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize());
mStorage.pop();
}
mUsedInBytes += protoSize;
@@ -115,7 +112,7 @@
}
void SurfaceTracing::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) {
- fileProto->mutable_entry()->Reserve(mStorage.size());
+ fileProto->mutable_entry()->Reserve(static_cast<int>(mStorage.size()));
while (!mStorage.empty()) {
auto entry = fileProto->add_entry();
@@ -124,85 +121,21 @@
}
}
-bool SurfaceTracing::enable() {
- std::scoped_lock lock(mTraceLock);
-
- if (mEnabled) {
- return false;
- }
-
- mBuffer.reset(mBufferSize);
- mEnabled = true;
- mThread = std::thread(&SurfaceTracing::mainLoop, this);
- return true;
+SurfaceTracing::Runner::Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config)
+ : mFlinger(flinger), mConfig(config) {
+ mBuffer.setSize(mConfig.bufferSize);
}
-status_t SurfaceTracing::writeToFile() {
- std::thread thread;
- {
- std::scoped_lock lock(mTraceLock);
- thread = std::move(mThread);
- }
- thread.join();
- return mLastErr;
+void SurfaceTracing::Runner::notify(const char* where) {
+ LayersTraceProto entry = traceLayers(where);
+ mBuffer.emplace(std::move(entry));
}
-bool SurfaceTracing::disable() {
- std::scoped_lock lock(mTraceLock);
-
- if (!mEnabled) {
- return false;
- }
-
- mEnabled = false;
- mWriteToFile = true;
- mCanStartTrace.notify_all();
- return true;
+status_t SurfaceTracing::Runner::stop() {
+ return writeToFile();
}
-bool SurfaceTracing::isEnabled() const {
- std::scoped_lock lock(mTraceLock);
- return mEnabled;
-}
-
-void SurfaceTracing::setBufferSize(size_t bufferSizeInByte) {
- std::scoped_lock lock(mTraceLock);
- mBufferSize = bufferSizeInByte;
- mBuffer.setSize(bufferSizeInByte);
-}
-
-void SurfaceTracing::setTraceFlags(uint32_t flags) {
- std::scoped_lock lock(mSfLock);
- mTraceFlags = flags;
-}
-
-LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where) {
- ATRACE_CALL();
-
- LayersTraceProto entry;
- entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
- entry.set_where(where);
- LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags));
-
- if (flagIsSetLocked(SurfaceTracing::TRACE_EXTRA)) {
- mFlinger.dumpOffscreenLayersProto(layers);
- }
- entry.mutable_layers()->Swap(&layers);
-
- if (mTraceFlags & SurfaceTracing::TRACE_HWC) {
- std::string hwcDump;
- mFlinger.dumpHwc(hwcDump);
- entry.set_hwc_blob(hwcDump);
- }
- if (!flagIsSetLocked(SurfaceTracing::TRACE_COMPOSITION)) {
- entry.set_excludes_composition_state(true);
- }
- entry.set_missed_entries(mMissedTraceEntries);
-
- return entry;
-}
-
-void SurfaceTracing::writeProtoFileLocked() {
+status_t SurfaceTracing::Runner::writeToFile() {
ATRACE_CALL();
LayersTraceFileProto fileProto;
@@ -211,33 +144,114 @@
fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
mBuffer.flush(&fileProto);
- mBuffer.reset(mBufferSize);
+ mBuffer.reset(mConfig.bufferSize);
if (!fileProto.SerializeToString(&output)) {
ALOGE("Could not save the proto file! Permission denied");
- mLastErr = PERMISSION_DENIED;
+ return PERMISSION_DENIED;
}
// -rw-r--r--
const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
- if (!android::base::WriteStringToFile(output, kDefaultFileName, mode, getuid(), getgid(),
+ if (!android::base::WriteStringToFile(output, DEFAULT_FILE_NAME, mode, getuid(), getgid(),
true)) {
ALOGE("Could not save the proto file! There are missing fields");
- mLastErr = PERMISSION_DENIED;
+ return PERMISSION_DENIED;
}
- mLastErr = NO_ERROR;
+ return NO_ERROR;
}
-void SurfaceTracing::dump(std::string& result) const {
- std::scoped_lock lock(mTraceLock);
- base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
+LayersTraceProto SurfaceTracing::Runner::traceLayers(const char* where) {
+ ATRACE_CALL();
+
+ LayersTraceProto entry;
+ entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
+ entry.set_where(where);
+ LayersProto layers(mFlinger.dumpDrawingStateProto(mConfig.flags));
+
+ if (flagIsSet(SurfaceTracing::TRACE_EXTRA)) {
+ mFlinger.dumpOffscreenLayersProto(layers);
+ }
+ entry.mutable_layers()->Swap(&layers);
+
+ if (flagIsSet(SurfaceTracing::TRACE_HWC)) {
+ std::string hwcDump;
+ mFlinger.dumpHwc(hwcDump);
+ entry.set_hwc_blob(hwcDump);
+ }
+ if (!flagIsSet(SurfaceTracing::TRACE_COMPOSITION)) {
+ entry.set_excludes_composition_state(true);
+ }
+ entry.set_missed_entries(mMissedTraceEntries);
+
+ return entry;
+}
+
+void SurfaceTracing::Runner::dump(std::string& result) const {
base::StringAppendF(&result, " number of entries: %zu (%.2fMB / %.2fMB)\n",
mBuffer.frameCount(), float(mBuffer.used()) / float(1_MB),
float(mBuffer.size()) / float(1_MB));
}
-} // namespace android
+SurfaceTracing::AsyncRunner::AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config,
+ std::mutex& sfLock)
+ : SurfaceTracing::Runner(flinger, config), mSfLock(sfLock) {
+ mEnabled = true;
+ mThread = std::thread(&AsyncRunner::loop, this);
+}
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
+void SurfaceTracing::AsyncRunner::loop() {
+ while (mEnabled) {
+ LayersTraceProto entry;
+ bool entryAdded = traceWhenNotified(&entry);
+ if (entryAdded) {
+ mBuffer.emplace(std::move(entry));
+ }
+ if (mWriteToFile) {
+ Runner::writeToFile();
+ mWriteToFile = false;
+ }
+ }
+}
+
+bool SurfaceTracing::AsyncRunner::traceWhenNotified(LayersTraceProto* outProto) {
+ std::unique_lock<std::mutex> lock(mSfLock);
+ mCanStartTrace.wait(lock);
+ if (!mAddEntry) {
+ return false;
+ }
+ *outProto = traceLayers(mWhere);
+ mAddEntry = false;
+ mMissedTraceEntries = 0;
+ return true;
+}
+
+void SurfaceTracing::AsyncRunner::notify(const char* where) {
+ std::scoped_lock lock(mSfLock);
+ notifyLocked(where);
+}
+
+void SurfaceTracing::AsyncRunner::notifyLocked(const char* where) {
+ mWhere = where;
+ if (mAddEntry) {
+ mMissedTraceEntries++;
+ }
+ mAddEntry = true;
+ mCanStartTrace.notify_one();
+}
+
+status_t SurfaceTracing::AsyncRunner::writeToFile() {
+ mWriteToFile = true;
+ mCanStartTrace.notify_one();
+ return STATUS_OK;
+}
+
+status_t SurfaceTracing::AsyncRunner::stop() {
+ mEnabled = false;
+ mCanStartTrace.notify_one();
+ mThread.join();
+ return Runner::writeToFile();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index f208eb8..576bba7 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -32,25 +32,31 @@
namespace android {
class SurfaceFlinger;
-
constexpr auto operator""_MB(unsigned long long const num) {
return num * 1024 * 1024;
}
/*
- * SurfaceTracing records layer states during surface flinging.
+ * SurfaceTracing records layer states during surface flinging. Manages tracing state and
+ * configuration.
*/
class SurfaceTracing {
public:
- explicit SurfaceTracing(SurfaceFlinger& flinger);
+ SurfaceTracing(SurfaceFlinger& flinger);
bool enable();
bool disable();
status_t writeToFile();
bool isEnabled() const;
+ /*
+ * Adds a trace entry, must be called from the drawing thread or while holding the
+ * SurfaceFlinger tracing lock.
+ */
void notify(const char* where);
- void notifyLocked(const char* where) NO_THREAD_SAFETY_ANALYSIS /* REQUIRES(mSfLock) */;
+ /*
+ * Adds a trace entry, called while holding the SurfaceFlinger tracing lock.
+ */
+ void notifyLocked(const char* where) /* REQUIRES(mSfLock) */;
- void setBufferSize(size_t bufferSizeInByte);
- void writeToFileAsync();
+ void setBufferSize(size_t bufferSizeInBytes) { mConfig.bufferSize = bufferSizeInBytes; }
void dump(std::string& result) const;
enum : uint32_t {
@@ -59,18 +65,34 @@
TRACE_COMPOSITION = 1 << 2,
TRACE_EXTRA = 1 << 3,
TRACE_HWC = 1 << 4,
- TRACE_ALL = 0xffffffff
+ // Add non-geometry composition changes to the trace.
+ TRACE_BUFFERS = 1 << 5,
+ // Add entries from the drawing thread post composition.
+ TRACE_SYNC = 1 << 6,
+ TRACE_ALL = TRACE_CRITICAL | TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA,
};
- void setTraceFlags(uint32_t flags);
- bool flagIsSetLocked(uint32_t flags) NO_THREAD_SAFETY_ANALYSIS /* REQUIRES(mSfLock) */ {
- return (mTraceFlags & flags) == flags;
- }
+ void setTraceFlags(uint32_t flags) { mConfig.flags = flags; }
+ bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
private:
- static constexpr auto kDefaultBufferCapInByte = 5_MB;
- static constexpr auto kDefaultFileName = "/data/misc/wmtrace/layers_trace.pb";
+ class Runner;
+ static constexpr auto DEFAULT_BUFFER_SIZE = 5_MB;
+ static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.pb";
- class LayersTraceBuffer { // ring buffer
+ SurfaceFlinger& mFlinger;
+ mutable std::mutex mTraceLock;
+ bool mEnabled = false;
+ std::unique_ptr<Runner> runner;
+
+ struct Config {
+ uint32_t flags = TRACE_CRITICAL | TRACE_INPUT;
+ size_t bufferSize = DEFAULT_BUFFER_SIZE;
+ } mConfig;
+
+ /*
+ * ring buffer.
+ */
+ class LayersTraceBuffer {
public:
size_t size() const { return mSizeInBytes; }
size_t used() const { return mUsedInBytes; }
@@ -83,35 +105,59 @@
private:
size_t mUsedInBytes = 0U;
- size_t mSizeInBytes = 0U;
+ size_t mSizeInBytes = DEFAULT_BUFFER_SIZE;
std::queue<LayersTraceProto> mStorage;
};
- void mainLoop();
- bool addFirstEntry();
- LayersTraceProto traceWhenNotified();
- LayersTraceProto traceLayersLocked(const char* where) REQUIRES(mSfLock);
+ /*
+ * Implements a synchronous way of adding trace entries. This must be called
+ * from the drawing thread.
+ */
+ class Runner {
+ public:
+ Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config);
+ virtual ~Runner() = default;
+ virtual status_t stop();
+ virtual status_t writeToFile();
+ virtual void notify(const char* where);
+ /* Cannot be called with a synchronous runner. */
+ virtual void notifyLocked(const char* /* where */) {}
+ void dump(std::string& result) const;
- // Returns true if trace is enabled.
- bool addTraceToBuffer(LayersTraceProto& entry);
- void writeProtoFileLocked() REQUIRES(mTraceLock);
+ protected:
+ bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
+ SurfaceFlinger& mFlinger;
+ SurfaceTracing::Config mConfig;
+ SurfaceTracing::LayersTraceBuffer mBuffer;
+ uint32_t mMissedTraceEntries = 0;
+ LayersTraceProto traceLayers(const char* where);
+ };
- SurfaceFlinger& mFlinger;
- status_t mLastErr = NO_ERROR;
- std::thread mThread;
- std::condition_variable mCanStartTrace;
+ /*
+ * Implements asynchronous way to add trace entries called from a separate thread while holding
+ * the SurfaceFlinger tracing lock. Trace entries may be missed if the tracing thread is not
+ * scheduled in time.
+ */
+ class AsyncRunner : public Runner {
+ public:
+ AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config, std::mutex& sfLock);
+ virtual ~AsyncRunner() = default;
+ status_t stop() override;
+ status_t writeToFile() override;
+ void notify(const char* where) override;
+ void notifyLocked(const char* where);
- std::mutex& mSfLock;
- uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_CRITICAL | TRACE_INPUT;
- const char* mWhere GUARDED_BY(mSfLock) = "";
- uint32_t mMissedTraceEntries GUARDED_BY(mSfLock) = 0;
- bool mTracingInProgress GUARDED_BY(mSfLock) = false;
-
- mutable std::mutex mTraceLock;
- LayersTraceBuffer mBuffer GUARDED_BY(mTraceLock);
- size_t mBufferSize GUARDED_BY(mTraceLock) = kDefaultBufferCapInByte;
- bool mEnabled GUARDED_BY(mTraceLock) = false;
- bool mWriteToFile GUARDED_BY(mTraceLock) = false;
+ private:
+ std::mutex& mSfLock;
+ std::condition_variable mCanStartTrace;
+ std::thread mThread;
+ const char* mWhere = "";
+ bool mWriteToFile = false;
+ bool mEnabled = false;
+ bool mAddEntry = false;
+ void loop();
+ bool traceWhenNotified(LayersTraceProto* outProto);
+ };
};
} // namespace android
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 37194c6..fe9e737 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
#undef LOG_TAG
#define LOG_TAG "TimeStats"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -33,6 +30,8 @@
#include <algorithm>
#include <chrono>
+#include "timestatsproto/TimeStatsHelper.h"
+
namespace android {
namespace impl {
@@ -115,6 +114,13 @@
mMaxPulledHistogramBuckets);
mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)renderEngineTimingBytes.c_str(),
renderEngineTimingBytes.size());
+
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalFrames);
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalJankyFrames);
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongCpu);
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongGpu);
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFUnattributed);
+ mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalAppUnattributed);
mStatsDelegate->statsEventBuild(event);
clearGlobalLocked();
@@ -160,6 +166,13 @@
mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames);
mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames);
+ mStatsDelegate->statsEventWriteInt32(event, layer->uid);
+ mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalFrames);
+ mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalJankyFrames);
+ mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongCpu);
+ mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongGpu);
+ mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFUnattributed);
+ mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalAppUnattributed);
mStatsDelegate->statsEventBuild(event);
}
@@ -397,11 +410,13 @@
timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
if (prevTimeRecord.ready) {
+ uid_t uid = layerRecord.uid;
const std::string& layerName = layerRecord.layerName;
- if (!mTimeStats.stats.count(layerName)) {
- mTimeStats.stats[layerName].layerName = layerName;
+ if (!mTimeStats.stats.count({uid, layerName})) {
+ mTimeStats.stats[{uid, layerName}].uid = uid;
+ mTimeStats.stats[{uid, layerName}].layerName = layerName;
}
- TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
+ TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}];
timeStatsLayer.totalFrames++;
timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames;
@@ -462,8 +477,13 @@
layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0;
}
+bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName) {
+ return mTimeStats.stats.count({uid, layerName}) > 0 ||
+ mTimeStats.stats.size() < MAX_NUM_LAYER_STATS;
+}
+
void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- nsecs_t postTime) {
+ uid_t uid, nsecs_t postTime) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -471,11 +491,12 @@
postTime);
std::lock_guard<std::mutex> lock(mMutex);
- if (!mTimeStats.stats.count(layerName) && mTimeStats.stats.size() >= MAX_NUM_LAYER_STATS) {
+ if (!canAddNewAggregatedStats(uid, layerName)) {
return;
}
if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
layerNameIsValid(layerName)) {
+ mTimeStatsTracker[layerId].uid = uid;
mTimeStatsTracker[layerId].layerName = layerName;
}
if (!mTimeStatsTracker.count(layerId)) return;
@@ -655,6 +676,66 @@
flushAvailableRecordsToStatsLocked(layerId);
}
+template <class T>
+static void updateJankPayload(T& t, int32_t reasons) {
+ t.jankPayload.totalFrames++;
+
+ static const constexpr int32_t kValidJankyReason =
+ TimeStats::JankType::SurfaceFlingerDeadlineMissed |
+ TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed |
+ TimeStats::JankType::AppDeadlineMissed | TimeStats::JankType::Display;
+ if (reasons & kValidJankyReason) {
+ t.jankPayload.totalJankyFrames++;
+ if ((reasons & TimeStats::JankType::SurfaceFlingerDeadlineMissed) != 0) {
+ t.jankPayload.totalSFLongCpu++;
+ }
+ if ((reasons & TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed) != 0) {
+ t.jankPayload.totalSFLongGpu++;
+ }
+ if ((reasons & TimeStats::JankType::Display) != 0) {
+ t.jankPayload.totalSFUnattributed++;
+ }
+ if ((reasons & TimeStats::JankType::AppDeadlineMissed) != 0) {
+ t.jankPayload.totalAppUnattributed++;
+ }
+ }
+}
+
+void TimeStats::incrementJankyFrames(int32_t reasons) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ updateJankPayload<TimeStatsHelper::TimeStatsGlobal>(mTimeStats, reasons);
+}
+
+void TimeStats::incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) {
+ if (!mEnabled.load()) return;
+
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ // Only update layer stats if we're allowed to do so.
+ // As an implementation detail, we do this because this method is expected to be
+ // called from FrameTimeline, which is allowed to do jank analysis well after a frame is
+ // presented. This means that we can't rely on TimeStats to flush layer records over to the
+ // aggregated stats.
+ if (!canAddNewAggregatedStats(uid, layerName)) {
+ return;
+ }
+
+ // Defensively initialize the stats in case FrameTimeline flushes its signaled present fences
+ // before TimeStats does.
+ if (!mTimeStats.stats.count({uid, layerName})) {
+ mTimeStats.stats[{uid, layerName}].uid = uid;
+ mTimeStats.stats[{uid, layerName}].layerName = layerName;
+ }
+
+ TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}];
+ updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, reasons);
+}
+
void TimeStats::onDestroy(int32_t layerId) {
ATRACE_CALL();
ALOGV("[%d]-onDestroy", layerId);
@@ -860,6 +941,7 @@
mTimeStats.presentToPresent.hist.clear();
mTimeStats.frameDuration.hist.clear();
mTimeStats.renderEngineTiming.hist.clear();
+ mTimeStats.jankPayload = TimeStatsHelper::JankPayload();
mTimeStats.refreshRateStats.clear();
mPowerTime.prevTime = systemTime();
mGlobalRecord.prevPresentTime = 0;
@@ -905,6 +987,3 @@
} // namespace impl
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 8de5d0c..4fa0a02 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -17,6 +17,7 @@
#pragma once
// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#include <cstdint>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -85,7 +86,7 @@
const std::shared_ptr<FenceTime>& readyFence) = 0;
virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- nsecs_t postTime) = 0;
+ uid_t uid, nsecs_t postTime) = 0;
virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
// Reasons why latching a particular buffer may be skipped
enum class LatchSkipReason {
@@ -108,6 +109,40 @@
virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence) = 0;
+
+ // Subset of jank metadata tracked by FrameTimeline for the purpose of funneling to telemetry.
+ enum JankType {
+ // No Jank
+ None = 0x0,
+ // Jank not related to SurfaceFlinger or the App
+ Display = 0x1,
+ // SF took too long on the CPU
+ SurfaceFlingerDeadlineMissed = 0x2,
+ // SF took too long on the GPU
+ SurfaceFlingerGpuDeadlineMissed = 0x4,
+ // Either App or GPU took too long on the frame
+ AppDeadlineMissed = 0x8,
+ // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a
+ // jank
+ // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame.
+ PredictionExpired = 0x10,
+ // Latching a buffer early might cause an early present of the frame
+ SurfaceFlingerEarlyLatch = 0x20,
+ };
+
+ // Increments janky frames, tracked globally. Because FrameTimeline is the infrastructure
+ // responsible for computing jank in the system, this is expected to be called from
+ // FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there are no
+ // jank reasons, then total frames are incremented but jank is not, for accurate accounting of
+ // janky frames.
+ virtual void incrementJankyFrames(int32_t reasons) = 0;
+ // Increments janky frames, blamed to the provided {uid, layerName} key, with JankMetadata as
+ // supplementary reasons for the jank. Because FrameTimeline is the infrastructure responsible
+ // for computing jank in the system, this is expected to be called from FrameTimeline, rather
+ // than directly from SurfaceFlinger or individual layers.
+ // If there are no jank reasons, then total frames are incremented but jank is not, for accurate
+ // accounting of janky frames.
+ virtual void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) = 0;
// Clean up the layer record
virtual void onDestroy(int32_t layerId) = 0;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -142,6 +177,7 @@
};
struct LayerRecord {
+ uid_t uid;
std::string layerName;
// This is the index in timeRecords, at which the timestamps for that
// specific frame are still not fully received. This is not waiting for
@@ -241,7 +277,7 @@
void recordRenderEngineDuration(nsecs_t startTime,
const std::shared_ptr<FenceTime>& readyFence) override;
- void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
+ void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid,
nsecs_t postTime) override;
void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
@@ -253,6 +289,8 @@
void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override;
void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence) override;
+ void incrementJankyFrames(int32_t reasons) override;
+ void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) override;
// Clean up the layer record
void onDestroy(int32_t layerId) override;
// If SF skips or rejects a buffer, remove the corresponding TimeRecord.
@@ -276,6 +314,7 @@
void flushAvailableRecordsToStatsLocked(int32_t layerId);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
+ bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName);
void enable();
void disable();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
index b937f41..9513cab 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp
@@ -30,3 +30,15 @@
"-Wno-unused-parameter",
],
}
+
+// ==== java host library for timestats proto ===========================
+// Note timestats is deprecated and is only used for legacy tests
+java_library_host {
+ name: "host-timestats-proto",
+ srcs: [
+ "timestats.proto",
+ ],
+ proto: {
+ type: "full",
+ },
+}
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index d77387a..0fb748f 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -77,14 +77,28 @@
return result;
}
+std::string TimeStatsHelper::JankPayload::toString() const {
+ std::string result;
+ StringAppendF(&result, "totalTimelineFrames = %d\n", totalFrames);
+ StringAppendF(&result, "jankyFrames = %d\n", totalJankyFrames);
+ StringAppendF(&result, "sfLongCpuJankyFrames = %d\n", totalSFLongCpu);
+ StringAppendF(&result, "sfLongGpuJankyFrames = %d\n", totalSFLongGpu);
+ StringAppendF(&result, "sfUnattributedJankyFrame = %d\n", totalSFUnattributed);
+ StringAppendF(&result, "appUnattributedJankyFrame = %d\n", totalAppUnattributed);
+ return result;
+}
+
std::string TimeStatsHelper::TimeStatsLayer::toString() const {
std::string result = "\n";
+ StringAppendF(&result, "uid = %d\n", uid);
StringAppendF(&result, "layerName = %s\n", layerName.c_str());
StringAppendF(&result, "packageName = %s\n", packageName.c_str());
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
+ result.append("Jank payload for this layer:\n");
+ result.append(jankPayload.toString());
const auto iter = deltas.find("present2present");
if (iter != deltas.end()) {
const float averageTime = iter->second.averageTime();
@@ -110,6 +124,8 @@
StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitches);
StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChanges);
StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
+ result.append("Global aggregated jank payload:\n");
+ result.append(jankPayload.toString());
StringAppendF(&result, "displayConfigStats is as below:\n");
for (const auto& [fps, duration] : refreshRateStats) {
StringAppendF(&result, "%dfps = %ldms\n", fps, ns2ms(duration));
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 0c75f96..033eb5d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -40,14 +40,28 @@
std::string toString() const;
};
+ struct JankPayload {
+ // note that transactions are counted for these frames.
+ int32_t totalFrames = 0;
+ int32_t totalJankyFrames = 0;
+ int32_t totalSFLongCpu = 0;
+ int32_t totalSFLongGpu = 0;
+ int32_t totalSFUnattributed = 0;
+ int32_t totalAppUnattributed = 0;
+
+ std::string toString() const;
+ };
+
class TimeStatsLayer {
public:
+ uid_t uid;
std::string layerName;
std::string packageName;
int32_t totalFrames = 0;
int32_t droppedFrames = 0;
int32_t lateAcquireFrames = 0;
int32_t badDesiredPresentFrames = 0;
+ JankPayload jankPayload;
std::unordered_map<std::string, Histogram> deltas;
std::string toString() const;
@@ -69,8 +83,17 @@
Histogram presentToPresent;
Histogram frameDuration;
Histogram renderEngineTiming;
- std::unordered_map<std::string, TimeStatsLayer> stats;
+
+ struct StatsHasher {
+ size_t operator()(const std::pair<uid_t, std::string>& p) const {
+ // Normally this isn't a very good hash function due to symmetry reasons,
+ // but these are distinct types so this should be good enough
+ return std::hash<uid_t>{}(p.first) ^ std::hash<std::string>{}(p.second);
+ }
+ };
+ std::unordered_map<std::pair<uid_t, std::string>, TimeStatsLayer, StatsHasher> stats;
std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
+ JankPayload jankPayload;
std::string toString(std::optional<uint32_t> maxLayers) const;
SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const;
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index 49cf80c..eee4bec 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -57,7 +57,9 @@
trace();
}
- operator T() const { return mData; }
+ T get() const { return mData; }
+
+ operator T() const { return get(); }
TracedOrdinal& operator=(T other) {
mData = other;
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index f3f5626..9f25674 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -125,6 +125,9 @@
int32 background_blur_radius = 52;
uint32 owner_uid = 53;
+
+ // Regions of a layer, where blur should be applied.
+ repeated BlurRegion blur_regions = 54;
}
message PositionProto {
@@ -211,3 +214,16 @@
// This will be a 4x4 matrix of float values
repeated float val = 1;
}
+
+message BlurRegion {
+ uint32 blur_radius = 1;
+ uint32 corner_radius_tl = 2;
+ uint32 corner_radius_tr = 3;
+ uint32 corner_radius_bl = 4;
+ float corner_radius_br = 5;
+ float alpha = 6;
+ int32 left = 7;
+ int32 top = 8;
+ int32 right = 9;
+ int32 bottom = 10;
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/layerproto/layerstrace.proto b/services/surfaceflinger/layerproto/layerstrace.proto
index acf621e..990f3cf 100644
--- a/services/surfaceflinger/layerproto/layerstrace.proto
+++ b/services/surfaceflinger/layerproto/layerstrace.proto
@@ -42,7 +42,7 @@
/* one window manager trace entry. */
message LayersTraceProto {
/* required: elapsed realtime in nanos since boot of when this entry was logged */
- optional fixed64 elapsed_realtime_nanos = 1;
+ optional sfixed64 elapsed_realtime_nanos = 1;
/* where the trace originated */
optional string where = 2;
@@ -56,5 +56,5 @@
optional bool excludes_composition_state = 5;
/* Number of missed entries since the last entry was recorded. */
- optional int32 missed_entries = 6;
+ optional uint32 missed_entries = 6;
}
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 7666f7f..421484f 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -435,3 +435,13 @@
access: Readonly
prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms"
}
+
+
+# Updates the DeviceProductInfo when a hoplug reconnect event is processed
+prop {
+ api_name: "update_device_product_info_on_hotplug_reconnect"
+ type: Boolean
+ scope: Public
+ access: Readonly
+ prop_name: "ro.surface_flinger.update_device_product_info_on_hotplug_reconnect"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index ba60a7d..da66ece 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -124,6 +124,10 @@
prop_name: "ro.surface_flinger.supports_background_blur"
}
prop {
+ api_name: "update_device_product_info_on_hotplug_reconnect"
+ prop_name: "ro.surface_flinger.update_device_product_info_on_hotplug_reconnect"
+ }
+ prop {
api_name: "use_color_management"
prop_name: "ro.surface_flinger.use_color_management"
}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index db5d820..02b4308 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -50,7 +50,7 @@
"android.hardware.graphics.composer@2.1",
],
shared_libs: [
- "android.hardware.graphics.common-ndk_platform",
+ "android.hardware.graphics.common-unstable-ndk_platform",
"android.hardware.graphics.common@1.2",
"libandroid",
"libbase",
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 7f541e2..9302463 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -218,18 +218,21 @@
TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) {
const auto display = SurfaceComposerClient::getInternalDisplayToken();
int32_t defaultConfig;
+ bool allowGroupSwitching;
float primaryFpsMin;
float primaryFpsMax;
float appRequestFpsMin;
float appRequestFpsMax;
status_t res =
SurfaceComposerClient::getDesiredDisplayConfigSpecs(display, &defaultConfig,
+ &allowGroupSwitching,
&primaryFpsMin, &primaryFpsMax,
&appRequestFpsMin,
&appRequestFpsMax);
ASSERT_EQ(res, NO_ERROR);
std::function<status_t()> condition = [=]() {
return SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, defaultConfig,
+ allowGroupSwitching,
primaryFpsMin, primaryFpsMax,
appRequestFpsMin,
appRequestFpsMax);
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index debfe83..3a8b40f 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
+#include <gtest/gtest.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <ui/DisplayConfig.h>
+#include <utils/Errors.h>
+#include <utils/Vector.h>
-#include <thread>
-#include "LayerTransactionTest.h"
+#include "utils/TransactionUtils.h"
+
namespace android {
-using android::hardware::graphics::common::V1_1::BufferUsage;
-
::testing::Environment* const binderEnv =
::testing::AddGlobalTestEnvironment(new BinderEnvironment());
@@ -31,32 +33,54 @@
* Test class for setting display configs and passing around refresh rate ranges.
*/
class RefreshRateRangeTest : public ::testing::Test {
+private:
+ int32_t initialDefaultConfig;
+ bool initialAllowGroupSwitching;
+ float initialPrimaryMin;
+ float initialPrimaryMax;
+ float initialAppRequestMin;
+ float initialAppRequestMax;
+
protected:
- void SetUp() override { mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); }
+ void SetUp() override {
+ mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
+ status_t res =
+ SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken,
+ &initialDefaultConfig,
+ &initialAllowGroupSwitching,
+ &initialPrimaryMin,
+ &initialPrimaryMax,
+ &initialAppRequestMin,
+ &initialAppRequestMax);
+ ASSERT_EQ(res, NO_ERROR);
+ }
+
+ void TearDown() override {
+ status_t res =
+ SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken,
+ initialDefaultConfig,
+ initialAllowGroupSwitching,
+ initialPrimaryMin,
+ initialPrimaryMax,
+ initialAppRequestMin,
+ initialAppRequestMax);
+ ASSERT_EQ(res, NO_ERROR);
+ }
+
+ void testSetAllowGroupSwitching(bool allowGroupSwitching);
sp<IBinder> mDisplayToken;
};
TEST_F(RefreshRateRangeTest, setAllConfigs) {
- int32_t initialDefaultConfig;
- float initialPrimaryMin;
- float initialPrimaryMax;
- float initialAppRequestMin;
- float initialAppRequestMax;
- status_t res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken,
- &initialDefaultConfig,
- &initialPrimaryMin,
- &initialPrimaryMax,
- &initialAppRequestMin,
- &initialAppRequestMax);
- ASSERT_EQ(res, NO_ERROR);
-
Vector<DisplayConfig> configs;
- res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs);
+ status_t res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs);
ASSERT_EQ(res, NO_ERROR);
+ ASSERT_GT(configs.size(), 0);
for (size_t i = 0; i < configs.size(); i++) {
- res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i,
+ res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken,
+ static_cast<int32_t>(i), false,
configs[i].refreshRate,
configs[i].refreshRate,
configs[i].refreshRate,
@@ -64,31 +88,58 @@
ASSERT_EQ(res, NO_ERROR);
int defaultConfig;
+ bool allowGroupSwitching;
float primaryRefreshRateMin;
float primaryRefreshRateMax;
float appRequestRefreshRateMin;
float appRequestRefreshRateMax;
res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
+ &allowGroupSwitching,
&primaryRefreshRateMin,
&primaryRefreshRateMax,
&appRequestRefreshRateMin,
&appRequestRefreshRateMax);
ASSERT_EQ(res, NO_ERROR);
ASSERT_EQ(defaultConfig, i);
+ ASSERT_EQ(allowGroupSwitching, false);
ASSERT_EQ(primaryRefreshRateMin, configs[i].refreshRate);
ASSERT_EQ(primaryRefreshRateMax, configs[i].refreshRate);
ASSERT_EQ(appRequestRefreshRateMin, configs[i].refreshRate);
ASSERT_EQ(appRequestRefreshRateMax, configs[i].refreshRate);
}
+}
- res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, initialDefaultConfig,
- initialPrimaryMin, initialPrimaryMax,
- initialAppRequestMin,
- initialAppRequestMax);
+void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) {
+ status_t res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 0,
+ allowGroupSwitching, 0.f,
+ 90.f, 0.f, 90.f);
ASSERT_EQ(res, NO_ERROR);
+ int defaultConfig;
+ bool newAllowGroupSwitching;
+ float primaryRefreshRateMin;
+ float primaryRefreshRateMax;
+ float appRequestRefreshRateMin;
+ float appRequestRefreshRateMax;
+
+ res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
+ &newAllowGroupSwitching,
+ &primaryRefreshRateMin,
+ &primaryRefreshRateMax,
+ &appRequestRefreshRateMin,
+ &appRequestRefreshRateMax);
+ ASSERT_EQ(res, NO_ERROR);
+ ASSERT_EQ(defaultConfig, 0);
+ ASSERT_EQ(newAllowGroupSwitching, allowGroupSwitching);
+ ASSERT_EQ(primaryRefreshRateMin, 0.f);
+ ASSERT_EQ(primaryRefreshRateMax, 90.f);
+ ASSERT_EQ(appRequestRefreshRateMin, 0.f);
+ ASSERT_EQ(appRequestRefreshRateMax, 90.f);
+}
+
+TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) {
+ testSetAllowGroupSwitching(true);
+ testSetAllowGroupSwitching(false);
+ testSetAllowGroupSwitching(true);
}
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
index 3dca391..fafb49e 100644
--- a/services/surfaceflinger/tests/EffectLayer_test.cpp
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -51,7 +51,7 @@
sp<SurfaceControl> effectLayer =
mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect,
- mParentLayer.get());
+ mParentLayer->getHandle());
EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
asTransaction([&](Transaction& t) {
@@ -72,7 +72,7 @@
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceEffect |
ISurfaceComposerClient::eNoColorFill,
- mParentLayer.get());
+ mParentLayer->getHandle());
EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
asTransaction([&](Transaction& t) {
@@ -93,7 +93,7 @@
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceEffect |
ISurfaceComposerClient::eNoColorFill,
- mParentLayer.get());
+ mParentLayer->getHandle());
EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
asTransaction([&](Transaction& t) {
diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index cfec0d2..152d2d2 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -56,7 +56,7 @@
auto notSc = makeNotSurfaceControl();
ASSERT_EQ(nullptr,
mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0,
- notSc.get())
+ notSc->getHandle())
.get());
}
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 4947289..52e1a4d 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -179,19 +179,6 @@
}
}
-TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // setSize is immediate with SCALE_TO_WINDOW, unlike setPosition
- Transaction()
- .setSize(layer, 64, 64)
- .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
- .apply();
- getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
-}
-
TEST_P(LayerRenderTypeTransactionTest, CreateLayer_BufferState) {
uint32_t transformHint = ui::Transform::ROT_INVALID;
sp<SurfaceControl> layer;
@@ -950,40 +937,6 @@
}
}
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
- // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition
- Transaction()
- .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
- .setSize(layer, 64, 64)
- .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
- .apply();
- getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
- sp<SurfaceControl> layer;
- ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE));
-
- // XXX SCALE_CROP is not respected; calling setSize and
- // setOverrideScalingMode in separate transactions does not work
- // (b/69315456)
- Transaction()
- .setSize(layer, 64, 16)
- .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
- .apply();
- {
- SCOPED_TRACE("SCALE_TO_WINDOW");
- getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
- Color::BLUE, Color::WHITE, true /* filtered */);
- }
-}
-
TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) {
sp<SurfaceControl> layer;
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index d4e952a..da71dad 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -16,6 +16,10 @@
#pragma once
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include <gtest/gtest.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
@@ -75,8 +79,9 @@
PixelFormat format, uint32_t flags,
SurfaceControl* parent = nullptr,
uint32_t* outTransformHint = nullptr) {
- auto layer = client->createSurface(String8(name), width, height, format, flags, parent,
- LayerMetadata(), outTransformHint);
+ sp<IBinder> parentHandle = (parent) ? parent->getHandle() : nullptr;
+ auto layer = client->createSurface(String8(name), width, height, format, flags,
+ parentHandle, LayerMetadata(), outTransformHint);
EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
return layer;
}
@@ -299,3 +304,6 @@
};
} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index 38da0b1..29473f2 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -566,7 +566,10 @@
}
asTransaction([&](Transaction& t) {
- t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ mFGSurfaceControl->getSurface()->setScalingMode(
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ // Resubmit buffer with new scaling mode
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
// We cause scaling by 2.
t.setSize(mFGSurfaceControl, 128, 128);
});
@@ -673,7 +676,10 @@
}
asTransaction([&](Transaction& t) {
- t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ mFGSurfaceControl->getSurface()->setScalingMode(
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ // Resubmit buffer with new scaling mode
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
// Set a scaling by 2.
t.setSize(mFGSurfaceControl, 128, 128);
});
@@ -705,7 +711,10 @@
// Change the size of the foreground to 128 * 64 so we can test rotation as well.
asTransaction([&](Transaction& t) {
- t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ mFGSurfaceControl->getSurface()->setScalingMode(
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ // Resubmit buffer with new scaling mode
+ TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
t.setSize(mFGSurfaceControl, 128, 64);
});
sp<Surface> s = mFGSurfaceControl->getSurface();
@@ -1019,12 +1028,13 @@
TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) {
sp<SurfaceControl> boundlessLayer =
mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- 0 /* flags */, mFGSurfaceControl.get());
+ 0 /* flags */, mFGSurfaceControl->getHandle());
ASSERT_TRUE(boundlessLayer != nullptr);
ASSERT_TRUE(boundlessLayer->isValid());
sp<SurfaceControl> colorLayer =
mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceEffect, boundlessLayer.get());
+ ISurfaceComposerClient::eFXSurfaceEffect,
+ boundlessLayer->getHandle());
ASSERT_TRUE(colorLayer != nullptr);
ASSERT_TRUE(colorLayer->isValid());
asTransaction([&](Transaction& t) {
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 104d919..81e648a 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -39,7 +39,6 @@
using Trace = surfaceflinger::Trace;
using Increment = surfaceflinger::Increment;
-constexpr int32_t SCALING_UPDATE = 1;
constexpr uint32_t BUFFER_UPDATES = 18;
constexpr uint32_t LAYER_UPDATE = INT_MAX - 2;
constexpr uint32_t SIZE_UPDATE = 134;
@@ -52,6 +51,7 @@
constexpr float POSITION_UPDATE = 121;
const Rect CROP_UPDATE(16, 16, 32, 32);
const float SHADOW_RADIUS_UPDATE = 35.0f;
+std::vector<BlurRegion> BLUR_REGIONS_UPDATE;
const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
@@ -182,6 +182,7 @@
bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
bool foundBackgroundBlurRadius);
+ bool blurRegionsUpdateFound(const SurfaceChange& change, bool foundBlurRegions);
bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
@@ -220,8 +221,8 @@
void cropUpdate(Transaction&);
void cornerRadiusUpdate(Transaction&);
void backgroundBlurRadiusUpdate(Transaction&);
+ void blurRegionsUpdate(Transaction&);
void matrixUpdate(Transaction&);
- void overrideScalingModeUpdate(Transaction&);
void transparentRegionHintUpdate(Transaction&);
void layerStackUpdate(Transaction&);
void hiddenFlagUpdate(Transaction&);
@@ -359,6 +360,12 @@
t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE);
}
+void SurfaceInterceptorTest::blurRegionsUpdate(Transaction& t) {
+ BLUR_REGIONS_UPDATE.empty();
+ BLUR_REGIONS_UPDATE.push_back(BlurRegion());
+ t.setBlurRegions(mBGSurfaceControl, BLUR_REGIONS_UPDATE);
+}
+
void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
}
@@ -371,10 +378,6 @@
t.setMatrix(mBGSurfaceControl, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2);
}
-void SurfaceInterceptorTest::overrideScalingModeUpdate(Transaction& t) {
- t.setOverrideScalingMode(mBGSurfaceControl, SCALING_UPDATE);
-}
-
void SurfaceInterceptorTest::transparentRegionHintUpdate(Transaction& t) {
Region region(CROP_UPDATE);
t.setTransparentRegionHint(mBGSurfaceControl, region);
@@ -436,10 +439,10 @@
runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate);
+ runInTransaction(&SurfaceInterceptorTest::blurRegionsUpdate);
runInTransaction(&SurfaceInterceptorTest::layerUpdate);
runInTransaction(&SurfaceInterceptorTest::cropUpdate);
runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
- runInTransaction(&SurfaceInterceptorTest::overrideScalingModeUpdate);
runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate);
runInTransaction(&SurfaceInterceptorTest::layerStackUpdate);
runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate);
@@ -525,6 +528,17 @@
return foundBackgroundBlur;
}
+bool SurfaceInterceptorTest::blurRegionsUpdateFound(const SurfaceChange& change,
+ bool foundBlurRegions) {
+ bool hasBlurRegions(change.blur_regions().blur_regions_size() == BLUR_REGIONS_UPDATE.size());
+ if (hasBlurRegions && !foundBlurRegions) {
+ foundBlurRegions = true;
+ } else if (hasBlurRegions && foundBlurRegions) {
+ []() { FAIL(); }();
+ }
+ return foundBlurRegions;
+}
+
bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
bool hasLayer(change.layer().layer() == LAYER_UPDATE);
if (hasLayer && !foundLayer) {
@@ -561,17 +575,6 @@
return foundMatrix;
}
-bool SurfaceInterceptorTest::scalingModeUpdateFound(const SurfaceChange& change,
- bool foundScalingMode) {
- bool hasScalingUpdate(change.override_scaling_mode().override_scaling_mode() == SCALING_UPDATE);
- if (hasScalingUpdate && !foundScalingMode) {
- foundScalingMode = true;
- } else if (hasScalingUpdate && foundScalingMode) {
- [] () { FAIL(); }();
- }
- return foundScalingMode;
-}
-
bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change,
bool foundTransparentRegion) {
auto traceRegion = change.transparent_region_hint().region(0);
@@ -724,12 +727,12 @@
case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius:
foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate);
break;
+ case SurfaceChange::SurfaceChangeCase::kBlurRegions:
+ foundUpdate = blurRegionsUpdateFound(change, foundUpdate);
+ break;
case SurfaceChange::SurfaceChangeCase::kMatrix:
foundUpdate = matrixUpdateFound(change, foundUpdate);
break;
- case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode:
- foundUpdate = scalingModeUpdateFound(change, foundUpdate);
- break;
case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate);
break;
@@ -780,7 +783,6 @@
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix));
- ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOverrideScalingMode));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack));
ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag));
@@ -911,13 +913,13 @@
SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius);
}
-TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
+TEST_F(SurfaceInterceptorTest, InterceptBlurRegionsUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::blurRegionsUpdate,
+ SurfaceChange::SurfaceChangeCase::kBlurRegions);
}
-TEST_F(SurfaceInterceptorTest, InterceptOverrideScalingModeUpdateWorks) {
- captureTest(&SurfaceInterceptorTest::overrideScalingModeUpdate,
- SurfaceChange::SurfaceChangeCase::kOverrideScalingMode);
+TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
}
TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) {
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 2861013..3535fbb 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -8,12 +8,12 @@
"FakeComposerUtils.cpp",
"SFFakeHwc_test.cpp"
],
+ require_root: true,
shared_libs: [
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
"android.hardware.graphics.composer@2.4",
- "android.hardware.graphics.composer@2.1-resources",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
"android.hardware.graphics.mapper@4.0",
@@ -33,6 +33,7 @@
"libutils",
],
static_libs: [
+ "android.hardware.graphics.composer@2.1-resources",
"libcompositionengine",
"libgmock",
"libperfetto_client_experimental",
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 07c558f..0a70f5c 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -447,7 +447,7 @@
const auto& config = configs[i];
if (config.resolution.getWidth() == 800) {
EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+ SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
config.refreshRate,
config.refreshRate,
config.refreshRate,
@@ -553,7 +553,7 @@
const auto& config = configs[i];
if (config.refreshRate == 1e9f / 11'111'111) {
EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+ SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
config.refreshRate,
config.refreshRate,
config.refreshRate,
@@ -670,7 +670,8 @@
if (config.resolution.getWidth() == 800 && config.refreshRate == 1e9f / 11'111'111) {
EXPECT_EQ(NO_ERROR,
SurfaceComposerClient::
- setDesiredDisplayConfigSpecs(display, i, configs[i].refreshRate,
+ setDesiredDisplayConfigSpecs(display, i, false,
+ configs[i].refreshRate,
configs[i].refreshRate,
configs[i].refreshRate,
configs[i].refreshRate));
@@ -716,7 +717,7 @@
const auto& config = configs[i];
if (config.refreshRate == 1e9f / 8'333'333) {
EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+ SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
config.refreshRate,
config.refreshRate,
config.refreshRate,
@@ -763,7 +764,7 @@
const auto& config = configs[i];
if (config.resolution.getWidth() == 1600 && config.refreshRate == 1e9f / 11'111'111) {
EXPECT_EQ(NO_ERROR,
- SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+ SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false,
config.refreshRate,
config.refreshRate,
config.refreshRate,
@@ -1469,7 +1470,7 @@
Base::SetUp();
mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
PIXEL_FORMAT_RGBA_8888, 0,
- Base::mFGSurfaceControl.get());
+ Base::mFGSurfaceControl->getHandle());
fillSurfaceRGBA8(mChild, LIGHT_GRAY);
Base::sFakeComposer->runVSyncAndWait();
@@ -1653,7 +1654,7 @@
sp<SurfaceControl> childNewClient =
newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
PIXEL_FORMAT_RGBA_8888, 0,
- Base::mFGSurfaceControl.get());
+ Base::mFGSurfaceControl->getHandle());
ASSERT_TRUE(childNewClient != nullptr);
ASSERT_TRUE(childNewClient->isValid());
fillSurfaceRGBA8(childNewClient, LIGHT_GRAY);
@@ -1691,30 +1692,6 @@
EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
}
- void Test_InheritNonTransformScalingFromParent() {
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 0, 0);
- ts.setPosition(Base::mFGSurfaceControl, 0, 0);
- }
-
- {
- TransactionScope ts(*Base::sFakeComposer);
- ts.setOverrideScalingMode(Base::mFGSurfaceControl,
- NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- // We cause scaling by 2.
- ts.setSize(Base::mFGSurfaceControl, 128, 128);
- }
-
- auto referenceFrame = Base::mBaseFrame;
- referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
- referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
- referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
- EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
- }
-
// Regression test for b/37673612
void Test_ChildrenWithParentBufferTransform() {
{
@@ -1756,7 +1733,7 @@
mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eHidden,
- Base::mFGSurfaceControl.get());
+ Base::mFGSurfaceControl->getHandle());
// Show the child layer in a deferred transaction
{
@@ -1823,10 +1800,6 @@
Test_DetachChildrenDifferentClient();
}
-TEST_F(ChildLayerTest_2_1, DISABLED_InheritNonTransformScalingFromParent) {
- Test_InheritNonTransformScalingFromParent();
-}
-
// Regression test for b/37673612
TEST_F(ChildLayerTest_2_1, DISABLED_ChildrenWithParentBufferTransform) {
Test_ChildrenWithParentBufferTransform();
@@ -1847,7 +1820,7 @@
Base::mComposerClient->createSurface(String8("Child surface"), 0, 0,
PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceEffect,
- Base::mFGSurfaceControl.get());
+ Base::mFGSurfaceControl->getHandle());
{
TransactionScope ts(*Base::sFakeComposer);
ts.setColor(Base::mChild,
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 9655cf3..8097a88 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -41,6 +41,8 @@
"DisplayIdentificationTest.cpp",
"DisplayIdGeneratorTest.cpp",
"DisplayTransactionTest.cpp",
+ "DisplayDevice_GetBestColorModeTest.cpp",
+ "DisplayDevice_SetProjectionTest.cpp",
"EventThreadTest.cpp",
"FrameTimelineTest.cpp",
"HWComposerTest.cpp",
@@ -50,6 +52,16 @@
"LayerMetadataTest.cpp",
"MessageQueueTest.cpp",
"PromiseTest.cpp",
+ "SurfaceFlinger_CreateDisplayTest.cpp",
+ "SurfaceFlinger_DestroyDisplayTest.cpp",
+ "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
+ "SurfaceFlinger_HandleTransactionLockedTest.cpp",
+ "SurfaceFlinger_NotifyPowerBoostTest.cpp",
+ "SurfaceFlinger_OnHotplugReceivedTest.cpp",
+ "SurfaceFlinger_OnInitializeDisplaysTest.cpp",
+ "SurfaceFlinger_SetDisplayStateTest.cpp",
+ "SurfaceFlinger_SetPowerModeInternalTest.cpp",
+ "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
"SetFrameRateTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index a4f7449..0911712 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -134,13 +134,15 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
.WillOnce(Return(
- new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
.WillOnce(Return(
- new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp
new file mode 100644
index 0000000..2e53cd1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+using hal::RenderIntent;
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class GetBestColorModeTest : public DisplayTransactionTest {
+public:
+ void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
+
+ void addHwcColorModesMapping(ui::ColorMode colorMode,
+ std::vector<ui::RenderIntent> renderIntents) {
+ mHwcColorModes[colorMode] = renderIntents;
+ }
+
+ void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; }
+
+ void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
+
+ void getBestColorMode() {
+ auto displayDevice =
+ injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+ injector.setHwcColorModes(mHwcColorModes);
+ injector.setHasWideColorGamut(mHasWideColorGamut);
+ injector.setNativeWindow(mNativeWindow);
+ });
+
+ displayDevice->getCompositionDisplay()
+ ->getDisplayColorProfile()
+ ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
+ &mOutColorMode, &mOutRenderIntent);
+ }
+
+ ui::Dataspace mOutDataspace;
+ ui::ColorMode mOutColorMode;
+ ui::RenderIntent mOutRenderIntent;
+
+private:
+ ui::Dataspace mInputDataspace;
+ ui::RenderIntent mInputRenderIntent;
+ bool mHasWideColorGamut = false;
+ std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
+};
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
+ addHwcColorModesMapping(ui::ColorMode::SRGB,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::V0_SRGB, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) {
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ addHwcColorModesMapping(ui::ColorMode::SRGB,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) {
+ addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
+ std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
+ setInputDataspace(ui::Dataspace::DISPLAY_P3);
+ setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
+ setHasWideColorGamut(true);
+
+ getBestColorMode();
+
+ ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace);
+ ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
new file mode 100644
index 0000000..9fe30f8
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+
+class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
+public:
+ static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080; // arbitrary
+ static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
+
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0;
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90;
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V;
+ static constexpr int32_t TRANSFORM_FLAGS_ROT_270 =
+ HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
+
+ DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize,
+ ui::Rotation physicalOrientation)
+ : mFlingerDisplaySize(flingerDisplaySize),
+ mHardwareDisplaySize(hardwareDisplaySize),
+ mPhysicalOrientation(physicalOrientation),
+ mDisplayDevice(createDisplayDevice()) {}
+
+ sp<DisplayDevice> createDisplayDevice() {
+ return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+ injector.setPhysicalOrientation(mPhysicalOrientation);
+ });
+ }
+
+ ui::Size swapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
+
+ void setProjectionForRotation0() {
+ // A logical rotation of 0 uses the SurfaceFlinger display size
+ mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize),
+ Rect(mFlingerDisplaySize));
+ }
+
+ void setProjectionForRotation90() {
+ // A logical rotation of 90 uses the SurfaceFlinger display size with
+ // the width/height swapped.
+ mDisplayDevice->setProjection(ui::ROTATION_90, Rect(swapWH(mFlingerDisplaySize)),
+ Rect(swapWH(mFlingerDisplaySize)));
+ }
+
+ void setProjectionForRotation180() {
+ // A logical rotation of 180 uses the SurfaceFlinger display size
+ mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize),
+ Rect(mFlingerDisplaySize));
+ }
+
+ void setProjectionForRotation270() {
+ // A logical rotation of 270 uses the SurfaceFlinger display size with
+ // the width/height swapped.
+ mDisplayDevice->setProjection(ui::ROTATION_270, Rect(swapWH(mFlingerDisplaySize)),
+ Rect(swapWH(mFlingerDisplaySize)));
+ }
+
+ void expectStateForHardwareTransform0() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ void expectStateForHardwareTransform90() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
+ // size width and height swapped
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+ compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ void expectStateForHardwareTransform180() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ void expectStateForHardwareTransform270() {
+ const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+ EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
+ mHardwareDisplaySize.height),
+ compositionState.transform);
+ EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.orientation);
+ EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
+ // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
+ // size width and height swapped
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
+ compositionState.orientedDisplaySpace.content);
+ EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
+ EXPECT_EQ(false, compositionState.needsFiltering);
+ }
+
+ const ui::Size mFlingerDisplaySize;
+ const ui::Size mHardwareDisplaySize;
+ const ui::Rotation mPhysicalOrientation;
+ const sp<DisplayDevice> mDisplayDevice;
+};
+
+struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed0()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_0) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform270();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed90()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_90) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform0();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed180()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_180) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform90();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest {
+ DisplayDeviceSetProjectionTest_Installed270()
+ : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+ ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ ui::ROTATION_270) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) {
+ setProjectionForRotation0();
+ expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) {
+ setProjectionForRotation90();
+ expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) {
+ setProjectionForRotation180();
+ expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) {
+ setProjectionForRotation270();
+ expectStateForHardwareTransform180();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index e055e95..f0311bd 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -14,160 +14,22 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
-#include <type_traits>
-
-#include <android/hardware/power/Boost.h>
-#include <binder/IPCThreadState.h>
-#include <compositionengine/Display.h>
-#include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/impl/Display.h>
-#include <compositionengine/impl/OutputCompositionState.h>
-#include <compositionengine/mock/Display.h>
-#include <compositionengine/mock/DisplayColorProfile.h>
-#include <compositionengine/mock/DisplaySurface.h>
-#include <compositionengine/mock/RenderSurface.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <gui/mock/GraphicBufferConsumer.h>
-#include <gui/mock/GraphicBufferProducer.h>
-#include <log/log.h>
-#include <private/android_filesystem_config.h>
-#include <renderengine/mock/RenderEngine.h>
-#include <ui/DebugUtils.h>
-
-#include "DisplayIdentificationTest.h"
-#include "TestableScheduler.h"
-#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockComposer.h"
-#include "mock/DisplayHardware/MockPowerAdvisor.h"
-#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
-#include "mock/MockNativeWindowSurface.h"
-#include "mock/MockSchedulerCallback.h"
-#include "mock/MockSurfaceInterceptor.h"
-#include "mock/MockVsyncController.h"
-#include "mock/system/window/MockNativeWindow.h"
+#include "DisplayTransactionTestHelpers.h"
namespace android {
-namespace {
-namespace hal = android::hardware::graphics::composer::hal;
-
-using android::hardware::power::Boost;
-
-using testing::_;
using testing::AnyNumber;
using testing::DoAll;
using testing::Mock;
-using testing::ResultOf;
using testing::Return;
-using testing::ReturnRefOfCopy;
using testing::SetArgPointee;
-using testing::StrictMock;
-using hal::ColorMode;
-using hal::Connection;
-using hal::DisplayCapability;
-using hal::DisplayType;
-using hal::Error;
-using hal::Hdr;
-using hal::HWDisplayId;
-using hal::IComposer;
-using hal::IComposerClient;
-using hal::PerFrameMetadataKey;
-using hal::PowerMode;
-using hal::RenderIntent;
+using android::hardware::graphics::composer::hal::HWDisplayId;
using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
-using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
-using HotplugEvent = TestableSurfaceFlinger::HotplugEvent;
-using HWC2Display = TestableSurfaceFlinger::HWC2Display;
-
-constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'667;
-constexpr int32_t DEFAULT_DPI = 320;
-constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565;
-
-constexpr int POWER_MODE_LEET = 1337; // An out of range power mode value
-
-/* ------------------------------------------------------------------------
- * Boolean avoidance
- *
- * To make calls and template instantiations more readable, we define some
- * local enums along with an implicit bool conversion.
- */
-
-#define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true };
-
-BOOL_SUBSTITUTE(Async);
-BOOL_SUBSTITUTE(Critical);
-BOOL_SUBSTITUTE(Primary);
-BOOL_SUBSTITUTE(Secure);
-BOOL_SUBSTITUTE(Virtual);
-
-/* ------------------------------------------------------------------------
- *
- */
-
-class DisplayTransactionTest : public testing::Test {
-public:
- DisplayTransactionTest();
- ~DisplayTransactionTest() override;
-
- // --------------------------------------------------------------------
- // Mock/Fake injection
-
- void injectMockScheduler();
- void injectMockComposer(int virtualDisplayCount);
- void injectFakeBufferQueueFactory();
- void injectFakeNativeWindowSurfaceFactory();
- sp<DisplayDevice> injectDefaultInternalDisplay(std::function<void(FakeDisplayDeviceInjector&)>);
-
- // --------------------------------------------------------------------
- // Postcondition helpers
-
- bool hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId);
- bool hasTransactionFlagSet(int flag);
- bool hasDisplayDevice(sp<IBinder> displayToken);
- sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken);
- bool hasCurrentDisplayState(sp<IBinder> displayToken);
- const DisplayDeviceState& getCurrentDisplayState(sp<IBinder> displayToken);
- bool hasDrawingDisplayState(sp<IBinder> displayToken);
- const DisplayDeviceState& getDrawingDisplayState(sp<IBinder> displayToken);
-
- // --------------------------------------------------------------------
- // Test instances
-
- TestableSurfaceFlinger mFlinger;
- sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
- sp<GraphicBuffer> mBuffer = new GraphicBuffer();
- Hwc2::mock::PowerAdvisor mPowerAdvisor;
-
- // These mocks are created by the test, but are destroyed by SurfaceFlinger
- // by virtue of being stored into a std::unique_ptr. However we still need
- // to keep a reference to them for use in setting up call expectations.
- renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
- Hwc2::mock::Composer* mComposer = nullptr;
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
- sp<mock::SurfaceInterceptor> mSurfaceInterceptor = new mock::SurfaceInterceptor;
-
- mock::VsyncController* mVsyncController = new mock::VsyncController;
- mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
- mock::SchedulerCallback mSchedulerCallback;
- mock::EventThread* mEventThread = new mock::EventThread;
- mock::EventThread* mSFEventThread = new mock::EventThread;
-
- // These mocks are created only when expected to be created via a factory.
- sp<mock::GraphicBufferConsumer> mConsumer;
- sp<mock::GraphicBufferProducer> mProducer;
- surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
-};
DisplayTransactionTest::DisplayTransactionTest() {
const ::testing::TestInfo* const test_info =
@@ -208,12 +70,14 @@
void DisplayTransactionTest::injectMockScheduler() {
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(mEventThread, ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(mEventThread, /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(mSFEventThread, ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(mSFEventThread, /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
@@ -330,3298 +194,4 @@
return mFlinger.mutableDrawingState().displays.valueFor(displayToken);
}
-/* ------------------------------------------------------------------------
- *
- */
-
-template <typename PhysicalDisplay>
-struct PhysicalDisplayIdType {};
-
-template <uint64_t displayId>
-using HalVirtualDisplayIdType = std::integral_constant<uint64_t, displayId>;
-
-struct GpuVirtualDisplayIdType {};
-
-template <typename>
-struct IsPhysicalDisplayId : std::bool_constant<false> {};
-
-template <typename PhysicalDisplay>
-struct IsPhysicalDisplayId<PhysicalDisplayIdType<PhysicalDisplay>> : std::bool_constant<true> {};
-
-template <typename>
-struct DisplayIdGetter;
-
-template <typename PhysicalDisplay>
-struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
- static PhysicalDisplayId get() {
- if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
- return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY)
- ? LEGACY_DISPLAY_TYPE_PRIMARY
- : LEGACY_DISPLAY_TYPE_EXTERNAL);
- }
-
- const auto info =
- parseDisplayIdentificationData(PhysicalDisplay::PORT,
- PhysicalDisplay::GET_IDENTIFICATION_DATA());
- return info ? info->id : PhysicalDisplayId::fromPort(PhysicalDisplay::PORT);
- }
-};
-
-template <uint64_t displayId>
-struct DisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
- static HalVirtualDisplayId get() { return HalVirtualDisplayId(displayId); }
-};
-
-template <>
-struct DisplayIdGetter<GpuVirtualDisplayIdType> {
- static GpuVirtualDisplayId get() { return GpuVirtualDisplayId(0); }
-};
-
-template <typename>
-struct DisplayConnectionTypeGetter {
- static constexpr std::optional<DisplayConnectionType> value;
-};
-
-template <typename PhysicalDisplay>
-struct DisplayConnectionTypeGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
- static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
-};
-
-template <typename>
-struct HwcDisplayIdGetter {
- static constexpr std::optional<HWDisplayId> value;
-};
-
-constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
-
-template <uint64_t displayId>
-struct HwcDisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
- static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
-};
-
-template <typename PhysicalDisplay>
-struct HwcDisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
- static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID;
-};
-
-// DisplayIdType can be:
-// 1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
-// 2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
-// 3) GpuVirtualDisplayIdType for virtual display without HWC backing.
-template <typename DisplayIdType, int width, int height, Critical critical, Async async,
- Secure secure, Primary primary, int grallocUsage>
-struct DisplayVariant {
- using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
- using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
- using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
-
- // The display width and height
- static constexpr int WIDTH = width;
- static constexpr int HEIGHT = height;
-
- static constexpr int GRALLOC_USAGE = grallocUsage;
-
- // Whether the display is virtual or physical
- static constexpr Virtual VIRTUAL =
- IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE;
-
- // When creating native window surfaces for the framebuffer, whether those should be critical
- static constexpr Critical CRITICAL = critical;
-
- // When creating native window surfaces for the framebuffer, whether those should be async
- static constexpr Async ASYNC = async;
-
- // Whether the display should be treated as secure
- static constexpr Secure SECURE = secure;
-
- // Whether the display is primary
- static constexpr Primary PRIMARY = primary;
-
- static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
- auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
- if (auto displayId = PhysicalDisplayId::tryCast(DISPLAY_ID::get())) {
- ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal});
- } else {
- // We turn off the use of HwcVirtualDisplays, to prevent Composition Engine
- // from calling into HWComposer. This way all virtual displays will get
- // a GpuVirtualDisplayId, even if we are in the HwcVirtualDisplayVariant.
- // In this case we later override it by calling display.setDisplayIdForTesting().
- ceDisplayArgs.setUseHwcVirtualDisplays(false);
-
- GpuVirtualDisplayId desiredDisplayId = GpuVirtualDisplayId::tryCast(DISPLAY_ID::get())
- .value_or(GpuVirtualDisplayId(0));
-
- ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
- .WillByDefault(Return(desiredDisplayId));
-
- auto& generator = test->mFlinger.gpuVirtualDisplayIdGenerator();
- ceDisplayArgs.setGpuVirtualDisplayIdGenerator(generator);
- }
- ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor);
-
- auto compositionDisplay =
- compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
- ceDisplayArgs.build());
-
- if (HalVirtualDisplayId::tryCast(DISPLAY_ID::get())) {
- // CompositionEngine has assigned a placeholder GpuVirtualDisplayId and we need to
- // override it with the correct HalVirtualDisplayId.
- compositionDisplay->setDisplayIdForTesting(DISPLAY_ID::get());
- }
-
- auto injector = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
- CONNECTION_TYPE::value, HWC_DISPLAY_ID_OPT::value,
- static_cast<bool>(PRIMARY));
-
- injector.setSecure(static_cast<bool>(SECURE));
- injector.setNativeWindow(test->mNativeWindow);
-
- // Creating a DisplayDevice requires getting default dimensions from the
- // native window along with some other initial setup.
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
- .WillRepeatedly(Return(0));
-
- return injector;
- }
-
- // Called by tests to set up any native window creation call expectations.
- static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
- .WillOnce(Return(test->mNativeWindow));
-
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
- .WillRepeatedly(Return(0));
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
- .WillRepeatedly(Return(0));
- }
-
- static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
- EXPECT_CALL(*test->mConsumer, setConsumerName(_)).WillRepeatedly(Return(NO_ERROR));
- EXPECT_CALL(*test->mConsumer, setConsumerUsageBits(GRALLOC_USAGE))
- .WillRepeatedly(Return(NO_ERROR));
- EXPECT_CALL(*test->mConsumer, setDefaultBufferSize(WIDTH, HEIGHT))
- .WillRepeatedly(Return(NO_ERROR));
- EXPECT_CALL(*test->mConsumer, setMaxAcquiredBufferCount(_))
- .WillRepeatedly(Return(NO_ERROR));
- }
-
- static void setupFramebufferProducerBufferQueueCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mProducer, allocateBuffers(0, 0, 0, 0)).WillRepeatedly(Return());
- }
-};
-
-template <HWDisplayId hwcDisplayId, DisplayType hwcDisplayType, typename DisplayVariant,
- typename PhysicalDisplay = void>
-struct HwcDisplayVariant {
- // The display id supplied by the HWC
- static constexpr HWDisplayId HWC_DISPLAY_ID = hwcDisplayId;
-
- // The HWC display type
- static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
-
- // The HWC active configuration id
- static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
- static constexpr PowerMode INIT_POWER_MODE = PowerMode::ON;
-
- static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
- test->mFlinger.mutablePendingHotplugEvents().emplace_back(
- HotplugEvent{HWC_DISPLAY_ID, connection});
- }
-
- // Called by tests to inject a HWC display setup
- static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
- const auto displayId = DisplayVariant::DISPLAY_ID::get();
- ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
- FakeHwcDisplayInjector(displayId, HWC_DISPLAY_TYPE,
- static_cast<bool>(DisplayVariant::PRIMARY))
- .setHwcDisplayId(HWC_DISPLAY_ID)
- .setWidth(DisplayVariant::WIDTH)
- .setHeight(DisplayVariant::HEIGHT)
- .setActiveConfig(HWC_ACTIVE_CONFIG_ID)
- .setPowerMode(INIT_POWER_MODE)
- .inject(&test->mFlinger, test->mComposer);
- }
-
- // Called by tests to inject a HWC display setup
- static void injectHwcDisplay(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
- Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
- .WillOnce(Return(Error::NONE));
- injectHwcDisplayWithNoDefaultCapabilities(test);
- }
-
- static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
- DisplayTransactionTest* test) {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
-
- auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
- .setPhysical({DisplayVariant::DISPLAY_ID::get(),
- PhysicalDisplay::CONNECTION_TYPE})
- .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
- .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
- .setPowerAdvisor(&test->mPowerAdvisor)
- .setName(std::string("Injected display for ") +
- test_info->test_case_name() + "." + test_info->name())
- .build();
-
- return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
- ceDisplayArgs);
- }
-
- static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
- constexpr auto CONNECTION_TYPE =
- PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
- ? IComposerClient::DisplayConnectionType::INTERNAL
- : IComposerClient::DisplayConnectionType::EXTERNAL;
-
- EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE)));
-
- EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
- .WillOnce(Return(hal::Error::NONE));
- EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}),
- Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::WIDTH, _))
- .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::HEIGHT, _))
- .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::VSYNC_PERIOD, _))
- .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::DPI_X, _))
- .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::DPI_Y, _))
- .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
- IComposerClient::Attribute::CONFIG_GROUP, _))
- .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
-
- if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
- EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(PhysicalDisplay::PORT),
- SetArgPointee<2>(PhysicalDisplay::GET_IDENTIFICATION_DATA()),
- Return(Error::NONE)));
- } else {
- EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
- .WillOnce(Return(Error::UNSUPPORTED));
- }
- }
-
- // Called by tests to set up HWC call expectations
- static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE)));
- }
-};
-
-// Physical displays are expected to be synchronous, secure, and have a HWC display for output.
-constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
- GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
-
-template <typename PhysicalDisplay, int width, int height, Critical critical>
-struct PhysicalDisplayVariant
- : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
- Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
- GRALLOC_USAGE_PHYSICAL_DISPLAY>,
- HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
- DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
- critical, Async::FALSE, Secure::TRUE,
- PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
- PhysicalDisplay> {};
-
-template <bool hasIdentificationData>
-struct PrimaryDisplay {
- static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal;
- static constexpr Primary PRIMARY = Primary::TRUE;
- static constexpr uint8_t PORT = 255;
- static constexpr HWDisplayId HWC_DISPLAY_ID = 1001;
- static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
- static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
-};
-
-template <bool hasIdentificationData>
-struct ExternalDisplay {
- static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External;
- static constexpr Primary PRIMARY = Primary::FALSE;
- static constexpr uint8_t PORT = 254;
- static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
- static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
- static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
-};
-
-struct TertiaryDisplay {
- static constexpr Primary PRIMARY = Primary::FALSE;
- static constexpr uint8_t PORT = 253;
- static constexpr HWDisplayId HWC_DISPLAY_ID = 1003;
- static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
-};
-
-// A primary display is a physical display that is critical
-using PrimaryDisplayVariant =
- PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
-
-// An external display is physical display that is not critical.
-using ExternalDisplayVariant =
- PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
-
-using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
-
-// A virtual display not supported by the HWC.
-constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
-
-template <int width, int height, Secure secure>
-struct NonHwcVirtualDisplayVariant
- : DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure,
- Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
- using Base =
- DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE,
- secure, Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
-
- static void injectHwcDisplay(DisplayTransactionTest*) {}
-
- static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
- DisplayTransactionTest* test) {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
-
- ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
- .WillByDefault(Return(Base::DISPLAY_ID::get()));
-
- auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
- .setPixels({Base::WIDTH, Base::HEIGHT})
- .setIsSecure(static_cast<bool>(Base::SECURE))
- .setPowerAdvisor(&test->mPowerAdvisor)
- .setName(std::string("Injected display for ") +
- test_info->test_case_name() + "." + test_info->name())
- .setGpuVirtualDisplayIdGenerator(
- test->mFlinger.gpuVirtualDisplayIdGenerator())
- .build();
-
- return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
- ceDisplayArgs);
- }
-
- static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
- }
-
- static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
- Base::setupNativeWindowSurfaceCreationCallExpectations(test);
- EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
- }
-};
-
-// A virtual display supported by the HWC.
-constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER;
-
-template <int width, int height, Secure secure>
-struct HwcVirtualDisplayVariant
- : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
- secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
- HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
- DisplayVariant<HalVirtualDisplayIdType<42>, width, height,
- Critical::FALSE, Async::TRUE, secure, Primary::FALSE,
- GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
- using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
- Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
- using Self = HwcVirtualDisplayVariant<width, height, secure>;
-
- static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
- DisplayTransactionTest* test) {
- const ::testing::TestInfo* const test_info =
- ::testing::UnitTest::GetInstance()->current_test_info();
-
- // In order to prevent compostition engine calling into HWComposer, we
- // 1. turn off the use of HWC virtual displays,
- // 2. provide a GpuVirtualDisplayIdGenerator which always returns some fake ID
- // 3. override the ID by calling setDisplayIdForTesting()
-
- ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
- .WillByDefault(Return(GpuVirtualDisplayId(0)));
-
- auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
- .setUseHwcVirtualDisplays(false)
- .setPixels({Base::WIDTH, Base::HEIGHT})
- .setIsSecure(static_cast<bool>(Base::SECURE))
- .setPowerAdvisor(&test->mPowerAdvisor)
- .setName(std::string("Injected display for ") +
- test_info->test_case_name() + "." + test_info->name())
- .setGpuVirtualDisplayIdGenerator(
- test->mFlinger.gpuVirtualDisplayIdGenerator())
- .build();
-
- auto compositionDisplay =
- compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
- ceDisplayArgs);
- compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
-
- // Insert display data so that the HWC thinks it created the virtual display.
- if (const auto displayId = Base::DISPLAY_ID::get();
- HalVirtualDisplayId::tryCast(displayId)) {
- test->mFlinger.mutableHwcDisplayData().try_emplace(displayId);
- }
-
- return compositionDisplay;
- }
-
- static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
- Base::setupNativeWindowSurfaceCreationCallExpectations(test);
- EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
- }
-
- static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _))
- .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
- }
-};
-
-// For this variant, SurfaceFlinger should not configure itself with wide
-// display support, so the display should not be configured for wide-color
-// support.
-struct WideColorSupportNotConfiguredVariant {
- static constexpr bool WIDE_COLOR_SUPPORTED = false;
-
- static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableHasWideColorDisplay() = false;
- test->mFlinger.mutableUseColorManagement() = false;
- test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
- }
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getColorModes(_, _)).Times(0);
- EXPECT_CALL(*test->mComposer, getRenderIntents(_, _, _)).Times(0);
- EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
- }
-};
-
-// For this variant, SurfaceFlinger should configure itself with wide display
-// support, and the display should respond with an non-empty list of supported
-// color modes. Wide-color support should be configured.
-template <typename Display>
-struct WideColorP3ColorimetricSupportedVariant {
- static constexpr bool WIDE_COLOR_SUPPORTED = true;
-
- static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableUseColorManagement() = true;
- test->mFlinger.mutableHasWideColorDisplay() = true;
- test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
- }
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1);
-
- EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})),
- Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- getRenderIntents(Display::HWC_DISPLAY_ID, ColorMode::DISPLAY_P3, _))
- .WillOnce(DoAll(SetArgPointee<2>(
- std::vector<RenderIntent>({RenderIntent::COLORIMETRIC})),
- Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer,
- setColorMode(Display::HWC_DISPLAY_ID, ColorMode::SRGB,
- RenderIntent::COLORIMETRIC))
- .WillOnce(Return(Error::NONE));
- }
-};
-
-// For this variant, SurfaceFlinger should configure itself with wide display
-// support, but the display should respond with an empty list of supported color
-// modes. Wide-color support for the display should not be configured.
-template <typename Display>
-struct WideColorNotSupportedVariant {
- static constexpr bool WIDE_COLOR_SUPPORTED = false;
-
- static void injectConfigChange(DisplayTransactionTest* test) {
- test->mFlinger.mutableUseColorManagement() = true;
- test->mFlinger.mutableHasWideColorDisplay() = true;
- }
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE)));
- EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
- }
-};
-
-// For this variant, the display is not a HWC display, so no HDR support should
-// be configured.
-struct NonHwcDisplayHdrSupportVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = false;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _)).Times(0);
- }
-};
-
-template <typename Display>
-struct Hdr10PlusSupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = true;
- static constexpr bool HDR10_SUPPORTED = true;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({
- Hdr::HDR10_PLUS,
- Hdr::HDR10,
- })),
- Return(Error::NONE)));
- }
-};
-
-// For this variant, the composer should respond with a non-empty list of HDR
-// modes containing HDR10, so HDR10 support should be configured.
-template <typename Display>
-struct Hdr10SupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = true;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HDR10})),
- Return(Error::NONE)));
- }
-};
-
-// For this variant, the composer should respond with a non-empty list of HDR
-// modes containing HLG, so HLG support should be configured.
-template <typename Display>
-struct HdrHlgSupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = false;
- static constexpr bool HDR_HLG_SUPPORTED = true;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
- .WillOnce(
- DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HLG})), Return(Error::NONE)));
- }
-};
-
-// For this variant, the composer should respond with a non-empty list of HDR
-// modes containing DOLBY_VISION, so DOLBY_VISION support should be configured.
-template <typename Display>
-struct HdrDolbyVisionSupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = false;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = true;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::DOLBY_VISION})),
- Return(Error::NONE)));
- }
-};
-
-// For this variant, the composer should respond with am empty list of HDR
-// modes, so no HDR support should be configured.
-template <typename Display>
-struct HdrNotSupportedVariant {
- static constexpr bool HDR10_PLUS_SUPPORTED = false;
- static constexpr bool HDR10_SUPPORTED = false;
- static constexpr bool HDR_HLG_SUPPORTED = false;
- static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE)));
- }
-};
-
-struct NonHwcPerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = 0;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0);
- }
-};
-
-template <typename Display>
-struct NoPerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = 0;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
- .WillOnce(Return(std::vector<PerFrameMetadataKey>()));
- }
-};
-
-template <typename Display>
-struct Smpte2086PerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
- .WillOnce(Return(std::vector<PerFrameMetadataKey>({
- PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
- PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
- PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
- PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
- PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
- PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
- PerFrameMetadataKey::WHITE_POINT_X,
- PerFrameMetadataKey::WHITE_POINT_Y,
- PerFrameMetadataKey::MAX_LUMINANCE,
- PerFrameMetadataKey::MIN_LUMINANCE,
- })));
- }
-};
-
-template <typename Display>
-struct Cta861_3_PerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
- .WillOnce(Return(std::vector<PerFrameMetadataKey>({
- PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
- PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
- })));
- }
-};
-
-template <typename Display>
-struct Hdr10_Plus_PerFrameMetadataSupportVariant {
- static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::HDR10PLUS;
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
- .WillOnce(Return(std::vector<PerFrameMetadataKey>({
- PerFrameMetadataKey::HDR10_PLUS_SEI,
- })));
- }
-};
-/* ------------------------------------------------------------------------
- * Typical display configurations to test
- */
-
-template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy,
- typename PerFrameMetadataSupportPolicy>
-struct Case {
- using Display = DisplayPolicy;
- using WideColorSupport = WideColorSupportPolicy;
- using HdrSupport = HdrSupportPolicy;
- using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy;
-};
-
-using SimplePrimaryDisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using SimpleExternalDisplayCase =
- Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>,
- HdrNotSupportedVariant<ExternalDisplayVariant>,
- NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>;
-using SimpleTertiaryDisplayCase =
- Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>,
- HdrNotSupportedVariant<TertiaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>;
-using NonHwcVirtualDisplayCase =
- Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>,
- WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant,
- NonHwcPerFrameMetadataSupportVariant>;
-using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>;
-using HwcVirtualDisplayCase =
- Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant,
- HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>,
- NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>;
-using WideColorP3ColorimetricDisplayCase =
- Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using Hdr10PlusDisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- Hdr10SupportedVariant<PrimaryDisplayVariant>,
- Hdr10_Plus_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using Hdr10DisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- Hdr10SupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrHlgDisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrHlgSupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrDolbyVisionDisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>,
- NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrSmpte2086DisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>,
- Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-using HdrCta861_3_DisplayCase =
- Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
- HdrNotSupportedVariant<PrimaryDisplayVariant>,
- Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
-
-/* ------------------------------------------------------------------------
- *
- * SurfaceFlinger::onHotplugReceived
- */
-
-TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) {
- constexpr int currentSequenceId = 123;
- constexpr HWDisplayId hwcDisplayId1 = 456;
- constexpr HWDisplayId hwcDisplayId2 = 654;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Set the current sequence id for accepted events
- mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
- // Set the main thread id so that the current thread does not appear to be
- // the main thread.
- mFlinger.mutableMainThreadId() = std::thread::id();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We expect invalidate() to be invoked once to trigger display transaction
- // processing.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- // Simulate two hotplug events (a connect and a disconnect)
- mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, Connection::CONNECTED);
- mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, Connection::DISCONNECTED);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display transaction needed flag should be set.
- EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // All events should be in the pending event queue.
- const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
- ASSERT_EQ(2u, pendingEvents.size());
- EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
- EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection);
- EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
- EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
-}
-
-TEST_F(DisplayTransactionTest, hotplugDiscardsUnexpectedEvents) {
- constexpr int currentSequenceId = 123;
- constexpr int otherSequenceId = 321;
- constexpr HWDisplayId displayId = 456;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Set the current sequence id for accepted events
- mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
- // Set the main thread id so that the current thread does not appear to be
- // the main thread.
- mFlinger.mutableMainThreadId() = std::thread::id();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We do not expect any calls to invalidate().
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(0);
-
- // --------------------------------------------------------------------
- // Invocation
-
- // Call with an unexpected sequence id
- mFlinger.onHotplugReceived(otherSequenceId, displayId, Connection::INVALID);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display transaction needed flag should not be set
- EXPECT_FALSE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // There should be no pending events
- EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
-}
-
-TEST_F(DisplayTransactionTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread) {
- constexpr int currentSequenceId = 123;
- constexpr HWDisplayId displayId1 = 456;
-
- // --------------------------------------------------------------------
- // Note:
- // --------------------------------------------------------------------
- // This test case is a bit tricky. We want to verify that
- // onHotplugReceived() calls processDisplayHotplugEventsLocked(), but we
- // don't really want to provide coverage for everything the later function
- // does as there are specific tests for it.
- // --------------------------------------------------------------------
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Set the current sequence id for accepted events
- mFlinger.mutableComposerSequenceId() = currentSequenceId;
-
- // Set the main thread id so that the current thread does appear to be the
- // main thread.
- mFlinger.mutableMainThreadId() = std::this_thread::get_id();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We expect invalidate() to be invoked once to trigger display transaction
- // processing.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- // Simulate a disconnect on a display id that is not connected. This should
- // be enqueued by onHotplugReceived(), and dequeued by
- // processDisplayHotplugEventsLocked(), but then ignored as invalid.
- mFlinger.onHotplugReceived(currentSequenceId, displayId1, Connection::DISCONNECTED);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display transaction needed flag should be set.
- EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // There should be no event queued on return, as it should have been
- // processed.
- EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::createDisplay
- */
-
-TEST_F(DisplayTransactionTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
- const String8 name("virtual.test");
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // The call should notify the interceptor that a display was created.
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- sp<IBinder> displayToken = mFlinger.createDisplay(name, false);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display should have been added to the current state
- ASSERT_TRUE(hasCurrentDisplayState(displayToken));
- const auto& display = getCurrentDisplayState(displayToken);
- EXPECT_TRUE(display.isVirtual());
- EXPECT_FALSE(display.isSecure);
- EXPECT_EQ(name.string(), display.displayName);
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- // Destroying the display invalidates the display state.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-}
-
-TEST_F(DisplayTransactionTest, createDisplaySetsCurrentStateForSecureDisplay) {
- const String8 name("virtual.test");
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // The call should notify the interceptor that a display was created.
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
- int64_t oldId = IPCThreadState::self()->clearCallingIdentity();
- // Set the calling identity to graphics so captureDisplay with secure is allowed.
- IPCThreadState::self()->restoreCallingIdentity(static_cast<int64_t>(AID_GRAPHICS) << 32 |
- AID_GRAPHICS);
- sp<IBinder> displayToken = mFlinger.createDisplay(name, true);
- IPCThreadState::self()->restoreCallingIdentity(oldId);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display should have been added to the current state
- ASSERT_TRUE(hasCurrentDisplayState(displayToken));
- const auto& display = getCurrentDisplayState(displayToken);
- EXPECT_TRUE(display.isVirtual());
- EXPECT_TRUE(display.isSecure);
- EXPECT_EQ(name.string(), display.displayName);
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- // Destroying the display invalidates the display state.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::destroyDisplay
- */
-
-TEST_F(DisplayTransactionTest, destroyDisplayClearsCurrentStateForDisplay) {
- using Case = NonHwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A virtual display exists
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // The call should notify the interceptor that a display was created.
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
- // Destroying the display invalidates the display state.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.destroyDisplay(existing.token());
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display should have been removed from the current state
- EXPECT_FALSE(hasCurrentDisplayState(existing.token()));
-
- // Ths display should still exist in the drawing state
- EXPECT_TRUE(hasDrawingDisplayState(existing.token()));
-
- // The display transaction needed flasg should be set
- EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-}
-
-TEST_F(DisplayTransactionTest, destroyDisplayHandlesUnknownDisplay) {
- // --------------------------------------------------------------------
- // Preconditions
-
- sp<BBinder> displayToken = new BBinder();
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.destroyDisplay(displayToken);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::notifyPowerBoost
- */
-
-TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
- mFlinger.scheduler()->replaceTouchTimer(100);
- std::this_thread::sleep_for(10ms); // wait for callback to be triggered
- EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
-
- std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
- EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
-
- EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT)));
- std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered
- EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
-
- std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
- EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
-
- EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION)));
- std::this_thread::sleep_for(10ms); // wait for callback to be triggered.
- EXPECT_TRUE(mFlinger.scheduler()->isTouchActive());
-}
-
-/* ------------------------------------------------------------------------
- * DisplayDevice::GetBestColorMode
- */
-class GetBestColorModeTest : public DisplayTransactionTest {
-public:
- void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
-
- void addHwcColorModesMapping(ui::ColorMode colorMode,
- std::vector<ui::RenderIntent> renderIntents) {
- mHwcColorModes[colorMode] = renderIntents;
- }
-
- void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; }
-
- void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
-
- void getBestColorMode() {
- auto displayDevice =
- injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
- injector.setHwcColorModes(mHwcColorModes);
- injector.setHasWideColorGamut(mHasWideColorGamut);
- injector.setNativeWindow(mNativeWindow);
- });
-
- displayDevice->getCompositionDisplay()
- ->getDisplayColorProfile()
- ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace,
- &mOutColorMode, &mOutRenderIntent);
- }
-
- ui::Dataspace mOutDataspace;
- ui::ColorMode mOutColorMode;
- ui::RenderIntent mOutRenderIntent;
-
-private:
- ui::Dataspace mInputDataspace;
- ui::RenderIntent mInputRenderIntent;
- bool mHasWideColorGamut = false;
- std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
-};
-
-TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
- addHwcColorModesMapping(ui::ColorMode::SRGB,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- setInputDataspace(ui::Dataspace::DISPLAY_P3);
- setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
- setHasWideColorGamut(true);
-
- getBestColorMode();
-
- ASSERT_EQ(ui::Dataspace::V0_SRGB, mOutDataspace);
- ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode);
- ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
-}
-
-TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) {
- addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- addHwcColorModesMapping(ui::ColorMode::SRGB,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- setInputDataspace(ui::Dataspace::DISPLAY_P3);
- setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
- setHasWideColorGamut(true);
-
- getBestColorMode();
-
- ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace);
- ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode);
- ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
-}
-
-TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) {
- addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020,
- std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC));
- setInputDataspace(ui::Dataspace::DISPLAY_P3);
- setInputRenderIntent(ui::RenderIntent::COLORIMETRIC);
- setHasWideColorGamut(true);
-
- getBestColorMode();
-
- ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace);
- ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode);
- ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent);
-}
-
-/* ------------------------------------------------------------------------
- * DisplayDevice::setProjection
- */
-
-class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
-public:
- static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080; // arbitrary
- static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
-
- static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0;
- static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90;
- static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V;
- static constexpr int32_t TRANSFORM_FLAGS_ROT_270 =
- HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
-
- DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize,
- ui::Rotation physicalOrientation)
- : mFlingerDisplaySize(flingerDisplaySize),
- mHardwareDisplaySize(hardwareDisplaySize),
- mPhysicalOrientation(physicalOrientation),
- mDisplayDevice(createDisplayDevice()) {}
-
- sp<DisplayDevice> createDisplayDevice() {
- return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
- injector.setPhysicalOrientation(mPhysicalOrientation);
- });
- }
-
- ui::Size swapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
-
- void setProjectionForRotation0() {
- // A logical rotation of 0 uses the SurfaceFlinger display size
- mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize),
- Rect(mFlingerDisplaySize));
- }
-
- void setProjectionForRotation90() {
- // A logical rotation of 90 uses the SurfaceFlinger display size with
- // the width/height swapped.
- mDisplayDevice->setProjection(ui::ROTATION_90, Rect(swapWH(mFlingerDisplaySize)),
- Rect(swapWH(mFlingerDisplaySize)));
- }
-
- void setProjectionForRotation180() {
- // A logical rotation of 180 uses the SurfaceFlinger display size
- mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize),
- Rect(mFlingerDisplaySize));
- }
-
- void setProjectionForRotation270() {
- // A logical rotation of 270 uses the SurfaceFlinger display size with
- // the width/height swapped.
- mDisplayDevice->setProjection(ui::ROTATION_270, Rect(swapWH(mFlingerDisplaySize)),
- Rect(swapWH(mFlingerDisplaySize)));
- }
-
- void expectStateForHardwareTransform0() {
- const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
- EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
- mHardwareDisplaySize.height),
- compositionState.transform);
- EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
- EXPECT_EQ(false, compositionState.needsFiltering);
- }
-
- void expectStateForHardwareTransform90() {
- const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
- EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
- mHardwareDisplaySize.height),
- compositionState.transform);
- EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
- // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
- // size width and height swapped
- EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
- compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
- EXPECT_EQ(false, compositionState.needsFiltering);
- }
-
- void expectStateForHardwareTransform180() {
- const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
- EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
- mHardwareDisplaySize.height),
- compositionState.transform);
- EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content);
- EXPECT_EQ(false, compositionState.needsFiltering);
- }
-
- void expectStateForHardwareTransform270() {
- const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
- EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
- mHardwareDisplaySize.height),
- compositionState.transform);
- EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.orientation);
- EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content);
- // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display
- // size width and height swapped
- EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)),
- compositionState.orientedDisplaySpace.content);
- EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content);
- EXPECT_EQ(false, compositionState.needsFiltering);
- }
-
- const ui::Size mFlingerDisplaySize;
- const ui::Size mHardwareDisplaySize;
- const ui::Rotation mPhysicalOrientation;
- const sp<DisplayDevice> mDisplayDevice;
-};
-
-struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest {
- DisplayDeviceSetProjectionTest_Installed0()
- : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::ROTATION_0) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) {
- setProjectionForRotation0();
- expectStateForHardwareTransform0();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) {
- setProjectionForRotation90();
- expectStateForHardwareTransform90();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) {
- setProjectionForRotation180();
- expectStateForHardwareTransform180();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) {
- setProjectionForRotation270();
- expectStateForHardwareTransform270();
-}
-
-struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest {
- DisplayDeviceSetProjectionTest_Installed90()
- : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
- ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::ROTATION_90) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) {
- setProjectionForRotation0();
- expectStateForHardwareTransform90();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) {
- setProjectionForRotation90();
- expectStateForHardwareTransform180();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) {
- setProjectionForRotation180();
- expectStateForHardwareTransform270();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) {
- setProjectionForRotation270();
- expectStateForHardwareTransform0();
-}
-
-struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest {
- DisplayDeviceSetProjectionTest_Installed180()
- : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::ROTATION_180) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) {
- setProjectionForRotation0();
- expectStateForHardwareTransform180();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) {
- setProjectionForRotation90();
- expectStateForHardwareTransform270();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) {
- setProjectionForRotation180();
- expectStateForHardwareTransform0();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) {
- setProjectionForRotation270();
- expectStateForHardwareTransform90();
-}
-
-struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest {
- DisplayDeviceSetProjectionTest_Installed270()
- : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
- ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- ui::ROTATION_270) {}
-};
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) {
- setProjectionForRotation0();
- expectStateForHardwareTransform270();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) {
- setProjectionForRotation90();
- expectStateForHardwareTransform0();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) {
- setProjectionForRotation180();
- expectStateForHardwareTransform90();
-}
-
-TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) {
- setProjectionForRotation270();
- expectStateForHardwareTransform180();
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::getDisplayNativePrimaries
- */
-
-class GetDisplayNativePrimaries : public DisplayTransactionTest {
-public:
- GetDisplayNativePrimaries();
- void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries);
- void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries);
-
-private:
- static constexpr float mStartingTestValue = 1.0f;
-};
-
-GetDisplayNativePrimaries::GetDisplayNativePrimaries() {
- SimplePrimaryDisplayCase::Display::injectHwcDisplay(this);
- injectFakeNativeWindowSurfaceFactory();
-}
-
-void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries(
- ui::DisplayPrimaries& primaries) {
- float startingVal = mStartingTestValue;
- primaries.red.X = startingVal++;
- primaries.red.Y = startingVal++;
- primaries.red.Z = startingVal++;
- primaries.green.X = startingVal++;
- primaries.green.Y = startingVal++;
- primaries.green.Z = startingVal++;
- primaries.blue.X = startingVal++;
- primaries.blue.Y = startingVal++;
- primaries.blue.Z = startingVal++;
- primaries.white.X = startingVal++;
- primaries.white.Y = startingVal++;
- primaries.white.Z = startingVal++;
-}
-
-void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries(
- const ui::DisplayPrimaries& primaries) {
- float startingVal = mStartingTestValue;
- EXPECT_EQ(primaries.red.X, startingVal++);
- EXPECT_EQ(primaries.red.Y, startingVal++);
- EXPECT_EQ(primaries.red.Z, startingVal++);
- EXPECT_EQ(primaries.green.X, startingVal++);
- EXPECT_EQ(primaries.green.Y, startingVal++);
- EXPECT_EQ(primaries.green.Z, startingVal++);
- EXPECT_EQ(primaries.blue.X, startingVal++);
- EXPECT_EQ(primaries.blue.Y, startingVal++);
- EXPECT_EQ(primaries.blue.Z, startingVal++);
- EXPECT_EQ(primaries.white.X, startingVal++);
- EXPECT_EQ(primaries.white.Y, startingVal++);
- EXPECT_EQ(primaries.white.Z, startingVal++);
-}
-
-TEST_F(GetDisplayNativePrimaries, nullDisplayToken) {
- ui::DisplayPrimaries primaries;
- EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries));
-}
-
-TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) {
- auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
- injector.inject();
- auto internalDisplayToken = injector.token();
-
- ui::DisplayPrimaries expectedPrimaries;
- populateDummyDisplayNativePrimaries(expectedPrimaries);
- mFlinger.setInternalDisplayPrimaries(expectedPrimaries);
-
- ui::DisplayPrimaries primaries;
- EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
-
- checkDummyDisplayNativePrimaries(primaries);
-}
-
-TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
- sp<BBinder> notInternalDisplayToken = new BBinder();
-
- ui::DisplayPrimaries primaries;
- populateDummyDisplayNativePrimaries(primaries);
- EXPECT_EQ(NAME_NOT_FOUND,
- mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
-
- // Check primaries argument wasn't modified in case of failure
- checkDummyDisplayNativePrimaries(primaries);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::setupNewDisplayDeviceInternal
- */
-
-class SetupNewDisplayDeviceInternalTest : public DisplayTransactionTest {
-public:
- template <typename T>
- void setupNewDisplayDeviceInternalTest();
-};
-
-template <typename Case>
-void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
- const sp<BBinder> displayToken = new BBinder();
- const sp<compositionengine::mock::DisplaySurface> displaySurface =
- new compositionengine::mock::DisplaySurface();
- const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Wide color displays support is configured appropriately
- Case::WideColorSupport::injectConfigChange(this);
-
- // The display is setup with the HWC.
- Case::Display::injectHwcDisplay(this);
-
- // SurfaceFlinger will use a test-controlled factory for native window
- // surfaces.
- injectFakeNativeWindowSurfaceFactory();
-
- // A compositionengine::Display has already been created
- auto compositionDisplay = Case::Display::injectCompositionDisplay(this);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // Various native window calls will be made.
- Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
- Case::Display::setupHwcGetActiveConfigCallExpectations(this);
- Case::WideColorSupport::setupComposerCallExpectations(this);
- Case::HdrSupport::setupComposerCallExpectations(this);
- Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
-
- // --------------------------------------------------------------------
- // Invocation
-
- DisplayDeviceState state;
- if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
- const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
- ASSERT_TRUE(displayId);
- const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
- ASSERT_TRUE(hwcDisplayId);
- state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId};
- }
-
- state.isSecure = static_cast<bool>(Case::Display::SECURE);
-
- auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
- displaySurface, producer);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- ASSERT_TRUE(device != nullptr);
- EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
- EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
- EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
- EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
- EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
- EXPECT_EQ(Case::Display::WIDTH, device->getWidth());
- EXPECT_EQ(Case::Display::HEIGHT, device->getHeight());
- EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut());
- EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport());
- EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
- EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport());
- EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
- // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
- // remapped, and the test only ever sets up one config. If there were an error
- // looking up the remapped index, device->getActiveConfig() would be -1 instead.
- EXPECT_EQ(0, device->getActiveConfig().value());
- EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
- device->getSupportedPerFrameMetadata());
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) {
- setupNewDisplayDeviceInternalTest<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) {
- setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) {
- setupNewDisplayDeviceInternalTest<NonHwcVirtualDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
- setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
- setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) {
- setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) {
- setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrHlgDisplay) {
- setupNewDisplayDeviceInternalTest<HdrHlgDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrDolbyVisionDisplay) {
- setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) {
- setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>();
-}
-
-TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) {
- setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>();
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::handleTransactionLocked(eDisplayTransactionNeeded)
- */
-
-class HandleTransactionLockedTest : public DisplayTransactionTest {
-public:
- template <typename Case>
- void setupCommonPreconditions();
-
- template <typename Case, bool connected>
- static void expectHotplugReceived(mock::EventThread*);
-
- template <typename Case>
- void setupCommonCallExpectationsForConnectProcessing();
-
- template <typename Case>
- void setupCommonCallExpectationsForDisconnectProcessing();
-
- template <typename Case>
- void processesHotplugConnectCommon();
-
- template <typename Case>
- void ignoresHotplugConnectCommon();
-
- template <typename Case>
- void processesHotplugDisconnectCommon();
-
- template <typename Case>
- void verifyDisplayIsConnected(const sp<IBinder>& displayToken);
-
- template <typename Case>
- void verifyPhysicalDisplayIsConnected();
-
- void verifyDisplayIsNotConnected(const sp<IBinder>& displayToken);
-};
-
-template <typename Case>
-void HandleTransactionLockedTest::setupCommonPreconditions() {
- // Wide color displays support is configured appropriately
- Case::WideColorSupport::injectConfigChange(this);
-
- // SurfaceFlinger will use a test-controlled factory for BufferQueues
- injectFakeBufferQueueFactory();
-
- // SurfaceFlinger will use a test-controlled factory for native window
- // surfaces.
- injectFakeNativeWindowSurfaceFactory();
-}
-
-template <typename Case, bool connected>
-void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
- const auto convert = [](auto physicalDisplayId) {
- return std::make_optional(DisplayId{physicalDisplayId});
- };
-
- EXPECT_CALL(*eventThread,
- onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
- .Times(1);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
- Case::Display::setupHwcHotplugCallExpectations(this);
-
- Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
- Case::Display::setupFramebufferProducerBufferQueueCallExpectations(this);
- Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
- Case::Display::setupHwcGetActiveConfigCallExpectations(this);
-
- Case::WideColorSupport::setupComposerCallExpectations(this);
- Case::HdrSupport::setupComposerCallExpectations(this);
- Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
-
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
- expectHotplugReceived<Case, true>(mEventThread);
- expectHotplugReceived<Case, true>(mSFEventThread);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
- EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-
- expectHotplugReceived<Case, false>(mEventThread);
- expectHotplugReceived<Case, false>(mSFEventThread);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
- // The display device should have been set up in the list of displays.
- ASSERT_TRUE(hasDisplayDevice(displayToken));
- const auto& device = getDisplayDevice(displayToken);
- EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
- EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
-
- std::optional<DisplayDeviceState::Physical> expectedPhysical;
- if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
- const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
- ASSERT_TRUE(displayId);
- const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
- ASSERT_TRUE(hwcDisplayId);
- expectedPhysical = {.id = *displayId,
- .type = *connectionType,
- .hwcDisplayId = *hwcDisplayId};
- }
-
- // The display should have been set up in the current display state
- ASSERT_TRUE(hasCurrentDisplayState(displayToken));
- const auto& current = getCurrentDisplayState(displayToken);
- EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual());
- EXPECT_EQ(expectedPhysical, current.physical);
-
- // The display should have been set up in the drawing display state
- ASSERT_TRUE(hasDrawingDisplayState(displayToken));
- const auto& draw = getDrawingDisplayState(displayToken);
- EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
- EXPECT_EQ(expectedPhysical, draw.physical);
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
- // HWComposer should have an entry for the display
- EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-
- // SF should have a display token.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
- auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[displayId];
-
- verifyDisplayIsConnected<Case>(displayToken);
-}
-
-void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
- EXPECT_FALSE(hasDisplayDevice(displayToken));
- EXPECT_FALSE(hasCurrentDisplayState(displayToken));
- EXPECT_FALSE(hasDrawingDisplayState(displayToken));
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::processesHotplugConnectCommon() {
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- setupCommonCallExpectationsForConnectProcessing<Case>();
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- verifyPhysicalDisplayIsConnected<Case>();
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- EXPECT_CALL(*mComposer,
- setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::ignoresHotplugConnectCommon() {
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // HWComposer should not have an entry for the display
- EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-}
-
-template <typename Case>
-void HandleTransactionLockedTest::processesHotplugDisconnectCommon() {
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // A hotplug disconnect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
-
- // The display is already completely set up.
- Case::Display::injectHwcDisplay(this);
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _))
- .Times(0);
-
- setupCommonCallExpectationsForDisconnectProcessing<Case>();
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // HWComposer should not have an entry for the display
- EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-
- // SF should not have a display token.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
-
- // The existing token should have been removed
- verifyDisplayIsNotConnected(existing.token());
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) {
- processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest,
- processesHotplugConnectPrimaryDisplayWithExternalAlreadyConnected) {
- // Inject an external display.
- ExternalDisplayVariant::injectHwcDisplay(this);
-
- processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
- // Inject a primary display.
- PrimaryDisplayVariant::injectHwcDisplay(this);
-
- processesHotplugConnectCommon<SimpleExternalDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
- // Inject both a primary and external display.
- PrimaryDisplayVariant::injectHwcDisplay(this);
- ExternalDisplayVariant::injectHwcDisplay(this);
-
- // TODO: This is an unnecessary call.
- EXPECT_CALL(*mComposer,
- getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
- .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT),
- SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
- Return(Error::NONE)));
-
- ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
- processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
- processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // A hotplug connect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
- // A hotplug disconnect event is also enqueued for the same display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- setupCommonCallExpectationsForConnectProcessing<Case>();
- setupCommonCallExpectationsForDisconnectProcessing<Case>();
-
- EXPECT_CALL(*mComposer,
- setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // HWComposer should not have an entry for the display
- EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
-
- // SF should not have a display token.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
-}
-
-TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- setupCommonPreconditions<Case>();
-
- // The display is already completely set up.
- Case::Display::injectHwcDisplay(this);
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
-
- // A hotplug disconnect event is enqueued for a display
- Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
- // A hotplug connect event is also enqueued for the same display
- Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- setupCommonCallExpectationsForConnectProcessing<Case>();
- setupCommonCallExpectationsForDisconnectProcessing<Case>();
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The existing token should have been removed
- verifyDisplayIsNotConnected(existing.token());
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
- ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
- EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
-
- // A new display should be connected in its place
-
- verifyPhysicalDisplayIsConnected<Case>();
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- EXPECT_CALL(*mComposer,
- setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-}
-
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) {
- using Case = HwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // The HWC supports at least one virtual display
- injectMockComposer(1);
-
- setupCommonPreconditions<Case>();
-
- // A virtual display was added to the current state, and it has a
- // surface(producer)
- sp<BBinder> displayToken = new BBinder();
-
- DisplayDeviceState state;
- state.isSecure = static_cast<bool>(Case::Display::SECURE);
-
- sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()};
- state.surface = surface;
- mFlinger.mutableCurrentState().displays.add(displayToken, state);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
- Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
-
- EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR)));
- EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::HEIGHT), Return(NO_ERROR)));
- EXPECT_CALL(*surface, query(NATIVE_WINDOW_FORMAT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT),
- Return(NO_ERROR)));
- EXPECT_CALL(*surface, query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
-
- EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);
-
- EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1);
- EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);
-
- Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
- Case::WideColorSupport::setupComposerCallExpectations(this);
- Case::HdrSupport::setupComposerCallExpectations(this);
- Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The display device should have been set up in the list of displays.
- verifyDisplayIsConnected<Case>(displayToken);
-
- // --------------------------------------------------------------------
- // Cleanup conditions
-
- EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID))
- .WillOnce(Return(Error::NONE));
- EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
-
- // Cleanup
- mFlinger.mutableCurrentState().displays.removeItem(displayToken);
- mFlinger.mutableDrawingState().displays.removeItem(displayToken);
-}
-
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
- using Case = HwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // The HWC supports at least one virtual display
- injectMockComposer(1);
-
- setupCommonPreconditions<Case>();
-
- // A virtual display was added to the current state, but it does not have a
- // surface.
- sp<BBinder> displayToken = new BBinder();
-
- DisplayDeviceState state;
- state.isSecure = static_cast<bool>(Case::Display::SECURE);
-
- mFlinger.mutableCurrentState().displays.add(displayToken, state);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // There will not be a display device set up.
- EXPECT_FALSE(hasDisplayDevice(displayToken));
-
- // The drawing display state will be set from the current display state.
- ASSERT_TRUE(hasDrawingDisplayState(displayToken));
- const auto& draw = getDrawingDisplayState(displayToken);
- EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
-}
-
-TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
- using Case = HwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A virtual display is set up but is removed from the current state.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
- mFlinger.mutableHwcDisplayData().try_emplace(displayId);
- Case::Display::injectHwcDisplay(this);
- auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
- existing.inject();
- mFlinger.mutableCurrentState().displays.removeItem(existing.token());
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The existing token should have been removed
- verifyDisplayIsNotConnected(existing.token());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- constexpr uint32_t oldLayerStack = 0u;
- constexpr uint32_t newLayerStack = 123u;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a change to the layerStack state
- display.mutableDrawingDisplayState().layerStack = oldLayerStack;
- display.mutableCurrentDisplayState().layerStack = newLayerStack;
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- constexpr ui::Rotation oldTransform = ui::ROTATION_0;
- constexpr ui::Rotation newTransform = ui::ROTATION_180;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a change to the orientation state
- display.mutableDrawingDisplayState().orientation = oldTransform;
- display.mutableCurrentDisplayState().orientation = newTransform;
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- const Rect oldLayerStackRect(0, 0, 0, 0);
- const Rect newLayerStackRect(0, 0, 123, 456);
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a change to the layerStackSpaceRect state
- display.mutableDrawingDisplayState().layerStackSpaceRect = oldLayerStackRect;
- display.mutableCurrentDisplayState().layerStackSpaceRect = newLayerStackRect;
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- const Rect oldFrame(0, 0, 0, 0);
- const Rect newFrame(0, 0, 123, 456);
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a change to the layerStackSpaceRect state
- display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldFrame;
- display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newFrame;
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- constexpr int oldWidth = 0;
- constexpr int oldHeight = 10;
- constexpr int newWidth = 123;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto nativeWindow = new mock::NativeWindow();
- auto displaySurface = new compositionengine::mock::DisplaySurface();
- sp<GraphicBuffer> buf = new GraphicBuffer();
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.setNativeWindow(nativeWindow);
- display.setDisplaySurface(displaySurface);
- // Setup injection expections
- EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
- EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
- display.inject();
-
- // There is a change to the layerStackSpaceRect state
- display.mutableDrawingDisplayState().width = oldWidth;
- display.mutableDrawingDisplayState().height = oldHeight;
- display.mutableCurrentDisplayState().width = newWidth;
- display.mutableCurrentDisplayState().height = oldHeight;
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-}
-
-TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) {
- using Case = NonHwcVirtualDisplayCase;
-
- constexpr int oldWidth = 0;
- constexpr int oldHeight = 10;
- constexpr int newHeight = 123;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto nativeWindow = new mock::NativeWindow();
- auto displaySurface = new compositionengine::mock::DisplaySurface();
- sp<GraphicBuffer> buf = new GraphicBuffer();
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.setNativeWindow(nativeWindow);
- display.setDisplaySurface(displaySurface);
- // Setup injection expections
- EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
- EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
- EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
- display.inject();
-
- // There is a change to the layerStackSpaceRect state
- display.mutableDrawingDisplayState().width = oldWidth;
- display.mutableDrawingDisplayState().height = oldHeight;
- display.mutableCurrentDisplayState().width = oldWidth;
- display.mutableCurrentDisplayState().height = newHeight;
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::setDisplayStateLocked
- */
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) {
- // --------------------------------------------------------------------
- // Preconditions
-
- // We have an unknown display token not associated with a known display
- sp<BBinder> displayToken = new BBinder();
-
- // The requested display state references the unknown display.
- DisplayState state;
- state.what = DisplayState::eLayerStackChanged;
- state.token = displayToken;
- state.layerStack = 456;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The display token still doesn't match anything known.
- EXPECT_FALSE(hasCurrentDisplayState(displayToken));
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWhenNoChanges) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is already set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // No changes are made to the display
- DisplayState state;
- state.what = 0;
- state.token = display.token();
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSurfaceDidNotChange) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is already set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a surface that can be set.
- sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
-
- // The current display state has the surface set
- display.mutableCurrentDisplayState().surface = surface;
-
- // The incoming request sets the same surface
- DisplayState state;
- state.what = DisplayState::eSurfaceChanged;
- state.token = display.token();
- state.surface = surface;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display state is unchanged.
- EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfSurfaceChanged) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is already set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // There is a surface that can be set.
- sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
-
- // The current display state does not have a surface
- display.mutableCurrentDisplayState().surface = nullptr;
-
- // The incoming request sets a surface
- DisplayState state;
- state.what = DisplayState::eSurfaceChanged;
- state.token = display.token();
- state.surface = surface;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display layer stack state is set to the new value
- EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfLayerStackDidNotChange) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is already set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display has a layer stack set
- display.mutableCurrentDisplayState().layerStack = 456u;
-
- // The incoming request sets the same layer stack
- DisplayState state;
- state.what = DisplayState::eLayerStackChanged;
- state.token = display.token();
- state.layerStack = 456u;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display state is unchanged
- EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display has a layer stack set
- display.mutableCurrentDisplayState().layerStack = 654u;
-
- // The incoming request sets a different layer stack
- DisplayState state;
- state.what = DisplayState::eLayerStackChanged;
- state.token = display.token();
- state.layerStack = 456u;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The desired display state has been set to the new value.
- EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
- using Case = SimplePrimaryDisplayCase;
- constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
- const Rect initialOrientedDisplayRect = {1, 2, 3, 4};
- const Rect initialLayerStackRect = {5, 6, 7, 8};
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state projection state is all set
- display.mutableCurrentDisplayState().orientation = initialOrientation;
- display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
- display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
-
- // The incoming request sets the same projection state
- DisplayState state;
- state.what = DisplayState::eDisplayProjectionChanged;
- state.token = display.token();
- state.orientation = initialOrientation;
- state.orientedDisplaySpaceRect = initialOrientedDisplayRect;
- state.layerStackSpaceRect = initialLayerStackRect;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display state is unchanged
- EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation);
-
- EXPECT_EQ(initialOrientedDisplayRect,
- display.getCurrentDisplayState().orientedDisplaySpaceRect);
- EXPECT_EQ(initialLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
- using Case = SimplePrimaryDisplayCase;
- constexpr ui::Rotation initialOrientation = ui::ROTATION_90;
- constexpr ui::Rotation desiredOrientation = ui::ROTATION_180;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state has an orientation set
- display.mutableCurrentDisplayState().orientation = initialOrientation;
-
- // The incoming request sets a different orientation
- DisplayState state;
- state.what = DisplayState::eDisplayProjectionChanged;
- state.token = display.token();
- state.orientation = desiredOrientation;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredOrientation, display.getCurrentDisplayState().orientation);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) {
- using Case = SimplePrimaryDisplayCase;
- const Rect initialOrientedDisplayRect = {0, 0, 0, 0};
- const Rect desiredOrientedDisplayRect = {5, 6, 7, 8};
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state does not have a orientedDisplaySpaceRect
- display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
-
- // The incoming request sets a orientedDisplaySpaceRect
- DisplayState state;
- state.what = DisplayState::eDisplayProjectionChanged;
- state.token = display.token();
- state.orientedDisplaySpaceRect = desiredOrientedDisplayRect;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredOrientedDisplayRect,
- display.getCurrentDisplayState().orientedDisplaySpaceRect);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfLayerStackRectChanged) {
- using Case = SimplePrimaryDisplayCase;
- const Rect initialLayerStackRect = {0, 0, 0, 0};
- const Rect desiredLayerStackRect = {5, 6, 7, 8};
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state does not have a layerStackSpaceRect
- display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
-
- // The incoming request sets a layerStackSpaceRect
- DisplayState state;
- state.what = DisplayState::eDisplayProjectionChanged;
- state.token = display.token();
- state.layerStackSpaceRect = desiredLayerStackRect;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) {
- using Case = SimplePrimaryDisplayCase;
- constexpr uint32_t initialWidth = 1024;
- constexpr uint32_t initialHeight = 768;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The current display state has a size set
- display.mutableCurrentDisplayState().width = initialWidth;
- display.mutableCurrentDisplayState().height = initialHeight;
-
- // The incoming request sets the same display size
- DisplayState state;
- state.what = DisplayState::eDisplaySizeChanged;
- state.token = display.token();
- state.width = initialWidth;
- state.height = initialHeight;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags are empty
- EXPECT_EQ(0u, flags);
-
- // The current display state is unchanged
- EXPECT_EQ(initialWidth, display.getCurrentDisplayState().width);
- EXPECT_EQ(initialHeight, display.getCurrentDisplayState().height);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfWidthChanged) {
- using Case = SimplePrimaryDisplayCase;
- constexpr uint32_t initialWidth = 0;
- constexpr uint32_t desiredWidth = 1024;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display does not yet have a width
- display.mutableCurrentDisplayState().width = initialWidth;
-
- // The incoming request sets a display width
- DisplayState state;
- state.what = DisplayState::eDisplaySizeChanged;
- state.token = display.token();
- state.width = desiredWidth;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredWidth, display.getCurrentDisplayState().width);
-}
-
-TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfHeightChanged) {
- using Case = SimplePrimaryDisplayCase;
- constexpr uint32_t initialHeight = 0;
- constexpr uint32_t desiredHeight = 768;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A display is set up
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display does not yet have a height
- display.mutableCurrentDisplayState().height = initialHeight;
-
- // The incoming request sets a display height
- DisplayState state;
- state.what = DisplayState::eDisplaySizeChanged;
- state.token = display.token();
- state.height = desiredHeight;
-
- // --------------------------------------------------------------------
- // Invocation
-
- uint32_t flags = mFlinger.setDisplayStateLocked(state);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The returned flags indicate a transaction is needed
- EXPECT_EQ(eDisplayTransactionNeeded, flags);
-
- // The current display state has the new value.
- EXPECT_EQ(desiredHeight, display.getCurrentDisplayState().height);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::onInitializeDisplays
- */
-
-TEST_F(DisplayTransactionTest, onInitializeDisplaysSetsUpPrimaryDisplay) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A primary display is set up
- Case::Display::injectHwcDisplay(this);
- auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this);
- primaryDisplay.inject();
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- // We expect the surface interceptor to possibly be used, but we treat it as
- // disabled since it is called as a side effect rather than directly by this
- // function.
- EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false));
-
- // We expect a call to get the active display config.
- Case::Display::setupHwcGetActiveConfigCallExpectations(this);
-
- // We expect invalidate() to be invoked once to trigger display transaction
- // processing.
- EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-
- EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.onInitializeDisplays();
-
- // --------------------------------------------------------------------
- // Postconditions
-
- // The primary display should have a current state
- ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
- const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
- // The layer stack state should be set to zero
- EXPECT_EQ(0u, primaryDisplayState.layerStack);
- // The orientation state should be set to zero
- EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
-
- // The orientedDisplaySpaceRect state should be set to INVALID
- EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
-
- // The layerStackSpaceRect state should be set to INVALID
- EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect);
-
- // The width and height should both be zero
- EXPECT_EQ(0u, primaryDisplayState.width);
- EXPECT_EQ(0u, primaryDisplayState.height);
-
- // The display should be set to PowerMode::ON
- ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token()));
- auto displayDevice = primaryDisplay.mutableDisplayDevice();
- EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
-
- // The display refresh period should be set in the orientedDisplaySpaceRect tracker.
- FrameStats stats;
- mFlinger.getAnimFrameTracker().getStats(&stats);
- EXPECT_EQ(DEFAULT_REFRESH_RATE, stats.refreshPeriodNano);
-
- // The display transaction needed flag should be set.
- EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
-
- // The compositor timing should be set to default values
- const auto& compositorTiming = mFlinger.getCompositorTiming();
- EXPECT_EQ(-DEFAULT_REFRESH_RATE, compositorTiming.deadline);
- EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.interval);
- EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.presentLatency);
-}
-
-/* ------------------------------------------------------------------------
- * SurfaceFlinger::setPowerModeInternal
- */
-
-// Used when we simulate a display that supports doze.
-template <typename Display>
-struct DozeIsSupportedVariant {
- static constexpr bool DOZE_SUPPORTED = true;
- static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
- IComposerClient::PowerMode::DOZE;
- static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
- IComposerClient::PowerMode::DOZE_SUSPEND;
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(
- std::vector<DisplayCapability>({DisplayCapability::DOZE})),
- Return(Error::NONE)));
- }
-};
-
-template <typename Display>
-// Used when we simulate a display that does not support doze.
-struct DozeNotSupportedVariant {
- static constexpr bool DOZE_SUPPORTED = false;
- static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
- IComposerClient::PowerMode::ON;
- static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
- IComposerClient::PowerMode::ON;
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
- .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
- Return(Error::NONE)));
- }
-};
-
-struct EventThreadBaseSupportedVariant {
- static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) {
- // The callback should not be notified to toggle VSYNC.
- EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(_)).Times(0);
-
- // The event thread should not be notified.
- EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
- EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
- }
-};
-
-struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
- static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- // These calls are only expected for the primary display.
-
- // Instead expect no calls.
- setupVsyncAndEventThreadNoCallExpectations(test);
- }
-
- static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- // These calls are only expected for the primary display.
-
- // Instead expect no calls.
- setupVsyncAndEventThreadNoCallExpectations(test);
- }
-};
-
-struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
- static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- // The callback should be notified to enable VSYNC.
- EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(true)).Times(1);
-
- // The event thread should be notified that the screen was acquired.
- EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
- }
-
- static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- // The callback should be notified to disable VSYNC.
- EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(false)).Times(1);
-
- // The event thread should not be notified that the screen was released.
- EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
- }
-};
-
-struct DispSyncIsSupportedVariant {
- static void setupResetModelCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mVsyncController, startPeriodTransition(DEFAULT_REFRESH_RATE)).Times(1);
- EXPECT_CALL(*test->mVSyncTracker, resetModel()).Times(1);
- }
-};
-
-struct DispSyncNotSupportedVariant {
- static void setupResetModelCallExpectations(DisplayTransactionTest* /* test */) {}
-};
-
-// --------------------------------------------------------------------
-// Note:
-//
-// There are a large number of transitions we could test, however we only test a
-// selected subset which provides complete test coverage of the implementation.
-// --------------------------------------------------------------------
-
-template <PowerMode initialPowerMode, PowerMode targetPowerMode>
-struct TransitionVariantCommon {
- static constexpr auto INITIAL_POWER_MODE = initialPowerMode;
- static constexpr auto TARGET_POWER_MODE = targetPowerMode;
-
- static void verifyPostconditions(DisplayTransactionTest*) {}
-};
-
-struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, PowerMode::ON> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
- Case::DispSync::setupResetModelCallExpectations(test);
- Case::setupRepaintEverythingCallExpectations(test);
- }
-
- static void verifyPostconditions(DisplayTransactionTest* test) {
- EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
- EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
- }
-};
-
-struct TransitionOffToDozeSuspendVariant
- : public TransitionVariantCommon<PowerMode::OFF, PowerMode::DOZE_SUSPEND> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
- Case::setupRepaintEverythingCallExpectations(test);
- }
-
- static void verifyPostconditions(DisplayTransactionTest* test) {
- EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
- EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
- }
-};
-
-struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
- }
-
- static void verifyPostconditions(DisplayTransactionTest* test) {
- EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
- }
-};
-
-struct TransitionDozeSuspendToOffVariant
- : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
- }
-
- static void verifyPostconditions(DisplayTransactionTest* test) {
- EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
- }
-};
-
-struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
- Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
- }
-};
-
-struct TransitionDozeSuspendToDozeVariant
- : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
- Case::DispSync::setupResetModelCallExpectations(test);
- Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
- }
-};
-
-struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
- }
-};
-
-struct TransitionDozeSuspendToOnVariant
- : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
- Case::DispSync::setupResetModelCallExpectations(test);
- Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
- }
-};
-
-struct TransitionOnToDozeSuspendVariant
- : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
- Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
- }
-};
-
-struct TransitionOnToUnknownVariant
- : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
- template <typename Case>
- static void setupCallExpectations(DisplayTransactionTest* test) {
- Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
- Case::setupNoComposerPowerModeCallExpectations(test);
- }
-};
-
-// --------------------------------------------------------------------
-// Note:
-//
-// Rather than testing the cartesian product of of
-// DozeIsSupported/DozeNotSupported with all other options, we use one for one
-// display type, and the other for another display type.
-// --------------------------------------------------------------------
-
-template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant,
- typename DispSyncVariant, typename TransitionVariant>
-struct DisplayPowerCase {
- using Display = DisplayVariant;
- using Doze = DozeVariant;
- using EventThread = EventThreadVariant;
- using DispSync = DispSyncVariant;
- using Transition = TransitionVariant;
-
- static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) {
- Display::injectHwcDisplayWithNoDefaultCapabilities(test);
- auto display = Display::makeFakeExistingDisplayInjector(test);
- display.inject();
- display.mutableDisplayDevice()->setPowerMode(mode);
- return display;
- }
-
- static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
- test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled;
- }
-
- static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
- }
-
- static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
- PowerMode mode) {
- EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
- EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode)))
- .Times(1);
- }
-
- static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
- // Any calls to get the active config will return a default value.
- EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID),
- Return(Error::NONE)));
-
- // Any calls to get whether the display supports dozing will return the value set by the
- // policy variant.
- EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE)));
-
- EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1);
- }
-
- static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0);
- }
-};
-
-// A sample configuration for the primary display.
-// In addition to having event thread support, we emulate doze support.
-template <typename TransitionVariant>
-using PrimaryDisplayPowerCase =
- DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>,
- EventThreadIsSupportedVariant, DispSyncIsSupportedVariant,
- TransitionVariant>;
-
-// A sample configuration for the external display.
-// In addition to not having event thread support, we emulate not having doze
-// support.
-template <typename TransitionVariant>
-using ExternalDisplayPowerCase =
- DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
- EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
- TransitionVariant>;
-
-class SetPowerModeInternalTest : public DisplayTransactionTest {
-public:
- template <typename Case>
- void transitionDisplayCommon();
-};
-
-template <PowerMode PowerMode>
-struct PowerModeInitialVSyncEnabled : public std::false_type {};
-
-template <>
-struct PowerModeInitialVSyncEnabled<PowerMode::ON> : public std::true_type {};
-
-template <>
-struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {};
-
-template <typename Case>
-void SetPowerModeInternalTest::transitionDisplayCommon() {
- // --------------------------------------------------------------------
- // Preconditions
-
- Case::Doze::setupComposerCallExpectations(this);
- auto display =
- Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
- Case::setInitialPrimaryHWVsyncEnabled(this,
- PowerModeInitialVSyncEnabled<
- Case::Transition::INITIAL_POWER_MODE>::value);
-
- // --------------------------------------------------------------------
- // Call Expectations
-
- Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
- Case::Transition::template setupCallExpectations<Case>(this);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
- Case::Transition::TARGET_POWER_MODE);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- Case::Transition::verifyPostconditions(this);
-}
-
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
- using Case = SimplePrimaryDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // A primary display device is set up
- Case::Display::injectHwcDisplay(this);
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display is already set to PowerMode::ON
- display.mutableDisplayDevice()->setPowerMode(PowerMode::ON);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
-}
-
-TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
- using Case = HwcVirtualDisplayCase;
-
- // --------------------------------------------------------------------
- // Preconditions
-
- // Insert display data so that the HWC thinks it created the virtual display.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
- mFlinger.mutableHwcDisplayData().try_emplace(displayId);
-
- // A virtual display device is set up
- Case::Display::injectHwcDisplay(this);
- auto display = Case::Display::makeFakeExistingDisplayInjector(this);
- display.inject();
-
- // The display is set to PowerMode::ON
- getDisplayDevice(display.token())->setPowerMode(PowerMode::ON);
-
- // --------------------------------------------------------------------
- // Invocation
-
- mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF);
-
- // --------------------------------------------------------------------
- // Postconditions
-
- EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
- transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
-}
-
-TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
- transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
-}
-
-} // namespace
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
new file mode 100644
index 0000000..01cdb28
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -0,0 +1,756 @@
+/*
+ * Copyright 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
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <type_traits>
+#include "DisplayIdentificationTest.h"
+
+#include <binder/IPCThreadState.h>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/mock/Display.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <compositionengine/mock/RenderSurface.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/mock/GraphicBufferConsumer.h>
+#include <gui/mock/GraphicBufferProducer.h>
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+#include <renderengine/mock/RenderEngine.h>
+#include <ui/DebugUtils.h>
+
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
+#include "mock/MockNativeWindowSurface.h"
+#include "mock/MockSchedulerCallback.h"
+#include "mock/MockSurfaceInterceptor.h"
+#include "mock/MockVsyncController.h"
+#include "mock/system/window/MockNativeWindow.h"
+
+namespace android {
+
+// TODO: Do not polute the android namespace
+namespace hal = android::hardware::graphics::composer::hal;
+
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::Mock;
+using testing::ResultOf;
+using testing::Return;
+using testing::SetArgPointee;
+
+using hal::ColorMode;
+using hal::Connection;
+using hal::DisplayCapability;
+using hal::DisplayType;
+using hal::Error;
+using hal::Hdr;
+using hal::HWDisplayId;
+using hal::IComposer;
+using hal::IComposerClient;
+using hal::PerFrameMetadataKey;
+using hal::PowerMode;
+
+class DisplayTransactionTest : public testing::Test {
+public:
+ ~DisplayTransactionTest() override;
+
+ // --------------------------------------------------------------------
+ // Mock/Fake injection
+
+ void injectMockScheduler();
+ void injectMockComposer(int virtualDisplayCount);
+ void injectFakeBufferQueueFactory();
+ void injectFakeNativeWindowSurfaceFactory();
+ sp<DisplayDevice> injectDefaultInternalDisplay(
+ std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)>);
+
+ // --------------------------------------------------------------------
+ // Postcondition helpers
+
+ bool hasPhysicalHwcDisplay(hal::HWDisplayId hwcDisplayId);
+ bool hasTransactionFlagSet(int flag);
+ bool hasDisplayDevice(sp<IBinder> displayToken);
+ sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken);
+ bool hasCurrentDisplayState(sp<IBinder> displayToken);
+ const DisplayDeviceState& getCurrentDisplayState(sp<IBinder> displayToken);
+ bool hasDrawingDisplayState(sp<IBinder> displayToken);
+ const DisplayDeviceState& getDrawingDisplayState(sp<IBinder> displayToken);
+
+ // --------------------------------------------------------------------
+ // Test instances
+
+ TestableSurfaceFlinger mFlinger;
+ sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
+ sp<GraphicBuffer> mBuffer = new GraphicBuffer();
+ Hwc2::mock::PowerAdvisor mPowerAdvisor;
+
+ // These mocks are created by the test, but are destroyed by SurfaceFlinger
+ // by virtue of being stored into a std::unique_ptr. However we still need
+ // to keep a reference to them for use in setting up call expectations.
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+ Hwc2::mock::Composer* mComposer = nullptr;
+ mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+ sp<mock::SurfaceInterceptor> mSurfaceInterceptor = new mock::SurfaceInterceptor;
+
+ mock::VsyncController* mVsyncController = new mock::VsyncController;
+ mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
+ mock::SchedulerCallback mSchedulerCallback;
+ mock::EventThread* mEventThread = new mock::EventThread;
+ mock::EventThread* mSFEventThread = new mock::EventThread;
+
+ // These mocks are created only when expected to be created via a factory.
+ sp<mock::GraphicBufferConsumer> mConsumer;
+ sp<mock::GraphicBufferProducer> mProducer;
+ surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr;
+
+protected:
+ DisplayTransactionTest();
+};
+
+constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'667;
+constexpr int32_t DEFAULT_DPI = 320;
+constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565;
+
+constexpr int POWER_MODE_LEET = 1337; // An out of range power mode value
+
+/* ------------------------------------------------------------------------
+ * Boolean avoidance
+ *
+ * To make calls and template instantiations more readable, we define some
+ * local enums along with an implicit bool conversion.
+ */
+
+#define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true };
+
+BOOL_SUBSTITUTE(Async);
+BOOL_SUBSTITUTE(Critical);
+BOOL_SUBSTITUTE(Primary);
+BOOL_SUBSTITUTE(Secure);
+BOOL_SUBSTITUTE(Virtual);
+
+template <typename PhysicalDisplay>
+struct PhysicalDisplayIdType {};
+
+template <uint64_t displayId>
+using HalVirtualDisplayIdType = std::integral_constant<uint64_t, displayId>;
+
+struct GpuVirtualDisplayIdType {};
+
+template <typename>
+struct IsPhysicalDisplayId : std::bool_constant<false> {};
+
+template <typename PhysicalDisplay>
+struct IsPhysicalDisplayId<PhysicalDisplayIdType<PhysicalDisplay>> : std::bool_constant<true> {};
+
+template <typename>
+struct DisplayIdGetter;
+
+template <typename PhysicalDisplay>
+struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+ static PhysicalDisplayId get() {
+ if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
+ return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY)
+ ? LEGACY_DISPLAY_TYPE_PRIMARY
+ : LEGACY_DISPLAY_TYPE_EXTERNAL);
+ }
+
+ const auto info =
+ parseDisplayIdentificationData(PhysicalDisplay::PORT,
+ PhysicalDisplay::GET_IDENTIFICATION_DATA());
+ return info ? info->id : PhysicalDisplayId::fromPort(PhysicalDisplay::PORT);
+ }
+};
+
+template <uint64_t displayId>
+struct DisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
+ static HalVirtualDisplayId get() { return HalVirtualDisplayId(displayId); }
+};
+
+template <>
+struct DisplayIdGetter<GpuVirtualDisplayIdType> {
+ static GpuVirtualDisplayId get() { return GpuVirtualDisplayId(0); }
+};
+
+template <typename>
+struct DisplayConnectionTypeGetter {
+ static constexpr std::optional<DisplayConnectionType> value;
+};
+
+template <typename PhysicalDisplay>
+struct DisplayConnectionTypeGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+ static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
+};
+
+template <typename>
+struct HwcDisplayIdGetter {
+ static constexpr std::optional<HWDisplayId> value;
+};
+
+constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
+
+template <uint64_t displayId>
+struct HwcDisplayIdGetter<HalVirtualDisplayIdType<displayId>> {
+ static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
+};
+
+template <typename PhysicalDisplay>
+struct HwcDisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> {
+ static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID;
+};
+
+// DisplayIdType can be:
+// 1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC.
+// 2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC.
+// 3) GpuVirtualDisplayIdType for virtual display without HWC backing.
+template <typename DisplayIdType, int width, int height, Critical critical, Async async,
+ Secure secure, Primary primary, int grallocUsage>
+struct DisplayVariant {
+ using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
+ using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
+ using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
+
+ // The display width and height
+ static constexpr int WIDTH = width;
+ static constexpr int HEIGHT = height;
+
+ static constexpr int GRALLOC_USAGE = grallocUsage;
+
+ // Whether the display is virtual or physical
+ static constexpr Virtual VIRTUAL =
+ IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE;
+
+ // When creating native window surfaces for the framebuffer, whether those should be critical
+ static constexpr Critical CRITICAL = critical;
+
+ // When creating native window surfaces for the framebuffer, whether those should be async
+ static constexpr Async ASYNC = async;
+
+ // Whether the display should be treated as secure
+ static constexpr Secure SECURE = secure;
+
+ // Whether the display is primary
+ static constexpr Primary PRIMARY = primary;
+
+ static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
+ if (auto displayId = PhysicalDisplayId::tryCast(DISPLAY_ID::get())) {
+ ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal});
+ } else {
+ // We turn off the use of HwcVirtualDisplays, to prevent Composition Engine
+ // from calling into HWComposer. This way all virtual displays will get
+ // a GpuVirtualDisplayId, even if we are in the HwcVirtualDisplayVariant.
+ // In this case we later override it by calling display.setDisplayIdForTesting().
+ ceDisplayArgs.setUseHwcVirtualDisplays(false);
+
+ GpuVirtualDisplayId desiredDisplayId = GpuVirtualDisplayId::tryCast(DISPLAY_ID::get())
+ .value_or(GpuVirtualDisplayId(0));
+
+ ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+ .WillByDefault(Return(desiredDisplayId));
+
+ auto& generator = test->mFlinger.gpuVirtualDisplayIdGenerator();
+ ceDisplayArgs.setGpuVirtualDisplayIdGenerator(generator);
+ }
+ ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor);
+
+ auto compositionDisplay =
+ compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs.build());
+
+ if (HalVirtualDisplayId::tryCast(DISPLAY_ID::get())) {
+ // CompositionEngine has assigned a placeholder GpuVirtualDisplayId and we need to
+ // override it with the correct HalVirtualDisplayId.
+ compositionDisplay->setDisplayIdForTesting(DISPLAY_ID::get());
+ }
+
+ auto injector =
+ TestableSurfaceFlinger::FakeDisplayDeviceInjector(test->mFlinger,
+ compositionDisplay,
+ CONNECTION_TYPE::value,
+ HWC_DISPLAY_ID_OPT::value,
+ static_cast<bool>(PRIMARY));
+
+ injector.setSecure(static_cast<bool>(SECURE));
+ injector.setNativeWindow(test->mNativeWindow);
+
+ // Creating a DisplayDevice requires getting default dimensions from the
+ // native window along with some other initial setup.
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
+ .WillRepeatedly(Return(0));
+
+ return injector;
+ }
+
+ // Called by tests to set up any native window creation call expectations.
+ static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow())
+ .WillOnce(Return(test->mNativeWindow));
+
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0)));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64))
+ .WillRepeatedly(Return(0));
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT))
+ .WillRepeatedly(Return(0));
+ }
+
+ static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*test->mConsumer, setConsumerName(_)).WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(*test->mConsumer, setConsumerUsageBits(GRALLOC_USAGE))
+ .WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(*test->mConsumer, setDefaultBufferSize(WIDTH, HEIGHT))
+ .WillRepeatedly(Return(NO_ERROR));
+ EXPECT_CALL(*test->mConsumer, setMaxAcquiredBufferCount(_))
+ .WillRepeatedly(Return(NO_ERROR));
+ }
+
+ static void setupFramebufferProducerBufferQueueCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mProducer, allocateBuffers(0, 0, 0, 0)).WillRepeatedly(Return());
+ }
+};
+
+template <HWDisplayId hwcDisplayId, DisplayType hwcDisplayType, typename DisplayVariant,
+ typename PhysicalDisplay = void>
+struct HwcDisplayVariant {
+ // The display id supplied by the HWC
+ static constexpr HWDisplayId HWC_DISPLAY_ID = hwcDisplayId;
+
+ // The HWC display type
+ static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
+
+ // The HWC active configuration id
+ static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
+ static constexpr PowerMode INIT_POWER_MODE = hal::PowerMode::ON;
+
+ static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
+ test->mFlinger.mutablePendingHotplugEvents().emplace_back(
+ TestableSurfaceFlinger::HotplugEvent{HWC_DISPLAY_ID, connection});
+ }
+
+ // Called by tests to inject a HWC display setup
+ static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) {
+ const auto displayId = DisplayVariant::DISPLAY_ID::get();
+ ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId));
+ TestableSurfaceFlinger::FakeHwcDisplayInjector(displayId, HWC_DISPLAY_TYPE,
+ static_cast<bool>(DisplayVariant::PRIMARY))
+ .setHwcDisplayId(HWC_DISPLAY_ID)
+ .setWidth(DisplayVariant::WIDTH)
+ .setHeight(DisplayVariant::HEIGHT)
+ .setActiveConfig(HWC_ACTIVE_CONFIG_ID)
+ .setPowerMode(INIT_POWER_MODE)
+ .inject(&test->mFlinger, test->mComposer);
+ }
+
+ // Called by tests to inject a HWC display setup
+ static void injectHwcDisplay(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
+ Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
+ .WillOnce(Return(Error::NONE));
+ injectHwcDisplayWithNoDefaultCapabilities(test);
+ }
+
+ static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+ DisplayTransactionTest* test) {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setPhysical({DisplayVariant::DISPLAY_ID::get(),
+ PhysicalDisplay::CONNECTION_TYPE})
+ .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
+ .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .build();
+
+ return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+ }
+
+ static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
+ constexpr auto CONNECTION_TYPE =
+ PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
+ ? IComposerClient::DisplayConnectionType::INTERNAL
+ : IComposerClient::DisplayConnectionType::EXTERNAL;
+
+ EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE)));
+
+ EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
+ .WillOnce(Return(hal::Error::NONE));
+ EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}),
+ Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::WIDTH, _))
+ .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::HEIGHT, _))
+ .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::VSYNC_PERIOD, _))
+ .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::DPI_X, _))
+ .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::DPI_Y, _))
+ .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::CONFIG_GROUP, _))
+ .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
+
+ if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
+ EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(PhysicalDisplay::PORT),
+ SetArgPointee<2>(PhysicalDisplay::GET_IDENTIFICATION_DATA()),
+ Return(Error::NONE)));
+ } else {
+ EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
+ .WillOnce(Return(Error::UNSUPPORTED));
+ }
+ }
+
+ // Called by tests to set up HWC call expectations
+ static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE)));
+ }
+};
+
+// Physical displays are expected to be synchronous, secure, and have a HWC display for output.
+constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
+ GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
+
+template <typename PhysicalDisplay, int width, int height, Critical critical>
+struct PhysicalDisplayVariant
+ : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical,
+ Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY,
+ GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+ HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
+ DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height,
+ critical, Async::FALSE, Secure::TRUE,
+ PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
+ PhysicalDisplay> {};
+
+template <bool hasIdentificationData>
+struct PrimaryDisplay {
+ static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal;
+ static constexpr Primary PRIMARY = Primary::TRUE;
+ static constexpr uint8_t PORT = 255;
+ static constexpr HWDisplayId HWC_DISPLAY_ID = 1001;
+ static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
+ static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
+};
+
+template <bool hasIdentificationData>
+struct ExternalDisplay {
+ static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External;
+ static constexpr Primary PRIMARY = Primary::FALSE;
+ static constexpr uint8_t PORT = 254;
+ static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
+ static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
+ static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+};
+
+struct TertiaryDisplay {
+ static constexpr Primary PRIMARY = Primary::FALSE;
+ static constexpr uint8_t PORT = 253;
+ static constexpr HWDisplayId HWC_DISPLAY_ID = 1003;
+ static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
+};
+
+// A primary display is a physical display that is critical
+using PrimaryDisplayVariant =
+ PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
+
+// An external display is physical display that is not critical.
+using ExternalDisplayVariant =
+ PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
+
+using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
+
+// A virtual display not supported by the HWC.
+constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
+
+template <int width, int height, Secure secure>
+struct NonHwcVirtualDisplayVariant
+ : DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure,
+ Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> {
+ using Base =
+ DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE,
+ secure, Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>;
+
+ static void injectHwcDisplay(DisplayTransactionTest*) {}
+
+ static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+ DisplayTransactionTest* test) {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+ .WillByDefault(Return(Base::DISPLAY_ID::get()));
+
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setPixels({Base::WIDTH, Base::HEIGHT})
+ .setIsSecure(static_cast<bool>(Base::SECURE))
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .setGpuVirtualDisplayIdGenerator(
+ test->mFlinger.gpuVirtualDisplayIdGenerator())
+ .build();
+
+ return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+ }
+
+ static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
+ }
+
+ static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
+ Base::setupNativeWindowSurfaceCreationCallExpectations(test);
+ EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
+ }
+};
+
+// A virtual display supported by the HWC.
+constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER;
+
+template <int width, int height, Secure secure>
+struct HwcVirtualDisplayVariant
+ : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE,
+ secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
+ HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
+ DisplayVariant<HalVirtualDisplayIdType<42>, width, height,
+ Critical::FALSE, Async::TRUE, secure, Primary::FALSE,
+ GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
+ using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE,
+ Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
+ using Self = HwcVirtualDisplayVariant<width, height, secure>;
+
+ static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+ DisplayTransactionTest* test) {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ // In order to prevent compostition engine calling into HWComposer, we
+ // 1. turn off the use of HWC virtual displays,
+ // 2. provide a GpuVirtualDisplayIdGenerator which always returns some fake ID
+ // 3. override the ID by calling setDisplayIdForTesting()
+
+ ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId())
+ .WillByDefault(Return(GpuVirtualDisplayId(0)));
+
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setUseHwcVirtualDisplays(false)
+ .setPixels({Base::WIDTH, Base::HEIGHT})
+ .setIsSecure(static_cast<bool>(Base::SECURE))
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .setGpuVirtualDisplayIdGenerator(
+ test->mFlinger.gpuVirtualDisplayIdGenerator())
+ .build();
+
+ auto compositionDisplay =
+ compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+ compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
+
+ // Insert display data so that the HWC thinks it created the virtual display.
+ if (const auto displayId = Base::DISPLAY_ID::get();
+ HalVirtualDisplayId::tryCast(displayId)) {
+ test->mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+ }
+
+ return compositionDisplay;
+ }
+
+ static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
+ Base::setupNativeWindowSurfaceCreationCallExpectations(test);
+ EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
+ }
+
+ static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
+ }
+};
+
+// For this variant, the display is not a HWC display, so no HDR support should
+// be configured.
+struct NonHwcDisplayHdrSupportVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = false;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _)).Times(0);
+ }
+};
+
+// For this variant, the composer should respond with am empty list of HDR
+// modes, so no HDR support should be configured.
+template <typename Display>
+struct HdrNotSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = false;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE)));
+ }
+};
+
+struct NonHwcPerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = 0;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0);
+ }
+};
+
+template <typename Display>
+struct NoPerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = 0;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>()));
+ }
+};
+
+// For this variant, SurfaceFlinger should configure itself with wide display
+// support, but the display should respond with an empty list of supported color
+// modes. Wide-color support for the display should not be configured.
+template <typename Display>
+struct WideColorNotSupportedVariant {
+ static constexpr bool WIDE_COLOR_SUPPORTED = false;
+
+ static void injectConfigChange(DisplayTransactionTest* test) {
+ test->mFlinger.mutableUseColorManagement() = true;
+ test->mFlinger.mutableHasWideColorDisplay() = true;
+ }
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
+ }
+};
+
+// For this variant, SurfaceFlinger should not configure itself with wide
+// display support, so the display should not be configured for wide-color
+// support.
+struct WideColorSupportNotConfiguredVariant {
+ static constexpr bool WIDE_COLOR_SUPPORTED = false;
+
+ static void injectConfigChange(DisplayTransactionTest* test) {
+ test->mFlinger.mutableHasWideColorDisplay() = false;
+ test->mFlinger.mutableUseColorManagement() = false;
+ test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
+ }
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getColorModes(_, _)).Times(0);
+ EXPECT_CALL(*test->mComposer, getRenderIntents(_, _, _)).Times(0);
+ EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0);
+ }
+};
+
+/* ------------------------------------------------------------------------
+ * Typical display configurations to test
+ */
+
+template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy,
+ typename PerFrameMetadataSupportPolicy>
+struct Case {
+ using Display = DisplayPolicy;
+ using WideColorSupport = WideColorSupportPolicy;
+ using HdrSupport = HdrSupportPolicy;
+ using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy;
+};
+
+using SimplePrimaryDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using SimpleExternalDisplayCase =
+ Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>,
+ HdrNotSupportedVariant<ExternalDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>;
+using SimpleTertiaryDisplayCase =
+ Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>,
+ HdrNotSupportedVariant<TertiaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>;
+
+using NonHwcVirtualDisplayCase =
+ Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>,
+ WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant,
+ NonHwcPerFrameMetadataSupportVariant>;
+using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>;
+using HwcVirtualDisplayCase =
+ Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant,
+ HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>;
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index f680bdb..3aafd45 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -59,9 +59,11 @@
protected:
class MockEventThreadConnection : public EventThreadConnection {
public:
- MockEventThreadConnection(impl::EventThread* eventThread, ResyncCallback&& resyncCallback,
+ MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid,
+ ResyncCallback&& resyncCallback,
ISurfaceComposer::ConfigChanged configChanged)
- : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {}
+ : EventThreadConnection(eventThread, callingUid, std::move(resyncCallback),
+ configChanged) {}
MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
};
@@ -73,7 +75,8 @@
void createThread(std::unique_ptr<VSyncSource>);
sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
- ISurfaceComposer::ConfigChanged configChanged);
+ ISurfaceComposer::ConfigChanged configChanged,
+ uid_t ownerUid = mConnectionUid);
void expectVSyncSetEnabledCallReceived(bool expectedState);
void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration,
@@ -89,6 +92,7 @@
void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
int32_t expectedConfigId,
nsecs_t expectedVsyncPeriod);
+ void expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t);
AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
@@ -96,12 +100,18 @@
mVSyncSetDurationCallRecorder;
AsyncCallRecorder<void (*)()> mResyncCallRecorder;
AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
+ AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder;
ConnectionEventRecorder mConnectionEventCallRecorder{0};
+ ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0};
MockVSyncSource* mVSyncSource;
VSyncSource::Callback* mCallback = nullptr;
std::unique_ptr<impl::EventThread> mThread;
sp<MockEventThreadConnection> mConnection;
+ sp<MockEventThreadConnection> mThrottledConnection;
+
+ static constexpr uid_t mConnectionUid = 443;
+ static constexpr uid_t mThrottledConnectionUid = 177;
};
EventThreadTest::EventThreadTest() {
@@ -124,6 +134,9 @@
createThread(std::move(vsyncSource));
mConnection = createConnection(mConnectionEventCallRecorder,
ISurfaceComposer::eConfigChangedDispatch);
+ mThrottledConnection =
+ createConnection(mThrottledConnectionEventCallRecorder,
+ ISurfaceComposer::eConfigChangedDispatch, mThrottledConnectionUid);
// A display must be connected for VSYNC events to be delivered.
mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true);
@@ -140,9 +153,15 @@
}
void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
+ const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) {
+ mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid);
+ return (uid == mThrottledConnectionUid);
+ };
+
mThread = std::make_unique<impl::EventThread>(std::move(source),
/*tokenManager=*/nullptr,
- mInterceptVSyncCallRecorder.getInvocable());
+ mInterceptVSyncCallRecorder.getInvocable(),
+ throttleVsync);
// EventThread should register itself as VSyncSource callback.
mCallback = expectVSyncSetCallbackCallReceived();
@@ -150,10 +169,11 @@
}
sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection(
- ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged) {
+ ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged,
+ uid_t ownerUid) {
sp<MockEventThreadConnection> connection =
- new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable(),
- configChanged);
+ new MockEventThreadConnection(mThread.get(), ownerUid,
+ mResyncCallRecorder.getInvocable(), configChanged);
EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable()));
return connection;
}
@@ -183,6 +203,13 @@
EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
}
+void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) {
+ auto args = mThrottleVsyncCallRecorder.waitForCall();
+ ASSERT_TRUE(args.has_value());
+ EXPECT_EQ(expectedTimestamp, std::get<0>(args.value()));
+ EXPECT_EQ(uid, std::get<1>(args.value()));
+}
+
void EventThreadTest::expectVsyncEventReceivedByConnection(
const char* name, ConnectionEventRecorder& connectionEventRecorder,
nsecs_t expectedTimestamp, unsigned expectedCount) {
@@ -267,13 +294,15 @@
// The interceptor should receive the event, as well as the connection.
mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
+ expectThrottleVsyncReceived(456, mConnectionUid);
expectVsyncEventReceivedByConnection(123, 1u);
// Use the received callback to signal a second vsync event.
- // The interceptor should receive the event, but the the connection should
+ // The interceptor should receive the event, but the connection should
// not as it was only interested in the first.
mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
// EventThread should also detect that at this point that it does not need
@@ -323,16 +352,19 @@
// interceptor, and the connection.
mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
+ expectThrottleVsyncReceived(456, mConnectionUid);
expectVsyncEventReceivedByConnection(123, 1u);
// A second event should go to the same places.
mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
+ expectThrottleVsyncReceived(123, mConnectionUid);
expectVsyncEventReceivedByConnection(456, 2u);
// A third event should go to the same places.
mCallback->onVSyncEvent(789, 777, 111);
expectInterceptCallReceived(789);
+ expectThrottleVsyncReceived(777, mConnectionUid);
expectVsyncEventReceivedByConnection(789, 3u);
}
@@ -346,16 +378,19 @@
mCallback->onVSyncEvent(123, 456, 789);
expectInterceptCallReceived(123);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
// The second event will be seen by the interceptor and the connection.
mCallback->onVSyncEvent(456, 123, 0);
expectInterceptCallReceived(456);
expectVsyncEventReceivedByConnection(456, 2u);
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
// The third event will be seen by the interceptor, and not the connection.
mCallback->onVSyncEvent(789, 777, 744);
expectInterceptCallReceived(789);
EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+ EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value());
// The fourth event will be seen by the interceptor and the connection.
mCallback->onVSyncEvent(101112, 7847, 86);
@@ -408,19 +443,19 @@
}
TEST_F(EventThreadTest, tracksEventConnections) {
- EXPECT_EQ(1, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
sp<MockEventThreadConnection> errorConnection =
createConnection(errorConnectionEventRecorder,
ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(1, errorConnection);
- EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
ConnectionEventRecorder secondConnectionEventRecorder{0};
sp<MockEventThreadConnection> secondConnection =
createConnection(secondConnectionEventRecorder,
ISurfaceComposer::eConfigChangedSuppress);
mThread->setVsyncRate(1, secondConnection);
- EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(4, mThread->getEventThreadConnectionCount());
// EventThread should enable vsync callbacks.
expectVSyncSetEnabledCallReceived(true);
@@ -432,7 +467,7 @@
expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
1u);
- EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+ EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
}
TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
@@ -514,5 +549,35 @@
ASSERT_FALSE(args.has_value());
}
+TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) {
+ // Signal that we want the next vsync event to be posted to the throttled connection
+ mThread->requestNextVsync(mThrottledConnection);
+
+ // EventThread should immediately request a resync.
+ EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
+
+ // EventThread should enable vsync callbacks.
+ expectVSyncSetEnabledCallReceived(true);
+
+ // Use the received callback to signal a first vsync event.
+ // The interceptor should receive the event, but not the connection.
+ mCallback->onVSyncEvent(123, 456, 789);
+ expectInterceptCallReceived(123);
+ expectThrottleVsyncReceived(456, mThrottledConnectionUid);
+ mThrottledConnectionEventCallRecorder.waitForUnexpectedCall();
+
+ // Use the received callback to signal a second vsync event.
+ // The interceptor should receive the event, but the connection should
+ // not as it was only interested in the first.
+ mCallback->onVSyncEvent(456, 123, 0);
+ expectInterceptCallReceived(456);
+ expectThrottleVsyncReceived(123, mThrottledConnectionUid);
+ EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
+
+ // EventThread should not change the vsync state as it didn't send the event
+ // yet
+ EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 69efd7f..03c6f70 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "gmock/gmock-spec-builders.h"
+#include "mock/MockTimeStats.h"
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
@@ -23,6 +25,11 @@
#include <cinttypes>
using namespace std::chrono_literals;
+using testing::Contains;
+
+MATCHER_P(HasBit, bit, "") {
+ return (arg & bit) != 0;
+}
namespace android::frametimeline {
@@ -41,7 +48,8 @@
}
void SetUp() override {
- mFrameTimeline = std::make_unique<impl::FrameTimeline>();
+ mTimeStats = std::make_shared<mock::TimeStats>();
+ mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats);
mTokenManager = &mFrameTimeline->mTokenManager;
maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames;
maxTokenRetentionTime = mTokenManager->kMaxRetentionTime;
@@ -76,6 +84,7 @@
return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size());
}
+ std::shared_ptr<mock::TimeStats> mTimeStats;
std::unique_ptr<impl::FrameTimeline> mFrameTimeline;
impl::TokenManager* mTokenManager;
FenceToFenceTimeMap fenceFactory;
@@ -83,6 +92,12 @@
nsecs_t maxTokenRetentionTime;
};
+static const std::string sLayerNameOne = "layer1";
+static const std::string sLayerNameTwo = "layer2";
+static constexpr const uid_t sUidOne = 0;
+static constexpr pid_t sPidOne = 10;
+static constexpr pid_t sPidTwo = 20;
+
TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
EXPECT_EQ(getPredictions().size(), 1);
@@ -98,22 +113,34 @@
EXPECT_EQ(compareTimelineItems(*predictions, TimelineItem(10, 20, 30)), true);
}
+TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, std::nullopt);
+ auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken(sPidTwo, sUidOne, sLayerNameOne,
+ sLayerNameOne, std::nullopt);
+ EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
+ EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
+}
+
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, std::nullopt);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
flushTokens(systemTime() + maxTokenRetentionTime);
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", token1);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, token1);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
}
TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) {
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", token1);
+ auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, token1);
EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -125,7 +152,8 @@
int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken("layer1", token1);
+ auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, token1);
// Set up the display frame
mFrameTimeline->setSfWakeUp(token1, 20);
@@ -148,8 +176,12 @@
int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 60});
- auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken1);
- auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken("layer2", surfaceFrameToken1);
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken1);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameTwo,
+ sLayerNameTwo, surfaceFrameToken1);
mFrameTimeline->setSfWakeUp(sfToken1, 22);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
SurfaceFrame::PresentState::Presented);
@@ -168,7 +200,9 @@
// Trigger a flush by finalizing the next DisplayFrame
auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
- auto surfaceFrame3 = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken2);
+ auto surfaceFrame3 =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken2);
mFrameTimeline->setSfWakeUp(sfToken2, 52);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame3), SurfaceFrame::PresentState::Dropped);
mFrameTimeline->setSfPresent(56, presentFence2);
@@ -189,7 +223,9 @@
{10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
int64_t sfToken = mTokenManager->generateTokenForPredictions(
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
SurfaceFrame::PresentState::Presented);
@@ -209,7 +245,9 @@
{10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor});
int64_t sfToken = mTokenManager->generateTokenForPredictions(
{22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", surfaceFrameToken);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken);
mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), SurfaceFrame::PresentState::Presented);
mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence);
@@ -224,7 +262,8 @@
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken("acquireFenceAfterQueue", std::nullopt);
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue", std::nullopt);
surfaceFrame->setActualQueueTime(123);
surfaceFrame->setAcquireFenceTime(456);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -232,7 +271,8 @@
TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
auto surfaceFrame =
- mFrameTimeline->createSurfaceFrameForToken("acquireFenceAfterQueue", std::nullopt);
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue",
+ "acquireFenceAfterQueue", std::nullopt);
surfaceFrame->setActualQueueTime(456);
surfaceFrame->setAcquireFenceTime(123);
EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -244,7 +284,9 @@
// Size shouldn't exceed maxDisplayFrames - 64
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, std::nullopt);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
@@ -258,7 +300,9 @@
EXPECT_EQ(*maxDisplayFrames, 256);
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, std::nullopt);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
@@ -272,7 +316,9 @@
EXPECT_EQ(*maxDisplayFrames, 128);
for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
- auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken("layer1", std::nullopt);
+ auto surfaceFrame =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, std::nullopt);
int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
mFrameTimeline->setSfWakeUp(sfToken, 22);
mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame),
@@ -281,4 +327,90 @@
}
EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames);
}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) {
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(sUidOne, sLayerNameOne,
+ HasBit(TimeStats::JankType::SurfaceFlingerDeadlineMissed)));
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(HasBit(TimeStats::JankType::SurfaceFlingerDeadlineMissed)));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken1);
+ mFrameTimeline->setSfWakeUp(sfToken1,
+ std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+ SurfaceFrame::PresentState::Presented);
+ presentFence1->signalForTest(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+
+ mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
+ presentFence1);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) {
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(TimeStats::JankType::Display)));
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(TimeStats::JankType::Display)));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken1);
+ mFrameTimeline->setSfWakeUp(sfToken1,
+ std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+ SurfaceFrame::PresentState::Presented);
+ presentFence1->signalForTest(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+ mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(),
+ presentFence1);
+}
+
+TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) {
+ EXPECT_CALL(*mTimeStats,
+ incrementJankyFrames(sUidOne, sLayerNameOne,
+ HasBit(TimeStats::JankType::AppDeadlineMissed)));
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(TimeStats::JankType::AppDeadlineMissed)));
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions(
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions(
+ {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()});
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne,
+ sLayerNameOne, surfaceFrameToken1);
+ surfaceFrame1->setAcquireFenceTime(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count());
+ mFrameTimeline->setSfWakeUp(sfToken1,
+ std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count());
+
+ mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1),
+ SurfaceFrame::PresentState::Presented);
+ presentFence1->signalForTest(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count());
+ mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(),
+ presentFence1);
+}
+
} // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 1f6f166..4762fd4 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -1471,6 +1471,34 @@
EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
}
+TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUnknownUid) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_30);
+ EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(1234));
+}
+
+TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUid) {
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+ /*currentConfigId=*/HWC_CONFIG_ID_30);
+ const uid_t uid = 1234;
+ refreshRateConfigs->setPreferredRefreshRateForUid(uid, 30);
+ EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
+ EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_72);
+ EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120);
+ EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid));
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index 2c8178e..8cd8372 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -131,12 +131,14 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index eee9400..509858a 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -42,7 +42,7 @@
class MockEventThreadConnection : public android::EventThreadConnection {
public:
explicit MockEventThreadConnection(EventThread* eventThread)
- : EventThreadConnection(eventThread, ResyncCallback(),
+ : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress) {}
~MockEventThreadConnection() = default;
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index efee826..e25d501 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -171,12 +171,14 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
auto vsyncController = std::make_unique<mock::VsyncController>();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
new file mode 100644
index 0000000..2362a31
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class CreateDisplayTest : public DisplayTransactionTest {};
+
+TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
+ const String8 name("virtual.test");
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // The call should notify the interceptor that a display was created.
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ sp<IBinder> displayToken = mFlinger.createDisplay(name, false);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display should have been added to the current state
+ ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+ const auto& display = getCurrentDisplayState(displayToken);
+ EXPECT_TRUE(display.isVirtual());
+ EXPECT_FALSE(display.isSecure);
+ EXPECT_EQ(name.string(), display.displayName);
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ // Destroying the display invalidates the display state.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+}
+
+TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) {
+ const String8 name("virtual.test");
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // The call should notify the interceptor that a display was created.
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+ int64_t oldId = IPCThreadState::self()->clearCallingIdentity();
+ // Set the calling identity to graphics so captureDisplay with secure is allowed.
+ IPCThreadState::self()->restoreCallingIdentity(static_cast<int64_t>(AID_GRAPHICS) << 32 |
+ AID_GRAPHICS);
+ sp<IBinder> displayToken = mFlinger.createDisplay(name, true);
+ IPCThreadState::self()->restoreCallingIdentity(oldId);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display should have been added to the current state
+ ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+ const auto& display = getCurrentDisplayState(displayToken);
+ EXPECT_TRUE(display.isVirtual());
+ EXPECT_TRUE(display.isSecure);
+ EXPECT_EQ(name.string(), display.displayName);
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ // Destroying the display invalidates the display state.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
new file mode 100644
index 0000000..0614434
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class DestroyDisplayTest : public DisplayTransactionTest {};
+
+TEST_F(DestroyDisplayTest, destroyDisplayClearsCurrentStateForDisplay) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A virtual display exists
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // The call should notify the interceptor that a display was created.
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
+
+ // Destroying the display invalidates the display state.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.destroyDisplay(existing.token());
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display should have been removed from the current state
+ EXPECT_FALSE(hasCurrentDisplayState(existing.token()));
+
+ // Ths display should still exist in the drawing state
+ EXPECT_TRUE(hasDrawingDisplayState(existing.token()));
+
+ // The display transaction needed flasg should be set
+ EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+}
+
+TEST_F(DestroyDisplayTest, destroyDisplayHandlesUnknownDisplay) {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ sp<BBinder> displayToken = new BBinder();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.destroyDisplay(displayToken);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp
new file mode 100644
index 0000000..0171f1b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+class GetDisplayNativePrimaries : public DisplayTransactionTest {
+public:
+ GetDisplayNativePrimaries();
+ void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries);
+ void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries);
+
+private:
+ static constexpr float mStartingTestValue = 1.0f;
+};
+
+GetDisplayNativePrimaries::GetDisplayNativePrimaries() {
+ SimplePrimaryDisplayCase::Display::injectHwcDisplay(this);
+ injectFakeNativeWindowSurfaceFactory();
+}
+
+void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries(
+ ui::DisplayPrimaries& primaries) {
+ float startingVal = mStartingTestValue;
+ primaries.red.X = startingVal++;
+ primaries.red.Y = startingVal++;
+ primaries.red.Z = startingVal++;
+ primaries.green.X = startingVal++;
+ primaries.green.Y = startingVal++;
+ primaries.green.Z = startingVal++;
+ primaries.blue.X = startingVal++;
+ primaries.blue.Y = startingVal++;
+ primaries.blue.Z = startingVal++;
+ primaries.white.X = startingVal++;
+ primaries.white.Y = startingVal++;
+ primaries.white.Z = startingVal++;
+}
+
+void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries(
+ const ui::DisplayPrimaries& primaries) {
+ float startingVal = mStartingTestValue;
+ EXPECT_EQ(primaries.red.X, startingVal++);
+ EXPECT_EQ(primaries.red.Y, startingVal++);
+ EXPECT_EQ(primaries.red.Z, startingVal++);
+ EXPECT_EQ(primaries.green.X, startingVal++);
+ EXPECT_EQ(primaries.green.Y, startingVal++);
+ EXPECT_EQ(primaries.green.Z, startingVal++);
+ EXPECT_EQ(primaries.blue.X, startingVal++);
+ EXPECT_EQ(primaries.blue.Y, startingVal++);
+ EXPECT_EQ(primaries.blue.Z, startingVal++);
+ EXPECT_EQ(primaries.white.X, startingVal++);
+ EXPECT_EQ(primaries.white.Y, startingVal++);
+ EXPECT_EQ(primaries.white.Z, startingVal++);
+}
+
+TEST_F(GetDisplayNativePrimaries, nullDisplayToken) {
+ ui::DisplayPrimaries primaries;
+ EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries));
+}
+
+TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) {
+ auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this);
+ injector.inject();
+ auto internalDisplayToken = injector.token();
+
+ ui::DisplayPrimaries expectedPrimaries;
+ populateDummyDisplayNativePrimaries(expectedPrimaries);
+ mFlinger.setInternalDisplayPrimaries(expectedPrimaries);
+
+ ui::DisplayPrimaries primaries;
+ EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries));
+
+ checkDummyDisplayNativePrimaries(primaries);
+}
+
+TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) {
+ sp<BBinder> notInternalDisplayToken = new BBinder();
+
+ ui::DisplayPrimaries primaries;
+ populateDummyDisplayNativePrimaries(primaries);
+ EXPECT_EQ(NAME_NOT_FOUND,
+ mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
+
+ // Check primaries argument wasn't modified in case of failure
+ checkDummyDisplayNativePrimaries(primaries);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
new file mode 100644
index 0000000..cd3f6ab
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp
@@ -0,0 +1,734 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class HandleTransactionLockedTest : public DisplayTransactionTest {
+public:
+ template <typename Case>
+ void setupCommonPreconditions();
+
+ template <typename Case, bool connected>
+ static void expectHotplugReceived(mock::EventThread*);
+
+ template <typename Case>
+ void setupCommonCallExpectationsForConnectProcessing();
+
+ template <typename Case>
+ void setupCommonCallExpectationsForDisconnectProcessing();
+
+ template <typename Case>
+ void processesHotplugConnectCommon();
+
+ template <typename Case>
+ void ignoresHotplugConnectCommon();
+
+ template <typename Case>
+ void processesHotplugDisconnectCommon();
+
+ template <typename Case>
+ void verifyDisplayIsConnected(const sp<IBinder>& displayToken);
+
+ template <typename Case>
+ void verifyPhysicalDisplayIsConnected();
+
+ void verifyDisplayIsNotConnected(const sp<IBinder>& displayToken);
+};
+
+template <typename Case>
+void HandleTransactionLockedTest::setupCommonPreconditions() {
+ // Wide color displays support is configured appropriately
+ Case::WideColorSupport::injectConfigChange(this);
+
+ // SurfaceFlinger will use a test-controlled factory for BufferQueues
+ injectFakeBufferQueueFactory();
+
+ // SurfaceFlinger will use a test-controlled factory for native window
+ // surfaces.
+ injectFakeNativeWindowSurfaceFactory();
+}
+
+template <typename Case, bool connected>
+void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) {
+ const auto convert = [](auto physicalDisplayId) {
+ return std::make_optional(DisplayId{physicalDisplayId});
+ };
+
+ EXPECT_CALL(*eventThread,
+ onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected))
+ .Times(1);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() {
+ Case::Display::setupHwcHotplugCallExpectations(this);
+
+ Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
+ Case::Display::setupFramebufferProducerBufferQueueCallExpectations(this);
+ Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+ Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+
+ Case::WideColorSupport::setupComposerCallExpectations(this);
+ Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
+
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
+ expectHotplugReceived<Case, true>(mEventThread);
+ expectHotplugReceived<Case, true>(mSFEventThread);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
+ EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
+
+ expectHotplugReceived<Case, false>(mEventThread);
+ expectHotplugReceived<Case, false>(mSFEventThread);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) {
+ // The display device should have been set up in the list of displays.
+ ASSERT_TRUE(hasDisplayDevice(displayToken));
+ const auto& device = getDisplayDevice(displayToken);
+ EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
+ EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
+
+ std::optional<DisplayDeviceState::Physical> expectedPhysical;
+ if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+ const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(displayId);
+ const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+ ASSERT_TRUE(hwcDisplayId);
+ expectedPhysical = {.id = *displayId,
+ .type = *connectionType,
+ .hwcDisplayId = *hwcDisplayId};
+ }
+
+ // The display should have been set up in the current display state
+ ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+ const auto& current = getCurrentDisplayState(displayToken);
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual());
+ EXPECT_EQ(expectedPhysical, current.physical);
+
+ // The display should have been set up in the drawing display state
+ ASSERT_TRUE(hasDrawingDisplayState(displayToken));
+ const auto& draw = getDrawingDisplayState(displayToken);
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
+ EXPECT_EQ(expectedPhysical, draw.physical);
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() {
+ // HWComposer should have an entry for the display
+ EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+
+ // SF should have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+ auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[displayId];
+
+ verifyDisplayIsConnected<Case>(displayToken);
+}
+
+void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) {
+ EXPECT_FALSE(hasDisplayDevice(displayToken));
+ EXPECT_FALSE(hasCurrentDisplayState(displayToken));
+ EXPECT_FALSE(hasDrawingDisplayState(displayToken));
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::processesHotplugConnectCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // A hotplug connect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ setupCommonCallExpectationsForConnectProcessing<Case>();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ verifyPhysicalDisplayIsConnected<Case>();
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::ignoresHotplugConnectCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // A hotplug connect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // HWComposer should not have an entry for the display
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+}
+
+template <typename Case>
+void HandleTransactionLockedTest::processesHotplugDisconnectCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // A hotplug disconnect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+
+ // The display is already completely set up.
+ Case::Display::injectHwcDisplay(this);
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _))
+ .Times(0);
+
+ setupCommonCallExpectationsForDisconnectProcessing<Case>();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // HWComposer should not have an entry for the display
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+
+ // SF should not have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+
+ // The existing token should have been removed
+ verifyDisplayIsNotConnected(existing.token());
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) {
+ processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest,
+ processesHotplugConnectPrimaryDisplayWithExternalAlreadyConnected) {
+ // Inject an external display.
+ ExternalDisplayVariant::injectHwcDisplay(this);
+
+ processesHotplugConnectCommon<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) {
+ // Inject a primary display.
+ PrimaryDisplayVariant::injectHwcDisplay(this);
+
+ processesHotplugConnectCommon<SimpleExternalDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) {
+ // Inject both a primary and external display.
+ PrimaryDisplayVariant::injectHwcDisplay(this);
+ ExternalDisplayVariant::injectHwcDisplay(this);
+
+ // TODO: This is an unnecessary call.
+ EXPECT_CALL(*mComposer,
+ getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT),
+ SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()),
+ Return(Error::NONE)));
+
+ ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
+ processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
+ processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // A hotplug connect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+ // A hotplug disconnect event is also enqueued for the same display
+ Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ setupCommonCallExpectationsForConnectProcessing<Case>();
+ setupCommonCallExpectationsForDisconnectProcessing<Case>();
+
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // HWComposer should not have an entry for the display
+ EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID));
+
+ // SF should not have a display token.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0);
+}
+
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ setupCommonPreconditions<Case>();
+
+ // The display is already completely set up.
+ Case::Display::injectHwcDisplay(this);
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
+
+ // A hotplug disconnect event is enqueued for a display
+ Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
+ // A hotplug connect event is also enqueued for the same display
+ Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ setupCommonCallExpectationsForConnectProcessing<Case>();
+ setupCommonCallExpectationsForDisconnectProcessing<Case>();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The existing token should have been removed
+ verifyDisplayIsNotConnected(existing.token());
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId));
+ ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1);
+ EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]);
+
+ // A new display should be connected in its place
+
+ verifyPhysicalDisplayIsConnected<Case>();
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ EXPECT_CALL(*mComposer,
+ setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+}
+
+TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // The HWC supports at least one virtual display
+ injectMockComposer(1);
+
+ setupCommonPreconditions<Case>();
+
+ // A virtual display was added to the current state, and it has a
+ // surface(producer)
+ sp<BBinder> displayToken = new BBinder();
+
+ DisplayDeviceState state;
+ state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+ sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()};
+ state.surface = surface;
+ mFlinger.mutableCurrentState().displays.add(displayToken, state);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this);
+ Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+
+ EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR)));
+ EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::HEIGHT), Return(NO_ERROR)));
+ EXPECT_CALL(*surface, query(NATIVE_WINDOW_FORMAT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT),
+ Return(NO_ERROR)));
+ EXPECT_CALL(*surface, query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR)));
+
+ EXPECT_CALL(*surface, setAsyncMode(true)).Times(1);
+
+ EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1);
+ EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1);
+
+ Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this);
+ Case::WideColorSupport::setupComposerCallExpectations(this);
+ Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display device should have been set up in the list of displays.
+ verifyDisplayIsConnected<Case>(displayToken);
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(Error::NONE));
+ EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR));
+
+ // Cleanup
+ mFlinger.mutableCurrentState().displays.removeItem(displayToken);
+ mFlinger.mutableDrawingState().displays.removeItem(displayToken);
+}
+
+TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // The HWC supports at least one virtual display
+ injectMockComposer(1);
+
+ setupCommonPreconditions<Case>();
+
+ // A virtual display was added to the current state, but it does not have a
+ // surface.
+ sp<BBinder> displayToken = new BBinder();
+
+ DisplayDeviceState state;
+ state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+ mFlinger.mutableCurrentState().displays.add(displayToken, state);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // There will not be a display device set up.
+ EXPECT_FALSE(hasDisplayDevice(displayToken));
+
+ // The drawing display state will be set from the current display state.
+ ASSERT_TRUE(hasDrawingDisplayState(displayToken));
+ const auto& draw = getDrawingDisplayState(displayToken);
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
+}
+
+TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A virtual display is set up but is removed from the current state.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
+ mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+ Case::Display::injectHwcDisplay(this);
+ auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
+ existing.inject();
+ mFlinger.mutableCurrentState().displays.removeItem(existing.token());
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The existing token should have been removed
+ verifyDisplayIsNotConnected(existing.token());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr uint32_t oldLayerStack = 0u;
+ constexpr uint32_t newLayerStack = 123u;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a change to the layerStack state
+ display.mutableDrawingDisplayState().layerStack = oldLayerStack;
+ display.mutableCurrentDisplayState().layerStack = newLayerStack;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr ui::Rotation oldTransform = ui::ROTATION_0;
+ constexpr ui::Rotation newTransform = ui::ROTATION_180;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a change to the orientation state
+ display.mutableDrawingDisplayState().orientation = oldTransform;
+ display.mutableCurrentDisplayState().orientation = newTransform;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ const Rect oldLayerStackRect(0, 0, 0, 0);
+ const Rect newLayerStackRect(0, 0, 123, 456);
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().layerStackSpaceRect = oldLayerStackRect;
+ display.mutableCurrentDisplayState().layerStackSpaceRect = newLayerStackRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ const Rect oldFrame(0, 0, 0, 0);
+ const Rect newFrame(0, 0, 123, 456);
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldFrame;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newFrame;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect());
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr int oldWidth = 0;
+ constexpr int oldHeight = 10;
+ constexpr int newWidth = 123;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto nativeWindow = new mock::NativeWindow();
+ auto displaySurface = new compositionengine::mock::DisplaySurface();
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.setNativeWindow(nativeWindow);
+ display.setDisplaySurface(displaySurface);
+ // Setup injection expectations
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().width = oldWidth;
+ display.mutableDrawingDisplayState().height = oldHeight;
+ display.mutableCurrentDisplayState().width = newWidth;
+ display.mutableCurrentDisplayState().height = oldHeight;
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+}
+
+TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) {
+ using Case = NonHwcVirtualDisplayCase;
+
+ constexpr int oldWidth = 0;
+ constexpr int oldHeight = 10;
+ constexpr int newHeight = 123;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto nativeWindow = new mock::NativeWindow();
+ auto displaySurface = new compositionengine::mock::DisplaySurface();
+ sp<GraphicBuffer> buf = new GraphicBuffer();
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.setNativeWindow(nativeWindow);
+ display.setDisplaySurface(displaySurface);
+ // Setup injection expectations
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0)));
+ EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0)));
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
+ EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
+ display.inject();
+
+ // There is a change to the layerStackSpaceRect state
+ display.mutableDrawingDisplayState().width = oldWidth;
+ display.mutableDrawingDisplayState().height = oldHeight;
+ display.mutableCurrentDisplayState().width = oldWidth;
+ display.mutableCurrentDisplayState().height = newHeight;
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.handleTransactionLocked(eDisplayTransactionNeeded);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
new file mode 100644
index 0000000..69e0501
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <android/hardware/power/Boost.h>
+
+namespace android {
+namespace {
+
+using android::hardware::power::Boost;
+
+TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
+ mFlinger.scheduler()->replaceTouchTimer(100);
+ std::this_thread::sleep_for(10ms); // wait for callback to be triggered
+ EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
+
+ std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT)));
+ std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+ EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION)));
+ std::this_thread::sleep_for(10ms); // wait for callback to be triggered.
+ EXPECT_TRUE(mFlinger.scheduler()->isTouchActive());
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp
new file mode 100644
index 0000000..42f4cf3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class OnHotplugReceivedTest : public DisplayTransactionTest {};
+
+TEST_F(OnHotplugReceivedTest, hotplugEnqueuesEventsForDisplayTransaction) {
+ constexpr int currentSequenceId = 123;
+ constexpr HWDisplayId hwcDisplayId1 = 456;
+ constexpr HWDisplayId hwcDisplayId2 = 654;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Set the current sequence id for accepted events
+ mFlinger.mutableComposerSequenceId() = currentSequenceId;
+
+ // Set the main thread id so that the current thread does not appear to be
+ // the main thread.
+ mFlinger.mutableMainThreadId() = std::thread::id();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // We expect invalidate() to be invoked once to trigger display transaction
+ // processing.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // Simulate two hotplug events (a connect and a disconnect)
+ mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, Connection::CONNECTED);
+ mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, Connection::DISCONNECTED);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display transaction needed flag should be set.
+ EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+ // All events should be in the pending event queue.
+ const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
+ ASSERT_EQ(2u, pendingEvents.size());
+ EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
+ EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection);
+ EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
+ EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
+}
+
+TEST_F(OnHotplugReceivedTest, hotplugDiscardsUnexpectedEvents) {
+ constexpr int currentSequenceId = 123;
+ constexpr int otherSequenceId = 321;
+ constexpr HWDisplayId displayId = 456;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Set the current sequence id for accepted events
+ mFlinger.mutableComposerSequenceId() = currentSequenceId;
+
+ // Set the main thread id so that the current thread does not appear to be
+ // the main thread.
+ mFlinger.mutableMainThreadId() = std::thread::id();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // We do not expect any calls to invalidate().
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(0);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // Call with an unexpected sequence id
+ mFlinger.onHotplugReceived(otherSequenceId, displayId, Connection::INVALID);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display transaction needed flag should not be set
+ EXPECT_FALSE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+ // There should be no pending events
+ EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
+}
+
+TEST_F(OnHotplugReceivedTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread) {
+ constexpr int currentSequenceId = 123;
+ constexpr HWDisplayId displayId1 = 456;
+
+ // --------------------------------------------------------------------
+ // Note:
+ // --------------------------------------------------------------------
+ // This test case is a bit tricky. We want to verify that
+ // onHotplugReceived() calls processDisplayHotplugEventsLocked(), but we
+ // don't really want to provide coverage for everything the later function
+ // does as there are specific tests for it.
+ // --------------------------------------------------------------------
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Set the current sequence id for accepted events
+ mFlinger.mutableComposerSequenceId() = currentSequenceId;
+
+ // Set the main thread id so that the current thread does appear to be the
+ // main thread.
+ mFlinger.mutableMainThreadId() = std::this_thread::get_id();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // We expect invalidate() to be invoked once to trigger display transaction
+ // processing.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ // Simulate a disconnect on a display id that is not connected. This should
+ // be enqueued by onHotplugReceived(), and dequeued by
+ // processDisplayHotplugEventsLocked(), but then ignored as invalid.
+ mFlinger.onHotplugReceived(currentSequenceId, displayId1, Connection::DISCONNECTED);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display transaction needed flag should be set.
+ EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+ // There should be no event queued on return, as it should have been
+ // processed.
+ EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
new file mode 100644
index 0000000..7a9403b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+class OnInitializeDisplaysTest : public DisplayTransactionTest {};
+
+TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A primary display is set up
+ Case::Display::injectHwcDisplay(this);
+ auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this);
+ primaryDisplay.inject();
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // We expect the surface interceptor to possibly be used, but we treat it as
+ // disabled since it is called as a side effect rather than directly by this
+ // function.
+ EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false));
+
+ // We expect a call to get the active display config.
+ Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+
+ // We expect invalidate() to be invoked once to trigger display transaction
+ // processing.
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.onInitializeDisplays();
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The primary display should have a current state
+ ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token()));
+ const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token());
+ // The layer stack state should be set to zero
+ EXPECT_EQ(0u, primaryDisplayState.layerStack);
+ // The orientation state should be set to zero
+ EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
+
+ // The orientedDisplaySpaceRect state should be set to INVALID
+ EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect);
+
+ // The layerStackSpaceRect state should be set to INVALID
+ EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect);
+
+ // The width and height should both be zero
+ EXPECT_EQ(0u, primaryDisplayState.width);
+ EXPECT_EQ(0u, primaryDisplayState.height);
+
+ // The display should be set to PowerMode::ON
+ ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token()));
+ auto displayDevice = primaryDisplay.mutableDisplayDevice();
+ EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
+
+ // The display refresh period should be set in the orientedDisplaySpaceRect tracker.
+ FrameStats stats;
+ mFlinger.getAnimFrameTracker().getStats(&stats);
+ EXPECT_EQ(DEFAULT_REFRESH_RATE, stats.refreshPeriodNano);
+
+ // The display transaction needed flag should be set.
+ EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
+
+ // The compositor timing should be set to default values
+ const auto& compositorTiming = mFlinger.getCompositorTiming();
+ EXPECT_EQ(-DEFAULT_REFRESH_RATE, compositorTiming.deadline);
+ EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.interval);
+ EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.presentLatency);
+}
+
+} // namespace
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
new file mode 100644
index 0000000..be01984
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp
@@ -0,0 +1,493 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+class SetDisplayStateLockedTest : public DisplayTransactionTest {};
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // We have an unknown display token not associated with a known display
+ sp<BBinder> displayToken = new BBinder();
+
+ // The requested display state references the unknown display.
+ DisplayState state;
+ state.what = DisplayState::eLayerStackChanged;
+ state.token = displayToken;
+ state.layerStack = 456;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The display token still doesn't match anything known.
+ EXPECT_FALSE(hasCurrentDisplayState(displayToken));
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWhenNoChanges) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is already set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // No changes are made to the display
+ DisplayState state;
+ state.what = 0;
+ state.token = display.token();
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfSurfaceDidNotChange) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is already set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a surface that can be set.
+ sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
+
+ // The current display state has the surface set
+ display.mutableCurrentDisplayState().surface = surface;
+
+ // The incoming request sets the same surface
+ DisplayState state;
+ state.what = DisplayState::eSurfaceChanged;
+ state.token = display.token();
+ state.surface = surface;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The current display state is unchanged.
+ EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfSurfaceChanged) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is already set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // There is a surface that can be set.
+ sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer();
+
+ // The current display state does not have a surface
+ display.mutableCurrentDisplayState().surface = nullptr;
+
+ // The incoming request sets a surface
+ DisplayState state;
+ state.what = DisplayState::eSurfaceChanged;
+ state.token = display.token();
+ state.surface = surface;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display layer stack state is set to the new value
+ EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get());
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfLayerStackDidNotChange) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is already set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display has a layer stack set
+ display.mutableCurrentDisplayState().layerStack = 456u;
+
+ // The incoming request sets the same layer stack
+ DisplayState state;
+ state.what = DisplayState::eLayerStackChanged;
+ state.token = display.token();
+ state.layerStack = 456u;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The current display state is unchanged
+ EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display has a layer stack set
+ display.mutableCurrentDisplayState().layerStack = 654u;
+
+ // The incoming request sets a different layer stack
+ DisplayState state;
+ state.what = DisplayState::eLayerStackChanged;
+ state.token = display.token();
+ state.layerStack = 456u;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The desired display state has been set to the new value.
+ EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
+ const Rect initialOrientedDisplayRect = {1, 2, 3, 4};
+ const Rect initialLayerStackRect = {5, 6, 7, 8};
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state projection state is all set
+ display.mutableCurrentDisplayState().orientation = initialOrientation;
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
+ display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
+
+ // The incoming request sets the same projection state
+ DisplayState state;
+ state.what = DisplayState::eDisplayProjectionChanged;
+ state.token = display.token();
+ state.orientation = initialOrientation;
+ state.orientedDisplaySpaceRect = initialOrientedDisplayRect;
+ state.layerStackSpaceRect = initialLayerStackRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The current display state is unchanged
+ EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation);
+
+ EXPECT_EQ(initialOrientedDisplayRect,
+ display.getCurrentDisplayState().orientedDisplaySpaceRect);
+ EXPECT_EQ(initialLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr ui::Rotation initialOrientation = ui::ROTATION_90;
+ constexpr ui::Rotation desiredOrientation = ui::ROTATION_180;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state has an orientation set
+ display.mutableCurrentDisplayState().orientation = initialOrientation;
+
+ // The incoming request sets a different orientation
+ DisplayState state;
+ state.what = DisplayState::eDisplayProjectionChanged;
+ state.token = display.token();
+ state.orientation = desiredOrientation;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredOrientation, display.getCurrentDisplayState().orientation);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ const Rect initialOrientedDisplayRect = {0, 0, 0, 0};
+ const Rect desiredOrientedDisplayRect = {5, 6, 7, 8};
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state does not have a orientedDisplaySpaceRect
+ display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect;
+
+ // The incoming request sets a orientedDisplaySpaceRect
+ DisplayState state;
+ state.what = DisplayState::eDisplayProjectionChanged;
+ state.token = display.token();
+ state.orientedDisplaySpaceRect = desiredOrientedDisplayRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredOrientedDisplayRect,
+ display.getCurrentDisplayState().orientedDisplaySpaceRect);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackRectChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ const Rect initialLayerStackRect = {0, 0, 0, 0};
+ const Rect desiredLayerStackRect = {5, 6, 7, 8};
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state does not have a layerStackSpaceRect
+ display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect;
+
+ // The incoming request sets a layerStackSpaceRect
+ DisplayState state;
+ state.what = DisplayState::eDisplayProjectionChanged;
+ state.token = display.token();
+ state.layerStackSpaceRect = desiredLayerStackRect;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr uint32_t initialWidth = 1024;
+ constexpr uint32_t initialHeight = 768;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The current display state has a size set
+ display.mutableCurrentDisplayState().width = initialWidth;
+ display.mutableCurrentDisplayState().height = initialHeight;
+
+ // The incoming request sets the same display size
+ DisplayState state;
+ state.what = DisplayState::eDisplaySizeChanged;
+ state.token = display.token();
+ state.width = initialWidth;
+ state.height = initialHeight;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags are empty
+ EXPECT_EQ(0u, flags);
+
+ // The current display state is unchanged
+ EXPECT_EQ(initialWidth, display.getCurrentDisplayState().width);
+ EXPECT_EQ(initialHeight, display.getCurrentDisplayState().height);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfWidthChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr uint32_t initialWidth = 0;
+ constexpr uint32_t desiredWidth = 1024;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display does not yet have a width
+ display.mutableCurrentDisplayState().width = initialWidth;
+
+ // The incoming request sets a display width
+ DisplayState state;
+ state.what = DisplayState::eDisplaySizeChanged;
+ state.token = display.token();
+ state.width = desiredWidth;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredWidth, display.getCurrentDisplayState().width);
+}
+
+TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfHeightChanged) {
+ using Case = SimplePrimaryDisplayCase;
+ constexpr uint32_t initialHeight = 0;
+ constexpr uint32_t desiredHeight = 768;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A display is set up
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display does not yet have a height
+ display.mutableCurrentDisplayState().height = initialHeight;
+
+ // The incoming request sets a display height
+ DisplayState state;
+ state.what = DisplayState::eDisplaySizeChanged;
+ state.token = display.token();
+ state.height = desiredHeight;
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ uint32_t flags = mFlinger.setDisplayStateLocked(state);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The returned flags indicate a transaction is needed
+ EXPECT_EQ(eDisplayTransactionNeeded, flags);
+
+ // The current display state has the new value.
+ EXPECT_EQ(desiredHeight, display.getCurrentDisplayState().height);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
new file mode 100644
index 0000000..2117628
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -0,0 +1,498 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace android {
+namespace {
+
+// Used when we simulate a display that supports doze.
+template <typename Display>
+struct DozeIsSupportedVariant {
+ static constexpr bool DOZE_SUPPORTED = true;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
+ IComposerClient::PowerMode::DOZE;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
+ IComposerClient::PowerMode::DOZE_SUSPEND;
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(
+ std::vector<DisplayCapability>({DisplayCapability::DOZE})),
+ Return(Error::NONE)));
+ }
+};
+
+template <typename Display>
+// Used when we simulate a display that does not support doze.
+struct DozeNotSupportedVariant {
+ static constexpr bool DOZE_SUPPORTED = false;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE =
+ IComposerClient::PowerMode::ON;
+ static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND =
+ IComposerClient::PowerMode::ON;
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
+ Return(Error::NONE)));
+ }
+};
+
+struct EventThreadBaseSupportedVariant {
+ static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) {
+ // The callback should not be notified to toggle VSYNC.
+ EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(_)).Times(0);
+
+ // The event thread should not be notified.
+ EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0);
+ EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0);
+ }
+};
+
+struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
+ static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // These calls are only expected for the primary display.
+
+ // Instead expect no calls.
+ setupVsyncAndEventThreadNoCallExpectations(test);
+ }
+
+ static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // These calls are only expected for the primary display.
+
+ // Instead expect no calls.
+ setupVsyncAndEventThreadNoCallExpectations(test);
+ }
+};
+
+struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant {
+ static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // The callback should be notified to enable VSYNC.
+ EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(true)).Times(1);
+
+ // The event thread should be notified that the screen was acquired.
+ EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1);
+ }
+
+ static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) {
+ // The callback should be notified to disable VSYNC.
+ EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(false)).Times(1);
+
+ // The event thread should not be notified that the screen was released.
+ EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1);
+ }
+};
+
+struct DispSyncIsSupportedVariant {
+ static void setupResetModelCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mVsyncController, startPeriodTransition(DEFAULT_REFRESH_RATE)).Times(1);
+ EXPECT_CALL(*test->mVSyncTracker, resetModel()).Times(1);
+ }
+};
+
+struct DispSyncNotSupportedVariant {
+ static void setupResetModelCallExpectations(DisplayTransactionTest* /* test */) {}
+};
+
+// --------------------------------------------------------------------
+// Note:
+//
+// There are a large number of transitions we could test, however we only test a
+// selected subset which provides complete test coverage of the implementation.
+// --------------------------------------------------------------------
+
+template <PowerMode initialPowerMode, PowerMode targetPowerMode>
+struct TransitionVariantCommon {
+ static constexpr auto INITIAL_POWER_MODE = initialPowerMode;
+ static constexpr auto TARGET_POWER_MODE = targetPowerMode;
+
+ static void verifyPostconditions(DisplayTransactionTest*) {}
+};
+
+struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, PowerMode::ON> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
+ Case::setupRepaintEverythingCallExpectations(test);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
+ }
+};
+
+struct TransitionOffToDozeSuspendVariant
+ : public TransitionVariantCommon<PowerMode::OFF, PowerMode::DOZE_SUSPEND> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupRepaintEverythingCallExpectations(test);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ EXPECT_TRUE(test->mFlinger.getHasPoweredOff());
+ }
+};
+
+struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ }
+};
+
+struct TransitionDozeSuspendToOffVariant
+ : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF);
+ }
+
+ static void verifyPostconditions(DisplayTransactionTest* test) {
+ EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty());
+ }
+};
+
+struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
+ }
+};
+
+struct TransitionDozeSuspendToDozeVariant
+ : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
+ }
+};
+
+struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ }
+};
+
+struct TransitionDozeSuspendToOnVariant
+ : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
+ Case::DispSync::setupResetModelCallExpectations(test);
+ Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
+ }
+};
+
+struct TransitionOnToDozeSuspendVariant
+ : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
+ Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
+ }
+};
+
+struct TransitionOnToUnknownVariant
+ : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
+ template <typename Case>
+ static void setupCallExpectations(DisplayTransactionTest* test) {
+ Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test);
+ Case::setupNoComposerPowerModeCallExpectations(test);
+ }
+};
+
+// --------------------------------------------------------------------
+// Note:
+//
+// Rather than testing the cartesian product of
+// DozeIsSupported/DozeNotSupported with all other options, we use one for one
+// display type, and the other for another display type.
+// --------------------------------------------------------------------
+
+template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant,
+ typename DispSyncVariant, typename TransitionVariant>
+struct DisplayPowerCase {
+ using Display = DisplayVariant;
+ using Doze = DozeVariant;
+ using EventThread = EventThreadVariant;
+ using DispSync = DispSyncVariant;
+ using Transition = TransitionVariant;
+
+ static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) {
+ Display::injectHwcDisplayWithNoDefaultCapabilities(test);
+ auto display = Display::makeFakeExistingDisplayInjector(test);
+ display.inject();
+ display.mutableDisplayDevice()->setPowerMode(mode);
+ return display;
+ }
+
+ static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
+ test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled;
+ }
+
+ static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
+ }
+
+ static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
+ PowerMode mode) {
+ EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
+ EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode)))
+ .Times(1);
+ }
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
+ // Any calls to get the active config will return a default value.
+ EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID),
+ Return(Error::NONE)));
+
+ // Any calls to get whether the display supports dozing will return the value set by the
+ // policy variant.
+ EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE)));
+
+ EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1);
+ }
+
+ static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0);
+ }
+};
+
+// A sample configuration for the primary display.
+// In addition to having event thread support, we emulate doze support.
+template <typename TransitionVariant>
+using PrimaryDisplayPowerCase =
+ DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>,
+ EventThreadIsSupportedVariant, DispSyncIsSupportedVariant,
+ TransitionVariant>;
+
+// A sample configuration for the external display.
+// In addition to not having event thread support, we emulate not having doze
+// support.
+template <typename TransitionVariant>
+using ExternalDisplayPowerCase =
+ DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,
+ EventThreadNotSupportedVariant, DispSyncNotSupportedVariant,
+ TransitionVariant>;
+
+class SetPowerModeInternalTest : public DisplayTransactionTest {
+public:
+ template <typename Case>
+ void transitionDisplayCommon();
+};
+
+template <PowerMode PowerMode>
+struct PowerModeInitialVSyncEnabled : public std::false_type {};
+
+template <>
+struct PowerModeInitialVSyncEnabled<PowerMode::ON> : public std::true_type {};
+
+template <>
+struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {};
+
+template <typename Case>
+void SetPowerModeInternalTest::transitionDisplayCommon() {
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ Case::Doze::setupComposerCallExpectations(this);
+ auto display =
+ Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE);
+ Case::setInitialPrimaryHWVsyncEnabled(this,
+ PowerModeInitialVSyncEnabled<
+ Case::Transition::INITIAL_POWER_MODE>::value);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE);
+ Case::Transition::template setupCallExpectations<Case>(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(),
+ Case::Transition::TARGET_POWER_MODE);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ Case::Transition::verifyPostconditions(this);
+}
+
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) {
+ using Case = SimplePrimaryDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // A primary display device is set up
+ Case::Display::injectHwcDisplay(this);
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display is already set to PowerMode::ON
+ display.mutableDisplayDevice()->setPowerMode(PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
+}
+
+TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
+ using Case = HwcVirtualDisplayCase;
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Insert display data so that the HWC thinks it created the virtual display.
+ const auto displayId = Case::Display::DISPLAY_ID::get();
+ ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId));
+ mFlinger.mutableHwcDisplayData().try_emplace(displayId);
+
+ // A virtual display device is set up
+ Case::Display::injectHwcDisplay(this);
+ auto display = Case::Display::makeFakeExistingDisplayInjector(this);
+ display.inject();
+
+ // The display is set to PowerMode::ON
+ getDisplayDevice(display.token())->setPowerMode(PowerMode::ON);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) {
+ transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>();
+}
+
+TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) {
+ transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
new file mode 100644
index 0000000..61f0788
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+namespace android {
+namespace {
+
+using hal::RenderIntent;
+
+// For this variant, SurfaceFlinger should configure itself with wide display
+// support, and the display should respond with an non-empty list of supported
+// color modes. Wide-color support should be configured.
+template <typename Display>
+struct WideColorP3ColorimetricSupportedVariant {
+ static constexpr bool WIDE_COLOR_SUPPORTED = true;
+
+ static void injectConfigChange(DisplayTransactionTest* test) {
+ test->mFlinger.mutableUseColorManagement() = true;
+ test->mFlinger.mutableHasWideColorDisplay() = true;
+ test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
+ }
+
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1);
+
+ EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})),
+ Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getRenderIntents(Display::HWC_DISPLAY_ID, ColorMode::DISPLAY_P3, _))
+ .WillOnce(DoAll(SetArgPointee<2>(
+ std::vector<RenderIntent>({RenderIntent::COLORIMETRIC})),
+ Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ setColorMode(Display::HWC_DISPLAY_ID, ColorMode::SRGB,
+ RenderIntent::COLORIMETRIC))
+ .WillOnce(Return(Error::NONE));
+ }
+};
+
+template <typename Display>
+struct Hdr10PlusSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = true;
+ static constexpr bool HDR10_SUPPORTED = true;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({
+ Hdr::HDR10_PLUS,
+ Hdr::HDR10,
+ })),
+ Return(Error::NONE)));
+ }
+};
+
+// For this variant, the composer should respond with a non-empty list of HDR
+// modes containing HDR10, so HDR10 support should be configured.
+template <typename Display>
+struct Hdr10SupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = true;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HDR10})),
+ Return(Error::NONE)));
+ }
+};
+
+// For this variant, the composer should respond with a non-empty list of HDR
+// modes containing HLG, so HLG support should be configured.
+template <typename Display>
+struct HdrHlgSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = false;
+ static constexpr bool HDR_HLG_SUPPORTED = true;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+ .WillOnce(
+ DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HLG})), Return(Error::NONE)));
+ }
+};
+
+// For this variant, the composer should respond with a non-empty list of HDR
+// modes containing DOLBY_VISION, so DOLBY_VISION support should be configured.
+template <typename Display>
+struct HdrDolbyVisionSupportedVariant {
+ static constexpr bool HDR10_PLUS_SUPPORTED = false;
+ static constexpr bool HDR10_SUPPORTED = false;
+ static constexpr bool HDR_HLG_SUPPORTED = false;
+ static constexpr bool HDR_DOLBY_VISION_SUPPORTED = true;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::DOLBY_VISION})),
+ Return(Error::NONE)));
+ }
+};
+
+template <typename Display>
+struct Smpte2086PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y,
+ PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y,
+ PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X,
+ PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y,
+ PerFrameMetadataKey::WHITE_POINT_X,
+ PerFrameMetadataKey::WHITE_POINT_Y,
+ PerFrameMetadataKey::MAX_LUMINANCE,
+ PerFrameMetadataKey::MIN_LUMINANCE,
+ })));
+ }
+};
+
+template <typename Display>
+struct Cta861_3_PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL,
+ PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL,
+ })));
+ }
+};
+
+template <typename Display>
+struct Hdr10_Plus_PerFrameMetadataSupportVariant {
+ static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::HDR10PLUS;
+ static void setupComposerCallExpectations(DisplayTransactionTest* test) {
+ EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID))
+ .WillOnce(Return(std::vector<PerFrameMetadataKey>({
+ PerFrameMetadataKey::HDR10_PLUS_SEI,
+ })));
+ }
+};
+
+using WideColorP3ColorimetricDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using Hdr10PlusDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ Hdr10SupportedVariant<PrimaryDisplayVariant>,
+ Hdr10_Plus_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using Hdr10DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ Hdr10SupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrHlgDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrHlgSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrDolbyVisionDisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>,
+ NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrSmpte2086DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+using HdrCta861_3_DisplayCase =
+ Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>,
+ HdrNotSupportedVariant<PrimaryDisplayVariant>,
+ Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>;
+
+class SetupNewDisplayDeviceInternalTest : public DisplayTransactionTest {
+public:
+ template <typename T>
+ void setupNewDisplayDeviceInternalTest();
+};
+
+template <typename Case>
+void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() {
+ const sp<BBinder> displayToken = new BBinder();
+ const sp<compositionengine::mock::DisplaySurface> displaySurface =
+ new compositionengine::mock::DisplaySurface();
+ const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer();
+
+ // --------------------------------------------------------------------
+ // Preconditions
+
+ // Wide color displays support is configured appropriately
+ Case::WideColorSupport::injectConfigChange(this);
+
+ // The display is setup with the HWC.
+ Case::Display::injectHwcDisplay(this);
+
+ // SurfaceFlinger will use a test-controlled factory for native window
+ // surfaces.
+ injectFakeNativeWindowSurfaceFactory();
+
+ // A compositionengine::Display has already been created
+ auto compositionDisplay = Case::Display::injectCompositionDisplay(this);
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // Various native window calls will be made.
+ Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this);
+ Case::Display::setupHwcGetActiveConfigCallExpectations(this);
+ Case::WideColorSupport::setupComposerCallExpectations(this);
+ Case::HdrSupport::setupComposerCallExpectations(this);
+ Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ DisplayDeviceState state;
+ if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+ const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
+ ASSERT_TRUE(displayId);
+ const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+ ASSERT_TRUE(hwcDisplayId);
+ state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId};
+ }
+
+ state.isSecure = static_cast<bool>(Case::Display::SECURE);
+
+ auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+ displaySurface, producer);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ ASSERT_TRUE(device != nullptr);
+ EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
+ EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
+ EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
+ EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
+ EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
+ EXPECT_EQ(Case::Display::WIDTH, device->getWidth());
+ EXPECT_EQ(Case::Display::HEIGHT, device->getHeight());
+ EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut());
+ EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport());
+ EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support());
+ EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport());
+ EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport());
+ // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
+ // remapped, and the test only ever sets up one config. If there were an error
+ // looking up the remapped index, device->getActiveConfig() would be -1 instead.
+ EXPECT_EQ(0, device->getActiveConfig().value());
+ EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
+ device->getSupportedPerFrameMetadata());
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) {
+ setupNewDisplayDeviceInternalTest<SimplePrimaryDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) {
+ setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) {
+ setupNewDisplayDeviceInternalTest<NonHwcVirtualDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
+ setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
+ setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) {
+ setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) {
+ setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrHlgDisplay) {
+ setupNewDisplayDeviceInternalTest<HdrHlgDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrDolbyVisionDisplay) {
+ setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) {
+ setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>();
+}
+
+TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) {
+ setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 0a24650..a90f424 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -58,6 +58,7 @@
#define FMT_STRING false
#define LAYER_ID_0 0
#define LAYER_ID_1 1
+#define UID_0 123
#define LAYER_ID_INVALID -1
#define NUM_LAYERS 1
#define NUM_LAYERS_INVALID "INVALID"
@@ -227,7 +228,8 @@
void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
switch (type) {
case TimeStamp::POST:
- ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), ts));
+ ASSERT_NO_FATAL_FAILURE(
+ mTimeStats->setPostTime(id, frameNumber, genLayerName(id), UID_0, ts));
break;
case TimeStamp::ACQUIRE:
ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts));
@@ -349,6 +351,61 @@
EXPECT_THAT(result, HasSubstr(expectedResult));
}
+TEST_F(TimeStatsTest, canIncreaseJankyFrames) {
+ // this stat is not in the proto so verify by checking the string dump
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::Display);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::AppDeadlineMissed);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::None);
+
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "totalTimelineFrames = " + std::to_string(5);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "jankyFrames = " + std::to_string(4);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appUnattributedJankyFrame = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canIncreaseJankyFramesForLayer) {
+ // this stat is not in the proto so verify by checking the string dump
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::AppDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None);
+
+ const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+ std::string expectedResult = "totalTimelineFrames = " + std::to_string(5);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "jankyFrames = " + std::to_string(4);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+ expectedResult = "appUnattributedJankyFrame = " + std::to_string(1);
+ EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
// this stat is not in the proto so verify by checking the string dump
constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
@@ -789,6 +846,16 @@
.count());
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::AppDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None);
+
EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
@@ -797,6 +864,11 @@
EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0"));
EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
+ EXPECT_THAT(result, HasSubstr("jankyFrames = 0"));
+ EXPECT_THAT(result, HasSubstr("sfLongCpuJankyFrames = 0"));
+ EXPECT_THAT(result, HasSubstr("sfLongGpuJankyFrames = 0"));
+ EXPECT_THAT(result, HasSubstr("sfUnattributedJankyFrame = 0"));
+ EXPECT_THAT(result, HasSubstr("appUnattributedJankyFrame = 0"));
}
TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
@@ -904,6 +976,8 @@
mTimeStats->incrementClientCompositionFrames();
}
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+
mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS);
mTimeStats->setPowerMode(PowerMode::ON);
mTimeStats->recordFrameDuration(1000000, 3000000);
@@ -913,6 +987,12 @@
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::Display);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::AppDeadlineMissed);
+ mTimeStats->incrementJankyFrames(TimeStats::JankType::None);
+
EXPECT_THAT(mDelegate->mAtomTags,
UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
android::util::SURFACEFLINGER_STATS_LAYER_INFO));
@@ -944,6 +1024,12 @@
expectedRenderEngineTiming.c_str(),
expectedRenderEngineTiming.size()),
expectedRenderEngineTiming.size()));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 5));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 4));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
}
EXPECT_EQ(AStatsManager_PULL_SUCCESS,
@@ -975,6 +1061,15 @@
}
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::SurfaceFlingerDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0),
+ TimeStats::JankType::AppDeadlineMissed);
+ mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None);
+
EXPECT_THAT(mDelegate->mAtomTags,
UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
android::util::SURFACEFLINGER_STATS_LAYER_INFO));
@@ -1033,6 +1128,14 @@
EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, LATE_ACQUIRE_FRAMES));
EXPECT_CALL(*mDelegate,
statsEventWriteInt64(mDelegate->mEvent, BAD_DESIRED_PRESENT_FRAMES));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, UID_0));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 5));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 4));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+ EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1));
+
EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
}
EXPECT_EQ(AStatsManager_PULL_SUCCESS,
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 760bf65..68cf330 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -66,13 +66,15 @@
EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*eventThread, createEventConnection(_, _))
.WillOnce(Return(
- new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
.WillOnce(Return(
- new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback(),
ISurfaceComposer::eConfigChangedSuppress)));
EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 1e5139c..0af5f30 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -52,6 +52,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
+ bool isVSyncInPhase(nsecs_t, int) const final { return false; }
void dump(std::string&) const final {}
private:
@@ -88,6 +89,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
bool needsMoreSamples() const final { return false; }
+ bool isVSyncInPhase(nsecs_t, int) const final { return false; }
void dump(std::string&) const final {}
private:
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 69731fd..72b5396 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -48,6 +48,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index d4cd11d..3d60479 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -59,16 +59,16 @@
};
TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
- auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ auto model = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(mPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ EXPECT_THAT(model.slope, Eq(mPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
auto const changedPeriod = 2000;
tracker.setPeriod(changedPeriod);
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(changedPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(changedPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
}
TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) {
@@ -264,17 +264,17 @@
}
auto const mMaxRoundingError = 100;
- auto [slope, intercept] = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, IsCloseTo(fastPeriod, mMaxRoundingError));
- EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+ auto model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, IsCloseTo(fastPeriod, mMaxRoundingError));
+ EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError));
tracker.setPeriod(slowPeriod);
for (auto const& timestamp : simulatedVsyncsSlow) {
tracker.addVsyncTimestamp(timestamp);
}
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, IsCloseTo(slowPeriod, mMaxRoundingError));
- EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, IsCloseTo(slowPeriod, mMaxRoundingError));
+ EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError));
}
TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) {
@@ -296,9 +296,9 @@
for (auto const& timestamp : simulatedVsyncsFast) {
tracker.addVsyncTimestamp(timestamp);
}
- auto [slope, intercept] = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(fastPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ auto model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(fastPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
tracker.setPeriod(slowPeriod);
for (auto const& timestamp : simulatedVsyncsSlow) {
@@ -308,16 +308,16 @@
// we had a model for 100ns mPeriod before, use that until the new samples are
// sufficiently built up
tracker.setPeriod(idealPeriod);
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(fastPeriod));
- EXPECT_THAT(intercept, Eq(0));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(fastPeriod));
+ EXPECT_THAT(model.intercept, Eq(0));
for (auto const& timestamp : simulatedVsyncsFast2) {
tracker.addVsyncTimestamp(timestamp);
}
- std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
- EXPECT_THAT(slope, Eq(fastPeriod2));
- EXPECT_THAT(intercept, Eq(0));
+ model = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(model.slope, Eq(fastPeriod2));
+ EXPECT_THAT(model.intercept, Eq(0));
}
TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) {
@@ -407,11 +407,9 @@
tracker.addVsyncTimestamp(i * realPeriod);
}
- EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
- IsCloseTo(realPeriod, mMaxRoundingError));
+ EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(realPeriod, mMaxRoundingError));
tracker.resetModel();
- EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
- IsCloseTo(idealPeriod, mMaxRoundingError));
+ EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(idealPeriod, mMaxRoundingError));
}
TEST_F(VSyncPredictorTest, slopeAlwaysValid) {
@@ -450,6 +448,33 @@
EXPECT_THAT(intercept, Eq(0));
}
+TEST_F(VSyncPredictorTest, isVSyncInPhase) {
+ auto last = mNow;
+ auto const bias = 10;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+ mNow += mPeriod - bias;
+ last = mNow;
+ tracker.addVsyncTimestamp(mNow);
+ mNow += bias;
+ }
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod - bias));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod - bias));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 990), Eq(mNow + 2 * mPeriod - bias));
+
+ const auto maxDivider = 5;
+ const auto maxPeriods = 15;
+ for (int divider = 1; divider < maxDivider; divider++) {
+ for (int i = 0; i < maxPeriods; i++) {
+ const bool expectedInPhase = (i % divider) == 0;
+ EXPECT_THAT(expectedInPhase, tracker.isVSyncInPhase(mNow + i * mPeriod - bias, divider))
+ << "vsync at " << mNow + (i + 1) * mPeriod - bias << " is "
+ << (expectedInPhase ? "not " : "") << "in phase for divider " << divider;
+ }
+ }
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 0dcaf26..a7568e4 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -42,6 +42,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index ff37ec8..99ec353 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -41,7 +41,7 @@
MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
- MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
+ MOCK_METHOD5(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t));
MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
@@ -50,6 +50,8 @@
MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
MOCK_METHOD3(setPresentTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setPresentFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD1(incrementJankyFrames, void(int32_t));
+ MOCK_METHOD3(incrementJankyFrames, void(uid_t, const std::string&, int32_t));
MOCK_METHOD1(onDestroy, void(int32_t));
MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
MOCK_METHOD1(setPowerMode,
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 03ddc85..de98025 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -33,6 +33,7 @@
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
MOCK_CONST_METHOD0(needsMoreSamples, bool());
+ MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
index 8e1f943..5c5b18e 100644
--- a/services/surfaceflinger/tests/utils/TransactionUtils.h
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -16,6 +16,9 @@
#pragma once
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
#include <chrono>
#include <android/native_window.h>
@@ -181,3 +184,6 @@
};
} // namespace
} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
index 0d4c55e..0acff06 100644
--- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -46,13 +46,26 @@
auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
- bool hasCapabilities(vibrator::HalResult<vibrator::Capabilities> result,
- vibrator::Capabilities query) {
+ bool hasCapabilities(const vibrator::HalResult<vibrator::Capabilities>& result,
+ vibrator::Capabilities&& query, State& state) {
+ if (result.isFailed()) {
+ state.SkipWithError(result.errorMessage());
+ return false;
+ }
if (!result.isOk()) {
return false;
}
return (result.value() & query) == query;
}
+
+ template <class R>
+ bool checkHalResult(const vibrator::HalResult<R>& result, State& state) {
+ if (result.isFailed()) {
+ state.SkipWithError(result.errorMessage());
+ return false;
+ }
+ return true;
+ }
};
#define BENCHMARK_WRAPPER(fixt, test, code) \
@@ -79,7 +92,10 @@
BENCHMARK_WRAPPER(VibratorBench, ping, {
for (auto _ : state) {
- mController.ping();
+ state.ResumeTiming();
+ auto ret = mController.ping();
+ state.PauseTiming();
+ checkHalResult(ret, state);
}
});
@@ -95,9 +111,11 @@
for (auto _ : state) {
state.ResumeTiming();
- mController.on(duration, callback);
+ auto ret = mController.on(duration, callback);
state.PauseTiming();
- mController.off();
+ if (checkHalResult(ret, state)) {
+ checkHalResult(mController.off(), state);
+ }
}
});
@@ -107,16 +125,18 @@
for (auto _ : state) {
state.PauseTiming();
- mController.on(duration, callback);
+ if (!checkHalResult(mController.on(duration, callback), state)) {
+ continue;
+ }
state.ResumeTiming();
- mController.off();
+ checkHalResult(mController.off(), state);
}
});
BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
- auto capabilitiesResult = mController.getCapabilities();
+ auto result = mController.getCapabilities();
- if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::AMPLITUDE_CONTROL)) {
+ if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
return;
}
@@ -128,18 +148,22 @@
state.PauseTiming();
vibrator::HalController controller;
controller.init();
- controller.on(duration, callback);
+ if (!checkHalResult(controller.on(duration, callback), state)) {
+ continue;
+ }
state.ResumeTiming();
- controller.setAmplitude(amplitude);
+ auto ret = controller.setAmplitude(amplitude);
state.PauseTiming();
- controller.off();
+ if (checkHalResult(ret, state)) {
+ checkHalResult(controller.off(), state);
+ }
}
});
BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, {
- auto capabilitiesResult = mController.getCapabilities();
+ auto result = mController.getCapabilities();
- if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::AMPLITUDE_CONTROL)) {
+ if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
return;
}
@@ -147,19 +171,19 @@
auto callback = []() {};
auto amplitude = UINT8_MAX;
- mController.on(duration, callback);
+ checkHalResult(mController.on(duration, callback), state);
for (auto _ : state) {
- mController.setAmplitude(amplitude);
+ checkHalResult(mController.setAmplitude(amplitude), state);
}
- mController.off();
+ checkHalResult(mController.off(), state);
});
BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
- auto capabilitiesResult = mController.getCapabilities();
+ auto result = mController.getCapabilities();
- if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::EXTERNAL_CONTROL)) {
+ if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
return;
}
@@ -168,43 +192,47 @@
vibrator::HalController controller;
controller.init();
state.ResumeTiming();
- controller.setExternalControl(true);
+ auto ret = controller.setExternalControl(true);
state.PauseTiming();
- controller.setExternalControl(false);
+ if (checkHalResult(ret, state)) {
+ checkHalResult(controller.setExternalControl(false), state);
+ }
}
});
BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, {
- auto capabilitiesResult = mController.getCapabilities();
+ auto result = mController.getCapabilities();
- if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::EXTERNAL_CONTROL)) {
+ if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
return;
}
for (auto _ : state) {
state.ResumeTiming();
- mController.setExternalControl(true);
+ auto ret = mController.setExternalControl(true);
state.PauseTiming();
- mController.setExternalControl(false);
+ if (checkHalResult(ret, state)) {
+ checkHalResult(mController.setExternalControl(false), state);
+ }
}
});
BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, {
- auto capabilitiesResult = mController.getCapabilities();
+ auto result = mController.getCapabilities();
- if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL)) {
+ if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) {
return;
}
auto amplitude = UINT8_MAX;
- mController.setExternalControl(true);
+ checkHalResult(mController.setExternalControl(true), state);
for (auto _ : state) {
- mController.setAmplitude(amplitude);
+ checkHalResult(mController.setAmplitude(amplitude), state);
}
- mController.setExternalControl(false);
+ checkHalResult(mController.setExternalControl(false), state);
});
BENCHMARK_WRAPPER(VibratorBench, getCapabilities, {
@@ -213,16 +241,16 @@
vibrator::HalController controller;
controller.init();
state.ResumeTiming();
- controller.getCapabilities();
+ checkHalResult(controller.getCapabilities(), state);
}
});
BENCHMARK_WRAPPER(VibratorBench, getCapabilitiesCached, {
// First call to cache values.
- mController.getCapabilities();
+ checkHalResult(mController.getCapabilities(), state);
for (auto _ : state) {
- mController.getCapabilities();
+ checkHalResult(mController.getCapabilities(), state);
}
});
@@ -232,16 +260,16 @@
vibrator::HalController controller;
controller.init();
state.ResumeTiming();
- controller.getSupportedEffects();
+ checkHalResult(controller.getSupportedEffects(), state);
}
});
BENCHMARK_WRAPPER(VibratorBench, getSupportedEffectsCached, {
// First call to cache values.
- mController.getSupportedEffects();
+ checkHalResult(mController.getSupportedEffects(), state);
for (auto _ : state) {
- mController.getSupportedEffects();
+ checkHalResult(mController.getSupportedEffects(), state);
}
});
@@ -251,16 +279,16 @@
vibrator::HalController controller;
controller.init();
state.ResumeTiming();
- controller.getSupportedPrimitives();
+ checkHalResult(controller.getSupportedPrimitives(), state);
}
});
BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitivesCached, {
// First call to cache values.
- mController.getSupportedPrimitives();
+ checkHalResult(mController.getSupportedPrimitives(), state);
for (auto _ : state) {
- mController.getSupportedPrimitives();
+ checkHalResult(mController.getSupportedPrimitives(), state);
}
});
@@ -275,6 +303,12 @@
std::vector<hardware::vibrator::Effect> supported = effectsResult.value();
b->ArgNames({"Effect", "Strength"});
+
+ if (supported.empty()) {
+ b->Args({static_cast<long>(-1), static_cast<long>(-1)});
+ return;
+ }
+
for (const auto& effect : enum_range<hardware::vibrator::Effect>()) {
if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
continue;
@@ -286,6 +320,8 @@
}
protected:
+ bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
+
auto getEffect(const State& state) const {
return static_cast<hardware::vibrator::Effect>(this->getOtherArg(state, 0));
}
@@ -296,9 +332,12 @@
};
BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, {
- auto capabilitiesResult = mController.getCapabilities();
+ auto result = mController.getCapabilities();
- if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::ALWAYS_ON_CONTROL)) {
+ if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
+ return;
+ }
+ if (!hasArgs(state)) {
return;
}
@@ -308,16 +347,21 @@
for (auto _ : state) {
state.ResumeTiming();
- mController.alwaysOnEnable(id, effect, strength);
+ auto ret = mController.alwaysOnEnable(id, effect, strength);
state.PauseTiming();
- mController.alwaysOnDisable(id);
+ if (checkHalResult(ret, state)) {
+ checkHalResult(mController.alwaysOnDisable(id), state);
+ }
}
});
BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
- auto capabilitiesResult = mController.getCapabilities();
+ auto result = mController.getCapabilities();
- if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::ALWAYS_ON_CONTROL)) {
+ if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
+ return;
+ }
+ if (!hasArgs(state)) {
return;
}
@@ -327,22 +371,30 @@
for (auto _ : state) {
state.PauseTiming();
- mController.alwaysOnEnable(id, effect, strength);
+ if (!checkHalResult(mController.alwaysOnEnable(id, effect, strength), state)) {
+ continue;
+ }
state.ResumeTiming();
- mController.alwaysOnDisable(id);
+ checkHalResult(mController.alwaysOnDisable(id), state);
}
});
BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, {
+ if (!hasArgs(state)) {
+ return;
+ }
+
auto effect = getEffect(state);
auto strength = getStrength(state);
auto callback = []() {};
for (auto _ : state) {
state.ResumeTiming();
- mController.performEffect(effect, strength, callback);
+ auto ret = mController.performEffect(effect, strength, callback);
state.PauseTiming();
- mController.off();
+ if (checkHalResult(ret, state)) {
+ checkHalResult(mController.off(), state);
+ }
}
});
@@ -357,31 +409,45 @@
std::vector<hardware::vibrator::CompositePrimitive> supported = primitivesResult.value();
b->ArgNames({"Primitive"});
+
+ if (supported.empty()) {
+ b->Args({static_cast<long>(-1)});
+ return;
+ }
+
for (const auto& primitive : enum_range<hardware::vibrator::CompositePrimitive>()) {
if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
continue;
}
+ if (primitive == hardware::vibrator::CompositePrimitive::NOOP) {
+ continue;
+ }
b->Args({static_cast<long>(primitive)});
}
}
protected:
+ bool hasArgs(const State& state) const { return this->getOtherArg(state, 0) >= 0; }
+
auto getPrimitive(const State& state) const {
return static_cast<hardware::vibrator::CompositePrimitive>(this->getOtherArg(state, 0));
}
};
BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, {
- auto capabilitiesResult = mController.getCapabilities();
+ auto result = mController.getCapabilities();
- if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::COMPOSE_EFFECTS)) {
+ if (!hasCapabilities(result, vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
+ return;
+ }
+ if (!hasArgs(state)) {
return;
}
hardware::vibrator::CompositeEffect effect;
effect.primitive = getPrimitive(state);
effect.scale = 1.0f;
- effect.delayMs = 0;
+ effect.delayMs = static_cast<int32_t>(0);
std::vector<hardware::vibrator::CompositeEffect> effects;
effects.push_back(effect);
@@ -389,9 +455,11 @@
for (auto _ : state) {
state.ResumeTiming();
- mController.performComposedEffect(effects, callback);
+ auto ret = mController.performComposedEffect(effects, callback);
state.PauseTiming();
- mController.off();
+ if (checkHalResult(ret, state)) {
+ checkHalResult(mController.off(), state);
+ }
}
});
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index f69de1f..1d29bab 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -21,7 +21,7 @@
}
llndk_library {
- name: "libvulkan",
+ name: "libvulkan.llndk",
symbol_file: "libvulkan.map.txt",
export_llndk_headers: [
"vulkan_headers_llndk",
@@ -30,6 +30,7 @@
cc_library_shared {
name: "libvulkan",
+ llndk_stubs: "libvulkan.llndk",
clang: true,
sanitize: {
misc_undefined: ["integer"],
@@ -89,7 +90,6 @@
"libhardware",
"libsync",
"libbase",
- "libdl_android",
"libhidlbase",
"liblog",
"libui",
@@ -100,6 +100,7 @@
"libnativebridge_lazy",
"libnativeloader_lazy",
"libnativewindow",
+ "libvndksupport",
"android.hardware.graphics.common@1.0",
"libSurfaceFlingerProp",
],
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index d9a9427..26052fb 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -226,6 +226,7 @@
INIT_PROC(true, dev, CreateQueryPool);
INIT_PROC(true, dev, DestroyQueryPool);
INIT_PROC(true, dev, GetQueryPoolResults);
+ INIT_PROC(false, dev, ResetQueryPool);
INIT_PROC(true, dev, CreateBuffer);
INIT_PROC(true, dev, DestroyBuffer);
INIT_PROC(true, dev, CreateBufferView);
@@ -337,8 +338,20 @@
INIT_PROC(false, dev, DestroySamplerYcbcrConversion);
INIT_PROC(false, dev, GetDeviceQueue2);
INIT_PROC(false, dev, GetDescriptorSetLayoutSupport);
+ INIT_PROC(false, dev, CreateRenderPass2);
+ INIT_PROC(false, dev, CmdBeginRenderPass2);
+ INIT_PROC(false, dev, CmdNextSubpass2);
+ INIT_PROC(false, dev, CmdEndRenderPass2);
+ INIT_PROC(false, dev, GetSemaphoreCounterValue);
+ INIT_PROC(false, dev, WaitSemaphores);
+ INIT_PROC(false, dev, SignalSemaphore);
INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, true, dev, GetAndroidHardwareBufferPropertiesANDROID);
INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, true, dev, GetMemoryAndroidHardwareBufferANDROID);
+ INIT_PROC(false, dev, CmdDrawIndirectCount);
+ INIT_PROC(false, dev, CmdDrawIndexedIndirectCount);
+ INIT_PROC(false, dev, GetBufferOpaqueCaptureAddress);
+ INIT_PROC(false, dev, GetBufferDeviceAddress);
+ INIT_PROC(false, dev, GetDeviceMemoryOpaqueCaptureAddress);
// clang-format on
return success;
@@ -391,6 +404,7 @@
VKAPI_ATTR VkResult CreateQueryPool(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool);
VKAPI_ATTR void DestroyQueryPool(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult GetQueryPoolResults(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags);
+VKAPI_ATTR void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
VKAPI_ATTR VkResult CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer);
VKAPI_ATTR void DestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult CreateBufferView(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView);
@@ -520,8 +534,20 @@
VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
+VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
+VKAPI_ATTR void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo);
+VKAPI_ATTR void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue);
+VKAPI_ATTR VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout);
+VKAPI_ATTR VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo);
VKAPI_ATTR VkResult GetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties);
VKAPI_ATTR VkResult GetMemoryAndroidHardwareBufferANDROID(VkDevice device, const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo, struct AHardwareBuffer** pBuffer);
+VKAPI_ATTR void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
+VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
+VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo);
VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices) {
return GetData(instance).dispatch.EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
@@ -551,6 +577,7 @@
"vkEnumerateInstanceVersion",
"vkEnumeratePhysicalDeviceGroups",
"vkEnumeratePhysicalDeviceGroupsKHR",
+ "vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR",
"vkEnumeratePhysicalDevices",
"vkGetDisplayModeProperties2KHR",
"vkGetDisplayPlaneCapabilities2KHR",
@@ -571,7 +598,7 @@
"vkGetPhysicalDeviceFormatProperties",
"vkGetPhysicalDeviceFormatProperties2",
"vkGetPhysicalDeviceFormatProperties2KHR",
- "vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX",
+ "vkGetPhysicalDeviceFragmentShadingRatesKHR",
"vkGetPhysicalDeviceImageFormatProperties",
"vkGetPhysicalDeviceImageFormatProperties2",
"vkGetPhysicalDeviceImageFormatProperties2KHR",
@@ -583,6 +610,7 @@
"vkGetPhysicalDeviceProperties",
"vkGetPhysicalDeviceProperties2",
"vkGetPhysicalDeviceProperties2KHR",
+ "vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR",
"vkGetPhysicalDeviceQueueFamilyProperties",
"vkGetPhysicalDeviceQueueFamilyProperties2",
"vkGetPhysicalDeviceQueueFamilyProperties2KHR",
@@ -595,6 +623,7 @@
"vkGetPhysicalDeviceSurfaceFormatsKHR",
"vkGetPhysicalDeviceSurfacePresentModesKHR",
"vkGetPhysicalDeviceSurfaceSupportKHR",
+ "vkGetPhysicalDeviceToolPropertiesEXT",
"vkSubmitDebugUtilsMessageEXT",
};
// clang-format on
@@ -646,6 +675,7 @@
{ "vkBindImageMemory2", reinterpret_cast<PFN_vkVoidFunction>(BindImageMemory2) },
{ "vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginQuery) },
{ "vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass) },
+ { "vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdBeginRenderPass2) },
{ "vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(CmdBindDescriptorSets) },
{ "vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdBindIndexBuffer) },
{ "vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(CmdBindPipeline) },
@@ -665,12 +695,16 @@
{ "vkCmdDraw", reinterpret_cast<PFN_vkVoidFunction>(CmdDraw) },
{ "vkCmdDrawIndexed", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndexed) },
{ "vkCmdDrawIndexedIndirect", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndexedIndirect) },
+ { "vkCmdDrawIndexedIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndexedIndirectCount) },
{ "vkCmdDrawIndirect", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndirect) },
+ { "vkCmdDrawIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(CmdDrawIndirectCount) },
{ "vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(CmdEndQuery) },
{ "vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRenderPass) },
+ { "vkCmdEndRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CmdEndRenderPass2) },
{ "vkCmdExecuteCommands", reinterpret_cast<PFN_vkVoidFunction>(CmdExecuteCommands) },
{ "vkCmdFillBuffer", reinterpret_cast<PFN_vkVoidFunction>(CmdFillBuffer) },
{ "vkCmdNextSubpass", reinterpret_cast<PFN_vkVoidFunction>(CmdNextSubpass) },
+ { "vkCmdNextSubpass2", reinterpret_cast<PFN_vkVoidFunction>(CmdNextSubpass2) },
{ "vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(CmdPipelineBarrier) },
{ "vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(CmdPushConstants) },
{ "vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(CmdResetEvent) },
@@ -709,6 +743,7 @@
{ "vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(CreatePipelineLayout) },
{ "vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(CreateQueryPool) },
{ "vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(CreateRenderPass) },
+ { "vkCreateRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(CreateRenderPass2) },
{ "vkCreateSampler", reinterpret_cast<PFN_vkVoidFunction>(CreateSampler) },
{ "vkCreateSamplerYcbcrConversion", reinterpret_cast<PFN_vkVoidFunction>(CreateSamplerYcbcrConversion) },
{ "vkCreateSemaphore", reinterpret_cast<PFN_vkVoidFunction>(CreateSemaphore) },
@@ -749,13 +784,16 @@
{ "vkFreeDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(FreeDescriptorSets) },
{ "vkFreeMemory", reinterpret_cast<PFN_vkVoidFunction>(FreeMemory) },
{ "vkGetAndroidHardwareBufferPropertiesANDROID", reinterpret_cast<PFN_vkVoidFunction>(GetAndroidHardwareBufferPropertiesANDROID) },
+ { "vkGetBufferDeviceAddress", reinterpret_cast<PFN_vkVoidFunction>(GetBufferDeviceAddress) },
{ "vkGetBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(GetBufferMemoryRequirements) },
{ "vkGetBufferMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(GetBufferMemoryRequirements2) },
+ { "vkGetBufferOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetBufferOpaqueCaptureAddress) },
{ "vkGetDescriptorSetLayoutSupport", reinterpret_cast<PFN_vkVoidFunction>(GetDescriptorSetLayoutSupport) },
{ "vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupPeerMemoryFeatures) },
{ "vkGetDeviceGroupPresentCapabilitiesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupPresentCapabilitiesKHR) },
{ "vkGetDeviceGroupSurfacePresentModesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceGroupSurfacePresentModesKHR) },
{ "vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryCommitment) },
+ { "vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceMemoryOpaqueCaptureAddress) },
{ "vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr) },
{ "vkGetDeviceQueue", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue) },
{ "vkGetDeviceQueue2", reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue2) },
@@ -771,6 +809,7 @@
{ "vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(GetPipelineCacheData) },
{ "vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(GetQueryPoolResults) },
{ "vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(GetRenderAreaGranularity) },
+ { "vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(GetSemaphoreCounterValue) },
{ "vkGetSwapchainImagesKHR", reinterpret_cast<PFN_vkVoidFunction>(GetSwapchainImagesKHR) },
{ "vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(InvalidateMappedMemoryRanges) },
{ "vkMapMemory", reinterpret_cast<PFN_vkVoidFunction>(MapMemory) },
@@ -784,12 +823,15 @@
{ "vkResetDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(ResetDescriptorPool) },
{ "vkResetEvent", reinterpret_cast<PFN_vkVoidFunction>(ResetEvent) },
{ "vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(ResetFences) },
+ { "vkResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(ResetQueryPool) },
{ "vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(SetEvent) },
+ { "vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(SignalSemaphore) },
{ "vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(TrimCommandPool) },
{ "vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(UnmapMemory) },
{ "vkUpdateDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(UpdateDescriptorSetWithTemplate) },
{ "vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(UpdateDescriptorSets) },
{ "vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(WaitForFences) },
+ { "vkWaitSemaphores", reinterpret_cast<PFN_vkVoidFunction>(WaitSemaphores) },
};
// clang-format on
constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]);
@@ -965,6 +1007,10 @@
return GetData(device).dispatch.GetQueryPoolResults(device, queryPool, firstQuery, queryCount, dataSize, pData, stride, flags);
}
+VKAPI_ATTR void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+ GetData(device).dispatch.ResetQueryPool(device, queryPool, firstQuery, queryCount);
+}
+
VKAPI_ATTR VkResult CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) {
return GetData(device).dispatch.CreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
}
@@ -1481,6 +1527,34 @@
GetData(device).dispatch.GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport);
}
+VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) {
+ return GetData(device).dispatch.CreateRenderPass2(device, pCreateInfo, pAllocator, pRenderPass);
+}
+
+VKAPI_ATTR void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo) {
+ GetData(commandBuffer).dispatch.CmdBeginRenderPass2(commandBuffer, pRenderPassBegin, pSubpassBeginInfo);
+}
+
+VKAPI_ATTR void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo) {
+ GetData(commandBuffer).dispatch.CmdNextSubpass2(commandBuffer, pSubpassBeginInfo, pSubpassEndInfo);
+}
+
+VKAPI_ATTR void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo) {
+ GetData(commandBuffer).dispatch.CmdEndRenderPass2(commandBuffer, pSubpassEndInfo);
+}
+
+VKAPI_ATTR VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
+ return GetData(device).dispatch.GetSemaphoreCounterValue(device, semaphore, pValue);
+}
+
+VKAPI_ATTR VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
+ return GetData(device).dispatch.WaitSemaphores(device, pWaitInfo, timeout);
+}
+
+VKAPI_ATTR VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) {
+ return GetData(device).dispatch.SignalSemaphore(device, pSignalInfo);
+}
+
VKAPI_ATTR VkResult GetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties) {
return GetData(device).dispatch.GetAndroidHardwareBufferPropertiesANDROID(device, buffer, pProperties);
}
@@ -1489,6 +1563,26 @@
return GetData(device).dispatch.GetMemoryAndroidHardwareBufferANDROID(device, pInfo, pBuffer);
}
+VKAPI_ATTR void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+ GetData(commandBuffer).dispatch.CmdDrawIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+ GetData(commandBuffer).dispatch.CmdDrawIndexedIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ return GetData(device).dispatch.GetBufferOpaqueCaptureAddress(device, pInfo);
+}
+
+VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ return GetData(device).dispatch.GetBufferDeviceAddress(device, pInfo);
+}
+
+VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
+ return GetData(device).dispatch.GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
+}
+
} // anonymous namespace
@@ -1755,6 +1849,11 @@
}
__attribute__((visibility("default")))
+VKAPI_ATTR void vkResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+ vulkan::api::ResetQueryPool(device, queryPool, firstQuery, queryCount);
+}
+
+__attribute__((visibility("default")))
VKAPI_ATTR VkResult vkCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer) {
return vulkan::api::CreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
}
@@ -2400,6 +2499,41 @@
}
__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass) {
+ return vulkan::api::CreateRenderPass2(device, pCreateInfo, pAllocator, pRenderPass);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo) {
+ vulkan::api::CmdBeginRenderPass2(commandBuffer, pRenderPassBegin, pSubpassBeginInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo) {
+ vulkan::api::CmdNextSubpass2(commandBuffer, pSubpassBeginInfo, pSubpassEndInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo) {
+ vulkan::api::CmdEndRenderPass2(commandBuffer, pSubpassEndInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
+ return vulkan::api::GetSemaphoreCounterValue(device, semaphore, pValue);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkWaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
+ return vulkan::api::WaitSemaphores(device, pWaitInfo, timeout);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkSignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) {
+ return vulkan::api::SignalSemaphore(device, pSignalInfo);
+}
+
+__attribute__((visibility("default")))
VKAPI_ATTR VkResult vkGetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties) {
return vulkan::api::GetAndroidHardwareBufferPropertiesANDROID(device, buffer, pProperties);
}
@@ -2409,4 +2543,29 @@
return vulkan::api::GetMemoryAndroidHardwareBufferANDROID(device, pInfo, pBuffer);
}
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+ vulkan::api::CmdDrawIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+ vulkan::api::CmdDrawIndexedIndirectCount(commandBuffer, buffer, offset, countBuffer, countBufferOffset, maxDrawCount, stride);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR uint64_t vkGetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ return vulkan::api::GetBufferOpaqueCaptureAddress(device, pInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkDeviceAddress vkGetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ return vulkan::api::GetBufferDeviceAddress(device, pInfo);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR uint64_t vkGetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
+ return vulkan::api::GetDeviceMemoryOpaqueCaptureAddress(device, pInfo);
+}
+
// clang-format on
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index 2195845..ad5cc34 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -99,6 +99,7 @@
PFN_vkCreateQueryPool CreateQueryPool;
PFN_vkDestroyQueryPool DestroyQueryPool;
PFN_vkGetQueryPoolResults GetQueryPoolResults;
+ PFN_vkResetQueryPool ResetQueryPool;
PFN_vkCreateBuffer CreateBuffer;
PFN_vkDestroyBuffer DestroyBuffer;
PFN_vkCreateBufferView CreateBufferView;
@@ -210,8 +211,20 @@
PFN_vkDestroySamplerYcbcrConversion DestroySamplerYcbcrConversion;
PFN_vkGetDeviceQueue2 GetDeviceQueue2;
PFN_vkGetDescriptorSetLayoutSupport GetDescriptorSetLayoutSupport;
+ PFN_vkCreateRenderPass2 CreateRenderPass2;
+ PFN_vkCmdBeginRenderPass2 CmdBeginRenderPass2;
+ PFN_vkCmdNextSubpass2 CmdNextSubpass2;
+ PFN_vkCmdEndRenderPass2 CmdEndRenderPass2;
+ PFN_vkGetSemaphoreCounterValue GetSemaphoreCounterValue;
+ PFN_vkWaitSemaphores WaitSemaphores;
+ PFN_vkSignalSemaphore SignalSemaphore;
PFN_vkGetAndroidHardwareBufferPropertiesANDROID GetAndroidHardwareBufferPropertiesANDROID;
PFN_vkGetMemoryAndroidHardwareBufferANDROID GetMemoryAndroidHardwareBufferANDROID;
+ PFN_vkCmdDrawIndirectCount CmdDrawIndirectCount;
+ PFN_vkCmdDrawIndexedIndirectCount CmdDrawIndexedIndirectCount;
+ PFN_vkGetBufferOpaqueCaptureAddress GetBufferOpaqueCaptureAddress;
+ PFN_vkGetBufferDeviceAddress GetBufferDeviceAddress;
+ PFN_vkGetDeviceMemoryOpaqueCaptureAddress GetDeviceMemoryOpaqueCaptureAddress;
// clang-format on
};
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 4068a16..6f09a8c 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -28,20 +28,17 @@
#include <android/dlext.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
-#include <cutils/properties.h>
#include <graphicsenv/GraphicsEnv.h>
#include <log/log.h>
-#include <nativeloader/dlext_namespaces.h>
#include <sys/prctl.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
+#include <vndksupport/linker.h>
#include <algorithm>
#include <array>
#include <climits>
#include <new>
-#include <string_view>
-#include <sstream>
#include <vector>
#include "stubhal.h"
@@ -157,19 +154,11 @@
Hal Hal::hal_;
-void* LoadLibrary(const android_dlextinfo& dlextinfo,
- const std::string_view subname) {
- ATRACE_CALL();
-
- std::stringstream ss;
- ss << "vulkan." << subname << ".so";
- return android_dlopen_ext(ss.str().c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo);
-}
-
const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{
"ro.hardware.vulkan",
"ro.board.platform",
}};
+constexpr int LIB_DL_FLAGS = RTLD_LOCAL | RTLD_NOW;
// LoadDriver returns:
// * 0 when succeed, or
@@ -180,20 +169,26 @@
const hwvulkan_module_t** module) {
ATRACE_CALL();
- const android_dlextinfo dlextinfo = {
- .flags = ANDROID_DLEXT_USE_NAMESPACE,
- .library_namespace = library_namespace,
- };
void* so = nullptr;
- char prop[PROPERTY_VALUE_MAX];
for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
- int prop_len = property_get(key, prop, nullptr);
- if (prop_len > 0 && prop_len <= UINT_MAX) {
- std::string_view lib_name(prop, static_cast<unsigned int>(prop_len));
- so = LoadLibrary(dlextinfo, lib_name);
- if (so)
- break;
+ std::string lib_name = android::base::GetProperty(key, "");
+ if (lib_name.empty())
+ continue;
+
+ lib_name = "vulkan." + lib_name + ".so";
+ if (library_namespace) {
+ // load updated driver
+ const android_dlextinfo dlextinfo = {
+ .flags = ANDROID_DLEXT_USE_NAMESPACE,
+ .library_namespace = library_namespace,
+ };
+ so = android_dlopen_ext(lib_name.c_str(), LIB_DL_FLAGS, &dlextinfo);
+ } else {
+ // load built-in driver
+ so = android_load_sphal_library(lib_name.c_str(), LIB_DL_FLAGS);
}
+ if (so)
+ break;
}
if (!so)
return -ENOENT;
@@ -217,12 +212,9 @@
int LoadBuiltinDriver(const hwvulkan_module_t** module) {
ATRACE_CALL();
- auto ns = android_get_exported_namespace("sphal");
- if (!ns)
- return -ENOENT;
android::GraphicsEnv::getInstance().setDriverToLoad(
android::GpuStatsInfo::Driver::VULKAN);
- return LoadDriver(ns, module);
+ return LoadDriver(nullptr, module);
}
int LoadUpdatedDriver(const hwvulkan_module_t** module) {
@@ -323,7 +315,7 @@
"hw_device_t::close() failed.");
// Close the opened shared library in the hw_module_t
- dlclose(hal_.dev_->common.module->dso);
+ android_unload_sphal_library(hal_.dev_->common.module->dso);
hal_.dev_ = nullptr;
hal_.debug_report_index_ = -1;
@@ -658,6 +650,7 @@
case ProcHook::GOOGLE_display_timing:
case ProcHook::EXTENSION_CORE_1_0:
case ProcHook::EXTENSION_CORE_1_1:
+ case ProcHook::EXTENSION_CORE_1_2:
case ProcHook::EXTENSION_COUNT:
// Device and meta extensions. If we ever get here it's a bug in
// our code. But enumerating them lets us avoid having a default
@@ -711,6 +704,7 @@
case ProcHook::ANDROID_native_buffer:
case ProcHook::EXTENSION_CORE_1_0:
case ProcHook::EXTENSION_CORE_1_1:
+ case ProcHook::EXTENSION_CORE_1_2:
case ProcHook::EXTENSION_COUNT:
// Instance and meta extensions. If we ever get here it's a bug
// in our code. But enumerating them lets us avoid having a
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 1aba674..047e774 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -57,6 +57,7 @@
EXTENSION_CORE_1_0,
EXTENSION_CORE_1_1,
+ EXTENSION_CORE_1_2,
EXTENSION_COUNT,
EXTENSION_UNKNOWN,
};
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index 0be66c9..df97d7f 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -12,6 +12,7 @@
vkBindImageMemory2; # introduced=28
vkCmdBeginQuery;
vkCmdBeginRenderPass;
+ vkCmdBeginRenderPass2; # introduced=31
vkCmdBindDescriptorSets;
vkCmdBindIndexBuffer;
vkCmdBindPipeline;
@@ -31,12 +32,16 @@
vkCmdDraw;
vkCmdDrawIndexed;
vkCmdDrawIndexedIndirect;
+ vkCmdDrawIndexedIndirectCount; # introduced=31
vkCmdDrawIndirect;
+ vkCmdDrawIndirectCount; # introduced=31
vkCmdEndQuery;
vkCmdEndRenderPass;
+ vkCmdEndRenderPass2; # introduced=31
vkCmdExecuteCommands;
vkCmdFillBuffer;
vkCmdNextSubpass;
+ vkCmdNextSubpass2; # introduced=31
vkCmdPipelineBarrier;
vkCmdPushConstants;
vkCmdResetEvent;
@@ -76,6 +81,7 @@
vkCreatePipelineLayout;
vkCreateQueryPool;
vkCreateRenderPass;
+ vkCreateRenderPass2; # introduced=31
vkCreateSampler;
vkCreateSamplerYcbcrConversion; # introduced=28
vkCreateSemaphore;
@@ -119,13 +125,16 @@
vkFreeDescriptorSets;
vkFreeMemory;
vkGetAndroidHardwareBufferPropertiesANDROID; # introduced=28
+ vkGetBufferDeviceAddress; # introduced=31
vkGetBufferMemoryRequirements;
vkGetBufferMemoryRequirements2; # introduced=28
+ vkGetBufferOpaqueCaptureAddress; # introduced=31
vkGetDescriptorSetLayoutSupport; # introduced=28
vkGetDeviceGroupPeerMemoryFeatures; # introduced=28
vkGetDeviceGroupPresentCapabilitiesKHR; # introduced=28
vkGetDeviceGroupSurfacePresentModesKHR; # introduced=28
vkGetDeviceMemoryCommitment;
+ vkGetDeviceMemoryOpaqueCaptureAddress; # introduced=31
vkGetDeviceProcAddr;
vkGetDeviceQueue;
vkGetDeviceQueue2; # introduced=28
@@ -163,6 +172,7 @@
vkGetPipelineCacheData;
vkGetQueryPoolResults;
vkGetRenderAreaGranularity;
+ vkGetSemaphoreCounterValue; # introduced=31
vkGetSwapchainImagesKHR;
vkInvalidateMappedMemoryRanges;
vkMapMemory;
@@ -176,12 +186,15 @@
vkResetDescriptorPool;
vkResetEvent;
vkResetFences;
+ vkResetQueryPool; # introduced=31
vkSetEvent;
+ vkSignalSemaphore; # introduced=31
vkTrimCommandPool; # introduced=28
vkUnmapMemory;
vkUpdateDescriptorSets;
vkUpdateDescriptorSetWithTemplate; # introduced=28
vkWaitForFences;
+ vkWaitSemaphores; # introduced=31
local:
*;
};
diff --git a/vulkan/nulldrv/null_driver.cpp b/vulkan/nulldrv/null_driver.cpp
index 4647a80..b94233b 100644
--- a/vulkan/nulldrv/null_driver.cpp
+++ b/vulkan/nulldrv/null_driver.cpp
@@ -1123,6 +1123,14 @@
return VK_SUCCESS;
}
+VkResult CreateRenderPass2(VkDevice device,
+ const VkRenderPassCreateInfo2*,
+ const VkAllocationCallbacks* /*allocator*/,
+ VkRenderPass* pRenderPass) {
+ *pRenderPass = AllocHandle<VkRenderPass>(device, HandleType::kRenderPass);
+ return VK_SUCCESS;
+}
+
// -----------------------------------------------------------------------------
// No-op entrypoints
@@ -1568,6 +1576,55 @@
void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport) {
}
+void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+}
+
+void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo) {
+}
+
+void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo) {
+}
+
+void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo) {
+}
+
+VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return VK_SUCCESS;
+}
+
+VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return VK_SUCCESS;
+}
+
+VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return VK_SUCCESS;
+}
+
+void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+}
+
+void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride) {
+}
+
+uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return 0;
+}
+
+VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return (VkDeviceAddress)0;
+}
+
+uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo) {
+ ALOGV("TODO: vk%s", __FUNCTION__);
+ return 0;
+}
+
#pragma clang diagnostic pop
// clang-format on
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index b8d7d2b..edda12c 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -67,6 +67,7 @@
{"vkBindImageMemory2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkBindImageMemory2>(BindImageMemory2))},
{"vkCmdBeginQuery", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginQuery>(CmdBeginQuery))},
{"vkCmdBeginRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRenderPass>(CmdBeginRenderPass))},
+ {"vkCmdBeginRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBeginRenderPass2>(CmdBeginRenderPass2))},
{"vkCmdBindDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindDescriptorSets>(CmdBindDescriptorSets))},
{"vkCmdBindIndexBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindIndexBuffer>(CmdBindIndexBuffer))},
{"vkCmdBindPipeline", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdBindPipeline>(CmdBindPipeline))},
@@ -86,12 +87,16 @@
{"vkCmdDraw", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDraw>(CmdDraw))},
{"vkCmdDrawIndexed", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndexed>(CmdDrawIndexed))},
{"vkCmdDrawIndexedIndirect", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndexedIndirect>(CmdDrawIndexedIndirect))},
+ {"vkCmdDrawIndexedIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndexedIndirectCount>(CmdDrawIndexedIndirectCount))},
{"vkCmdDrawIndirect", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndirect>(CmdDrawIndirect))},
+ {"vkCmdDrawIndirectCount", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdDrawIndirectCount>(CmdDrawIndirectCount))},
{"vkCmdEndQuery", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndQuery>(CmdEndQuery))},
{"vkCmdEndRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndRenderPass>(CmdEndRenderPass))},
+ {"vkCmdEndRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdEndRenderPass2>(CmdEndRenderPass2))},
{"vkCmdExecuteCommands", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdExecuteCommands>(CmdExecuteCommands))},
{"vkCmdFillBuffer", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdFillBuffer>(CmdFillBuffer))},
{"vkCmdNextSubpass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdNextSubpass>(CmdNextSubpass))},
+ {"vkCmdNextSubpass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdNextSubpass2>(CmdNextSubpass2))},
{"vkCmdPipelineBarrier", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPipelineBarrier>(CmdPipelineBarrier))},
{"vkCmdPushConstants", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdPushConstants>(CmdPushConstants))},
{"vkCmdResetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCmdResetEvent>(CmdResetEvent))},
@@ -131,6 +136,7 @@
{"vkCreatePipelineLayout", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreatePipelineLayout>(CreatePipelineLayout))},
{"vkCreateQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateQueryPool>(CreateQueryPool))},
{"vkCreateRenderPass", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateRenderPass>(CreateRenderPass))},
+ {"vkCreateRenderPass2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateRenderPass2>(CreateRenderPass2))},
{"vkCreateSampler", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSampler>(CreateSampler))},
{"vkCreateSamplerYcbcrConversion", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSamplerYcbcrConversion>(CreateSamplerYcbcrConversion))},
{"vkCreateSemaphore", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkCreateSemaphore>(CreateSemaphore))},
@@ -172,11 +178,14 @@
{"vkFreeCommandBuffers", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkFreeCommandBuffers>(FreeCommandBuffers))},
{"vkFreeDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkFreeDescriptorSets>(FreeDescriptorSets))},
{"vkFreeMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkFreeMemory>(FreeMemory))},
+ {"vkGetBufferDeviceAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferDeviceAddress>(GetBufferDeviceAddress))},
{"vkGetBufferMemoryRequirements", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferMemoryRequirements>(GetBufferMemoryRequirements))},
{"vkGetBufferMemoryRequirements2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferMemoryRequirements2>(GetBufferMemoryRequirements2))},
+ {"vkGetBufferOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetBufferOpaqueCaptureAddress>(GetBufferOpaqueCaptureAddress))},
{"vkGetDescriptorSetLayoutSupport", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDescriptorSetLayoutSupport>(GetDescriptorSetLayoutSupport))},
{"vkGetDeviceGroupPeerMemoryFeatures", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceGroupPeerMemoryFeatures>(GetDeviceGroupPeerMemoryFeatures))},
{"vkGetDeviceMemoryCommitment", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryCommitment>(GetDeviceMemoryCommitment))},
+ {"vkGetDeviceMemoryOpaqueCaptureAddress", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceMemoryOpaqueCaptureAddress>(GetDeviceMemoryOpaqueCaptureAddress))},
{"vkGetDeviceProcAddr", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceProcAddr>(GetDeviceProcAddr))},
{"vkGetDeviceQueue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceQueue>(GetDeviceQueue))},
{"vkGetDeviceQueue2", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetDeviceQueue2>(GetDeviceQueue2))},
@@ -215,6 +224,7 @@
{"vkGetPipelineCacheData", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetPipelineCacheData>(GetPipelineCacheData))},
{"vkGetQueryPoolResults", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetQueryPoolResults>(GetQueryPoolResults))},
{"vkGetRenderAreaGranularity", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetRenderAreaGranularity>(GetRenderAreaGranularity))},
+ {"vkGetSemaphoreCounterValue", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSemaphoreCounterValue>(GetSemaphoreCounterValue))},
{"vkGetSwapchainGrallocUsage2ANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsage2ANDROID>(GetSwapchainGrallocUsage2ANDROID))},
{"vkGetSwapchainGrallocUsageANDROID", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkGetSwapchainGrallocUsageANDROID>(GetSwapchainGrallocUsageANDROID))},
{"vkInvalidateMappedMemoryRanges", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkInvalidateMappedMemoryRanges>(InvalidateMappedMemoryRanges))},
@@ -229,12 +239,15 @@
{"vkResetDescriptorPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetDescriptorPool>(ResetDescriptorPool))},
{"vkResetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetEvent>(ResetEvent))},
{"vkResetFences", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetFences>(ResetFences))},
+ {"vkResetQueryPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkResetQueryPool>(ResetQueryPool))},
{"vkSetEvent", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSetEvent>(SetEvent))},
+ {"vkSignalSemaphore", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkSignalSemaphore>(SignalSemaphore))},
{"vkTrimCommandPool", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkTrimCommandPool>(TrimCommandPool))},
{"vkUnmapMemory", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUnmapMemory>(UnmapMemory))},
{"vkUpdateDescriptorSetWithTemplate", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUpdateDescriptorSetWithTemplate>(UpdateDescriptorSetWithTemplate))},
{"vkUpdateDescriptorSets", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkUpdateDescriptorSets>(UpdateDescriptorSets))},
{"vkWaitForFences", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkWaitForFences>(WaitForFences))},
+ {"vkWaitSemaphores", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_vkWaitSemaphores>(WaitSemaphores))},
// clang-format on
};
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index 0d3f688..e59cae9 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -79,6 +79,7 @@
VKAPI_ATTR VkResult CreateQueryPool(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool);
VKAPI_ATTR void DestroyQueryPool(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult GetQueryPoolResults(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags);
+VKAPI_ATTR void ResetQueryPool(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount);
VKAPI_ATTR VkResult CreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer);
VKAPI_ATTR void DestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator);
VKAPI_ATTR VkResult CreateBufferView(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView);
@@ -207,6 +208,18 @@
VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
+VKAPI_ATTR VkResult CreateRenderPass2(VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass);
+VKAPI_ATTR void CmdBeginRenderPass2(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfo* pSubpassBeginInfo);
+VKAPI_ATTR void CmdNextSubpass2(VkCommandBuffer commandBuffer, const VkSubpassBeginInfo* pSubpassBeginInfo, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR void CmdEndRenderPass2(VkCommandBuffer commandBuffer, const VkSubpassEndInfo* pSubpassEndInfo);
+VKAPI_ATTR VkResult GetSemaphoreCounterValue(VkDevice device, VkSemaphore semaphore, uint64_t* pValue);
+VKAPI_ATTR VkResult WaitSemaphores(VkDevice device, const VkSemaphoreWaitInfo* pWaitInfo, uint64_t timeout);
+VKAPI_ATTR VkResult SignalSemaphore(VkDevice device, const VkSemaphoreSignalInfo* pSignalInfo);
+VKAPI_ATTR void CmdDrawIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR void CmdDrawIndexedIndirectCount(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride);
+VKAPI_ATTR uint64_t GetBufferOpaqueCaptureAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
+VKAPI_ATTR VkDeviceAddress GetBufferDeviceAddress(VkDevice device, const VkBufferDeviceAddressInfo* pInfo);
+VKAPI_ATTR uint64_t GetDeviceMemoryOpaqueCaptureAddress(VkDevice device, const VkDeviceMemoryOpaqueCaptureAddressInfo* pInfo);
// clang-format on
} // namespace null_driver
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
index ef021f2..72fd4fb 100644
--- a/vulkan/scripts/generator_common.py
+++ b/vulkan/scripts/generator_common.py
@@ -25,6 +25,7 @@
_BLOCKED_EXTENSIONS = [
'VK_EXT_acquire_xlib_display',
'VK_EXT_direct_mode_display',
+ 'VK_EXT_directfb_surface',
'VK_EXT_display_control',
'VK_EXT_display_surface_counter',
'VK_EXT_full_screen_exclusive',
@@ -314,12 +315,12 @@
else:
f.write('INIT_PROC(')
- if name in version_dict and version_dict[name] == 'VK_VERSION_1_1':
+ if name in _OPTIONAL_COMMANDS:
f.write('false, ')
- elif name in _OPTIONAL_COMMANDS:
- f.write('false, ')
- else:
+ elif version_dict[name] == 'VK_VERSION_1_0':
f.write('true, ')
+ else:
+ f.write('false, ')
if is_instance_dispatched(name):
f.write('instance, ')
@@ -389,7 +390,7 @@
for exts in root.iter('extensions'):
for extension in exts:
- apiversion = ''
+ apiversion = 'VK_VERSION_1_0'
if extension.tag == 'extension':
extname = extension.get('name')
if (extension.get('type') == 'instance' and
@@ -404,8 +405,7 @@
cmd_name = commands.get('name')
if cmd_name not in extension_dict:
extension_dict[cmd_name] = extname
- if apiversion:
- version_dict[cmd_name] = apiversion
+ version_dict[cmd_name] = apiversion
for feature in root.iter('feature'):
apiversion = feature.get('name')
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index b0b466c..bfc240e 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -57,9 +57,17 @@
template <typename T> struct EnumTraits;
template <> struct EnumTraits<VkPhysicalDeviceType> {
- static uint32_t min() { return VK_PHYSICAL_DEVICE_TYPE_BEGIN_RANGE; }
- static uint32_t max() { return VK_PHYSICAL_DEVICE_TYPE_END_RANGE; }
- static bool exist(uint32_t e) { return e >= min() && e <= max(); }
+ static bool exist(uint32_t e) {
+ switch (e) {
+ case VK_PHYSICAL_DEVICE_TYPE_OTHER:
+ case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
+ case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
+ case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
+ case VK_PHYSICAL_DEVICE_TYPE_CPU:
+ return true;
+ }
+ return false;
+ }
};
template <> struct EnumTraits<VkFormat> {
@@ -250,6 +258,40 @@
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
+ case VK_FORMAT_G8B8G8R8_422_UNORM:
+ case VK_FORMAT_B8G8R8G8_422_UNORM:
+ case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
+ case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
+ case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
+ case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
+ case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM:
+ case VK_FORMAT_R10X6_UNORM_PACK16:
+ case VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
+ case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
+ case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
+ case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
+ case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
+ case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
+ case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16:
+ case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16:
+ case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
+ case VK_FORMAT_R12X4_UNORM_PACK16:
+ case VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
+ case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
+ case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
+ case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16:
+ case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
+ case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
+ case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16:
+ case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
+ case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
+ case VK_FORMAT_G16B16G16R16_422_UNORM:
+ case VK_FORMAT_B16G16R16G16_422_UNORM:
+ case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
+ case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
+ case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
+ case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
+ case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM:
case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
@@ -258,40 +300,22 @@
case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
- case VK_FORMAT_G8B8G8R8_422_UNORM_KHR:
- case VK_FORMAT_B8G8R8G8_422_UNORM_KHR:
- case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR:
- case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR:
- case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR:
- case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR:
- case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR:
- case VK_FORMAT_R10X6_UNORM_PACK16_KHR:
- case VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR:
- case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR:
- case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR:
- case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR:
- case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR:
- case VK_FORMAT_R12X4_UNORM_PACK16_KHR:
- case VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR:
- case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR:
- case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR:
- case VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR:
- case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR:
- case VK_FORMAT_G16B16G16R16_422_UNORM_KHR:
- case VK_FORMAT_B16G16R16G16_422_UNORM_KHR:
- case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR:
- case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR:
- case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR:
- case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR:
- case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR:
+ case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT:
+ case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT:
+ case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT:
return true;
}
return false;
@@ -300,9 +324,14 @@
template <>
struct EnumTraits<VkPointClippingBehavior> {
- static uint32_t min() { return VK_POINT_CLIPPING_BEHAVIOR_BEGIN_RANGE; }
- static uint32_t max() { return VK_POINT_CLIPPING_BEHAVIOR_END_RANGE; }
- static bool exist(uint32_t e) { return e >= min() && e <= max(); }
+ static bool exist(uint32_t e) {
+ switch (e) {
+ case VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES:
+ case VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY:
+ return true;
+ }
+ return false;
+ }
};
template <>
@@ -336,9 +365,26 @@
template <>
struct EnumTraits<VkDriverIdKHR> {
- static uint32_t min() { return VK_DRIVER_ID_BEGIN_RANGE_KHR; }
- static uint32_t max() { return VK_DRIVER_ID_END_RANGE_KHR; }
- static bool exist(uint32_t e) { return e >= min() && e <= max(); }
+ static bool exist(uint32_t e) {
+ switch (e) {
+ case VK_DRIVER_ID_AMD_PROPRIETARY:
+ case VK_DRIVER_ID_AMD_OPEN_SOURCE:
+ case VK_DRIVER_ID_MESA_RADV:
+ case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
+ case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
+ case VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA:
+ case VK_DRIVER_ID_IMAGINATION_PROPRIETARY:
+ case VK_DRIVER_ID_QUALCOMM_PROPRIETARY:
+ case VK_DRIVER_ID_ARM_PROPRIETARY:
+ case VK_DRIVER_ID_GOOGLE_SWIFTSHADER:
+ case VK_DRIVER_ID_GGP_PROPRIETARY:
+ case VK_DRIVER_ID_BROADCOM_PROPRIETARY:
+ case VK_DRIVER_ID_MESA_LLVMPIPE:
+ case VK_DRIVER_ID_MOLTENVK:
+ return true;
+ }
+ return false;
+ }
};
// VkSparseImageFormatProperties
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index ace713b..eb0fcc3 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -129,7 +129,9 @@
VkFormatProperties format_properties = {};
for (VkFormat format = VK_FORMAT_R4G4_UNORM_PACK8;
- format <= VK_FORMAT_END_RANGE;
+ // TODO(http://b/171403054): avoid hard-coding last value in the
+ // contiguous range
+ format <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK;
format = static_cast<VkFormat>(format + 1)) {
vkGetPhysicalDeviceFormatProperties(physical_device, format,
&format_properties);
@@ -142,6 +144,8 @@
if (device.properties.apiVersion >= VK_API_VERSION_1_1) {
for (VkFormat format = VK_FORMAT_G8B8G8R8_422_UNORM;
+ // TODO(http://b/171403054): avoid hard-coding last value in the
+ // contiguous range
format <= VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM;
format = static_cast<VkFormat>(format + 1)) {
vkGetPhysicalDeviceFormatProperties(physical_device, format,