[automerger skipped] RESTRICT AUTOMERGE: Fix removal of handle from map am: 6a8f8af9d0 -s ours am skip reason: subject contains skip directive Change-Id: I54c7cc16837e55dcfde9659cd4e8be466324d9e7
diff --git a/CleanSpec.mk b/CleanSpec.mk new file mode 100644 index 0000000..6a9007c --- /dev/null +++ b/CleanSpec.mk
@@ -0,0 +1,54 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "libdvr.so" -print0 | xargs -0 rm -f) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvr_intermediates) +$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "libgui*" -print0 | xargs -0 rm -f) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgui_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/thermalserviced) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/thermalservice.rc) +$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "gpuservice*" -print0 | xargs -0 rm -f) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/gpuservice_intermediates)
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg new file mode 100644 index 0000000..1a932c3 --- /dev/null +++ b/PREUPLOAD.cfg
@@ -0,0 +1,23 @@ +[Builtin Hooks] +clang_format = true + +[Builtin Hooks Options] +# Only turn on clang-format check for the following subfolders. +clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp + libs/binder/ndk/ + libs/graphicsenv/ + libs/gui/ + libs/renderengine/ + libs/ui/ + libs/vr/ + services/bufferhub/ + services/surfaceflinger/ + services/vr/ + +[Hook Scripts] +owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$" +installd_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/installd/" +dumpstate_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/dumpstate/" +dumpsys_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/dumpsys/" +# bugreports matches both cmds/bugreport and cmds/bugreportz +bugreports_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "^cmds/bugreport"
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp index bb84a18..cc2a6f7 100644 --- a/cmds/atrace/Android.bp +++ b/cmds/atrace/Android.bp
@@ -19,6 +19,7 @@ "libz", "libbase", "libpdx_default_transport", + "android.hardware.atrace@1.0", ], init_rc: ["atrace.rc"],
diff --git a/cmds/atrace/TEST_MAPPING b/cmds/atrace/TEST_MAPPING new file mode 100644 index 0000000..f43db22 --- /dev/null +++ b/cmds/atrace/TEST_MAPPING
@@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "CtsAtraceHostTestCases" + } + ] +}
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 31e73fb..a4b00f8 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp
@@ -37,6 +37,7 @@ #include <binder/IServiceManager.h> #include <binder/Parcel.h> +#include <android/hardware/atrace/1.0/IAtraceDevice.h> #include <android/hidl/manager/1.0/IServiceManager.h> #include <hidl/ServiceManagement.h> @@ -52,10 +53,16 @@ using namespace android; using pdx::default_transport::ServiceUtility; +using hardware::hidl_vec; +using hardware::hidl_string; +using hardware::Return; +using hardware::atrace::V1_0::IAtraceDevice; +using hardware::atrace::V1_0::Status; +using hardware::atrace::V1_0::toString; using std::string; -#define MAX_SYS_FILES 10 +#define MAX_SYS_FILES 11 const char* k_traceTagsProperty = "debug.atrace.tags.enableflags"; const char* k_userInitiatedTraceProperty = "debug.atrace.user_initiated"; @@ -92,32 +99,31 @@ /* Tracing categories */ static const TracingCategory k_categories[] = { - { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, { - { OPT, "events/mdss/enable" }, - { OPT, "events/sde/enable" }, - } }, - { "input", "Input", ATRACE_TAG_INPUT, { } }, - { "view", "View System", ATRACE_TAG_VIEW, { } }, - { "webview", "WebView", ATRACE_TAG_WEBVIEW, { } }, - { "wm", "Window Manager", ATRACE_TAG_WINDOW_MANAGER, { } }, - { "am", "Activity Manager", ATRACE_TAG_ACTIVITY_MANAGER, { } }, - { "sm", "Sync Manager", ATRACE_TAG_SYNC_MANAGER, { } }, - { "audio", "Audio", ATRACE_TAG_AUDIO, { } }, - { "video", "Video", ATRACE_TAG_VIDEO, { } }, - { "camera", "Camera", ATRACE_TAG_CAMERA, { } }, - { "hal", "Hardware Modules", ATRACE_TAG_HAL, { } }, - { "res", "Resource Loading", ATRACE_TAG_RESOURCES, { } }, - { "dalvik", "Dalvik VM", ATRACE_TAG_DALVIK, { } }, - { "rs", "RenderScript", ATRACE_TAG_RS, { } }, - { "bionic", "Bionic C Library", ATRACE_TAG_BIONIC, { } }, - { "power", "Power Management", ATRACE_TAG_POWER, { } }, - { "pm", "Package Manager", ATRACE_TAG_PACKAGE_MANAGER, { } }, - { "ss", "System Server", ATRACE_TAG_SYSTEM_SERVER, { } }, - { "database", "Database", ATRACE_TAG_DATABASE, { } }, - { "network", "Network", ATRACE_TAG_NETWORK, { } }, - { "adb", "ADB", ATRACE_TAG_ADB, { } }, - { "vibrator", "Vibrator", ATRACE_TAG_VIBRATOR, { } }, - { "aidl", "AIDL calls", ATRACE_TAG_AIDL, { } }, + { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, { } }, + { "input", "Input", ATRACE_TAG_INPUT, { } }, + { "view", "View System", ATRACE_TAG_VIEW, { } }, + { "webview", "WebView", ATRACE_TAG_WEBVIEW, { } }, + { "wm", "Window Manager", ATRACE_TAG_WINDOW_MANAGER, { } }, + { "am", "Activity Manager", ATRACE_TAG_ACTIVITY_MANAGER, { } }, + { "sm", "Sync Manager", ATRACE_TAG_SYNC_MANAGER, { } }, + { "audio", "Audio", ATRACE_TAG_AUDIO, { } }, + { "video", "Video", ATRACE_TAG_VIDEO, { } }, + { "camera", "Camera", ATRACE_TAG_CAMERA, { } }, + { "hal", "Hardware Modules", ATRACE_TAG_HAL, { } }, + { "res", "Resource Loading", ATRACE_TAG_RESOURCES, { } }, + { "dalvik", "Dalvik VM", ATRACE_TAG_DALVIK, { } }, + { "rs", "RenderScript", ATRACE_TAG_RS, { } }, + { "bionic", "Bionic C Library", ATRACE_TAG_BIONIC, { } }, + { "power", "Power Management", ATRACE_TAG_POWER, { } }, + { "pm", "Package Manager", ATRACE_TAG_PACKAGE_MANAGER, { } }, + { "ss", "System Server", ATRACE_TAG_SYSTEM_SERVER, { } }, + { "database", "Database", ATRACE_TAG_DATABASE, { } }, + { "network", "Network", ATRACE_TAG_NETWORK, { } }, + { "adb", "ADB", ATRACE_TAG_ADB, { } }, + { "vibrator", "Vibrator", ATRACE_TAG_VIBRATOR, { } }, + { "aidl", "AIDL calls", ATRACE_TAG_AIDL, { } }, + { "nnapi", "NNAPI", ATRACE_TAG_NNAPI, { } }, + { "rro", "Runtime Resource Overlay", ATRACE_TAG_RRO, { } }, { k_coreServiceCategory, "Core services", 0, { } }, { k_pdxServiceCategory, "PDX services", 0, { } }, { "sched", "CPU Scheduling", 0, { @@ -127,7 +133,11 @@ { OPT, "events/sched/sched_blocked_reason/enable" }, { OPT, "events/sched/sched_cpu_hotplug/enable" }, { OPT, "events/sched/sched_pi_setprio/enable" }, + { OPT, "events/sched/sched_process_exit/enable" }, { OPT, "events/cgroup/enable" }, + { OPT, "events/oom/oom_score_adj_update/enable" }, + { OPT, "events/task/task_rename/enable" }, + { OPT, "events/task/task_newtask/enable" }, } }, { "irq", "IRQ Events", 0, { { REQ, "events/irq/enable" }, @@ -187,10 +197,12 @@ { REQ, "events/cpufreq_interactive/enable" }, } }, { "sync", "Synchronization", 0, { - // before linux kernel 4.9 + // linux kernel < 4.9 { OPT, "events/sync/enable" }, - // starting in linux kernel 4.9 + // linux kernel == 4.9.x { OPT, "events/fence/enable" }, + // linux kernel > 4.9 + { OPT, "events/dma_fence/enable" }, } }, { "workq", "Kernel Workqueues", 0, { { REQ, "events/workqueue/enable" }, @@ -208,6 +220,7 @@ { "binder_driver", "Binder Kernel driver", 0, { { REQ, "events/binder/binder_transaction/enable" }, { REQ, "events/binder/binder_transaction_received/enable" }, + { REQ, "events/binder/binder_transaction_alloc_buf/enable" }, { OPT, "events/binder/binder_set_priority/enable" }, } }, { "binder_lock", "Binder global lock trace", 0, { @@ -218,6 +231,28 @@ { "pagecache", "Page cache", 0, { { REQ, "events/filemap/enable" }, } }, + { "memory", "Memory", 0, { + { OPT, "events/kmem/rss_stat/enable" }, + { OPT, "events/kmem/ion_heap_grow/enable" }, + { OPT, "events/kmem/ion_heap_shrink/enable" }, + } }, +}; + +struct TracingVendorCategory { + // The name identifying the category. + std::string name; + + // A longer description of the category. + std::string description; + + // If the category is enabled through command. + bool enabled; + + TracingVendorCategory(string &&name, string &&description, bool enabled) + : name(std::move(name)) + , description(std::move(description)) + , enabled(enabled) + {} }; /* Command line options */ @@ -227,8 +262,8 @@ static bool g_compress = false; static bool g_nohup = false; static int g_initialSleepSecs = 0; -static const char* g_categoriesFile = NULL; -static const char* g_kernelTraceFuncs = NULL; +static const char* g_categoriesFile = nullptr; +static const char* g_kernelTraceFuncs = nullptr; static const char* g_debugAppCmdLine = ""; static const char* g_outputFile = nullptr; @@ -237,6 +272,8 @@ static bool g_traceAborted = false; static bool g_categoryEnables[arraysize(k_categories)] = {}; static std::string g_traceFolder; +static sp<IAtraceDevice> g_atraceHal; +static std::vector<TracingVendorCategory> g_vendorCategories; /* Sys file paths */ static const char* k_traceClockPath = @@ -260,6 +297,9 @@ static const char* k_printTgidPath = "options/print-tgid"; +static const char* k_recordTgidPath = + "options/record-tgid"; + static const char* k_funcgraphAbsTimePath = "options/funcgraph-abstime"; @@ -401,15 +441,11 @@ for (int i = 0; i < MAX_SYS_FILES; i++) { const char* path = category.sysfiles[i].path; bool req = category.sysfiles[i].required == REQ; - if (path != NULL) { - if (req) { - if (!fileIsWritable(path)) { - return false; - } else { - ok = true; - } - } else { + if (path != nullptr) { + if (fileIsWritable(path)) { ok = true; + } else if (req) { + return false; } } } @@ -426,7 +462,7 @@ for (int i = 0; i < MAX_SYS_FILES; i++) { const char* path = category.sysfiles[i].path; bool req = category.sysfiles[i].required == REQ; - if (path != NULL) { + if (path != nullptr) { if (req) { if (!fileExists(path)) { return false; @@ -522,9 +558,15 @@ static bool setPrintTgidEnableIfPresent(bool enable) { + // Pre-4.13 this was options/print-tgid as an android-specific option. + // In 4.13+ this is an upstream option called options/record-tgid + // Both options produce the same ftrace format change if (fileExists(k_printTgidPath)) { return setKernelOptionEnable(k_printTgidPath, enable); } + if (fileExists(k_recordTgidPath)) { + return setKernelOptionEnable(k_recordTgidPath, enable); + } return true; } @@ -536,10 +578,10 @@ Vector<String16> services = sm->listServices(); for (size_t i = 0; i < services.size(); i++) { sp<IBinder> obj = sm->checkService(services[i]); - if (obj != NULL) { + if (obj != nullptr) { Parcel data; if (obj->transact(IBinder::SYSPROPS_TRANSACTION, data, - NULL, 0) != OK) { + nullptr, 0) != OK) { if (false) { // XXX: For some reason this fails on tablets trying to // poke the "phone" service. It's not clear whether some @@ -629,9 +671,9 @@ { int i = 0; char* start = cmdline; - while (start != NULL) { + while (start != nullptr) { char* end = strchr(start, ','); - if (end != NULL) { + if (end != nullptr) { *end = '\0'; end++; } @@ -661,7 +703,7 @@ const TracingCategory &c = k_categories[i]; for (int j = 0; j < MAX_SYS_FILES; j++) { const char* path = c.sysfiles[j].path; - if (path != NULL && fileIsWritable(path)) { + if (path != nullptr && fileIsWritable(path)) { ok &= setKernelOptionEnable(path, false); } } @@ -697,7 +739,7 @@ ok = false; } } - func = strtok(NULL, ","); + func = strtok(nullptr, ","); } free(myFuncs); return ok; @@ -708,7 +750,7 @@ { bool ok = true; - if (funcs == NULL || funcs[0] == '\0') { + if (funcs == nullptr || funcs[0] == '\0') { // Disable kernel function tracing. if (fileIsWritable(k_currentTracerPath)) { ok &= writeStr(k_currentTracerPath, "nop"); @@ -730,7 +772,7 @@ char* func = strtok(myFuncs, ","); while (func) { ok &= appendStr(k_ftraceFilterPath, func); - func = strtok(NULL, ","); + func = strtok(nullptr, ","); } free(myFuncs); @@ -743,13 +785,20 @@ return ok; } -static bool setCategoryEnable(const char* name, bool enable) +static bool setCategoryEnable(const char* name) { + bool vendor_found = false; + for (auto &c : g_vendorCategories) { + if (strcmp(name, c.name.c_str()) == 0) { + c.enabled = true; + vendor_found = true; + } + } for (size_t i = 0; i < arraysize(k_categories); i++) { const TracingCategory& c = k_categories[i]; if (strcmp(name, c.name) == 0) { if (isCategorySupported(c)) { - g_categoryEnables[i] = enable; + g_categoryEnables[i] = true; return true; } else { if (isCategorySupportedForRoot(c)) { @@ -763,6 +812,9 @@ } } } + if (vendor_found) { + return true; + } fprintf(stderr, "error: unknown tracing category \"%s\"\n", name); return false; } @@ -772,7 +824,7 @@ if (!categories_file) { return true; } - Tokenizer* tokenizer = NULL; + Tokenizer* tokenizer = nullptr; if (Tokenizer::open(String8(categories_file), &tokenizer) != NO_ERROR) { return false; } @@ -783,7 +835,7 @@ tokenizer->skipDelimiters(" "); continue; } - ok &= setCategoryEnable(token.string(), true); + ok &= setCategoryEnable(token.string()); } delete tokenizer; return ok; @@ -838,6 +890,7 @@ setTagsProperty(0); clearAppProperties(); pokeBinderServices(); + pokeHalServices(); if (g_tracePdx) { ServiceUtility::PokeServices(); @@ -874,7 +927,7 @@ for (int j = 0; j < MAX_SYS_FILES; j++) { const char* path = c.sysfiles[j].path; bool required = c.sysfiles[j].required == REQ; - if (path != NULL) { + if (path != nullptr) { if (fileIsWritable(path)) { ok &= setKernelOptionEnable(path, true); } else if (required) { @@ -899,7 +952,7 @@ setTraceOverwriteEnable(true); setTraceBufferSizeKB(1); setPrintTgidEnableIfPresent(false); - setKernelTraceFuncs(NULL); + setKernelTraceFuncs(nullptr); setUserInitiatedTraceProperty(false); } @@ -1057,10 +1110,10 @@ sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = handleSignal; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGQUIT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); + sigaction(SIGHUP, &sa, nullptr); + sigaction(SIGINT, &sa, nullptr); + sigaction(SIGQUIT, &sa, nullptr); + sigaction(SIGTERM, &sa, nullptr); } static void listSupportedCategories() @@ -1071,6 +1124,9 @@ printf(" %10s - %s\n", c.name, c.longname); } } + for (const auto &c : g_vendorCategories) { + printf(" %10s - %s (HAL)\n", c.name.c_str(), c.description.c_str()); + } } // Print the command usage help to stderr. @@ -1127,6 +1183,79 @@ return true; } +void initVendorCategories() +{ + g_atraceHal = IAtraceDevice::getService(); + + if (g_atraceHal == nullptr) { + // No atrace HAL + return; + } + + Return<void> ret = g_atraceHal->listCategories( + [](const auto& list) { + g_vendorCategories.reserve(list.size()); + for (const auto& category : list) { + g_vendorCategories.emplace_back(category.name, category.description, false); + } + }); + if (!ret.isOk()) { + fprintf(stderr, "calling atrace HAL failed: %s\n", ret.description().c_str()); + } +} + +static bool setUpVendorTracing() +{ + if (g_atraceHal == nullptr) { + // No atrace HAL + return true; + } + + std::vector<hidl_string> categories; + for (const auto &c : g_vendorCategories) { + if (c.enabled) { + categories.emplace_back(c.name); + } + } + + if (!categories.size()) { + return true; + } + + auto ret = g_atraceHal->enableCategories(categories); + if (!ret.isOk()) { + fprintf(stderr, "calling atrace HAL failed: %s\n", ret.description().c_str()); + return false; + } else if (ret != Status::SUCCESS) { + fprintf(stderr, "calling atrace HAL failed: %s\n", toString(ret).c_str()); + return false; + } + return true; +} + +static bool cleanUpVendorTracing() +{ + if (g_atraceHal == nullptr) { + // No atrace HAL + return true; + } + + if (!g_vendorCategories.size()) { + // No vendor categories + return true; + } + + auto ret = g_atraceHal->disableAllCategories(); + if (!ret.isOk()) { + fprintf(stderr, "calling atrace HAL failed: %s\n", ret.description().c_str()); + return false; + } else if (ret != Status::SUCCESS) { + fprintf(stderr, "calling atrace HAL failed: %s\n", toString(ret).c_str()); + return false; + } + return true; +} + int main(int argc, char **argv) { bool async = false; @@ -1146,17 +1275,19 @@ exit(-1); } + initVendorCategories(); + for (;;) { int ret; int option_index = 0; static struct option long_options[] = { - {"async_start", no_argument, 0, 0 }, - {"async_stop", no_argument, 0, 0 }, - {"async_dump", no_argument, 0, 0 }, - {"only_userspace", no_argument, 0, 0 }, - {"list_categories", no_argument, 0, 0 }, - {"stream", no_argument, 0, 0 }, - { 0, 0, 0, 0 } + {"async_start", no_argument, nullptr, 0 }, + {"async_stop", no_argument, nullptr, 0 }, + {"async_dump", no_argument, nullptr, 0 }, + {"only_userspace", no_argument, nullptr, 0 }, + {"list_categories", no_argument, nullptr, 0 }, + {"stream", no_argument, nullptr, 0 }, + {nullptr, 0, nullptr, 0 } }; ret = getopt_long(argc, argv, "a:b:cf:k:ns:t:zo:", @@ -1164,7 +1295,7 @@ if (ret < 0) { for (int i = optind; i < argc; i++) { - if (!setCategoryEnable(argv[i], true)) { + if (!setCategoryEnable(argv[i])) { fprintf(stderr, "error enabling tracing category \"%s\"\n", argv[i]); exit(1); } @@ -1267,6 +1398,7 @@ if (ok && traceStart && !onlyUserspace) { ok &= setUpKernelTracing(); + ok &= setUpVendorTracing(); ok &= startTrace(); } @@ -1285,9 +1417,7 @@ if (!onlyUserspace) ok = clearTrace(); - if (!onlyUserspace) - writeClockSyncMarker(); - + writeClockSyncMarker(); if (ok && !async && !traceStream) { // Sleep to allow the trace to be captured. struct timespec timeLeft; @@ -1337,6 +1467,7 @@ // Reset the trace buffer size to 1. if (traceStop) { + cleanUpVendorTracing(); cleanUpUserspaceTracing(); if (!onlyUserspace) cleanUpKernelTracing();
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index d3d0711..f1426b6 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc
@@ -19,6 +19,8 @@ chmod 0666 /sys/kernel/tracing/options/overwrite chmod 0666 /sys/kernel/debug/tracing/options/print-tgid chmod 0666 /sys/kernel/tracing/options/print-tgid + chmod 0666 /sys/kernel/debug/tracing/options/record-tgid + chmod 0666 /sys/kernel/tracing/options/record-tgid chmod 0666 /sys/kernel/debug/tracing/saved_cmdlines_size chmod 0666 /sys/kernel/tracing/saved_cmdlines_size chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_switch/enable @@ -31,6 +33,10 @@ chmod 0666 /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_pi_setprio/enable chmod 0666 /sys/kernel/tracing/events/sched/sched_pi_setprio/enable + chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_process_exit/enable + chmod 0666 /sys/kernel/tracing/events/sched/sched_process_exit/enable + chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_waking/enable + chmod 0666 /sys/kernel/tracing/events/sched/sched_waking/enable chmod 0666 /sys/kernel/debug/tracing/events/cgroup/enable chmod 0666 /sys/kernel/tracing/events/cgroup/enable chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable @@ -41,6 +47,8 @@ chmod 0666 /sys/kernel/tracing/events/power/clock_set_rate/enable chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable chmod 0666 /sys/kernel/tracing/events/power/cpu_frequency_limits/enable + chmod 0666 /sys/kernel/debug/tracing/events/power/gpu_frequency/enable + chmod 0666 /sys/kernel/tracing/events/power/gpu_frequency/enable chmod 0666 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable chmod 0666 /sys/kernel/tracing/events/cpufreq_interactive/enable chmod 0666 /sys/kernel/debug/tracing/events/vmscan/mm_vmscan_direct_reclaim_begin/enable @@ -57,6 +65,8 @@ chmod 0666 /sys/kernel/tracing/events/binder/binder_transaction/enable chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_transaction_received/enable chmod 0666 /sys/kernel/tracing/events/binder/binder_transaction_received/enable + chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_transaction_alloc_buf/enable + chmod 0666 /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf/enable chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_lock/enable chmod 0666 /sys/kernel/tracing/events/binder/binder_lock/enable chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_locked/enable @@ -87,9 +97,34 @@ chmod 0666 /sys/kernel/tracing/events/sync/enable chmod 0666 /sys/kernel/debug/tracing/events/fence/enable chmod 0666 /sys/kernel/tracing/events/fence/enable - + chmod 0666 /sys/kernel/debug/tracing/events/dma_fence/enable + chmod 0666 /sys/kernel/tracing/events/dma_fence/enable + chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/enable + chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/enable + chmod 0666 /sys/kernel/debug/tracing/events/kmem/ion_heap_grow/enable + chmod 0666 /sys/kernel/tracing/events/kmem/ion_heap_grow/enable + chmod 0666 /sys/kernel/debug/tracing/events/kmem/ion_heap_shrink/enable + chmod 0666 /sys/kernel/tracing/events/kmem/ion_heap_shrink/enable + chmod 0666 /sys/kernel/debug/tracing/events/signal/signal_generate/enable + chmod 0666 /sys/kernel/tracing/events/signal/signal_generate/enable + chmod 0666 /sys/kernel/debug/tracing/events/signal/signal_deliver/enable + chmod 0666 /sys/kernel/tracing/events/signal/signal_deliver/enable + chmod 0666 /sys/kernel/debug/tracing/events/mm_event/mm_event_record/enable + chmod 0666 /sys/kernel/tracing/events/mm_event/mm_event_record/enable + chmod 0666 /sys/kernel/debug/tracing/events/lowmemorykiller/lowmemory_kill/enable + chmod 0666 /sys/kernel/tracing/events/lowmemorykiller/lowmemory_kill/enable + chmod 0666 /sys/kernel/debug/tracing/events/oom/oom_score_adj_update/enable + chmod 0666 /sys/kernel/tracing/events/oom/oom_score_adj_update/enable + chmod 0666 /sys/kernel/debug/tracing/events/task/task_rename/enable + chmod 0666 /sys/kernel/tracing/events/task/task_rename/enable + chmod 0666 /sys/kernel/debug/tracing/events/task/task_newtask/enable + chmod 0666 /sys/kernel/tracing/events/task/task_newtask/enable # disk + chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable + chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_get_data_block/enable + chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_iget/enable + chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_iget/enable chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_sync_file_enter/enable chmod 0666 /sys/kernel/debug/tracing/events/f2fs/f2fs_sync_file_enter/enable chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_sync_file_exit/enable @@ -102,6 +137,12 @@ chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_begin/enable chmod 0666 /sys/kernel/tracing/events/ext4/ext4_da_write_end/enable chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_da_write_end/enable + chmod 0666 /sys/kernel/tracing/events/ext4/ext4_es_lookup_extent_enter/enable + chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_es_lookup_extent_enter/enable + chmod 0666 /sys/kernel/tracing/events/ext4/ext4_es_lookup_extent_exit/enable + chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_es_lookup_extent_exit/enable + chmod 0666 /sys/kernel/tracing/events/ext4/ext4_load_inode/enable + chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_load_inode/enable chmod 0666 /sys/kernel/tracing/events/ext4/ext4_sync_file_enter/enable chmod 0666 /sys/kernel/debug/tracing/events/ext4/ext4_sync_file_enter/enable chmod 0666 /sys/kernel/tracing/events/ext4/ext4_sync_file_exit/enable @@ -111,11 +152,11 @@ chmod 0666 /sys/kernel/tracing/events/block/block_rq_complete/enable chmod 0666 /sys/kernel/debug/tracing/events/block/block_rq_complete/enable - # graphics - chmod 0666 /sys/kernel/tracing/events/sde/enable - chmod 0666 /sys/kernel/debug/tracing/events/sde/enable - chmod 0666 /sys/kernel/tracing/events/mdss/enable - chmod 0666 /sys/kernel/debug/tracing/events/mdss/enable + # filemap events for iorapd + chmod 0666 /sys/kernel/tracing/events/filemap/mm_filemap_add_to_page_cache/enable + chmod 0666 /sys/kernel/debug/tracing/events/filemap/mm_filemap_add_to_page_cache/enable + chmod 0666 /sys/kernel/tracing/events/filemap/mm_filemap_delete_from_page_cache/enable + chmod 0666 /sys/kernel/debug/tracing/events/filemap/mm_filemap_delete_from_page_cache/enable # Tracing disabled by default write /sys/kernel/debug/tracing/tracing_on 0 @@ -125,6 +166,42 @@ chmod 0666 /sys/kernel/debug/tracing/trace chmod 0666 /sys/kernel/tracing/trace +# Read and truncate the per-CPU kernel trace. +# Cannot use wildcards in .rc files. Update this if there is a phone with +# more CPUs. + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu0/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu0/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu1/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu1/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu2/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu2/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu3/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu3/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu4/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu4/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu5/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu5/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu6/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu6/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu7/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu7/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu8/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu8/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu9/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu9/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu10/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu10/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu11/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu11/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu12/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu12/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu13/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu13/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu14/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu14/trace + chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu15/trace + chmod 0666 /sys/kernel/tracing/per_cpu/cpu15/trace + on property:persist.debug.atrace.boottrace=1 start boottrace
diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc index f4e5b98..6c86c21 100644 --- a/cmds/atrace/atrace_userdebug.rc +++ b/cmds/atrace/atrace_userdebug.rc
@@ -5,12 +5,18 @@ # Access control to these files is now entirely in selinux policy. on post-fs + # On userdebug allow to enable any event via the generic + # set_event interface: + # echo sched/foo > set_event == echo 1 > events/sched/foo/enable. + chmod 0666 /sys/kernel/tracing/set_event + chmod 0666 /sys/kernel/debug/tracing/set_event + chmod 0666 /sys/kernel/tracing/events/workqueue/enable chmod 0666 /sys/kernel/debug/tracing/events/workqueue/enable chmod 0666 /sys/kernel/tracing/events/regulator/enable chmod 0666 /sys/kernel/debug/tracing/events/regulator/enable - chmod 0666 /sys/kernel/tracing/events/pagecache/enable - chmod 0666 /sys/kernel/debug/tracing/events/pagecache/enable + chmod 0666 /sys/kernel/tracing/events/filemap/enable + chmod 0666 /sys/kernel/debug/tracing/events/filemap/enable # irq chmod 0666 /sys/kernel/tracing/events/irq/enable
diff --git a/cmds/bugreport/OWNERS b/cmds/bugreport/OWNERS new file mode 100644 index 0000000..1ba7cff --- /dev/null +++ b/cmds/bugreport/OWNERS
@@ -0,0 +1,6 @@ +set noparent + +felipeal@google.com +nandana@google.com +jsharkey@android.com +enh@google.com
diff --git a/cmds/bugreportz/Android.bp b/cmds/bugreportz/Android.bp new file mode 100644 index 0000000..924a3a3 --- /dev/null +++ b/cmds/bugreportz/Android.bp
@@ -0,0 +1,44 @@ +// bugreportz +// ========== +cc_binary { + name: "bugreportz", + + srcs: [ + "bugreportz.cpp", + "main.cpp", + ], + + cflags: [ + "-Werror", + "-Wall", + ], + + shared_libs: [ + "libbase", + "libcutils", + ], +} + +// bugreportz_test +// =============== +cc_test { + name: "bugreportz_test", + test_suites: ["device-tests"], + + cflags: [ + "-Werror", + "-Wall", + ], + + srcs: [ + "bugreportz.cpp", + "bugreportz_test.cpp", + ], + + static_libs: ["libgmock"], + + shared_libs: [ + "libbase", + "libutils", + ], +}
diff --git a/cmds/bugreportz/Android.mk b/cmds/bugreportz/Android.mk deleted file mode 100644 index 10dda56..0000000 --- a/cmds/bugreportz/Android.mk +++ /dev/null
@@ -1,44 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -# bugreportz -# ========== - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - bugreportz.cpp \ - main.cpp \ - -LOCAL_MODULE:= bugreportz - -LOCAL_CFLAGS := -Werror -Wall - -LOCAL_SHARED_LIBRARIES := \ - libbase \ - libcutils \ - -include $(BUILD_EXECUTABLE) - -# bugreportz_test -# =============== - -include $(CLEAR_VARS) - -LOCAL_MODULE := bugreportz_test -LOCAL_COMPATIBILITY_SUITE := device-tests -LOCAL_MODULE_TAGS := tests - -LOCAL_CFLAGS := -Werror -Wall - -LOCAL_SRC_FILES := \ - bugreportz.cpp \ - bugreportz_test.cpp \ - -LOCAL_STATIC_LIBRARIES := \ - libgmock \ - -LOCAL_SHARED_LIBRARIES := \ - libbase \ - libutils \ - -include $(BUILD_NATIVE_TEST)
diff --git a/cmds/bugreportz/OWNERS b/cmds/bugreportz/OWNERS new file mode 100644 index 0000000..1ba7cff --- /dev/null +++ b/cmds/bugreportz/OWNERS
@@ -0,0 +1,6 @@ +set noparent + +felipeal@google.com +nandana@google.com +jsharkey@android.com +enh@google.com
diff --git a/cmds/bugreportz/bugreportz.cpp b/cmds/bugreportz/bugreportz.cpp index 75855cf..ded0ed3 100644 --- a/cmds/bugreportz/bugreportz.cpp +++ b/cmds/bugreportz/bugreportz.cpp
@@ -55,7 +55,7 @@ errno = ETIMEDOUT; } printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno)); - break; + return EXIT_FAILURE; } // Writes line by line. @@ -71,8 +71,5 @@ // Process final line, in case it didn't finish with newline write_line(line, show_progress); - if (close(s) == -1) { - fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno)); - } return EXIT_SUCCESS; }
diff --git a/cmds/bugreportz/bugreportz.h b/cmds/bugreportz/bugreportz.h index 304e4b3..7af289b 100644 --- a/cmds/bugreportz/bugreportz.h +++ b/cmds/bugreportz/bugreportz.h
@@ -16,6 +16,7 @@ #define BUGREPORTZ_H // Calls dumpstate using the given socket and output its result to stdout. +// Ownership of the socket is not transferred. int bugreportz(int s, bool show_progress); #endif // BUGREPORTZ_H
diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp index a3ae1ff..74a95b0 100644 --- a/cmds/bugreportz/main.cpp +++ b/cmds/bugreportz/main.cpp
@@ -82,7 +82,7 @@ if (s == -1) { printf("FAIL:Failed to connect to dumpstatez service: %s\n", strerror(errno)); - return EXIT_SUCCESS; + return EXIT_FAILURE; } // Set a timeout so that if nothing is read in 10 minutes, we'll stop @@ -92,8 +92,16 @@ tv.tv_sec = 10 * 60; tv.tv_usec = 0; if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { - fprintf(stderr, "WARNING: Cannot set socket timeout: %s\n", strerror(errno)); + fprintf(stderr, + "WARNING: Cannot set socket timeout, bugreportz might hang indefinitely: %s\n", + strerror(errno)); } - bugreportz(s, show_progress); + int ret = bugreportz(s, show_progress); + + if (close(s) == -1) { + fprintf(stderr, "WARNING: error closing socket: %s\n", strerror(errno)); + ret = EXIT_FAILURE; + } + return ret; }
diff --git a/cmds/cmd/Android.bp b/cmds/cmd/Android.bp new file mode 100644 index 0000000..8ea71cd --- /dev/null +++ b/cmds/cmd/Android.bp
@@ -0,0 +1,42 @@ +cc_library_static { + name: "libcmd", + + srcs: ["cmd.cpp"], + export_include_dirs: ["."], + + shared_libs: [ + "libutils", + "liblog", + "libselinux", + "libbinder", + ], + + cflags: [ + "-Wall", + "-Werror", + "-DXP_UNIX", + ], +} + +cc_binary { + name: "cmd", + + srcs: ["main.cpp"], + + static_libs: [ + "libcmd", + ], + + shared_libs: [ + "libutils", + "liblog", + "libselinux", + "libbinder", + ], + + cflags: [ + "-Wall", + "-Werror", + "-DXP_UNIX", + ], +}
diff --git a/cmds/cmd/Android.mk b/cmds/cmd/Android.mk deleted file mode 100644 index 4868555..0000000 --- a/cmds/cmd/Android.mk +++ /dev/null
@@ -1,25 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - cmd.cpp - -LOCAL_SHARED_LIBRARIES := \ - libutils \ - liblog \ - libselinux \ - libbinder - -LOCAL_CFLAGS := -Wall -Werror - -LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) - -ifeq ($(TARGET_OS),linux) - LOCAL_CFLAGS += -DXP_UNIX - #LOCAL_SHARED_LIBRARIES += librt -endif - -LOCAL_MODULE:= cmd - -include $(BUILD_EXECUTABLE)
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp index 48d5d4a..7b4aeb2 100644 --- a/cmds/cmd/cmd.cpp +++ b/cmds/cmd/cmd.cpp
@@ -40,6 +40,8 @@ #include "selinux/selinux.h" #include "selinux/android.h" +#include "cmd.h" + #define DEBUG 0 using namespace android; @@ -59,8 +61,11 @@ class MyShellCallback : public BnShellCallback { public: + TextOutput& mErrorLog; bool mActive = true; + MyShellCallback(TextOutput& errorLog) : mErrorLog(errorLog) {} + virtual int openFile(const String16& path, const String16& seLinuxContext, const String16& mode) { String8 path8(path); @@ -69,7 +74,7 @@ String8 fullPath(cwd); fullPath.appendPath(path8); if (!mActive) { - aerr << "Open attempt after active for: " << fullPath << endl; + mErrorLog << "Open attempt after active for: " << fullPath << endl; return -EPERM; } #if DEBUG @@ -78,20 +83,20 @@ int flags = 0; bool checkRead = false; bool checkWrite = false; - if (mode == String16("w")) { + if (mode == u"w") { flags = O_WRONLY|O_CREAT|O_TRUNC; checkWrite = true; - } else if (mode == String16("w+")) { + } else if (mode == u"w+") { flags = O_RDWR|O_CREAT|O_TRUNC; checkRead = checkWrite = true; - } else if (mode == String16("r")) { + } else if (mode == u"r") { flags = O_RDONLY; checkRead = true; - } else if (mode == String16("r+")) { + } else if (mode == u"r+") { flags = O_RDWR; checkRead = checkWrite = true; } else { - aerr << "Invalid mode requested: " << mode.string() << endl; + mErrorLog << "Invalid mode requested: " << mode.string() << endl; return -EINVAL; } int fd = open(fullPath.string(), flags, S_IRWXU|S_IRWXG); @@ -103,34 +108,30 @@ } if (is_selinux_enabled() && seLinuxContext.size() > 0) { String8 seLinuxContext8(seLinuxContext); - security_context_t tmp = NULL; + security_context_t tmp = nullptr; getfilecon(fullPath.string(), &tmp); Unique_SecurityContext context(tmp); if (checkWrite) { int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), - "file", "write", NULL); + "file", "write", nullptr); if (accessGranted != 0) { #if DEBUG ALOGD("openFile: failed selinux write check!"); #endif close(fd); - aerr << "System server has no access to write file context " << context.get() - << " (from path " << fullPath.string() << ", context " - << seLinuxContext8.string() << ")" << endl; + mErrorLog << "System server has no access to write file context " << context.get() << " (from path " << fullPath.string() << ", context " << seLinuxContext8.string() << ")" << endl; return -EPERM; } } if (checkRead) { int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), - "file", "read", NULL); + "file", "read", nullptr); if (accessGranted != 0) { #if DEBUG ALOGD("openFile: failed selinux read check!"); #endif close(fd); - aerr << "System server has no access to read file context " << context.get() - << " (from path " << fullPath.string() << ", context " - << seLinuxContext8.string() << ")" << endl; + mErrorLog << "System server has no access to read file context " << context.get() << " (from path " << fullPath.string() << ", context " << seLinuxContext8.string() << ")" << endl; return -EPERM; } } @@ -163,85 +164,86 @@ } }; -int main(int argc, char* const argv[]) -{ - signal(SIGPIPE, SIG_IGN); +int cmdMain(const std::vector<std::string_view>& argv, TextOutput& outputLog, TextOutput& errorLog, + int in, int out, int err, RunMode runMode) { sp<ProcessState> proc = ProcessState::self(); - // setThreadPoolMaxThreadCount(0) actually tells the kernel it's - // not allowed to spawn any additional threads, but we still spawn - // a binder thread from userspace when we call startThreadPool(). - // This is safe because we only have 2 callbacks, neither of which - // block. - // See b/36066697 for rationale - proc->setThreadPoolMaxThreadCount(0); proc->startThreadPool(); #if DEBUG ALOGD("cmd: starting"); #endif sp<IServiceManager> sm = defaultServiceManager(); - fflush(stdout); - if (sm == NULL) { + if (runMode == RunMode::kStandalone) { + fflush(stdout); + } + if (sm == nullptr) { ALOGW("Unable to get default service manager!"); - aerr << "cmd: Unable to get default service manager!" << endl; + errorLog << "cmd: Unable to get default service manager!" << endl; return 20; } - if (argc == 1) { - aerr << "cmd: No service specified; use -l to list all services" << endl; + int argc = argv.size(); + + if (argc == 0) { + errorLog << "cmd: No service specified; use -l to list all services" << endl; return 20; } - if ((argc == 2) && (strcmp(argv[1], "-l") == 0)) { + if ((argc == 1) && (argv[0] == "-l")) { Vector<String16> services = sm->listServices(); services.sort(sort_func); - aout << "Currently running services:" << endl; + outputLog << "Currently running services:" << endl; for (size_t i=0; i<services.size(); i++) { sp<IBinder> service = sm->checkService(services[i]); - if (service != NULL) { - aout << " " << services[i] << endl; + if (service != nullptr) { + outputLog << " " << services[i] << endl; } } return 0; } + const auto cmd = argv[0]; + Vector<String16> args; - for (int i=2; i<argc; i++) { - args.add(String16(argv[i])); + String16 serviceName = String16(cmd.data(), cmd.size()); + for (int i = 1; i < argc; i++) { + args.add(String16(argv[i].data(), argv[i].size())); } - String16 cmd = String16(argv[1]); - sp<IBinder> service = sm->checkService(cmd); - if (service == NULL) { - ALOGW("Can't find service %s", argv[1]); - aerr << "cmd: Can't find service: " << argv[1] << endl; + sp<IBinder> service = sm->checkService(serviceName); + if (service == nullptr) { + if (runMode == RunMode::kStandalone) { + ALOGW("Can't find service %.*s", static_cast<int>(cmd.size()), cmd.data()); + } + errorLog << "cmd: Can't find service: " << cmd << endl; return 20; } - sp<MyShellCallback> cb = new MyShellCallback(); + sp<MyShellCallback> cb = new MyShellCallback(errorLog); sp<MyResultReceiver> result = new MyResultReceiver(); #if DEBUG - ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", argv[1], STDIN_FILENO, STDOUT_FILENO, - STDERR_FILENO); + ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", cmd, in, out, err); #endif // TODO: block until a result is returned to MyResultReceiver. - status_t err = IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args, - cb, result); - if (err < 0) { + status_t error = IBinder::shellCommand(service, in, out, err, args, cb, result); + if (error < 0) { const char* errstr; - switch (err) { + switch (error) { case BAD_TYPE: errstr = "Bad type"; break; case FAILED_TRANSACTION: errstr = "Failed transaction"; break; case FDS_NOT_ALLOWED: errstr = "File descriptors not allowed"; break; case UNEXPECTED_NULL: errstr = "Unexpected null"; break; - default: errstr = strerror(-err); break; + default: errstr = strerror(-error); break; } - ALOGW("Failure calling service %s: %s (%d)", argv[1], errstr, -err); - aout << "cmd: Failure calling service " << argv[1] << ": " << errstr << " (" - << (-err) << ")" << endl; - return err; + if (runMode == RunMode::kStandalone) { + ALOGW("Failure calling service %.*s: %s (%d)", static_cast<int>(cmd.size()), cmd.data(), + errstr, -error); + } + outputLog << "cmd: Failure calling service " << cmd << ": " << errstr << " (" << (-error) + << ")" << endl; + return error; } cb->mActive = false;
diff --git a/cmds/cmd/cmd.h b/cmds/cmd/cmd.h new file mode 100644 index 0000000..a91e009 --- /dev/null +++ b/cmds/cmd/cmd.h
@@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/TextOutput.h> + +#include <string_view> +#include <vector> + +enum class RunMode { + kStandalone, + kLibrary, +}; + +int cmdMain(const std::vector<std::string_view>& argv, android::TextOutput& outputLog, + android::TextOutput& errorLog, int in, int out, int err, RunMode runMode);
diff --git a/cmds/cmd/main.cpp b/cmds/cmd/main.cpp new file mode 100644 index 0000000..2256e2a --- /dev/null +++ b/cmds/cmd/main.cpp
@@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 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 <unistd.h> + +#include "cmd.h" + +int main(int argc, char* const argv[]) { + signal(SIGPIPE, SIG_IGN); + + std::vector<std::string_view> arguments; + arguments.reserve(argc - 1); + // 0th argument is a program name, skipping. + for (int i = 1; i < argc; ++i) { + arguments.emplace_back(argv[i]); + } + + return cmdMain(arguments, android::aout, android::aerr, STDIN_FILENO, STDOUT_FILENO, + STDERR_FILENO, RunMode::kStandalone); +}
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index b04543b..ee32cb4 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp
@@ -57,10 +57,19 @@ export_aidl_headers: true, }, srcs: [ - "binder/android/os/IDumpstate.aidl", + ":dumpstate_aidl", + ], + export_include_dirs: ["binder"], +} + +filegroup { + name: "dumpstate_aidl", + srcs: [ "binder/android/os/IDumpstateListener.aidl", "binder/android/os/IDumpstateToken.aidl", + "binder/android/os/IDumpstate.aidl", ], + path: "binder", } cc_defaults { @@ -88,8 +97,9 @@ "utils.cpp", ], static_libs: [ + "libincidentcompanion", "libdumpsys", - "libserviceutils" + "libserviceutils", ], } @@ -100,6 +110,31 @@ "dumpstate.cpp", "main.cpp", ], + required: [ + "atrace", + "df", + "getprop", + "ip", + "iptables", + "ip6tables", + "kill", + "librank", + "logcat", + "lsmod", + "lsof", + "netstat", + "parse_radio_log", + "printenv", + "procrank", + "screencap", + "showmap", + "ss", + "storaged", + "top", + "uptime", + "vdc", + "vril-dump", + ], init_rc: ["dumpstate.rc"], } @@ -107,6 +142,7 @@ name: "dumpstate_test", defaults: ["dumpstate_defaults"], srcs: [ + "dumpstate.cpp", "tests/dumpstate_test.cpp", ], static_libs: ["libgmock"], @@ -121,3 +157,22 @@ ], static_libs: ["libgmock"], } + + +// =======================# +// dumpstate_test_fixture # +// =======================# +cc_test { + + name: "dumpstate_test_fixture", + test_suites: ["device-tests"], + cflags: [ + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-unused-variable", + "-Wunused-parameter", + ], + srcs: ["tests/dumpstate_test_fixture.cpp"], + data: ["tests/testdata/**/*"], +}
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk deleted file mode 100644 index ea5fbf1..0000000 --- a/cmds/dumpstate/Android.mk +++ /dev/null
@@ -1,22 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -# =======================# -# dumpstate_test_fixture # -# =======================# -include $(CLEAR_VARS) - -LOCAL_MODULE := dumpstate_test_fixture -LOCAL_COMPATIBILITY_SUITE := device-tests -LOCAL_MODULE_TAGS := tests - -LOCAL_CFLAGS := \ - -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter - -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk - -LOCAL_SRC_FILES := \ - tests/dumpstate_test_fixture.cpp - -LOCAL_TEST_DATA := $(call find-test-data-in-subdirs, $(LOCAL_PATH), *, tests/testdata) - -include $(BUILD_NATIVE_TEST)
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index 49a78e7..ddae9ea 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp
@@ -18,20 +18,51 @@ #include "DumpstateService.h" -#include <android-base/stringprintf.h> +#include <memory> +#include <android-base/stringprintf.h> #include "android/os/BnDumpstate.h" #include "DumpstateInternal.h" +using android::base::StringPrintf; + namespace android { namespace os { namespace { -class DumpstateToken : public BnDumpstateToken {}; + +struct DumpstateInfo { + public: + Dumpstate* ds = nullptr; + int32_t calling_uid = -1; + std::string calling_package; +}; + +static binder::Status exception(uint32_t code, const std::string& msg) { + MYLOGE("%s (%d) ", msg.c_str(), code); + return binder::Status::fromExceptionCode(code, String8(msg.c_str())); } -DumpstateService::DumpstateService() : ds_(Dumpstate::GetInstance()) { +// Creates a bugreport and exits, thus preserving the oneshot nature of the service. +// Note: takes ownership of data. +[[noreturn]] static void* dumpstate_thread_main(void* data) { + std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data)); + ds_info->ds->Run(ds_info->calling_uid, ds_info->calling_package); + MYLOGD("Finished taking a bugreport. Exiting.\n"); + exit(0); +} + +[[noreturn]] static void signalErrorAndExit(sp<IDumpstateListener> listener, int error_code) { + listener->onError(error_code); + exit(0); +} + +class DumpstateToken : public BnDumpstateToken {}; + +} // namespace + +DumpstateService::DumpstateService() : ds_(nullptr) { } char const* DumpstateService::getServiceName() { @@ -50,6 +81,8 @@ return android::OK; } +// Note: this method is part of the old flow and is not expected to be used in combination +// with startBugreport. binder::Status DumpstateService::setListener(const std::string& name, const sp<IDumpstateListener>& listener, bool getSectionDetails, @@ -64,43 +97,129 @@ return binder::Status::ok(); } std::lock_guard<std::mutex> lock(lock_); - if (ds_.listener_ != nullptr) { - MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_.listener_name_.c_str()); + if (ds_ == nullptr) { + ds_ = &(Dumpstate::GetInstance()); + } + if (ds_->listener_ != nullptr) { + MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_->listener_name_.c_str()); return binder::Status::ok(); } - ds_.listener_name_ = name; - ds_.listener_ = listener; - ds_.report_section_ = getSectionDetails; + ds_->listener_name_ = name; + ds_->listener_ = listener; + ds_->report_section_ = getSectionDetails; *returned_token = new DumpstateToken(); return binder::Status::ok(); } +binder::Status DumpstateService::startBugreport(int32_t calling_uid, + const std::string& calling_package, + const android::base::unique_fd& bugreport_fd, + const android::base::unique_fd& screenshot_fd, + int bugreport_mode, + const sp<IDumpstateListener>& listener) { + MYLOGI("startBugreport() with mode: %d\n", bugreport_mode); + + // This is the bugreporting API flow, so ensure there is only one bugreport in progress at a + // time. + std::lock_guard<std::mutex> lock(lock_); + if (ds_ != nullptr) { + MYLOGE("Error! There is already a bugreport in progress. Returning."); + if (listener != nullptr) { + listener->onError(IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS); + } + return exception(binder::Status::EX_SERVICE_SPECIFIC, + "There is already a bugreport in progress"); + } + + // From here on, all conditions that indicate we are done with this incoming request should + // result in exiting the service to free it up for next invocation. + if (listener == nullptr) { + MYLOGE("Invalid input: no listener"); + exit(0); + } + + if (bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_FULL && + bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE && + bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_REMOTE && + bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WEAR && + bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_TELEPHONY && + bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WIFI && + bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_DEFAULT) { + MYLOGE("Invalid input: bad bugreport mode: %d", bugreport_mode); + signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); + } + + if (bugreport_fd.get() == -1 || screenshot_fd.get() == -1) { + // TODO(b/111441001): screenshot fd should be optional + MYLOGE("Invalid filedescriptor"); + signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); + } + + std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>(); + options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd, + screenshot_fd); + + ds_ = &(Dumpstate::GetInstance()); + ds_->SetOptions(std::move(options)); + ds_->listener_ = listener; + + DumpstateInfo* ds_info = new DumpstateInfo(); + ds_info->ds = ds_; + ds_info->calling_uid = calling_uid; + ds_info->calling_package = calling_package; + + pthread_t thread; + status_t err = pthread_create(&thread, nullptr, dumpstate_thread_main, ds_info); + if (err != 0) { + delete ds_info; + ds_info = nullptr; + MYLOGE("Could not create a thread"); + signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR); + } + return binder::Status::ok(); +} + +binder::Status DumpstateService::cancelBugreport() { + // This is a no-op since the cancellation is done from java side via setting sys properties. + // See BugreportManagerServiceImpl. + // TODO(b/111441001): maybe make native and java sides use different binder interface + // to avoid these annoyances. + return binder::Status::ok(); +} + status_t DumpstateService::dump(int fd, const Vector<String16>&) { - dprintf(fd, "id: %d\n", ds_.id_); - dprintf(fd, "pid: %d\n", ds_.pid_); - dprintf(fd, "update_progress: %s\n", ds_.update_progress_ ? "true" : "false"); - dprintf(fd, "update_progress_threshold: %d\n", ds_.update_progress_threshold_); - dprintf(fd, "last_updated_progress: %d\n", ds_.last_updated_progress_); + if (ds_ == nullptr) { + dprintf(fd, "Bugreport not in progress yet"); + return NO_ERROR; + } + std::string destination = ds_->options_->bugreport_fd.get() != -1 + ? StringPrintf("[fd:%d]", ds_->options_->bugreport_fd.get()) + : ds_->bugreport_internal_dir_.c_str(); + dprintf(fd, "id: %d\n", ds_->id_); + dprintf(fd, "pid: %d\n", ds_->pid_); + dprintf(fd, "update_progress: %s\n", ds_->options_->do_progress_updates ? "true" : "false"); + dprintf(fd, "update_progress_threshold: %d\n", ds_->update_progress_threshold_); + dprintf(fd, "last_updated_progress: %d\n", ds_->last_updated_progress_); dprintf(fd, "progress:\n"); - ds_.progress_->Dump(fd, " "); - dprintf(fd, "args: %s\n", ds_.args_.c_str()); - dprintf(fd, "extra_options: %s\n", ds_.extra_options_.c_str()); - dprintf(fd, "version: %s\n", ds_.version_.c_str()); - dprintf(fd, "bugreport_dir: %s\n", ds_.bugreport_dir_.c_str()); - dprintf(fd, "screenshot_path: %s\n", ds_.screenshot_path_.c_str()); - dprintf(fd, "log_path: %s\n", ds_.log_path_.c_str()); - dprintf(fd, "tmp_path: %s\n", ds_.tmp_path_.c_str()); - dprintf(fd, "path: %s\n", ds_.path_.c_str()); - dprintf(fd, "extra_options: %s\n", ds_.extra_options_.c_str()); - dprintf(fd, "base_name: %s\n", ds_.base_name_.c_str()); - dprintf(fd, "name: %s\n", ds_.name_.c_str()); - dprintf(fd, "now: %ld\n", ds_.now_); - dprintf(fd, "is_zipping: %s\n", ds_.IsZipping() ? "true" : "false"); - dprintf(fd, "listener: %s\n", ds_.listener_name_.c_str()); - dprintf(fd, "notification title: %s\n", ds_.notification_title.c_str()); - dprintf(fd, "notification description: %s\n", ds_.notification_description.c_str()); + ds_->progress_->Dump(fd, " "); + dprintf(fd, "args: %s\n", ds_->options_->args.c_str()); + dprintf(fd, "extra_options: %s\n", ds_->options_->extra_options.c_str()); + dprintf(fd, "version: %s\n", ds_->version_.c_str()); + dprintf(fd, "bugreport_dir: %s\n", destination.c_str()); + dprintf(fd, "screenshot_path: %s\n", ds_->screenshot_path_.c_str()); + dprintf(fd, "log_path: %s\n", ds_->log_path_.c_str()); + dprintf(fd, "tmp_path: %s\n", ds_->tmp_path_.c_str()); + dprintf(fd, "path: %s\n", ds_->path_.c_str()); + dprintf(fd, "extra_options: %s\n", ds_->options_->extra_options.c_str()); + dprintf(fd, "base_name: %s\n", ds_->base_name_.c_str()); + dprintf(fd, "name: %s\n", ds_->name_.c_str()); + dprintf(fd, "now: %ld\n", ds_->now_); + dprintf(fd, "is_zipping: %s\n", ds_->IsZipping() ? "true" : "false"); + dprintf(fd, "listener: %s\n", ds_->listener_name_.c_str()); + dprintf(fd, "notification title: %s\n", ds_->options_->notification_title.c_str()); + dprintf(fd, "notification description: %s\n", ds_->options_->notification_description.c_str()); return NO_ERROR; }
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h index 7bca24a..68eda47 100644 --- a/cmds/dumpstate/DumpstateService.h +++ b/cmds/dumpstate/DumpstateService.h
@@ -20,6 +20,7 @@ #include <mutex> #include <vector> +#include <android-base/unique_fd.h> #include <binder/BinderService.h> #include "android/os/BnDumpstate.h" @@ -41,8 +42,20 @@ bool getSectionDetails, sp<IDumpstateToken>* returned_token) override; + binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package, + const android::base::unique_fd& bugreport_fd, + const android::base::unique_fd& screenshot_fd, int bugreport_mode, + const sp<IDumpstateListener>& listener) override; + + // No-op + binder::Status cancelBugreport(); + private: - Dumpstate& ds_; + // Dumpstate object which contains all the bugreporting logic. + // Note that dumpstate is a oneshot service, so this object is meant to be used at most for + // one bugreport. + // This service does not own this object. + Dumpstate* ds_; std::mutex lock_; };
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp index 85eb464..97c8ae2 100644 --- a/cmds/dumpstate/DumpstateUtil.cpp +++ b/cmds/dumpstate/DumpstateUtil.cpp
@@ -56,11 +56,11 @@ timespec ts; ts.tv_sec = MSEC_TO_SEC(timeout_ms); ts.tv_nsec = (timeout_ms % 1000) * 1000000; - int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts)); + int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, nullptr, &ts)); int saved_errno = errno; // Set the signals back the way they were. - if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) { + if (sigprocmask(SIG_SETMASK, &old_mask, nullptr) == -1) { printf("*** sigprocmask failed: %s\n", strerror(errno)); if (ret == 0) { return false; @@ -101,13 +101,16 @@ } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() { - values.account_mode_ = SU_ROOT; + if (!PropertiesHelper::IsUnroot()) { + values.account_mode_ = SU_ROOT; + } return *this; } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRootIfAvailable() { - if (!PropertiesHelper::IsUserBuild()) - values.account_mode_ = SU_ROOT; + if (!PropertiesHelper::IsUserBuild()) { + return AsRoot(); + } return *this; } @@ -176,6 +179,7 @@ std::string PropertiesHelper::build_type_ = ""; int PropertiesHelper::dry_run_ = -1; +int PropertiesHelper::unroot_ = -1; bool PropertiesHelper::IsUserBuild() { if (build_type_.empty()) { @@ -191,6 +195,13 @@ return dry_run_ == 1; } +bool PropertiesHelper::IsUnroot() { + if (unroot_ == -1) { + unroot_ = android::base::GetBoolProperty("dumpstate.unroot", false) ? 1 : 0; + } + return unroot_ == 1; +} + int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) { android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC))); if (fd.get() < 0) { @@ -310,7 +321,7 @@ struct sigaction sigact; memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sigact, NULL); + sigaction(SIGPIPE, &sigact, nullptr); execvp(path, (char**)args.data()); // execvp's result will be handled after waitpid_with_timeout() below, but
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h index 8342099..d75b08c 100644 --- a/cmds/dumpstate/DumpstateUtil.h +++ b/cmds/dumpstate/DumpstateUtil.h
@@ -76,7 +76,7 @@ private: class CommandOptionsValues { private: - CommandOptionsValues(int64_t timeout_ms); + explicit CommandOptionsValues(int64_t timeout_ms); int64_t timeout_ms_; bool always_; @@ -88,7 +88,7 @@ friend class CommandOptionsBuilder; }; - CommandOptions(const CommandOptionsValues& values); + explicit CommandOptions(const CommandOptionsValues& values); const CommandOptionsValues values; @@ -97,9 +97,16 @@ public: /* Sets the command to always run, even on `dry-run` mode. */ CommandOptionsBuilder& Always(); - /* Sets the command's PrivilegeMode as `SU_ROOT` */ + /* + * Sets the command's PrivilegeMode as `SU_ROOT` unless overridden by system property + * 'dumpstate.unroot'. + */ CommandOptionsBuilder& AsRoot(); - /* If !IsUserBuild(), sets the command's PrivilegeMode as `SU_ROOT` */ + /* + * Runs AsRoot() on userdebug builds. No-op on user builds since 'su' is + * not available. This is used for commands that return some useful information even + * when run as shell. + */ CommandOptionsBuilder& AsRootIfAvailable(); /* Sets the command's PrivilegeMode as `DROP_ROOT` */ CommandOptionsBuilder& DropRoot(); @@ -112,7 +119,7 @@ CommandOptions Build(); private: - CommandOptionsBuilder(int64_t timeout_ms); + explicit CommandOptionsBuilder(int64_t timeout_ms); CommandOptionsValues values; friend class CommandOptions; }; @@ -162,9 +169,17 @@ */ static bool IsDryRun(); + /** + * Checks whether root availability should be overridden. + * + * Useful to verify how dumpstate would work in a device with an user build. + */ + static bool IsUnroot(); + private: static std::string build_type_; static int dry_run_; + static int unroot_; }; /*
diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS new file mode 100644 index 0000000..1ba7cff --- /dev/null +++ b/cmds/dumpstate/OWNERS
@@ -0,0 +1,6 @@ +set noparent + +felipeal@google.com +nandana@google.com +jsharkey@android.com +enh@google.com
diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md index 0302ea5..c818c05 100644 --- a/cmds/dumpstate/README.md +++ b/cmds/dumpstate/README.md
@@ -14,7 +14,8 @@ mmm -j frameworks/native/cmds/dumpstate ``` -If you're working on device-specific code, you might need to build them as well. Example: +If you're working on device-specific code, you might need to build them as well. +Example: ``` mmm -j frameworks/native/cmds/dumpstate device/acme/secret_device/dumpstate/ hardware/interfaces/dumpstate @@ -23,27 +24,42 @@ ## To build, deploy, and take a bugreport ``` -mmm -j frameworks/native/cmds/dumpstate && adb push ${OUT}/system/bin/dumpstate system/bin && adb shell am bug-report +mmm -j frameworks/native/cmds/dumpstate && adb push ${OUT}/system/bin/dumpstate system/bin && adb push ${OUT}/system/lib64/*dumpstate*.so /system/lib64/ && adb shell am bug-report ``` +Make sure that the device is remounted before running the above command. * If +you're working with `userdebug` variant, you may need to run the following to +remount your device: + +``` + adb root && adb remount -R && adb wait-for-device && adb root && adb remount +``` + +* If you're working with `eng` variant, you may need to run the following to + remount your device: + + ``` + adb root && adb remount + ``` + ## To build, deploy, and run unit tests -First create `/data/nativetest`: +First create `/data/nativetest64`: ``` -adb shell mkdir /data/nativetest +adb shell mkdir /data/nativetest64 ``` Then run: ``` -mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest/dumpstate_test* /data/nativetest && adb shell /data/nativetest/dumpstate_test/dumpstate_test +mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_* /data/nativetest64 && adb shell /data/nativetest64/dumpstate_test/dumpstate_test ``` And to run just one test (for example, `DumpstateTest.RunCommandNoArgs`): ``` -mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest/dumpstate_test* /data/nativetest && adb shell /data/nativetest/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandNoArgs +mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest64/dumpstate_test* /data/nativetest64 && adb shell /data/nativetest64/dumpstate_test/dumpstate_test --gtest_filter=DumpstateTest.RunCommandNoArgs ``` ## To take quick bugreports @@ -52,6 +68,12 @@ adb shell setprop dumpstate.dry_run true ``` +## To emulate a device with user build + +``` +adb shell setprop dumpstate.unroot true +``` + ## To change the `dumpstate` version ``` @@ -64,7 +86,6 @@ adb shell setprop dumpstate.version split-dumpsys && adb shell dumpstate -v ``` - Then to restore the default version: ``` @@ -73,8 +94,9 @@ ## Code style and formatting -Use the style defined at the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) -and make sure to run the following command prior to `repo upload`: +Use the style defined at the +[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) and +make sure to run the following command prior to `repo upload`: ``` git clang-format --style=file HEAD~
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl index 9b11b96..347856d 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -24,7 +24,7 @@ * {@hide} */ interface IDumpstate { - + // TODO: remove method once startBugReport is used by Shell. /* * Sets the listener for this dumpstate progress. * @@ -35,4 +35,54 @@ */ IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener, boolean getSectionDetails); + + // NOTE: If you add to or change these modes, please also change the corresponding enums + // in system server, in BugreportParams.java. + + // These modes encapsulate a set of run time options for generating bugreports. + // Takes a bugreport without user interference. + const int BUGREPORT_MODE_FULL = 0; + + // Interactive bugreport, i.e. triggered by the user. + const int BUGREPORT_MODE_INTERACTIVE = 1; + + // Remote bugreport triggered by DevicePolicyManager, for e.g. + const int BUGREPORT_MODE_REMOTE = 2; + + // Bugreport triggered on a wear device. + const int BUGREPORT_MODE_WEAR = 3; + + // Bugreport limited to only telephony info. + const int BUGREPORT_MODE_TELEPHONY = 4; + + // Bugreport limited to only wifi info. + const int BUGREPORT_MODE_WIFI = 5; + + // Default mode. + const int BUGREPORT_MODE_DEFAULT = 6; + + /* + * Starts a bugreport in the background. + * + *<p>Shows the user a dialog to get consent for sharing the bugreport with the calling + * application. If they deny {@link IDumpstateListener#onError} will be called. If they + * consent and bugreport generation is successful artifacts will be copied to the given fds and + * {@link IDumpstateListener#onFinished} will be called. If there + * are errors in bugreport generation {@link IDumpstateListener#onError} will be called. + * + * @param callingUid UID of the original application that requested the report. + * @param callingPackage package of the original application that requested the report. + * @param bugreportFd the file to which the zipped bugreport should be written + * @param screenshotFd the file to which screenshot should be written; optional + * @param bugreportMode the mode that specifies other run time options; must be one of above + * @param listener callback for updates; optional + */ + void startBugreport(int callingUid, @utf8InCpp String callingPackage, + FileDescriptor bugreportFd, FileDescriptor screenshotFd, + int bugreportMode, IDumpstateListener listener); + + /* + * Cancels the bugreport currently in progress. + */ + void cancelBugreport(); }
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl index 030d69d..ea1e467 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -19,21 +19,63 @@ /** * Listener for dumpstate events. * + * <p>When bugreport creation is complete one of {@code onError} or {@code onFinished} is called. + * + * <p>These methods are synchronous by design in order to make dumpstate's lifecycle simpler + * to handle. + * * {@hide} */ interface IDumpstateListener { + /** + * Called when there is a progress update. + * + * @param progress the progress in [0, 100] + */ + void onProgress(int progress); + + // NOTE: If you add to or change these error codes, please also change the corresponding enums + // in system server, in BugreportManager.java. + + /* Options specified are invalid or incompatible */ + const int BUGREPORT_ERROR_INVALID_INPUT = 1; + + /* Bugreport encountered a runtime error */ + const int BUGREPORT_ERROR_RUNTIME_ERROR = 2; + + /* User denied consent to share the bugreport with the specified app */ + const int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3; + + /* The request to get user consent timed out */ + const int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; + + /* There is currently a bugreport running. The caller should try again later. */ + const int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; + + /** + * Called on an error condition with one of the error codes listed above. + */ + void onError(int errorCode); + + /** + * Called when taking bugreport finishes successfully. + */ + void onFinished(); + + // TODO(b/111441001): Remove old methods when not used anymore. void onProgressUpdated(int progress); void onMaxProgressUpdated(int maxProgress); /** - * Called after every section is complete. - * @param name section name - * @param status values from status_t - * {@code OK} section completed successfully - * {@code TIMEOUT} dump timed out - * {@code != OK} error - * @param size size in bytes, may be invalid if status != OK - * @param durationMs duration in ms - */ + * Called after every section is complete. + * + * @param name section name + * @param status values from status_t + * {@code OK} section completed successfully + * {@code TIMEOUT} dump timed out + * {@code != OK} error + * @param size size in bytes, may be invalid if status != OK + * @param durationMs duration in ms + */ void onSectionComplete(@utf8InCpp String name, int status, int size, int durationMs); }
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 1d951be..4ac7b68 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp
@@ -19,6 +19,7 @@ #include <dirent.h> #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <libgen.h> #include <limits.h> #include <stdbool.h> @@ -34,6 +35,7 @@ #include <unistd.h> #include <chrono> +#include <fstream> #include <functional> #include <future> #include <memory> @@ -49,11 +51,16 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> +#include <android/content/pm/IPackageManagerNative.h> #include <android/hardware/dumpstate/1.0/IDumpstateDevice.h> #include <android/hidl/manager/1.0/IServiceManager.h> +#include <android/os/IIncidentCompanion.h> +#include <binder/IServiceManager.h> #include <cutils/native_handle.h> #include <cutils/properties.h> +#include <debuggerd/client.h> #include <dumpsys.h> +#include <dumputils/dump_utils.h> #include <hidl/ServiceManagement.h> #include <openssl/sha.h> #include <private/android_filesystem_config.h> @@ -82,15 +89,20 @@ using android::TIMED_OUT; using android::UNKNOWN_ERROR; using android::Vector; +using android::base::StringPrintf; +using android::os::IDumpstateListener; using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; using android::os::dumpstate::DumpstateSectionReporter; using android::os::dumpstate::GetPidByName; using android::os::dumpstate::PropertiesHelper; +typedef Dumpstate::ConsentCallback::ConsentResult UserConsentResult; + /* read before root is shed */ static char cmdline_buf[16384] = "(unknown)"; -static const char *dump_traces_path = NULL; +static const char *dump_traces_path = nullptr; +static const uint64_t USER_CONSENT_TIMEOUT_MS = 30 * 1000; // TODO: variables and functions below should be part of dumpstate object @@ -101,13 +113,13 @@ #define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0" #define BLK_DEV_SYS_DIR "/sys/block" -#define RAFT_DIR "/data/misc/raft" #define RECOVERY_DIR "/cache/recovery" #define RECOVERY_DATA_DIR "/data/misc/recovery" #define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log" #define LOGPERSIST_DATA_DIR "/data/misc/logd" #define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur" #define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref" +#define XFRM_STAT_PROC_FILE "/proc/net/xfrm_stat" #define WLUTIL "/vendor/xbin/wlutil" #define WMTRACE_DATA_DIR "/data/misc/wmtrace" @@ -122,6 +134,107 @@ // TODO: temporary variables and functions used during C++ refactoring static Dumpstate& ds = Dumpstate::GetInstance(); + +#define RETURN_IF_USER_DENIED_CONSENT() \ + if (ds.IsUserConsentDenied()) { \ + MYLOGE("Returning early as user denied consent to share bugreport with calling app."); \ + return Dumpstate::RunStatus::USER_CONSENT_DENIED; \ + } + +// Runs func_ptr, but checks user consent before and after running it. Returns USER_CONSENT_DENIED +// if consent is found to be denied. +#define RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(func_ptr, ...) \ + RETURN_IF_USER_DENIED_CONSENT(); \ + func_ptr(__VA_ARGS__); \ + RETURN_IF_USER_DENIED_CONSENT(); + +namespace android { +namespace os { +namespace { + +static int Open(std::string path, int flags, mode_t mode = 0) { + int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)); + if (fd == -1) { + MYLOGE("open(%s, %s)\n", path.c_str(), strerror(errno)); + } + return fd; +} + + +static int OpenForRead(std::string path) { + return Open(path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); +} + +bool CopyFile(int in_fd, int out_fd) { + char buf[4096]; + ssize_t byte_count; + while ((byte_count = TEMP_FAILURE_RETRY(read(in_fd, buf, sizeof(buf)))) > 0) { + if (!android::base::WriteFully(out_fd, buf, byte_count)) { + return false; + } + } + return (byte_count != -1); +} + +static bool CopyFileToFd(const std::string& input_file, int out_fd) { + MYLOGD("Going to copy file (%s) to %d\n", input_file.c_str(), out_fd); + + // Obtain a handle to the source file. + android::base::unique_fd in_fd(OpenForRead(input_file)); + if (out_fd != -1 && in_fd.get() != -1) { + if (CopyFile(in_fd.get(), out_fd)) { + return true; + } + MYLOGE("Failed to copy file: %s\n", strerror(errno)); + } + return false; +} + +static bool UnlinkAndLogOnError(const std::string& file) { + if (unlink(file.c_str())) { + MYLOGE("Failed to unlink file (%s): %s\n", file.c_str(), strerror(errno)); + return false; + } + return true; +} + +static bool IsFileEmpty(const std::string& file_path) { + std::ifstream file(file_path, std::ios::binary | std::ios::ate); + if(file.bad()) { + MYLOGE("Cannot open file: %s\n", file_path.c_str()); + return true; + } + return file.tellg() <= 0; +} + +int64_t GetModuleMetadataVersion() { + auto binder = defaultServiceManager()->getService(android::String16("package_native")); + if (binder == nullptr) { + MYLOGE("Failed to retrieve package_native service"); + return 0L; + } + auto package_service = android::interface_cast<content::pm::IPackageManagerNative>(binder); + std::string package_name; + auto status = package_service->getModuleMetadataPackageName(&package_name); + if (!status.isOk()) { + MYLOGE("Failed to retrieve module metadata package name: %s", status.toString8().c_str()); + return 0L; + } + MYLOGD("Module metadata package name: %s", package_name.c_str()); + int64_t version_code; + status = package_service->getVersionCodeForPackage(android::String16(package_name.c_str()), + &version_code); + if (!status.isOk()) { + MYLOGE("Failed to retrieve module metadata version: %s", status.toString8().c_str()); + return 0L; + } + return version_code; +} + +} // namespace +} // namespace os +} // namespace android + static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand, const CommandOptions& options = CommandOptions::DEFAULT) { return ds.RunCommand(title, fullCommand, options); @@ -138,8 +251,6 @@ // Relative directory (inside the zip) for all files copied as-is into the bugreport. static const std::string ZIP_ROOT_DIR = "FS"; -// Must be hardcoded because dumpstate HAL implementation need SELinux access to it -static const std::string kDumpstateBoardPath = "/bugreports/"; static const std::string kProtoPath = "proto/"; static const std::string kProtoExt = ".proto"; static const std::string kDumpstateBoardFiles[] = { @@ -295,7 +406,7 @@ char path[PATH_MAX]; d = opendir(driverpath); - if (d == NULL) { + if (d == nullptr) { return; } @@ -333,7 +444,7 @@ } // find anrd's pid if it is running. - pid = GetPidByName("/system/xbin/anrd"); + pid = GetPidByName("/system/bin/anrd"); if (pid > 0) { if (stat(trace_path, &st) == 0) { @@ -402,9 +513,7 @@ if (!ds.AddZipEntry("anrd_trace.txt", path)) { MYLOGE("Unable to add anrd_trace file %s to zip file\n", path); } else { - if (remove(path)) { - MYLOGE("Error removing anrd_trace file %s: %s", path, strerror(errno)); - } + android::os::UnlinkAndLogOnError(path); return true; } } else { @@ -414,81 +523,6 @@ return false; } -static void dump_systrace() { - if (!ds.IsZipping()) { - MYLOGD("Not dumping systrace because it's not a zipped bugreport\n"); - return; - } - std::string systrace_path = ds.GetPath("-systrace.txt"); - if (systrace_path.empty()) { - MYLOGE("Not dumping systrace because path is empty\n"); - return; - } - const char* path = "/sys/kernel/debug/tracing/tracing_on"; - long int is_tracing; - if (read_file_as_long(path, &is_tracing)) { - return; // error already logged - } - if (is_tracing <= 0) { - MYLOGD("Skipping systrace because '%s' content is '%ld'\n", path, is_tracing); - return; - } - - MYLOGD("Running '/system/bin/atrace --async_dump -o %s', which can take several minutes", - systrace_path.c_str()); - if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--async_dump", "-o", systrace_path}, - CommandOptions::WithTimeout(120).Build())) { - MYLOGE("systrace timed out, its zip entry will be incomplete\n"); - // TODO: RunCommand tries to kill the process, but atrace doesn't die - // peacefully; ideally, we should call strace to stop itself, but there is no such option - // yet (just a --async_stop, which stops and dump - // if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--kill"})) { - // MYLOGE("could not stop systrace "); - // } - } - if (!ds.AddZipEntry("systrace.txt", systrace_path)) { - MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str()); - } else { - if (remove(systrace_path.c_str())) { - MYLOGE("Error removing systrace file %s: %s", systrace_path.c_str(), strerror(errno)); - } - } -} - -static void dump_raft() { - if (PropertiesHelper::IsUserBuild()) { - return; - } - - std::string raft_path = ds.GetPath("-raft_log.txt"); - if (raft_path.empty()) { - MYLOGD("raft_path is empty\n"); - return; - } - - struct stat s; - if (stat(RAFT_DIR, &s) != 0 || !S_ISDIR(s.st_mode)) { - MYLOGD("%s does not exist or is not a directory\n", RAFT_DIR); - return; - } - - CommandOptions options = CommandOptions::WithTimeout(600).Build(); - if (!ds.IsZipping()) { - // Write compressed and encoded raft logs to stdout if it's not a zipped bugreport. - RunCommand("RAFT LOGS", {"logcompressor", "-r", RAFT_DIR}, options); - return; - } - - RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_path}, options); - if (!ds.AddZipEntry("raft_log.txt", raft_path)) { - MYLOGE("Unable to add raft log %s to zip file\n", raft_path.c_str()); - } else { - if (remove(raft_path.c_str())) { - MYLOGE("Error removing raft file %s: %s\n", raft_path.c_str(), strerror(errno)); - } - } -} - static bool skip_not_stat(const char *path) { static const char stat[] = "/stat"; size_t len = strlen(path); @@ -587,9 +621,9 @@ static int dump_stat_from_fd(const char *title __unused, const char *path, int fd) { unsigned long long fields[__STAT_NUMBER_FIELD]; bool z; - char *cp, *buffer = NULL; + char *cp, *buffer = nullptr; size_t i = 0; - FILE *fp = fdopen(fd, "rb"); + FILE *fp = fdopen(dup(fd), "rb"); getline(&buffer, &i, fp); fclose(fp); if (!buffer) { @@ -686,6 +720,32 @@ return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS; } +Dumpstate::ConsentCallback::ConsentCallback() : result_(UNAVAILABLE), start_time_(Nanotime()) { +} + +android::binder::Status Dumpstate::ConsentCallback::onReportApproved() { + std::lock_guard<std::mutex> lock(lock_); + result_ = APPROVED; + MYLOGD("User approved consent to share bugreport\n"); + return android::binder::Status::ok(); +} + +android::binder::Status Dumpstate::ConsentCallback::onReportDenied() { + std::lock_guard<std::mutex> lock(lock_); + result_ = DENIED; + MYLOGW("User denied consent to share bugreport\n"); + return android::binder::Status::ok(); +} + +UserConsentResult Dumpstate::ConsentCallback::getResult() { + std::lock_guard<std::mutex> lock(lock_); + return result_; +} + +uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const { + return Nanotime() - start_time_; +} + void Dumpstate::PrintHeader() const { std::string build, fingerprint, radio, bootloader, network; char date[80]; @@ -708,6 +768,10 @@ printf("Bootloader: %s\n", bootloader.c_str()); printf("Radio: %s\n", radio.c_str()); printf("Network: %s\n", network.c_str()); + int64_t module_metadata_version = android::os::GetModuleMetadataVersion(); + if (module_metadata_version != 0) { + printf("Module Metadata version: %" PRId64 "\n", module_metadata_version); + } printf("Kernel: "); DumpFileToFd(STDOUT_FILENO, "", "/proc/version"); @@ -717,7 +781,7 @@ CommandOptions::WithTimeout(1).Always().Build()); printf("Bugreport format version: %s\n", version_.c_str()); printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_, - PropertiesHelper::IsDryRun(), args_.c_str(), extra_options_.c_str()); + PropertiesHelper::IsDryRun(), options_->args.c_str(), options_->extra_options.c_str()); printf("\n"); } @@ -758,6 +822,17 @@ ZipWriter::ErrorCodeString(err)); return UNKNOWN_ERROR; } + bool finished_entry = false; + auto finish_entry = [this, &finished_entry] { + if (!finished_entry) { + // This should only be called when we're going to return an earlier error, + // which would've been logged. This may imply the file is already corrupt + // and any further logging from FinishEntry is more likely to mislead than + // not. + this->zip_writer_->FinishEntry(); + } + }; + auto scope_guard = android::base::make_scope_guard(finish_entry); auto start = std::chrono::steady_clock::now(); auto end = start + timeout; struct pollfd pfd = {fd, POLLIN}; @@ -774,11 +849,11 @@ int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms())); if (rc < 0) { - MYLOGE("Error in poll while adding from fd to zip entry %s:%s", entry_name.c_str(), - strerror(errno)); + MYLOGE("Error in poll while adding from fd to zip entry %s:%s\n", + entry_name.c_str(), strerror(errno)); return -errno; } else if (rc == 0) { - MYLOGE("Timed out adding from fd to zip entry %s:%s Timeout:%lldms", + MYLOGE("Timed out adding from fd to zip entry %s:%s Timeout:%lldms\n", entry_name.c_str(), strerror(errno), timeout.count()); return TIMED_OUT; } @@ -799,6 +874,7 @@ } err = zip_writer_->FinishEntry(); + finished_entry = true; if (err != 0) { MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err)); return UNKNOWN_ERROR; @@ -926,53 +1002,6 @@ RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"}); } -static void AddGlobalAnrTraceFile(const bool add_to_zip, const std::string& anr_traces_file, - const std::string& anr_traces_dir) { - std::string dump_traces_dir; - - if (dump_traces_path != nullptr) { - if (add_to_zip) { - dump_traces_dir = dirname(dump_traces_path); - MYLOGD("Adding ANR traces (directory %s) to the zip file\n", dump_traces_dir.c_str()); - ds.AddDir(dump_traces_dir, true); - } else { - MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n", - dump_traces_path); - ds.DumpFile("VM TRACES JUST NOW", dump_traces_path); - } - } - - - // Make sure directory is not added twice. - // TODO: this is an overzealous check because it's relying on dump_traces_path - which is - // generated by dump_traces() - and anr_traces_path - which is retrieved from a system - // property - but in reality they're the same path (although the former could be nullptr). - // Anyways, once dump_traces() is refactored as a private Dumpstate function, this logic should - // be revisited. - bool already_dumped = anr_traces_dir == dump_traces_dir; - - MYLOGD("AddGlobalAnrTraceFile(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n", - dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped); - - android::base::unique_fd fd(TEMP_FAILURE_RETRY( - open(anr_traces_file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK))); - if (fd.get() < 0) { - printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_file.c_str(), strerror(errno)); - } else { - if (add_to_zip) { - if (!already_dumped) { - MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n", - anr_traces_dir.c_str()); - ds.AddDir(anr_traces_dir, true); - } - } else { - MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n", - anr_traces_file.c_str()); - dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_file.c_str(), fd.get()); - } - } -} - static void AddAnrTraceDir(const bool add_to_zip, const std::string& anr_traces_dir) { MYLOGD("AddAnrTraceDir(): dump_traces_file=%s, anr_traces_dir=%s\n", dump_traces_path, anr_traces_dir.c_str()); @@ -1015,50 +1044,24 @@ static void AddAnrTraceFiles() { const bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR; - std::string anr_traces_file; - std::string anr_traces_dir; - bool is_global_trace_file = true; + std::string anr_traces_dir = "/data/anr"; - // First check whether the stack-trace-dir property is set. When it's set, - // each ANR trace will be written to a separate file and not to a global - // stack trace file. - anr_traces_dir = android::base::GetProperty("dalvik.vm.stack-trace-dir", ""); - if (anr_traces_dir.empty()) { - anr_traces_file = android::base::GetProperty("dalvik.vm.stack-trace-file", ""); - if (!anr_traces_file.empty()) { - anr_traces_dir = dirname(anr_traces_file.c_str()); - } - } else { - is_global_trace_file = false; - } + AddAnrTraceDir(add_to_zip, anr_traces_dir); - // We have neither configured a global trace file nor a trace directory, - // there will be nothing to dump. - if (anr_traces_file.empty() && anr_traces_dir.empty()) { - printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n"); - return; - } + RunCommand("ANR FILES", {"ls", "-lt", ANR_DIR}); - if (is_global_trace_file) { - AddGlobalAnrTraceFile(add_to_zip, anr_traces_file, anr_traces_dir); - } else { - AddAnrTraceDir(add_to_zip, anr_traces_dir); - } - - /* slow traces for slow operations */ + // Slow traces for slow operations. struct stat st; - if (!anr_traces_dir.empty()) { - int i = 0; - while (true) { - const std::string slow_trace_path = - anr_traces_dir + android::base::StringPrintf("slow%02d.txt", i); - if (stat(slow_trace_path.c_str(), &st)) { - // No traces file at this index, done with the files. - break; - } - ds.DumpFile("VM TRACES WHEN SLOW", slow_trace_path.c_str()); - i++; + int i = 0; + while (true) { + const std::string slow_trace_path = + anr_traces_dir + android::base::StringPrintf("slow%02d.txt", i); + if (stat(slow_trace_path.c_str(), &st)) { + // No traces file at this index, done with the files. + break; } + ds.DumpFile("VM TRACES WHEN SLOW", slow_trace_path.c_str()); + i++; } } @@ -1105,9 +1108,9 @@ RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"}); } -static void RunDumpsysTextByPriority(const std::string& title, int priority, - std::chrono::milliseconds timeout, - std::chrono::milliseconds service_timeout) { +static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, int priority, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { auto start = std::chrono::steady_clock::now(); sp<android::IServiceManager> sm = defaultServiceManager(); Dumpsys dumpsys(sm.get()); @@ -1115,6 +1118,7 @@ Dumpsys::setServiceArgs(args, /* asProto = */ false, priority); Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ false); for (const String16& service : services) { + RETURN_IF_USER_DENIED_CONSENT(); std::string path(title); path.append(" - ").append(String8(service).c_str()); DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); @@ -1140,6 +1144,7 @@ break; } } + return Dumpstate::RunStatus::OK; } static void RunDumpsysText(const std::string& title, int priority, @@ -1152,24 +1157,27 @@ } /* Dump all services registered with Normal or Default priority. */ -static void RunDumpsysTextNormalPriority(const std::string& title, - std::chrono::milliseconds timeout, - std::chrono::milliseconds service_timeout) { +static Dumpstate::RunStatus RunDumpsysTextNormalPriority(const std::string& title, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { DurationReporter duration_reporter(title); dprintf(STDOUT_FILENO, "------ %s (/system/bin/dumpsys) ------\n", title.c_str()); fsync(STDOUT_FILENO); RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, timeout, service_timeout); - RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT, timeout, - service_timeout); + + RETURN_IF_USER_DENIED_CONSENT(); + + return RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT, timeout, + service_timeout); } -static void RunDumpsysProto(const std::string& title, int priority, - std::chrono::milliseconds timeout, - std::chrono::milliseconds service_timeout) { +static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priority, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { if (!ds.IsZipping()) { MYLOGD("Not dumping %s because it's not a zipped bugreport\n", title.c_str()); - return; + return Dumpstate::RunStatus::OK; } sp<android::IServiceManager> sm = defaultServiceManager(); Dumpsys dumpsys(sm.get()); @@ -1180,6 +1188,7 @@ auto start = std::chrono::steady_clock::now(); Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ true); for (const String16& service : services) { + RETURN_IF_USER_DENIED_CONSENT(); std::string path(kProtoPath); path.append(String8(service).c_str()); if (priority == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) { @@ -1208,35 +1217,54 @@ break; } } + return Dumpstate::RunStatus::OK; } -// Runs dumpsys on services that must dump first and and will take less than 100ms to dump. -static void RunDumpsysCritical() { +// Runs dumpsys on services that must dump first and will take less than 100ms to dump. +static Dumpstate::RunStatus RunDumpsysCritical() { RunDumpsysText("DUMPSYS CRITICAL", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, /* timeout= */ 5s, /* service_timeout= */ 500ms); - RunDumpsysProto("DUMPSYS CRITICAL PROTO", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, - /* timeout= */ 5s, /* service_timeout= */ 500ms); + + RETURN_IF_USER_DENIED_CONSENT(); + + return RunDumpsysProto("DUMPSYS CRITICAL PROTO", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, + /* timeout= */ 5s, /* service_timeout= */ 500ms); } // Runs dumpsys on services that must dump first but can take up to 250ms to dump. -static void RunDumpsysHigh() { +static Dumpstate::RunStatus RunDumpsysHigh() { // TODO meminfo takes ~10s, connectivity takes ~5sec to dump. They are both // high priority. Reduce timeout once they are able to dump in a shorter time or // moved to a parallel task. RunDumpsysText("DUMPSYS HIGH", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, /* timeout= */ 90s, /* service_timeout= */ 30s); - RunDumpsysProto("DUMPSYS HIGH PROTO", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, - /* timeout= */ 5s, /* service_timeout= */ 1s); + + RETURN_IF_USER_DENIED_CONSENT(); + + return RunDumpsysProto("DUMPSYS HIGH PROTO", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, + /* timeout= */ 5s, /* service_timeout= */ 1s); } // Runs dumpsys on services that must dump but can take up to 10s to dump. -static void RunDumpsysNormal() { +static Dumpstate::RunStatus RunDumpsysNormal() { RunDumpsysTextNormalPriority("DUMPSYS", /* timeout= */ 90s, /* service_timeout= */ 10s); - RunDumpsysProto("DUMPSYS PROTO", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, - /* timeout= */ 90s, /* service_timeout= */ 10s); + + RETURN_IF_USER_DENIED_CONSENT(); + + return RunDumpsysProto("DUMPSYS PROTO", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, + /* timeout= */ 90s, /* service_timeout= */ 10s); } static void DumpHals() { + if (!ds.IsZipping()) { + RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"}, + CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); + return; + } + DurationReporter duration_reporter("DUMP HALS"); + RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"}, + CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); + using android::hidl::manager::V1_0::IServiceManager; using android::hardware::defaultServiceManager; @@ -1255,7 +1283,7 @@ return !isalnum(c) && std::string("@-_:.").find(c) == std::string::npos; }, '_'); - const std::string path = kDumpstateBoardPath + "lshal_debug_" + cleanName; + const std::string path = ds.bugreport_internal_dir_ + "/lshal_debug_" + cleanName; { auto fd = android::base::unique_fd( @@ -1286,9 +1314,16 @@ } } -static void dumpstate() { +// Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent +// via the consent they are shown. Ignores other errors that occur while running various +// commands. The consent checking is currently done around long running tasks, which happen to +// be distributed fairly evenly throughout the function. +static Dumpstate::RunStatus dumpstate() { DurationReporter duration_reporter("DUMPSTATE"); + // Dump various things. Note that anything that takes "long" (i.e. several seconds) should + // check intermittently (if it's intrerruptable like a foreach on pids) and/or should be wrapped + // in a consent check (via RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK). dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version"); RunCommand("UPTIME", {"uptime"}); DumpBlockStatFiles(); @@ -1296,7 +1331,9 @@ DumpFile("MEMORY INFO", "/proc/meminfo"); RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o", "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"}); - RunCommand("PROCRANK", {"procrank"}, AS_ROOT_20); + + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "PROCRANK", {"procrank"}, AS_ROOT_20); + DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat"); DumpFile("VMALLOC INFO", "/proc/vmallocinfo"); DumpFile("SLAB INFO", "/proc/slabinfo"); @@ -1311,14 +1348,11 @@ RunCommand("PROCESSES AND THREADS", {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy,time"}); - RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT); - if (ds.IsZipping()) { - RunCommand("HARDWARE HALS", {"lshal"}, CommandOptions::WithTimeout(2).AsRootIfAvailable().Build()); - DumpHals(); - } else { - RunCommand("HARDWARE HALS", {"lshal", "--debug"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); - } + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"}, + CommandOptions::AS_ROOT); + + DumpHals(); RunCommand("PRINTENV", {"printenv"}); RunCommand("NETSTAT", {"netstat", "-nW"}); @@ -1337,7 +1371,9 @@ } RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT); - for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES"); + + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(for_each_pid, do_showmap, "SMAPS OF ALL PROCESSES"); + for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS"); for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)"); @@ -1363,6 +1399,8 @@ DumpPacketStats(); + RunDumpsys("EBPF MAP STATS", {"netd", "trafficcontroller"}); + DoKmsg(); DumpIpAddrAndRules(); @@ -1373,7 +1411,7 @@ RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"}); RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"}); - RunDumpsysHigh(); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysHigh); RunCommand("SYSTEM PROPERTIES", {"getprop"}); @@ -1390,12 +1428,13 @@ DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats"); DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state"); + RunDumpsys("WINSCOPE TRACE", {"window", "trace"}); /* Add window and surface trace files. */ if (!PropertiesHelper::IsUserBuild()) { ds.AddDir(WMTRACE_DATA_DIR, false); } - ds.DumpstateBoard(); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard); /* Migrate the ril_dumpstate to a device specific dumpstate? */ int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0); @@ -1415,14 +1454,16 @@ printf("== Android Framework Services\n"); printf("========================================================\n"); - RunDumpsysNormal(); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal); printf("========================================================\n"); printf("== Checkins\n"); printf("========================================================\n"); RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); - RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}); + + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsys, "CHECKIN MEMINFO", {"meminfo", "--checkin"}); + RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}); RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}); RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}); @@ -1443,7 +1484,7 @@ printf("== Running Application Services (platform)\n"); printf("========================================================\n"); - RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform"}, + RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform-non-critical"}, DUMPSYS_COMPONENTS_OPTIONS); printf("========================================================\n"); @@ -1480,6 +1521,73 @@ printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); printf("========================================================\n"); + + printf("========================================================\n"); + printf("== Obtaining statsd metadata\n"); + printf("========================================================\n"); + // This differs from the usual dumpsys stats, which is the stats report data. + RunDumpsys("STATSDSTATS", {"stats", "--metadata"}); + return Dumpstate::RunStatus::OK; +} + +/* + * Dumps state for the default case; drops root after it's no longer necessary. + * + * Returns RunStatus::OK if everything went fine. + * Returns RunStatus::ERROR if there was an error. + * Returns RunStatus::USER_DENIED_CONSENT if user explicitly denied consent to sharing the bugreport + * with the caller. + */ +static Dumpstate::RunStatus DumpstateDefault() { + // Try to dump anrd trace if the daemon is running. + dump_anrd_trace(); + + // Invoking the following dumpsys calls before DumpTraces() to try and + // keep the system stats as close to its initial state as possible. + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysCritical); + + /* collect stack traces from Dalvik and native processes (needs root) */ + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path); + + /* Run some operations that require root. */ + ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping()); + ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping()); + + ds.AddDir(RECOVERY_DIR, true); + ds.AddDir(RECOVERY_DATA_DIR, true); + ds.AddDir(UPDATE_ENGINE_LOG_DIR, true); + ds.AddDir(LOGPERSIST_DATA_DIR, false); + if (!PropertiesHelper::IsUserBuild()) { + ds.AddDir(PROFILE_DATA_DIR_CUR, true); + ds.AddDir(PROFILE_DATA_DIR_REF, true); + } + add_mountinfo(); + DumpIpTablesAsRoot(); + + // Capture any IPSec policies in play. No keys are exposed here. + RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"}, CommandOptions::WithTimeout(10).Build()); + + // Dump IPsec stats. No keys are exposed here. + DumpFile("XFRM STATS", XFRM_STAT_PROC_FILE); + + // Run ss as root so we can see socket marks. + RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"}, CommandOptions::WithTimeout(10).Build()); + + // Run iotop as root to show top 100 IO threads + RunCommand("IOTOP", {"iotop", "-n", "1", "-m", "100"}); + + // Gather shared memory buffer info if the product implements it + struct stat st; + if (!stat("/product/bin/dmabuf_dump", &st)) { + RunCommand("Dmabuf dump", {"/product/bin/dmabuf_dump"}); + } + + if (!DropRootUser()) { + return Dumpstate::RunStatus::ERROR; + } + + RETURN_IF_USER_DENIED_CONSENT(); + return dumpstate(); } // This method collects common dumpsys for telephony and wifi @@ -1516,6 +1624,9 @@ RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(), @@ -1562,11 +1673,123 @@ RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + DumpHals(); + printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); printf("========================================================\n"); } +Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { + DurationReporter duration_reporter("DUMP TRACES"); + + const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX"; + const size_t buf_size = temp_file_pattern.length() + 1; + std::unique_ptr<char[]> file_name_buf(new char[buf_size]); + memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size); + + // Create a new, empty file to receive all trace dumps. + // + // TODO: This can be simplified once we remove support for the old style + // dumps. We can have a file descriptor passed in to dump_traces instead + // of creating a file, closing it and then reopening it again. + android::base::unique_fd fd(mkostemp(file_name_buf.get(), O_APPEND | O_CLOEXEC)); + if (fd < 0) { + MYLOGE("mkostemp on pattern %s: %s\n", file_name_buf.get(), strerror(errno)); + return RunStatus::OK; + } + + // Nobody should have access to this temporary file except dumpstate, but we + // temporarily grant 'read' to 'others' here because this file is created + // when tombstoned is still running as root, but dumped after dropping. This + // can go away once support for old style dumping has. + const int chmod_ret = fchmod(fd, 0666); + if (chmod_ret < 0) { + MYLOGE("fchmod on %s failed: %s\n", file_name_buf.get(), strerror(errno)); + return RunStatus::OK; + } + + std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir); + if (proc.get() == nullptr) { + MYLOGE("opendir /proc failed: %s\n", strerror(errno)); + return RunStatus::OK; + } + + // Number of times process dumping has timed out. If we encounter too many + // failures, we'll give up. + int timeout_failures = 0; + bool dalvik_found = false; + + const std::set<int> hal_pids = get_interesting_hal_pids(); + + struct dirent* d; + while ((d = readdir(proc.get()))) { + RETURN_IF_USER_DENIED_CONSENT(); + int pid = atoi(d->d_name); + if (pid <= 0) { + continue; + } + + const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid); + std::string exe; + if (!android::base::Readlink(link_name, &exe)) { + continue; + } + + bool is_java_process; + if (exe == "/system/bin/app_process32" || exe == "/system/bin/app_process64") { + // Don't bother dumping backtraces for the zygote. + if (IsZygote(pid)) { + continue; + } + + dalvik_found = true; + is_java_process = true; + } else if (should_dump_native_traces(exe.c_str()) || hal_pids.find(pid) != hal_pids.end()) { + is_java_process = false; + } else { + // Probably a native process we don't care about, continue. + continue; + } + + // If 3 backtrace dumps fail in a row, consider debuggerd dead. + if (timeout_failures == 3) { + dprintf(fd, "ERROR: Too many stack dump failures, exiting.\n"); + break; + } + + const uint64_t start = Nanotime(); + const int ret = dump_backtrace_to_file_timeout( + pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace, + is_java_process ? 5 : 20, fd); + + if (ret == -1) { + // For consistency, the header and footer to this message match those + // dumped by debuggerd in the success case. + dprintf(fd, "\n---- pid %d at [unknown] ----\n", pid); + dprintf(fd, "Dump failed, likely due to a timeout.\n"); + dprintf(fd, "---- end %d ----", pid); + timeout_failures++; + continue; + } + + // We've successfully dumped stack traces, reset the failure count + // and write a summary of the elapsed time to the file and continue with the + // next process. + timeout_failures = 0; + + dprintf(fd, "[dump %s stack %d: %.3fs elapsed]\n", is_java_process ? "dalvik" : "native", + pid, (float)(Nanotime() - start) / NANOS_PER_SEC); + } + + if (!dalvik_found) { + MYLOGE("Warning: no Dalvik processes found to dump stacks\n"); + } + + *path = file_name_buf.release(); + return RunStatus::OK; +} + void Dumpstate::DumpstateBoard() { DurationReporter duration_reporter("dumpstate_board()"); printf("========================================================\n"); @@ -1581,14 +1804,10 @@ std::vector<std::string> paths; std::vector<android::base::ScopeGuard<std::function<void()>>> remover; for (int i = 0; i < NUM_OF_DUMPS; i++) { - paths.emplace_back(kDumpstateBoardPath + kDumpstateBoardFiles[i]); - remover.emplace_back(android::base::make_scope_guard(std::bind( - [](std::string path) { - if (remove(path.c_str()) != 0 && errno != ENOENT) { - MYLOGE("Could not remove(%s): %s\n", path.c_str(), strerror(errno)); - } - }, - paths[i]))); + paths.emplace_back(StringPrintf("%s/%s", ds.bugreport_internal_dir_.c_str(), + kDumpstateBoardFiles[i].c_str())); + remover.emplace_back(android::base::make_scope_guard( + std::bind([](std::string path) { android::os::UnlinkAndLogOnError(path); }, paths[i]))); } sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService()); @@ -1609,6 +1828,7 @@ return; } + // TODO(128270426): Check for consent in between? for (size_t i = 0; i < paths.size(); i++) { MYLOGI("Calling IDumpstateDevice implementation using path %s\n", paths[i].c_str()); @@ -1681,7 +1901,7 @@ printf("*** See dumpstate-board.txt entry ***\n"); } -static void ShowUsageAndExit(int exitCode = 1) { +static void ShowUsage() { fprintf(stderr, "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file] [-d] [-p] " "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n" @@ -1700,13 +1920,8 @@ "progress (requires -o and -B)\n" " -R: take bugreport in remote mode (requires -o, -z, -d and -B, " "shouldn't be used with -P)\n" + " -w: start binder service and make it wait for a call to startBugreport\n" " -v: prints the dumpstate header and exit\n"); - exit(exitCode); -} - -static void ExitOnInvalidArgs() { - fprintf(stderr, "invalid combination of args\n"); - ShowUsageAndExit(); } static void register_sig_handler() { @@ -1739,8 +1954,11 @@ MYLOGE("Failed to add dumpstate log to .zip file\n"); return false; } - // ... and re-opens it for further logging. - redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str())); + // TODO: Should truncate the existing file. + // ... and re-open it for further logging. + if (!redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()))) { + return false; + } fprintf(stderr, "\n"); int32_t err = zip_writer_->Finish(); @@ -1753,9 +1971,7 @@ ds.zip_file.reset(nullptr); MYLOGD("Removing temporary file %s\n", tmp_path_.c_str()) - if (remove(tmp_path_.c_str()) != 0) { - MYLOGE("Failed to remove temporary file (%s): %s\n", tmp_path_.c_str(), strerror(errno)); - } + android::os::UnlinkAndLogOnError(tmp_path_); return true; } @@ -1765,7 +1981,7 @@ | O_CLOEXEC | O_NOFOLLOW))); if (fd == -1) { MYLOGE("open(%s): %s\n", filepath.c_str(), strerror(errno)); - return NULL; + return nullptr; } SHA256_CTX ctx; @@ -1778,7 +1994,7 @@ break; } else if (bytes_read == -1) { MYLOGE("read(%s): %s\n", filepath.c_str(), strerror(errno)); - return NULL; + return nullptr; } SHA256_Update(&ctx, buffer.data(), bytes_read); @@ -1822,24 +2038,466 @@ // clang-format on } -/** Main entry point for dumpstate. */ -int run_main(int argc, char* argv[]) { - int do_add_date = 0; - int do_zip_file = 0; - int do_vibrate = 1; - char* use_outfile = 0; - int use_socket = 0; - int use_control_socket = 0; - int do_fb = 0; - int do_broadcast = 0; - int is_remote_mode = 0; - bool show_header_only = false; - bool do_start_service = false; - bool telephony_only = false; - bool wifi_only = false; - int dup_stdout_fd; - int dup_stderr_fd; +static void MaybeResolveSymlink(std::string* path) { + std::string resolved_path; + if (android::base::Readlink(*path, &resolved_path)) { + *path = resolved_path; + } +} +/* + * Prepares state like filename, screenshot path, etc in Dumpstate. Also initializes ZipWriter + * if we are writing zip files and adds the version file. + */ +static void PrepareToWriteToFile() { + MaybeResolveSymlink(&ds.bugreport_internal_dir_); + + std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD"); + std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE"); + ds.base_name_ = StringPrintf("bugreport-%s-%s", device_name.c_str(), build_id.c_str()); + if (ds.options_->do_add_date) { + char date[80]; + strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_)); + ds.name_ = date; + } else { + ds.name_ = "undated"; + } + + if (ds.options_->telephony_only) { + ds.base_name_ += "-telephony"; + } else if (ds.options_->wifi_only) { + ds.base_name_ += "-wifi"; + } + + if (ds.options_->do_fb) { + ds.screenshot_path_ = ds.GetPath(".png"); + } + ds.tmp_path_ = ds.GetPath(".tmp"); + ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt"); + + std::string destination = ds.options_->bugreport_fd.get() != -1 + ? StringPrintf("[fd:%d]", ds.options_->bugreport_fd.get()) + : ds.bugreport_internal_dir_.c_str(); + MYLOGD( + "Bugreport dir: %s\n" + "Base name: %s\n" + "Suffix: %s\n" + "Log path: %s\n" + "Temporary path: %s\n" + "Screenshot path: %s\n", + destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(), + ds.tmp_path_.c_str(), ds.screenshot_path_.c_str()); + + if (ds.options_->do_zip_file) { + ds.path_ = ds.GetPath(".zip"); + MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str()); + create_parent_dirs(ds.path_.c_str()); + ds.zip_file.reset(fopen(ds.path_.c_str(), "wb")); + if (ds.zip_file == nullptr) { + MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno)); + } else { + ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get())); + } + ds.AddTextZipEntry("version.txt", ds.version_); + } +} + +/* + * Finalizes writing to the file by renaming or zipping the tmp file to the final location, + * printing zipped file status, etc. + */ +static void FinalizeFile() { + /* check if user changed the suffix using system properties */ + std::string name = + android::base::GetProperty(android::base::StringPrintf("dumpstate.%d.name", ds.pid_), ""); + bool change_suffix = false; + if (!name.empty()) { + /* must whitelist which characters are allowed, otherwise it could cross directories */ + std::regex valid_regex("^[-_a-zA-Z0-9]+$"); + if (std::regex_match(name.c_str(), valid_regex)) { + change_suffix = true; + } else { + MYLOGE("invalid suffix provided by user: %s\n", name.c_str()); + } + } + if (change_suffix) { + MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str()); + ds.name_ = name; + if (!ds.screenshot_path_.empty()) { + std::string new_screenshot_path = ds.GetPath(".png"); + if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) { + MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(), + new_screenshot_path.c_str(), strerror(errno)); + } else { + ds.screenshot_path_ = new_screenshot_path; + } + } + } + + bool do_text_file = true; + if (ds.options_->do_zip_file) { + if (!ds.FinishZipFile()) { + MYLOGE("Failed to finish zip file; sending text bugreport instead\n"); + do_text_file = true; + } else { + do_text_file = false; + // If the user has changed the suffix, we need to change the zip file name. + std::string new_path = ds.GetPath(".zip"); + if (ds.path_ != new_path) { + MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str()); + if (rename(ds.path_.c_str(), new_path.c_str())) { + MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(), + strerror(errno)); + } else { + ds.path_ = new_path; + } + } + } + } + if (do_text_file) { + ds.path_ = ds.GetPath(".txt"); + MYLOGD("Generating .txt bugreport at %s from %s\n", ds.path_.c_str(), ds.tmp_path_.c_str()); + if (rename(ds.tmp_path_.c_str(), ds.path_.c_str())) { + MYLOGE("rename(%s, %s): %s\n", ds.tmp_path_.c_str(), ds.path_.c_str(), strerror(errno)); + ds.path_.clear(); + } + } + if (ds.options_->use_control_socket) { + if (do_text_file) { + dprintf(ds.control_socket_fd_, + "FAIL:could not create zip file, check %s " + "for more details\n", + ds.log_path_.c_str()); + } else { + dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str()); + } + } +} + +/* Broadcasts that we are done with the bugreport */ +static void SendBugreportFinishedBroadcast() { + // TODO(b/111441001): use callback instead of broadcast. + if (!ds.path_.empty()) { + MYLOGI("Final bugreport path: %s\n", ds.path_.c_str()); + // clang-format off + + std::vector<std::string> am_args = { + "--receiver-permission", "android.permission.DUMP", + "--ei", "android.intent.extra.ID", std::to_string(ds.id_), + "--ei", "android.intent.extra.PID", std::to_string(ds.pid_), + "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()), + "--es", "android.intent.extra.BUGREPORT", ds.path_, + "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_ + }; + // clang-format on + if (ds.options_->do_fb && !android::os::IsFileEmpty(ds.screenshot_path_)) { + am_args.push_back("--es"); + am_args.push_back("android.intent.extra.SCREENSHOT"); + am_args.push_back(ds.screenshot_path_); + } + if (!ds.options_->notification_title.empty()) { + am_args.push_back("--es"); + am_args.push_back("android.intent.extra.TITLE"); + am_args.push_back(ds.options_->notification_title); + if (!ds.options_->notification_description.empty()) { + am_args.push_back("--es"); + am_args.push_back("android.intent.extra.DESCRIPTION"); + am_args.push_back(ds.options_->notification_description); + } + } + if (ds.options_->is_remote_mode) { + am_args.push_back("--es"); + am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH"); + am_args.push_back(SHA256_file_hash(ds.path_)); + SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED", am_args); + } else { + SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args); + } + } else { + MYLOGE("Skipping finished broadcast because bugreport could not be generated\n"); + } +} + +static inline const char* ModeToString(Dumpstate::BugreportMode mode) { + switch (mode) { + case Dumpstate::BugreportMode::BUGREPORT_FULL: + return "BUGREPORT_FULL"; + case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: + return "BUGREPORT_INTERACTIVE"; + case Dumpstate::BugreportMode::BUGREPORT_REMOTE: + return "BUGREPORT_REMOTE"; + case Dumpstate::BugreportMode::BUGREPORT_WEAR: + return "BUGREPORT_WEAR"; + case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: + return "BUGREPORT_TELEPHONY"; + case Dumpstate::BugreportMode::BUGREPORT_WIFI: + return "BUGREPORT_WIFI"; + case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: + return "BUGREPORT_DEFAULT"; + } +} + +static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOptions* options) { + options->extra_options = ModeToString(mode); + switch (mode) { + case Dumpstate::BugreportMode::BUGREPORT_FULL: + options->do_broadcast = true; + options->do_fb = true; + break; + case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: + // Currently, the dumpstate binder is only used by Shell to update progress. + options->do_start_service = true; + options->do_progress_updates = true; + options->do_fb = false; + options->do_broadcast = true; + break; + case Dumpstate::BugreportMode::BUGREPORT_REMOTE: + options->do_vibrate = false; + options->is_remote_mode = true; + options->do_fb = false; + options->do_broadcast = true; + break; + case Dumpstate::BugreportMode::BUGREPORT_WEAR: + options->do_start_service = true; + options->do_progress_updates = true; + options->do_zip_file = true; + options->do_fb = true; + options->do_broadcast = true; + break; + case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: + options->telephony_only = true; + options->do_fb = false; + options->do_broadcast = true; + break; + case Dumpstate::BugreportMode::BUGREPORT_WIFI: + options->wifi_only = true; + options->do_zip_file = true; + options->do_fb = false; + options->do_broadcast = true; + break; + case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: + break; + } +} + +static Dumpstate::BugreportMode getBugreportModeFromProperty() { + // If the system property is not set, it's assumed to be a default bugreport. + Dumpstate::BugreportMode mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT; + + std::string extra_options = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, ""); + if (!extra_options.empty()) { + // Framework uses a system property to override some command-line args. + // Currently, it contains the type of the requested bugreport. + if (extra_options == "bugreportplus") { + mode = Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE; + } else if (extra_options == "bugreportfull") { + mode = Dumpstate::BugreportMode::BUGREPORT_FULL; + } else if (extra_options == "bugreportremote") { + mode = Dumpstate::BugreportMode::BUGREPORT_REMOTE; + } else if (extra_options == "bugreportwear") { + mode = Dumpstate::BugreportMode::BUGREPORT_WEAR; + } else if (extra_options == "bugreporttelephony") { + mode = Dumpstate::BugreportMode::BUGREPORT_TELEPHONY; + } else if (extra_options == "bugreportwifi") { + mode = Dumpstate::BugreportMode::BUGREPORT_WIFI; + } else { + MYLOGE("Unknown extra option: %s\n", extra_options.c_str()); + } + // Reset the property + android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, ""); + } + return mode; +} + +// TODO: Move away from system properties when we have options passed via binder calls. +/* Sets runtime options from the system properties and then clears those properties. */ +static void SetOptionsFromProperties(Dumpstate::DumpOptions* options) { + Dumpstate::BugreportMode mode = getBugreportModeFromProperty(); + SetOptionsFromMode(mode, options); + + options->notification_title = android::base::GetProperty(PROPERTY_EXTRA_TITLE, ""); + if (!options->notification_title.empty()) { + // Reset the property + android::base::SetProperty(PROPERTY_EXTRA_TITLE, ""); + + options->notification_description = + android::base::GetProperty(PROPERTY_EXTRA_DESCRIPTION, ""); + if (!options->notification_description.empty()) { + // Reset the property + android::base::SetProperty(PROPERTY_EXTRA_DESCRIPTION, ""); + } + MYLOGD("notification (title: %s, description: %s)\n", options->notification_title.c_str(), + options->notification_description.c_str()); + } +} + +static void LogDumpOptions(const Dumpstate::DumpOptions& options) { + MYLOGI("do_zip_file: %d\n", options.do_zip_file); + MYLOGI("do_add_date: %d\n", options.do_add_date); + MYLOGI("do_vibrate: %d\n", options.do_vibrate); + MYLOGI("use_socket: %d\n", options.use_socket); + MYLOGI("use_control_socket: %d\n", options.use_control_socket); + MYLOGI("do_fb: %d\n", options.do_fb); + MYLOGI("do_broadcast: %d\n", options.do_broadcast); + MYLOGI("is_remote_mode: %d\n", options.is_remote_mode); + MYLOGI("show_header_only: %d\n", options.show_header_only); + MYLOGI("do_start_service: %d\n", options.do_start_service); + MYLOGI("telephony_only: %d\n", options.telephony_only); + MYLOGI("wifi_only: %d\n", options.wifi_only); + MYLOGI("do_progress_updates: %d\n", options.do_progress_updates); + MYLOGI("fd: %d\n", options.bugreport_fd.get()); + MYLOGI("extra_options: %s\n", options.extra_options.c_str()); + MYLOGI("args: %s\n", options.args.c_str()); + MYLOGI("notification_title: %s\n", options.notification_title.c_str()); + MYLOGI("notification_description: %s\n", options.notification_description.c_str()); +} + +void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, + const android::base::unique_fd& bugreport_fd_in, + const android::base::unique_fd& screenshot_fd_in) { + // In the new API world, date is always added; output is always a zip file. + // TODO(111441001): remove these options once they are obsolete. + do_add_date = true; + do_zip_file = true; + + // Duplicate the fds because the passed in fds don't outlive the binder transaction. + bugreport_fd.reset(dup(bugreport_fd_in.get())); + screenshot_fd.reset(dup(screenshot_fd_in.get())); + + extra_options = ModeToString(bugreport_mode); + SetOptionsFromMode(bugreport_mode, this); +} + +Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) { + RunStatus status = RunStatus::OK; + int c; + while ((c = getopt(argc, argv, "dho:svqzpPBRSV:w")) != -1) { + switch (c) { + // clang-format off + case 'd': do_add_date = true; break; + case 'z': do_zip_file = true; break; + // o=use_outfile not supported anymore. + // TODO(b/111441001): Remove when all callers have migrated. + case 'o': break; + case 's': use_socket = true; break; + case 'S': use_control_socket = true; break; + case 'v': show_header_only = true; break; + case 'q': do_vibrate = false; break; + case 'p': do_fb = true; break; + case 'P': do_progress_updates = true; break; + case 'R': is_remote_mode = true; break; + case 'B': do_broadcast = true; break; + case 'V': break; // compatibility no-op + case 'w': + // This was already processed + break; + case 'h': + status = RunStatus::HELP; + break; + default: + fprintf(stderr, "Invalid option: %c\n", c); + status = RunStatus::INVALID_INPUT; + break; + // clang-format on + } + } + + // TODO: use helper function to convert argv into a string + for (int i = 0; i < argc; i++) { + args += argv[i]; + if (i < argc - 1) { + args += " "; + } + } + + // Reset next index used by getopt so this can be called multiple times, for eg, in tests. + optind = 1; + + SetOptionsFromProperties(this); + return status; +} + +bool Dumpstate::DumpOptions::ValidateOptions() const { + if (bugreport_fd.get() != -1 && !do_zip_file) { + return false; + } + + if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) && !OutputToFile()) { + return false; + } + + if (use_control_socket && !do_zip_file) { + return false; + } + + if (do_progress_updates && !do_broadcast) { + return false; + } + + if (is_remote_mode && (do_progress_updates || !do_broadcast || !do_zip_file || !do_add_date)) { + return false; + } + return true; +} + +void Dumpstate::SetOptions(std::unique_ptr<DumpOptions> options) { + options_ = std::move(options); +} + +Dumpstate::RunStatus Dumpstate::Run(int32_t calling_uid, const std::string& calling_package) { + Dumpstate::RunStatus status = RunInternal(calling_uid, calling_package); + if (listener_ != nullptr) { + switch (status) { + case Dumpstate::RunStatus::OK: + listener_->onFinished(); + break; + case Dumpstate::RunStatus::HELP: + break; + case Dumpstate::RunStatus::INVALID_INPUT: + listener_->onError(IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); + break; + case Dumpstate::RunStatus::ERROR: + listener_->onError(IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR); + break; + case Dumpstate::RunStatus::USER_CONSENT_DENIED: + listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT); + break; + case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT: + listener_->onError(IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT); + break; + } + } + return status; +} + +/* + * Dumps relevant information to a bugreport based on the given options. + * + * The bugreport can be dumped to a file or streamed to a socket. + * + * How dumping to file works: + * stdout is redirected to a temporary file. This will later become the main bugreport entry. + * stderr is redirected a log file. + * + * The temporary bugreport is then populated via printfs, dumping contents of files and + * output of commands to stdout. + * + * If zipping, the temporary bugreport file is added to the zip archive. Else it's renamed to final + * text file. + * + * If zipping, a bunch of other files and dumps also get added to the zip archive. The log file also + * gets added to the archive. + * + * Bugreports are first generated in a local directory and later copied to the caller's fd if + * supplied. + */ +Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, + const std::string& calling_package) { + LogDumpOptions(*options_); + if (!options_->ValidateOptions()) { + MYLOGE("Invalid options specified\n"); + return RunStatus::INVALID_INPUT; + } /* set as high priority, and protect from OOM killer */ setpriority(PRIO_PROCESS, 0, -20); @@ -1856,135 +2514,49 @@ } } - /* parse arguments */ - int c; - while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) { - switch (c) { - // clang-format off - case 'd': do_add_date = 1; break; - case 'z': do_zip_file = 1; break; - case 'o': use_outfile = optarg; break; - case 's': use_socket = 1; break; - case 'S': use_control_socket = 1; break; - case 'v': show_header_only = true; break; - case 'q': do_vibrate = 0; break; - case 'p': do_fb = 1; break; - case 'P': ds.update_progress_ = true; break; - case 'R': is_remote_mode = 1; break; - case 'B': do_broadcast = 1; break; - case 'V': break; // compatibility no-op - case 'h': - ShowUsageAndExit(0); - break; - default: - fprintf(stderr, "Invalid option: %c\n", c); - ShowUsageAndExit(); - // clang-format on - } + if (version_ == VERSION_DEFAULT) { + version_ = VERSION_CURRENT; } - // TODO: use helper function to convert argv into a string - for (int i = 0; i < argc; i++) { - ds.args_ += argv[i]; - if (i < argc - 1) { - ds.args_ += " "; - } - } - - ds.extra_options_ = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, ""); - if (!ds.extra_options_.empty()) { - // Framework uses a system property to override some command-line args. - // Currently, it contains the type of the requested bugreport. - if (ds.extra_options_ == "bugreportplus") { - // Currently, the dumpstate binder is only used by Shell to update progress. - do_start_service = true; - ds.update_progress_ = true; - do_fb = 0; - } else if (ds.extra_options_ == "bugreportremote") { - do_vibrate = 0; - is_remote_mode = 1; - do_fb = 0; - } else if (ds.extra_options_ == "bugreportwear") { - do_start_service = true; - ds.update_progress_ = true; - do_zip_file = 1; - } else if (ds.extra_options_ == "bugreporttelephony") { - telephony_only = true; - } else if (ds.extra_options_ == "bugreportwifi") { - wifi_only = true; - do_zip_file = 1; - } else { - MYLOGE("Unknown extra option: %s\n", ds.extra_options_.c_str()); - } - // Reset the property - android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, ""); - } - - ds.notification_title = android::base::GetProperty(PROPERTY_EXTRA_TITLE, ""); - if (!ds.notification_title.empty()) { - // Reset the property - android::base::SetProperty(PROPERTY_EXTRA_TITLE, ""); - - ds.notification_description = android::base::GetProperty(PROPERTY_EXTRA_DESCRIPTION, ""); - if (!ds.notification_description.empty()) { - // Reset the property - android::base::SetProperty(PROPERTY_EXTRA_DESCRIPTION, ""); - } - MYLOGD("notification (title: %s, description: %s)\n", - ds.notification_title.c_str(), ds.notification_description.c_str()); - } - - if ((do_zip_file || do_add_date || ds.update_progress_ || do_broadcast) && !use_outfile) { - ExitOnInvalidArgs(); - } - - if (use_control_socket && !do_zip_file) { - ExitOnInvalidArgs(); - } - - if (ds.update_progress_ && !do_broadcast) { - ExitOnInvalidArgs(); - } - - if (is_remote_mode && (ds.update_progress_ || !do_broadcast || !do_zip_file || !do_add_date)) { - ExitOnInvalidArgs(); - } - - if (ds.version_ == VERSION_DEFAULT) { - ds.version_ = VERSION_CURRENT; - } - - if (ds.version_ != VERSION_CURRENT && ds.version_ != VERSION_SPLIT_ANR) { + if (version_ != VERSION_CURRENT && version_ != VERSION_SPLIT_ANR) { MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n", - ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(), + version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(), VERSION_SPLIT_ANR.c_str()); - exit(1); + return RunStatus::INVALID_INPUT; } - if (show_header_only) { - ds.PrintHeader(); - exit(0); + if (options_->show_header_only) { + PrintHeader(); + return RunStatus::OK; } - /* redirect output if needed */ - bool is_redirecting = !use_socket && use_outfile; + if (options_->bugreport_fd.get() != -1) { + // If the output needs to be copied over to the caller's fd, get user consent. + android::String16 package(calling_package.c_str()); + CheckUserConsent(calling_uid, package); + } + + // Redirect output if needed + bool is_redirecting = options_->OutputToFile(); // TODO: temporarily set progress until it's part of the Dumpstate constructor std::string stats_path = - is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", dirname(use_outfile)) - : ""; - ds.progress_.reset(new Progress(stats_path)); + is_redirecting + ? android::base::StringPrintf("%s/dumpstate-stats.txt", bugreport_internal_dir_.c_str()) + : ""; + progress_.reset(new Progress(stats_path)); /* gets the sequential id */ uint32_t last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0); - ds.id_ = ++last_id; + id_ = ++last_id; android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id)); MYLOGI("begin\n"); register_sig_handler(); - if (do_start_service) { + // TODO(b/111441001): maybe skip if already started? + if (options_->do_start_service) { MYLOGI("Starting 'dumpstate' service\n"); android::status_t ret; if ((ret = android::os::DumpstateService::Start()) != android::OK) { @@ -1996,91 +2568,48 @@ MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n"); } - MYLOGI("dumpstate info: id=%d, args='%s', extra_options= %s)\n", ds.id_, ds.args_.c_str(), - ds.extra_options_.c_str()); + MYLOGI("dumpstate info: id=%d, args='%s', extra_options= %s)\n", id_, options_->args.c_str(), + options_->extra_options.c_str()); - MYLOGI("bugreport format version: %s\n", ds.version_.c_str()); + MYLOGI("bugreport format version: %s\n", version_.c_str()); - ds.do_early_screenshot_ = ds.update_progress_; + do_early_screenshot_ = options_->do_progress_updates; // If we are going to use a socket, do it as early as possible // to avoid timeouts from bugreport. - if (use_socket) { - redirect_to_socket(stdout, "dumpstate"); + if (options_->use_socket) { + if (!redirect_to_socket(stdout, "dumpstate")) { + return ERROR; + } } - if (use_control_socket) { + if (options_->use_control_socket) { MYLOGD("Opening control socket\n"); - ds.control_socket_fd_ = open_socket("dumpstate"); - ds.update_progress_ = 1; + control_socket_fd_ = open_socket("dumpstate"); + if (control_socket_fd_ == -1) { + return ERROR; + } + options_->do_progress_updates = 1; } if (is_redirecting) { - ds.bugreport_dir_ = dirname(use_outfile); - std::string build_id = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD"); - std::string device_name = android::base::GetProperty("ro.product.name", "UNKNOWN_DEVICE"); - ds.base_name_ = android::base::StringPrintf("%s-%s-%s", basename(use_outfile), - device_name.c_str(), build_id.c_str()); - if (do_add_date) { - char date[80]; - strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_)); - ds.name_ = date; - } else { - ds.name_ = "undated"; - } + PrepareToWriteToFile(); - if (telephony_only) { - ds.base_name_ += "-telephony"; - } else if (wifi_only) { - ds.base_name_ += "-wifi"; - } - - if (do_fb) { - ds.screenshot_path_ = ds.GetPath(".png"); - } - ds.tmp_path_ = ds.GetPath(".tmp"); - ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt"); - - MYLOGD( - "Bugreport dir: %s\n" - "Base name: %s\n" - "Suffix: %s\n" - "Log path: %s\n" - "Temporary path: %s\n" - "Screenshot path: %s\n", - ds.bugreport_dir_.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), - ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str()); - - if (do_zip_file) { - ds.path_ = ds.GetPath(".zip"); - MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str()); - create_parent_dirs(ds.path_.c_str()); - ds.zip_file.reset(fopen(ds.path_.c_str(), "wb")); - if (ds.zip_file == nullptr) { - MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno)); - do_zip_file = 0; - } else { - ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get())); - } - ds.AddTextZipEntry("version.txt", ds.version_); - } - - if (ds.update_progress_) { - if (do_broadcast) { + if (options_->do_progress_updates) { + if (options_->do_broadcast) { // clang-format off - std::vector<std::string> am_args = { "--receiver-permission", "android.permission.DUMP", - "--es", "android.intent.extra.NAME", ds.name_, - "--ei", "android.intent.extra.ID", std::to_string(ds.id_), - "--ei", "android.intent.extra.PID", std::to_string(ds.pid_), - "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()), + "--es", "android.intent.extra.NAME", name_, + "--ei", "android.intent.extra.ID", std::to_string(id_), + "--ei", "android.intent.extra.PID", std::to_string(pid_), + "--ei", "android.intent.extra.MAX", std::to_string(progress_->GetMax()), }; // clang-format on SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args); } - if (use_control_socket) { - dprintf(ds.control_socket_fd_, "BEGIN:%s\n", ds.path_.c_str()); + if (options_->use_control_socket) { + dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str()); } } } @@ -2092,42 +2621,53 @@ fclose(cmdline); } - if (do_vibrate) { + if (options_->do_vibrate) { Vibrate(150); } - if (do_fb && ds.do_early_screenshot_) { - if (ds.screenshot_path_.empty()) { + if (options_->do_fb && do_early_screenshot_) { + if (screenshot_path_.empty()) { // should not have happened MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n"); } else { MYLOGI("taking early screenshot\n"); - ds.TakeScreenshot(); + TakeScreenshot(); } } - if (do_zip_file) { - if (chown(ds.path_.c_str(), AID_SHELL, AID_SHELL)) { - MYLOGE("Unable to change ownership of zip file %s: %s\n", ds.path_.c_str(), + if (options_->do_zip_file && zip_file != nullptr) { + if (chown(path_.c_str(), AID_SHELL, AID_SHELL)) { + MYLOGE("Unable to change ownership of zip file %s: %s\n", path_.c_str(), strerror(errno)); } } + int dup_stdout_fd; + int dup_stderr_fd; if (is_redirecting) { + // Redirect stderr to log_path_ for debugging. TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr))); - redirect_to_file(stderr, const_cast<char*>(ds.log_path_.c_str())); - if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) { - MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", - ds.log_path_.c_str(), strerror(errno)); + if (!redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()))) { + return ERROR; } + if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) { + MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(), + strerror(errno)); + } + + // Redirect stdout to tmp_path_. This is the main bugreport entry and will be + // moved into zip file later, if zipping. TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout))); + // TODO: why not write to a file instead of stdout to overcome this problem? /* TODO: rather than generating a text file now and zipping it later, it would be more efficient to redirect stdout to the zip entry directly, but the libziparchive doesn't support that option yet. */ - redirect_to_file(stdout, const_cast<char*>(ds.tmp_path_.c_str())); - if (chown(ds.tmp_path_.c_str(), AID_SHELL, AID_SHELL)) { + if (!redirect_to_file(stdout, const_cast<char*>(tmp_path_.c_str()))) { + return ERROR; + } + if (chown(tmp_path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n", - ds.tmp_path_.c_str(), strerror(errno)); + tmp_path_.c_str(), strerror(errno)); } } @@ -2137,62 +2677,22 @@ // NOTE: there should be no stdout output until now, otherwise it would break the header. // In particular, DurationReport objects should be created passing 'title, NULL', so their // duration is logged into MYLOG instead. - ds.PrintHeader(); + PrintHeader(); - if (telephony_only) { + if (options_->telephony_only) { DumpstateTelephonyOnly(); - ds.DumpstateBoard(); - } else if (wifi_only) { + DumpstateBoard(); + } else if (options_->wifi_only) { DumpstateWifiOnly(); } else { - // Dumps systrace right away, otherwise it will be filled with unnecessary events. - // First try to dump anrd trace if the daemon is running. Otherwise, dump - // the raw trace. - if (!dump_anrd_trace()) { - dump_systrace(); + // Dump state for the default case. This also drops root. + RunStatus s = DumpstateDefault(); + if (s != RunStatus::OK) { + if (s == RunStatus::USER_CONSENT_TIMED_OUT) { + HandleUserConsentDenied(); + } + return s; } - - // Invoking the following dumpsys calls before dump_traces() to try and - // keep the system stats as close to its initial state as possible. - RunDumpsysCritical(); - - // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed. - dump_raft(); - - /* collect stack traces from Dalvik and native processes (needs root) */ - dump_traces_path = dump_traces(); - - /* Run some operations that require root. */ - ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping()); - ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping()); - - ds.AddDir(RECOVERY_DIR, true); - ds.AddDir(RECOVERY_DATA_DIR, true); - ds.AddDir(UPDATE_ENGINE_LOG_DIR, true); - ds.AddDir(LOGPERSIST_DATA_DIR, false); - if (!PropertiesHelper::IsUserBuild()) { - ds.AddDir(PROFILE_DATA_DIR_CUR, true); - ds.AddDir(PROFILE_DATA_DIR_REF, true); - } - add_mountinfo(); - DumpIpTablesAsRoot(); - - // Capture any IPSec policies in play. No keys are exposed here. - RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"}, - CommandOptions::WithTimeout(10).Build()); - - // Run ss as root so we can see socket marks. - RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"}, - CommandOptions::WithTimeout(10).Build()); - - // Run iotop as root to show top 100 IO threads - RunCommand("IOTOP", {"iotop", "-n", "1", "-m", "100"}); - - if (!DropRootUser()) { - return -1; - } - - dumpstate(); } /* close output if needed */ @@ -2200,80 +2700,48 @@ TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout))); } - /* rename or zip the (now complete) .tmp file to its final location */ - if (use_outfile) { + // Rename, and/or zip the (now complete) .tmp file within the internal directory. + if (options_->OutputToFile()) { + FinalizeFile(); + } - /* check if user changed the suffix using system properties */ - std::string name = android::base::GetProperty( - android::base::StringPrintf("dumpstate.%d.name", ds.pid_), ""); - bool change_suffix= false; - if (!name.empty()) { - /* must whitelist which characters are allowed, otherwise it could cross directories */ - std::regex valid_regex("^[-_a-zA-Z0-9]+$"); - if (std::regex_match(name.c_str(), valid_regex)) { - change_suffix = true; + // Share the final file with the caller if the user has consented. + Dumpstate::RunStatus status = Dumpstate::RunStatus::OK; + if (options_->bugreport_fd.get() != -1) { + status = CopyBugreportIfUserConsented(); + if (status != Dumpstate::RunStatus::OK && + status != Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) { + // Do an early return if there were errors. We make an exception for consent + // timing out because it's possible the user got distracted. In this case the + // bugreport is not shared but made available for manual retrieval. + MYLOGI("User denied consent. Returning\n"); + return status; + } + if (options_->do_fb && options_->screenshot_fd.get() != -1) { + bool copy_succeeded = android::os::CopyFileToFd(screenshot_path_, + options_->screenshot_fd.get()); + if (copy_succeeded) { + android::os::UnlinkAndLogOnError(screenshot_path_); + } + } + if (status == Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) { + MYLOGI( + "Did not receive user consent yet." + " Will not copy the bugreport artifacts to caller.\n"); + const String16 incidentcompanion("incidentcompanion"); + sp<android::IBinder> ics(defaultServiceManager()->getService(incidentcompanion)); + if (ics != nullptr) { + MYLOGD("Canceling user consent request via incidentcompanion service\n"); + android::interface_cast<android::os::IIncidentCompanion>(ics)->cancelAuthorization( + consent_callback_.get()); } else { - MYLOGE("invalid suffix provided by user: %s\n", name.c_str()); - } - } - if (change_suffix) { - MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str()); - ds.name_ = name; - if (!ds.screenshot_path_.empty()) { - std::string new_screenshot_path = ds.GetPath(".png"); - if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) { - MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(), - new_screenshot_path.c_str(), strerror(errno)); - } else { - ds.screenshot_path_ = new_screenshot_path; - } - } - } - - bool do_text_file = true; - if (do_zip_file) { - if (!ds.FinishZipFile()) { - MYLOGE("Failed to finish zip file; sending text bugreport instead\n"); - do_text_file = true; - } else { - do_text_file = false; - // Since zip file is already created, it needs to be renamed. - std::string new_path = ds.GetPath(".zip"); - if (ds.path_ != new_path) { - MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str()); - if (rename(ds.path_.c_str(), new_path.c_str())) { - MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(), - strerror(errno)); - } else { - ds.path_ = new_path; - } - } - } - } - if (do_text_file) { - ds.path_ = ds.GetPath(".txt"); - MYLOGD("Generating .txt bugreport at %s from %s\n", ds.path_.c_str(), - ds.tmp_path_.c_str()); - if (rename(ds.tmp_path_.c_str(), ds.path_.c_str())) { - MYLOGE("rename(%s, %s): %s\n", ds.tmp_path_.c_str(), ds.path_.c_str(), - strerror(errno)); - ds.path_.clear(); - } - } - if (use_control_socket) { - if (do_text_file) { - dprintf(ds.control_socket_fd_, - "FAIL:could not create zip file, check %s " - "for more details\n", - ds.log_path_.c_str()); - } else { - dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str()); + MYLOGD("Unable to cancel user consent; incidentcompanion service unavailable\n"); } } } /* vibrate a few but shortly times to let user know it's finished */ - if (do_vibrate) { + if (options_->do_vibrate) { for (int i = 0; i < 3; i++) { Vibrate(75); usleep((75 + 50) * 1000); @@ -2281,65 +2749,138 @@ } /* tell activity manager we're done */ - if (do_broadcast) { - if (!ds.path_.empty()) { - MYLOGI("Final bugreport path: %s\n", ds.path_.c_str()); - // clang-format off - - std::vector<std::string> am_args = { - "--receiver-permission", "android.permission.DUMP", - "--ei", "android.intent.extra.ID", std::to_string(ds.id_), - "--ei", "android.intent.extra.PID", std::to_string(ds.pid_), - "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()), - "--es", "android.intent.extra.BUGREPORT", ds.path_, - "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_ - }; - // clang-format on - if (do_fb) { - am_args.push_back("--es"); - am_args.push_back("android.intent.extra.SCREENSHOT"); - am_args.push_back(ds.screenshot_path_); - } - if (!ds.notification_title.empty()) { - am_args.push_back("--es"); - am_args.push_back("android.intent.extra.TITLE"); - am_args.push_back(ds.notification_title); - if (!ds.notification_description.empty()) { - am_args.push_back("--es"); - am_args.push_back("android.intent.extra.DESCRIPTION"); - am_args.push_back(ds.notification_description); - } - } - if (is_remote_mode) { - am_args.push_back("--es"); - am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH"); - am_args.push_back(SHA256_file_hash(ds.path_)); - SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED", - am_args); - } else { - SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args); - } - } else { - MYLOGE("Skipping finished broadcast because bugreport could not be generated\n"); - } + if (options_->do_broadcast) { + SendBugreportFinishedBroadcast(); + // Note that listener_ is notified in Run(); } - MYLOGD("Final progress: %d/%d (estimated %d)\n", ds.progress_->Get(), ds.progress_->GetMax(), - ds.progress_->GetInitialMax()); - ds.progress_->Save(); - MYLOGI("done (id %d)\n", ds.id_); + MYLOGD("Final progress: %d/%d (estimated %d)\n", progress_->Get(), progress_->GetMax(), + progress_->GetInitialMax()); + progress_->Save(); + MYLOGI("done (id %d)\n", id_); if (is_redirecting) { TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr))); } - if (use_control_socket && ds.control_socket_fd_ != -1) { + if (options_->use_control_socket && control_socket_fd_ != -1) { MYLOGD("Closing control socket\n"); - close(ds.control_socket_fd_); + close(control_socket_fd_); } - ds.tombstone_data_.clear(); - ds.anr_data_.clear(); + tombstone_data_.clear(); + anr_data_.clear(); - return 0; + return (consent_callback_ != nullptr && + consent_callback_->getResult() == UserConsentResult::UNAVAILABLE) + ? USER_CONSENT_TIMED_OUT + : RunStatus::OK; +} + +void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& calling_package) { + consent_callback_ = new ConsentCallback(); + const String16 incidentcompanion("incidentcompanion"); + sp<android::IBinder> ics(defaultServiceManager()->getService(incidentcompanion)); + if (ics != nullptr) { + MYLOGD("Checking user consent via incidentcompanion service\n"); + android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport( + calling_uid, calling_package, String16(), String16(), + 0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get()); + } else { + MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n"); + } +} + +bool Dumpstate::IsUserConsentDenied() const { + return ds.consent_callback_ != nullptr && + ds.consent_callback_->getResult() == UserConsentResult::DENIED; +} + +void Dumpstate::CleanupFiles() { + android::os::UnlinkAndLogOnError(tmp_path_); + android::os::UnlinkAndLogOnError(screenshot_path_); + android::os::UnlinkAndLogOnError(path_); +} + +Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() { + MYLOGD("User denied consent; deleting files and returning\n"); + CleanupFiles(); + return USER_CONSENT_DENIED; +} + +Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented() { + // If the caller has asked to copy the bugreport over to their directory, we need explicit + // user consent. + UserConsentResult consent_result = consent_callback_->getResult(); + if (consent_result == UserConsentResult::UNAVAILABLE) { + // User has not responded yet. + uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs(); + if (elapsed_ms < USER_CONSENT_TIMEOUT_MS) { + uint delay_seconds = (USER_CONSENT_TIMEOUT_MS - elapsed_ms) / 1000; + MYLOGD("Did not receive user consent yet; going to wait for %d seconds", delay_seconds); + sleep(delay_seconds); + } + consent_result = consent_callback_->getResult(); + } + if (consent_result == UserConsentResult::DENIED) { + // User has explicitly denied sharing with the app. To be safe delete the + // internal bugreport & tmp files. + return HandleUserConsentDenied(); + } + if (consent_result == UserConsentResult::APPROVED) { + bool copy_succeeded = android::os::CopyFileToFd(path_, options_->bugreport_fd.get()); + if (copy_succeeded) { + android::os::UnlinkAndLogOnError(path_); + } + return copy_succeeded ? Dumpstate::RunStatus::OK : Dumpstate::RunStatus::ERROR; + } else if (consent_result == UserConsentResult::UNAVAILABLE) { + // consent_result is still UNAVAILABLE. The user has likely not responded yet. + // Since we do not have user consent to share the bugreport it does not get + // copied over to the calling app but remains in the internal directory from + // where the user can manually pull it. + return Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT; + } + // Unknown result; must be a programming error. + MYLOGE("Unknown user consent result:%d\n", consent_result); + return Dumpstate::RunStatus::ERROR; +} + +Dumpstate::RunStatus Dumpstate::ParseCommandlineAndRun(int argc, char* argv[]) { + std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>(); + Dumpstate::RunStatus status = options->Initialize(argc, argv); + if (status == Dumpstate::RunStatus::OK) { + SetOptions(std::move(options)); + // When directly running dumpstate binary, the output is not expected to be written + // to any external file descriptor. + assert(options_->bugreport_fd.get() == -1); + + // calling_uid and calling_package are for user consent to share the bugreport with + // an app; they are irrelvant here because bugreport is only written to a local + // directory, and not shared. + status = Run(-1 /* calling_uid */, "" /* calling_package */); + } + return status; +} + +/* Main entry point for dumpstate binary. */ +int run_main(int argc, char* argv[]) { + Dumpstate::RunStatus status = ds.ParseCommandlineAndRun(argc, argv); + + switch (status) { + case Dumpstate::RunStatus::OK: + exit(0); + case Dumpstate::RunStatus::HELP: + ShowUsage(); + exit(0); + case Dumpstate::RunStatus::INVALID_INPUT: + fprintf(stderr, "Invalid combination of args\n"); + ShowUsage(); + exit(1); + case Dumpstate::RunStatus::ERROR: + FALLTHROUGH_INTENDED; + case Dumpstate::RunStatus::USER_CONSENT_DENIED: + FALLTHROUGH_INTENDED; + case Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT: + exit(2); + } }
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index b220013..d02ec75 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h
@@ -27,6 +27,8 @@ #include <android-base/macros.h> #include <android-base/unique_fd.h> +#include <android/os/BnIncidentAuthListener.h> +#include <android/os/IDumpstate.h> #include <android/os/IDumpstateListener.h> #include <utils/StrongPointer.h> #include <ziparchive/zip_writer.h> @@ -42,6 +44,9 @@ // TODO: and then remove explicitly android::os::dumpstate:: prefixes namespace android { namespace os { + +struct DumpstateOptions; + namespace dumpstate { class DumpstateTest; @@ -68,13 +73,13 @@ */ class DurationReporter { public: - DurationReporter(const std::string& title, bool log_only = false); + explicit DurationReporter(const std::string& title, bool logcat_only = false); ~DurationReporter(); private: std::string title_; - bool log_only_; + bool logcat_only_; uint64_t started_; DISALLOW_COPY_AND_ASSIGN(DurationReporter); @@ -107,7 +112,7 @@ */ static const int kDefaultMax; - Progress(const std::string& path = ""); + explicit Progress(const std::string& path = ""); // Gets the current progress. int32_t Get() const; @@ -138,7 +143,7 @@ float growth_factor_; int32_t n_runs_; int32_t average_max_; - const std::string& path_; + std::string path_; }; /* @@ -160,6 +165,11 @@ static std::string VERSION_DEFAULT = "default"; /* + * Directory used by Dumpstate binary to keep its local files. + */ +static const std::string DUMPSTATE_DIRECTORY = "/bugreports"; + +/* * Structure that contains the information of an open dump file. */ struct DumpData { @@ -183,6 +193,19 @@ friend class DumpstateTest; public: + enum RunStatus { OK, HELP, INVALID_INPUT, ERROR, USER_CONSENT_DENIED, USER_CONSENT_TIMED_OUT }; + + // The mode under which the bugreport should be run. Each mode encapsulates a few options. + enum BugreportMode { + BUGREPORT_FULL = android::os::IDumpstate::BUGREPORT_MODE_FULL, + BUGREPORT_INTERACTIVE = android::os::IDumpstate::BUGREPORT_MODE_INTERACTIVE, + BUGREPORT_REMOTE = android::os::IDumpstate::BUGREPORT_MODE_REMOTE, + BUGREPORT_WEAR = android::os::IDumpstate::BUGREPORT_MODE_WEAR, + BUGREPORT_TELEPHONY = android::os::IDumpstate::BUGREPORT_MODE_TELEPHONY, + BUGREPORT_WIFI = android::os::IDumpstate::BUGREPORT_MODE_WIFI, + BUGREPORT_DEFAULT = android::os::IDumpstate::BUGREPORT_MODE_DEFAULT + }; + static android::os::dumpstate::CommandOptions DEFAULT_DUMPSYS; static Dumpstate& GetInstance(); @@ -245,7 +268,7 @@ std::chrono::milliseconds timeout); /* - * Adds a text entry entry to the existing zip file. + * Adds a text entry to the existing zip file. */ bool AddTextZipEntry(const std::string& entry_name, const std::string& content); @@ -268,6 +291,12 @@ // TODO: temporary method until Dumpstate object is properly set void SetProgress(std::unique_ptr<Progress> progress); + // Dumps Dalvik and native stack traces, sets the trace file location to path + // if it succeeded. + // Note that it returns early if user consent is denied with status USER_CONSENT_DENIED. + // Returns OK in all other cases. + RunStatus DumpTraces(const char** path); + void DumpstateBoard(); /* @@ -284,22 +313,92 @@ */ bool FinishZipFile(); - /* Gets the path of a bugreport file with the given suffix. */ + /* Constructs a full path inside directory with file name formatted using the given suffix. */ + std::string GetPath(const std::string& directory, const std::string& suffix) const; + + /* Constructs a full path inside bugreport_internal_dir_ with file name formatted using the + * given suffix. */ std::string GetPath(const std::string& suffix) const; /* Returns true if the current version supports priority dump feature. */ bool CurrentVersionSupportsPriorityDumps() const; - // TODO: initialize fields on constructor + struct DumpOptions; + /* Main entry point for running a complete bugreport. */ + RunStatus Run(int32_t calling_uid, const std::string& calling_package); + + RunStatus ParseCommandlineAndRun(int argc, char* argv[]); + + /* Sets runtime options. */ + void SetOptions(std::unique_ptr<DumpOptions> options); + + /* + * Returns true if user consent is necessary and has been denied. + * Consent is only necessary if the caller has asked to copy over the bugreport to a file they + * provided. + */ + bool IsUserConsentDenied() const; + + /* + * Structure to hold options that determine the behavior of dumpstate. + */ + struct DumpOptions { + bool do_add_date = false; + bool do_zip_file = false; + bool do_vibrate = true; + // Writes bugreport content to a socket; only flatfile format is supported. + bool use_socket = false; + bool use_control_socket = false; + bool do_fb = false; + bool do_broadcast = false; + bool is_remote_mode = false; + bool show_header_only = false; + bool do_start_service = false; + bool telephony_only = false; + bool wifi_only = false; + // Whether progress updates should be published. + bool do_progress_updates = false; + // File descriptor to output zip file. + android::base::unique_fd bugreport_fd; + // File descriptor to screenshot file. + android::base::unique_fd screenshot_fd; + // TODO: rename to MODE. + // Extra options passed as system property. + std::string extra_options; + // Command-line arguments as string + std::string args; + // Notification title and description + std::string notification_title; + std::string notification_description; + + /* Initializes options from commandline arguments and system properties. */ + RunStatus Initialize(int argc, char* argv[]); + + /* Initializes options from the requested mode. */ + void Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd, + const android::base::unique_fd& screenshot_fd); + + /* Returns true if the options set so far are consistent. */ + bool ValidateOptions() const; + + /* Returns if options specified require writing bugreport to a file */ + bool OutputToFile() const { + // If we are not writing to socket, we will write to a file. If bugreport_fd is + // specified, it is preferred. If not bugreport is written to /bugreports. + return !use_socket; + } + }; + + // TODO: initialize fields on constructor // dumpstate id - unique after each device reboot. uint32_t id_; // dumpstate pid pid_t pid_; - // Whether progress updates should be published. - bool update_progress_ = false; + // Runtime options. + std::unique_ptr<DumpOptions> options_; // How frequently the progess should be updated;the listener will only be notificated when the // delta from the previous update is more than the threshold. @@ -319,18 +418,6 @@ // Bugreport format version; std::string version_ = VERSION_CURRENT; - // Command-line arguments as string - std::string args_; - - // Extra options passed as system property. - std::string extra_options_; - - // Full path of the directory where the bugreport files will be written. - std::string bugreport_dir_; - - // Full path of the temporary file containing the screenshot (when requested). - std::string screenshot_path_; - time_t now_; // Base name (without suffix or extensions) of the bugreport files, typically @@ -341,15 +428,23 @@ // `-d`), but it could be changed by the user.. std::string name_; - // Full path of the temporary file containing the bugreport. + std::string bugreport_internal_dir_ = DUMPSTATE_DIRECTORY; + + // Full path of the temporary file containing the bugreport, inside bugreport_internal_dir_. + // At the very end this file is pulled into the zip file. std::string tmp_path_; - // Full path of the file containing the dumpstate logs. + // Full path of the file containing the dumpstate logs, inside bugreport_internal_dir_. + // This is useful for debugging. std::string log_path_; - // Pointer to the actual path, be it zip or text. + // Full path of the bugreport file, be it zip or text, inside bugreport_internal_dir_. std::string path_; + // TODO: If temporary this should be removed at the end. + // Full path of the temporary file containing the screenshot (when requested). + std::string screenshot_path_; + // Pointer to the zipped file. std::unique_ptr<FILE, int (*)(FILE*)> zip_file{nullptr, fclose}; @@ -361,19 +456,52 @@ std::string listener_name_; bool report_section_; - // Notification title and description - std::string notification_title; - std::string notification_description; - // List of open tombstone dump files. std::vector<DumpData> tombstone_data_; // List of open ANR dump files. std::vector<DumpData> anr_data_; + // A callback to IncidentCompanion service, which checks user consent for sharing the + // bugreport with the calling app. If the user has not responded yet to the dialog it will + // be neither confirmed nor denied. + class ConsentCallback : public android::os::BnIncidentAuthListener { + public: + ConsentCallback(); + android::binder::Status onReportApproved() override; + android::binder::Status onReportDenied() override; + + enum ConsentResult { APPROVED, DENIED, UNAVAILABLE }; + + ConsentResult getResult(); + + // Returns the time since creating this listener + uint64_t getElapsedTimeMs() const; + + private: + ConsentResult result_; + uint64_t start_time_; + std::mutex lock_; + }; + private: + RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package); + + void CheckUserConsent(int32_t calling_uid, const android::String16& calling_package); + + // Removes the in progress files output files (tmp file, zip/txt file, screenshot), + // but leaves the log file alone. + void CleanupFiles(); + + RunStatus HandleUserConsentDenied(); + + // Copies bugreport artifacts over to the caller's directories provided there is user consent. + RunStatus CopyBugreportIfUserConsented(); + // Used by GetInstance() only. - Dumpstate(const std::string& version = VERSION_CURRENT); + explicit Dumpstate(const std::string& version = VERSION_CURRENT); + + android::sp<ConsentCallback> consent_callback_; DISALLOW_COPY_AND_ASSIGN(Dumpstate); }; @@ -404,21 +532,30 @@ /** opens a socket and returns its file descriptor */ int open_socket(const char *service); -/* redirect output to a service control socket */ -void redirect_to_socket(FILE *redirect, const char *service); +/* + * Redirects 'redirect' to a service control socket. + * + * Returns true if redirect succeeds. + */ +bool redirect_to_socket(FILE* redirect, const char* service); -/* redirect output to a new file */ -void redirect_to_file(FILE *redirect, char *path); +/* + * Redirects 'redirect' to a file indicated by 'path', truncating it. + * + * Returns true if redirect succeeds. + */ +bool redirect_to_file(FILE* redirect, char* path); -/* redirect output to an existing file */ -void redirect_to_existing_file(FILE *redirect, char *path); +/* + * Redirects 'redirect' to an existing file indicated by 'path', appending it. + * + * Returns true if redirect succeeds. + */ +bool redirect_to_existing_file(FILE* redirect, char* path); /* create leading directories, if necessary */ void create_parent_dirs(const char *path); -/* dump Dalvik and native stack traces, return the trace file location (NULL if none) */ -const char *dump_traces(); - /* for each process in the system, run the specified function */ void for_each_pid(for_each_pid_func func, const char *header);
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc index 2e72574..14937b8 100644 --- a/cmds/dumpstate/dumpstate.rc +++ b/cmds/dumpstate/dumpstate.rc
@@ -17,3 +17,9 @@ class main disabled oneshot + +# bugreportd starts dumpstate binder service and makes it wait for a listener to connect. +service bugreportd /system/bin/dumpstate -w + class main + disabled + oneshot
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp index 78aad11..68d3733 100644 --- a/cmds/dumpstate/main.cpp +++ b/cmds/dumpstate/main.cpp
@@ -14,8 +14,55 @@ * limitations under the License. */ +#define LOG_TAG "dumpstate" + +#include <binder/IPCThreadState.h> + +#include "DumpstateInternal.h" +#include "DumpstateService.h" #include "dumpstate.h" +namespace { + +// Returns true if we should start the service and wait for a listener +// to bind with bugreport options. +bool ShouldStartServiceAndWait(int argc, char* argv[]) { + bool do_wait = false; + int c; + // Keep flags in sync with Dumpstate::DumpOptions::Initialize. + while ((c = getopt(argc, argv, "wdho:svqzpPBRSV:")) != -1 && !do_wait) { + switch (c) { + case 'w': + do_wait = true; + break; + default: + // Ignore all other options + break; + } + } + + // Reset next index used by getopt so getopt can be called called again in Dumpstate::Run to + // parse bugreport options. + optind = 1; + return do_wait; +} + +} // namespace + int main(int argc, char* argv[]) { - return run_main(argc, argv); + if (ShouldStartServiceAndWait(argc, argv)) { + int ret; + if ((ret = android::os::DumpstateService::Start()) != android::OK) { + MYLOGE("Unable to start 'dumpstate' service: %d", ret); + exit(1); + } + MYLOGI("'dumpstate' service started and will wait for a call to startBugreport()"); + + // Waits forever for an incoming connection. + // TODO(b/111441001): should this time out? + android::IPCThreadState::self()->joinThreadPool(); + return 0; + } else { + return run_main(argc, argv); + } }
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp index 61a5ef5..fc3642c 100644 --- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -21,6 +21,10 @@ #include <libgen.h> #include <android-base/file.h> +#include <android/os/BnDumpstate.h> +#include <android/os/BnDumpstateListener.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> #include <cutils/properties.h> #include <ziparchive/zip_archive.h> @@ -34,6 +38,24 @@ using ::testing::Test; using ::std::literals::chrono_literals::operator""s; +using android::base::unique_fd; + +class DumpstateListener; + +namespace { + +sp<IDumpstate> GetDumpstateService() { + return android::interface_cast<IDumpstate>( + android::defaultServiceManager()->getService(String16("dumpstate"))); +} + +int OpenForWrite(const std::string& filename) { + return TEMP_FAILURE_RETRY(open(filename.c_str(), + O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); +} + +} // namespace struct SectionInfo { std::string name; @@ -46,29 +68,71 @@ * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the * section details generated by dumpstate are added to a vector to be used by Tests later. */ -class DumpstateListener : public IDumpstateListener { +class DumpstateListener : public BnDumpstateListener { public: - int outFd_, max_progress_; - std::shared_ptr<std::vector<SectionInfo>> sections_; DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections) - : outFd_(fd), max_progress_(5000), sections_(sections) { + : out_fd_(fd), sections_(sections) { } - binder::Status onProgressUpdated(int32_t progress) override { - dprintf(outFd_, "\rIn progress %d/%d", progress, max_progress_); + + DumpstateListener(int fd) : out_fd_(fd) { + } + + binder::Status onProgress(int32_t progress) override { + dprintf(out_fd_, "\rIn progress %d", progress); return binder::Status::ok(); } + + binder::Status onError(int32_t error_code) override { + std::lock_guard<std::mutex> lock(lock_); + error_code_ = error_code; + dprintf(out_fd_, "\rError code %d", error_code); + return binder::Status::ok(); + } + + binder::Status onFinished() override { + std::lock_guard<std::mutex> lock(lock_); + is_finished_ = true; + dprintf(out_fd_, "\rFinished"); + return binder::Status::ok(); + } + + binder::Status onProgressUpdated(int32_t progress) override { + dprintf(out_fd_, "\rIn progress %d/%d", progress, max_progress_); + return binder::Status::ok(); + } + binder::Status onMaxProgressUpdated(int32_t max_progress) override { + std::lock_guard<std::mutex> lock(lock_); max_progress_ = max_progress; return binder::Status::ok(); } + binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes, int32_t duration_ms) override { - sections_->push_back({name, status, size_bytes, duration_ms}); + std::lock_guard<std::mutex> lock(lock_); + if (sections_.get() != nullptr) { + sections_->push_back({name, status, size_bytes, duration_ms}); + } return binder::Status::ok(); } - IBinder* onAsBinder() override { - return nullptr; + + bool getIsFinished() { + std::lock_guard<std::mutex> lock(lock_); + return is_finished_; } + + int getErrorCode() { + std::lock_guard<std::mutex> lock(lock_); + return error_code_; + } + + private: + int out_fd_; + int max_progress_ = 5000; + int error_code_ = -1; + bool is_finished_ = false; + std::shared_ptr<std::vector<SectionInfo>> sections_; + std::mutex lock_; }; /** @@ -97,7 +161,7 @@ ds.listener_name_ = "Smokey"; ds.report_section_ = true; auto start = std::chrono::steady_clock::now(); - run_main(ARRAY_SIZE(argv), argv); + ds.ParseCommandlineAndRun(ARRAY_SIZE(argv), argv); auto end = std::chrono::steady_clock::now(); duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); } @@ -281,6 +345,148 @@ SectionExists("DUMPSYS - wifi", /* bytes= */ 100000); } +class DumpstateBinderTest : public Test { + protected: + void SetUp() override { + // In case there is a stray service, stop it first. + property_set("ctl.stop", "bugreportd"); + // dry_run results in a faster bugreport. + property_set("dumpstate.dry_run", "true"); + // We need to receive some async calls later. Ensure we have binder threads. + ProcessState::self()->startThreadPool(); + } + + void TearDown() override { + property_set("ctl.stop", "bugreportd"); + property_set("dumpstate.dry_run", ""); + + unlink("/data/local/tmp/tmp.zip"); + unlink("/data/local/tmp/tmp.png"); + } + + // Waits until listener gets the callbacks. + void WaitTillExecutionComplete(DumpstateListener* listener) { + // Wait till one of finished, error or timeout. + static const int kBugreportTimeoutSeconds = 120; + int i = 0; + while (!listener->getIsFinished() && listener->getErrorCode() == -1 && + i < kBugreportTimeoutSeconds) { + sleep(1); + i++; + } + } +}; + +TEST_F(DumpstateBinderTest, Baseline) { + // In the beginning dumpstate binder service is not running. + sp<android::os::IDumpstate> ds_binder(GetDumpstateService()); + EXPECT_EQ(ds_binder, nullptr); + + // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service + // and makes it wait. + property_set("dumpstate.dry_run", "true"); + property_set("ctl.start", "bugreportd"); + + // Now we are able to retrieve dumpstate binder service. + ds_binder = GetDumpstateService(); + EXPECT_NE(ds_binder, nullptr); + + // Prepare arguments + unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip")); + unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png")); + + EXPECT_NE(bugreport_fd.get(), -1); + EXPECT_NE(screenshot_fd.get(), -1); + + sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)))); + android::binder::Status status = + ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, + Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener); + // startBugreport is an async call. Verify binder call succeeded first, then wait till listener + // gets expected callbacks. + EXPECT_TRUE(status.isOk()); + WaitTillExecutionComplete(listener.get()); + + // Bugreport generation requires user consent, which we cannot get in a test set up, + // so instead of getting is_finished_, we are more likely to get a consent error. + EXPECT_TRUE( + listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT || + listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT); + + // The service should have died on its own, freeing itself up for a new invocation. + sleep(2); + ds_binder = GetDumpstateService(); + EXPECT_EQ(ds_binder, nullptr); +} + +TEST_F(DumpstateBinderTest, ServiceDies_OnInvalidInput) { + // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service + // and makes it wait. + property_set("ctl.start", "bugreportd"); + sp<android::os::IDumpstate> ds_binder(GetDumpstateService()); + EXPECT_NE(ds_binder, nullptr); + + // Prepare arguments + unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip")); + unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png")); + + EXPECT_NE(bugreport_fd.get(), -1); + EXPECT_NE(screenshot_fd.get(), -1); + + // Call startBugreport with bad arguments. + sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)))); + android::binder::Status status = + ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, + 2000, // invalid bugreport mode + listener); + EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); + + // The service should have died, freeing itself up for a new invocation. + sleep(2); + ds_binder = GetDumpstateService(); + EXPECT_EQ(ds_binder, nullptr); +} + +TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) { + // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service + // and makes it wait. + property_set("dumpstate.dry_run", "true"); + property_set("ctl.start", "bugreportd"); + sp<android::os::IDumpstate> ds_binder(GetDumpstateService()); + EXPECT_NE(ds_binder, nullptr); + + // Prepare arguments + unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip")); + unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png")); + + EXPECT_NE(bugreport_fd.get(), -1); + EXPECT_NE(screenshot_fd.get(), -1); + + sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout)))); + android::binder::Status status = + ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, + Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1); + EXPECT_TRUE(status.isOk()); + + // try to make another call to startBugreport. This should fail. + sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout)))); + status = ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, + Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2); + EXPECT_FALSE(status.isOk()); + WaitTillExecutionComplete(listener2.get()); + EXPECT_EQ(listener2->getErrorCode(), + IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS); + + // Meanwhile the first call works as expected. Service should not die in this case. + WaitTillExecutionComplete(listener1.get()); + + // Bugreport generation requires user consent, which we cannot get in a test set up, + // so instead of getting is_finished_, we are more likely to get a consent error. + EXPECT_TRUE( + listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT || + listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT); +} + } // namespace dumpstate } // namespace os } // namespace android
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index 838b385..71d15f4 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -36,6 +36,7 @@ #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> +#include <cutils/properties.h> namespace android { namespace os { @@ -54,8 +55,13 @@ using ::testing::internal::GetCapturedStderr; using ::testing::internal::GetCapturedStdout; +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + class DumpstateListenerMock : public IDumpstateListener { public: + MOCK_METHOD1(onProgress, binder::Status(int32_t progress)); + MOCK_METHOD1(onError, binder::Status(int32_t error_code)); + MOCK_METHOD0(onFinished, binder::Status()); MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress)); MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress)); MOCK_METHOD4(onSectionComplete, binder::Status(const ::std::string& name, int32_t status, @@ -83,6 +89,10 @@ PropertiesHelper::build_type_ = build_type; } + void SetUnroot(bool unroot) const { + PropertiesHelper::unroot_ = unroot; + } + bool IsStandalone() const { return calls_ == 1; } @@ -135,6 +145,445 @@ } }; +class DumpOptionsTest : public Test { + public: + virtual ~DumpOptionsTest() { + } + virtual void SetUp() { + options_ = Dumpstate::DumpOptions(); + } + void TearDown() { + // Reset the property + property_set("dumpstate.options", ""); + } + Dumpstate::DumpOptions options_; +}; + +TEST_F(DumpOptionsTest, InitializeNone) { + // clang-format off + char* argv[] = { + const_cast<char*>("dumpstate") + }; + // clang-format on + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + + EXPECT_FALSE(options_.do_add_date); + EXPECT_FALSE(options_.do_zip_file); + EXPECT_FALSE(options_.use_socket); + EXPECT_FALSE(options_.use_control_socket); + EXPECT_FALSE(options_.show_header_only); + EXPECT_TRUE(options_.do_vibrate); + EXPECT_FALSE(options_.do_fb); + EXPECT_FALSE(options_.do_progress_updates); + EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.do_broadcast); +} + +TEST_F(DumpOptionsTest, InitializeAdbBugreport) { + // clang-format off + char* argv[] = { + const_cast<char*>("dumpstatez"), + const_cast<char*>("-S"), + const_cast<char*>("-d"), + const_cast<char*>("-z"), + }; + // clang-format on + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + EXPECT_TRUE(options_.do_add_date); + EXPECT_TRUE(options_.do_zip_file); + EXPECT_TRUE(options_.use_control_socket); + + // Other options retain default values + EXPECT_TRUE(options_.do_vibrate); + EXPECT_FALSE(options_.show_header_only); + EXPECT_FALSE(options_.do_fb); + EXPECT_FALSE(options_.do_progress_updates); + EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.do_broadcast); + EXPECT_FALSE(options_.use_socket); +} + +TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) { + // clang-format off + char* argv[] = { + const_cast<char*>("dumpstate"), + const_cast<char*>("-s"), + }; + // clang-format on + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + EXPECT_TRUE(options_.use_socket); + + // Other options retain default values + EXPECT_TRUE(options_.do_vibrate); + EXPECT_FALSE(options_.do_add_date); + EXPECT_FALSE(options_.do_zip_file); + EXPECT_FALSE(options_.use_control_socket); + EXPECT_FALSE(options_.show_header_only); + EXPECT_FALSE(options_.do_fb); + EXPECT_FALSE(options_.do_progress_updates); + EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.do_broadcast); +} + +TEST_F(DumpOptionsTest, InitializeFullBugReport) { + // clang-format off + char* argv[] = { + const_cast<char*>("bugreport"), + const_cast<char*>("-d"), + const_cast<char*>("-p"), + const_cast<char*>("-B"), + const_cast<char*>("-z"), + }; + // clang-format on + property_set("dumpstate.options", "bugreportfull"); + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + EXPECT_TRUE(options_.do_add_date); + EXPECT_TRUE(options_.do_fb); + EXPECT_TRUE(options_.do_zip_file); + EXPECT_TRUE(options_.do_broadcast); + + // Other options retain default values + EXPECT_TRUE(options_.do_vibrate); + EXPECT_FALSE(options_.use_control_socket); + EXPECT_FALSE(options_.show_header_only); + EXPECT_FALSE(options_.do_progress_updates); + EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.use_socket); + EXPECT_FALSE(options_.do_start_service); +} + +TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) { + // clang-format off + char* argv[] = { + const_cast<char*>("bugreport"), + const_cast<char*>("-d"), + const_cast<char*>("-p"), + const_cast<char*>("-B"), + const_cast<char*>("-z"), + }; + // clang-format on + + property_set("dumpstate.options", "bugreportplus"); + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + EXPECT_TRUE(options_.do_add_date); + EXPECT_TRUE(options_.do_broadcast); + EXPECT_TRUE(options_.do_zip_file); + EXPECT_TRUE(options_.do_progress_updates); + EXPECT_TRUE(options_.do_start_service); + EXPECT_FALSE(options_.do_fb); + + // Other options retain default values + EXPECT_TRUE(options_.do_vibrate); + EXPECT_FALSE(options_.use_control_socket); + EXPECT_FALSE(options_.show_header_only); + EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.use_socket); +} + +TEST_F(DumpOptionsTest, InitializeRemoteBugReport) { + // clang-format off + char* argv[] = { + const_cast<char*>("bugreport"), + const_cast<char*>("-d"), + const_cast<char*>("-p"), + const_cast<char*>("-B"), + const_cast<char*>("-z"), + }; + // clang-format on + + property_set("dumpstate.options", "bugreportremote"); + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + EXPECT_TRUE(options_.do_add_date); + EXPECT_TRUE(options_.do_broadcast); + EXPECT_TRUE(options_.do_zip_file); + EXPECT_TRUE(options_.is_remote_mode); + EXPECT_FALSE(options_.do_vibrate); + EXPECT_FALSE(options_.do_fb); + + // Other options retain default values + EXPECT_FALSE(options_.use_control_socket); + EXPECT_FALSE(options_.show_header_only); + EXPECT_FALSE(options_.do_progress_updates); + EXPECT_FALSE(options_.use_socket); +} + +TEST_F(DumpOptionsTest, InitializeWearBugReport) { + // clang-format off + char* argv[] = { + const_cast<char*>("bugreport"), + const_cast<char*>("-d"), + const_cast<char*>("-p"), + const_cast<char*>("-B"), + const_cast<char*>("-z"), + }; + // clang-format on + + property_set("dumpstate.options", "bugreportwear"); + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + EXPECT_TRUE(options_.do_add_date); + EXPECT_TRUE(options_.do_fb); + EXPECT_TRUE(options_.do_broadcast); + EXPECT_TRUE(options_.do_zip_file); + EXPECT_TRUE(options_.do_progress_updates); + EXPECT_TRUE(options_.do_start_service); + + // Other options retain default values + EXPECT_TRUE(options_.do_vibrate); + EXPECT_FALSE(options_.use_control_socket); + EXPECT_FALSE(options_.show_header_only); + EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.use_socket); +} + +TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { + // clang-format off + char* argv[] = { + const_cast<char*>("bugreport"), + const_cast<char*>("-d"), + const_cast<char*>("-p"), + const_cast<char*>("-B"), + const_cast<char*>("-z"), + }; + // clang-format on + + property_set("dumpstate.options", "bugreporttelephony"); + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + EXPECT_TRUE(options_.do_add_date); + EXPECT_FALSE(options_.do_fb); + EXPECT_TRUE(options_.do_broadcast); + EXPECT_TRUE(options_.do_zip_file); + EXPECT_TRUE(options_.telephony_only); + + // Other options retain default values + EXPECT_TRUE(options_.do_vibrate); + EXPECT_FALSE(options_.use_control_socket); + EXPECT_FALSE(options_.show_header_only); + EXPECT_FALSE(options_.do_progress_updates); + EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.use_socket); +} + +TEST_F(DumpOptionsTest, InitializeWifiBugReport) { + // clang-format off + char* argv[] = { + const_cast<char*>("bugreport"), + const_cast<char*>("-d"), + const_cast<char*>("-p"), + const_cast<char*>("-B"), + const_cast<char*>("-z"), + }; + // clang-format on + + property_set("dumpstate.options", "bugreportwifi"); + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + EXPECT_TRUE(options_.do_add_date); + EXPECT_FALSE(options_.do_fb); + EXPECT_TRUE(options_.do_broadcast); + EXPECT_TRUE(options_.do_zip_file); + EXPECT_TRUE(options_.wifi_only); + + // Other options retain default values + EXPECT_TRUE(options_.do_vibrate); + EXPECT_FALSE(options_.use_control_socket); + EXPECT_FALSE(options_.show_header_only); + EXPECT_FALSE(options_.do_progress_updates); + EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.use_socket); +} + +TEST_F(DumpOptionsTest, InitializeDefaultBugReport) { + // default: commandline options are not overridden + // clang-format off + char* argv[] = { + const_cast<char*>("bugreport"), + const_cast<char*>("-d"), + const_cast<char*>("-p"), + const_cast<char*>("-B"), + const_cast<char*>("-z"), + }; + // clang-format on + + property_set("dumpstate.options", ""); + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + EXPECT_TRUE(options_.do_add_date); + EXPECT_TRUE(options_.do_fb); + EXPECT_TRUE(options_.do_zip_file); + EXPECT_TRUE(options_.do_broadcast); + + // Other options retain default values + EXPECT_TRUE(options_.do_vibrate); + EXPECT_FALSE(options_.use_control_socket); + EXPECT_FALSE(options_.show_header_only); + EXPECT_FALSE(options_.do_progress_updates); + EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.use_socket); + EXPECT_FALSE(options_.wifi_only); +} + +TEST_F(DumpOptionsTest, InitializePartial1) { + // clang-format off + char* argv[] = { + const_cast<char*>("dumpstate"), + const_cast<char*>("-d"), + const_cast<char*>("-z"), + const_cast<char*>("-s"), + const_cast<char*>("-S"), + + }; + // clang-format on + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + EXPECT_TRUE(options_.do_add_date); + EXPECT_TRUE(options_.do_zip_file); + // TODO: Maybe we should trim the filename + EXPECT_TRUE(options_.use_socket); + EXPECT_TRUE(options_.use_control_socket); + + // Other options retain default values + EXPECT_FALSE(options_.show_header_only); + EXPECT_TRUE(options_.do_vibrate); + EXPECT_FALSE(options_.do_fb); + EXPECT_FALSE(options_.do_progress_updates); + EXPECT_FALSE(options_.is_remote_mode); + EXPECT_FALSE(options_.do_broadcast); +} + +TEST_F(DumpOptionsTest, InitializePartial2) { + // clang-format off + char* argv[] = { + const_cast<char*>("dumpstate"), + const_cast<char*>("-v"), + const_cast<char*>("-q"), + const_cast<char*>("-p"), + const_cast<char*>("-P"), + const_cast<char*>("-R"), + const_cast<char*>("-B"), + }; + // clang-format on + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + EXPECT_EQ(status, Dumpstate::RunStatus::OK); + EXPECT_TRUE(options_.show_header_only); + EXPECT_FALSE(options_.do_vibrate); + EXPECT_TRUE(options_.do_fb); + EXPECT_TRUE(options_.do_progress_updates); + EXPECT_TRUE(options_.is_remote_mode); + EXPECT_TRUE(options_.do_broadcast); + + // Other options retain default values + EXPECT_FALSE(options_.do_add_date); + EXPECT_FALSE(options_.do_zip_file); + EXPECT_FALSE(options_.use_socket); + EXPECT_FALSE(options_.use_control_socket); +} + +TEST_F(DumpOptionsTest, InitializeHelp) { + // clang-format off + char* argv[] = { + const_cast<char*>("dumpstate"), + const_cast<char*>("-h") + }; + // clang-format on + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + // -h is for help. + EXPECT_EQ(status, Dumpstate::RunStatus::HELP); +} + +TEST_F(DumpOptionsTest, InitializeUnknown) { + // clang-format off + char* argv[] = { + const_cast<char*>("dumpstate"), + const_cast<char*>("-u") // unknown flag + }; + // clang-format on + + Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv); + + // -u is unknown. + EXPECT_EQ(status, Dumpstate::RunStatus::INVALID_INPUT); +} + +TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile1) { + options_.do_zip_file = true; + // Writing to socket = !writing to file. + options_.use_socket = true; + EXPECT_FALSE(options_.ValidateOptions()); + + options_.use_socket = false; + EXPECT_TRUE(options_.ValidateOptions()); +} + +TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile2) { + options_.do_broadcast = true; + // Writing to socket = !writing to file. + options_.use_socket = true; + EXPECT_FALSE(options_.ValidateOptions()); + + options_.use_socket = false; + EXPECT_TRUE(options_.ValidateOptions()); +} + +TEST_F(DumpOptionsTest, ValidateOptionsNeedZipfile) { + options_.use_control_socket = true; + EXPECT_FALSE(options_.ValidateOptions()); + + options_.do_zip_file = true; + EXPECT_TRUE(options_.ValidateOptions()); +} + +TEST_F(DumpOptionsTest, ValidateOptionsUpdateProgressNeedsBroadcast) { + options_.do_progress_updates = true; + EXPECT_FALSE(options_.ValidateOptions()); + + options_.do_broadcast = true; + EXPECT_TRUE(options_.ValidateOptions()); +} + +TEST_F(DumpOptionsTest, ValidateOptionsRemoteMode) { + options_.is_remote_mode = true; + EXPECT_FALSE(options_.ValidateOptions()); + + options_.do_broadcast = true; + options_.do_zip_file = true; + options_.do_add_date = true; + EXPECT_TRUE(options_.ValidateOptions()); +} + class DumpstateTest : public DumpstateBaseTest { public: void SetUp() { @@ -142,8 +591,8 @@ SetDryRun(false); SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)")); ds.progress_.reset(new Progress()); - ds.update_progress_ = false; ds.update_progress_threshold_ = 0; + ds.options_.reset(new Dumpstate::DumpOptions()); } // Runs a command and capture `stdout` and `stderr`. @@ -168,7 +617,7 @@ } void SetProgress(long progress, long initial_max, long threshold = 0) { - ds.update_progress_ = true; + ds.options_->do_progress_updates = true; ds.update_progress_threshold_ = threshold; ds.last_updated_progress_ = 0; ds.progress_.reset(new Progress(initial_max, progress, 1.2)); @@ -188,8 +637,9 @@ } if (update_progress) { - message += android::base::StringPrintf("Setting progress (%s): %d/%d\n", - listener_name.c_str(), progress, max); + message += android::base::StringPrintf("Setting progress (%s): %d/%d (%d%%)\n", + listener_name.c_str(), progress, max, + (100 * progress / max)); } return message; @@ -216,8 +666,7 @@ EXPECT_THAT(err, StrEq("stderr\n")); // We don't know the exact duration, so we check the prefix and suffix EXPECT_THAT(out, - StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n------")); - EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n")); + StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n")); } TEST_F(DumpstateTest, RunCommandWithLoggingMessage) { @@ -252,8 +701,7 @@ EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand})); // We don't know the exact duration, so we check the prefix and suffix EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + kSimpleCommand + - ") ------\n\t(skipped on dry run)\n------")); - EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n")); + ") ------\n\t(skipped on dry run)\n")); EXPECT_THAT(err, IsEmpty()); } @@ -348,12 +796,14 @@ SetProgress(0, 30); EXPECT_CALL(*listener, onProgressUpdated(20)); + EXPECT_CALL(*listener, onProgress(66)); // 20/30 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(20).Build())); std::string progress_message = GetProgressMessage(ds.listener_name_, 20, 30); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); EXPECT_CALL(*listener, onProgressUpdated(30)); + EXPECT_CALL(*listener, onProgress(100)); // 35/35 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Build())); progress_message = GetProgressMessage(ds.listener_name_, 30, 30); EXPECT_THAT(out, StrEq("stdout\n")); @@ -362,6 +812,7 @@ // Run a command that will increase maximum timeout. EXPECT_CALL(*listener, onProgressUpdated(31)); EXPECT_CALL(*listener, onMaxProgressUpdated(37)); + EXPECT_CALL(*listener, onProgress(83)); // 31/37 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build())); progress_message = GetProgressMessage(ds.listener_name_, 31, 37, 30); // 20% increase EXPECT_THAT(out, StrEq("stdout\n")); @@ -370,6 +821,7 @@ // Make sure command ran while in dry_run is counted. SetDryRun(true); EXPECT_CALL(*listener, onProgressUpdated(35)); + EXPECT_CALL(*listener, onProgress(94)); // 35/37 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build())); progress_message = GetProgressMessage(ds.listener_name_, 35, 37); EXPECT_THAT(out, IsEmpty()); @@ -386,6 +838,7 @@ // First update should always be sent. EXPECT_CALL(*listener, onProgressUpdated(1)); + EXPECT_CALL(*listener, onProgress(12)); // 1/12 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build())); std::string progress_message = GetProgressMessage(ds.listener_name_, 1, 8); EXPECT_THAT(out, StrEq("stdout\n")); @@ -398,6 +851,7 @@ // Third update should be sent because it reaches threshold (6 - 1 = 5). EXPECT_CALL(*listener, onProgressUpdated(6)); + EXPECT_CALL(*listener, onProgress(75)); // 6/8 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build())); progress_message = GetProgressMessage(ds.listener_name_, 6, 8); EXPECT_THAT(out, StrEq("stdout\n")); @@ -479,6 +933,32 @@ EXPECT_THAT(err, StrEq("stderr\n")); } +TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild_withUnroot) { + if (!IsStandalone()) { + // TODO: temporarily disabled because it might cause other tests to fail after dropping + // to Shell - need to refactor tests to avoid this problem) + MYLOGE( + "Skipping DumpstateTest.RunCommandAsRootNonUserBuild_withUnroot() " + "on test suite\n") + return; + } + if (PropertiesHelper::IsUserBuild()) { + ALOGI("Skipping RunCommandAsRootNonUserBuild_withUnroot on user builds\n"); + return; + } + + // Same test as above, but with unroot property set, which will override su availability. + SetUnroot(true); + DropRoot(); + + EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, + CommandOptions::WithTimeout(1).AsRoot().Build())); + + // AsRoot is ineffective. + EXPECT_THAT(out, StrEq("2000\nstdout\n")); + EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n")); +} + TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnUserBuild) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping @@ -521,6 +1001,32 @@ EXPECT_THAT(err, StrEq("stderr\n")); } +TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnDebugBuild_withUnroot) { + if (!IsStandalone()) { + // TODO: temporarily disabled because it might cause other tests to fail after dropping + // to Shell - need to refactor tests to avoid this problem) + MYLOGE( + "Skipping DumpstateTest.RunCommandAsRootIfAvailableOnDebugBuild_withUnroot() " + "on test suite\n") + return; + } + if (PropertiesHelper::IsUserBuild()) { + ALOGI("Skipping RunCommandAsRootIfAvailableOnDebugBuild_withUnroot on user builds\n"); + return; + } + // Same test as above, but with unroot property set, which will override su availability. + SetUnroot(true); + + DropRoot(); + + EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, + CommandOptions::WithTimeout(1).AsRootIfAvailable().Build())); + + // It's a userdebug build, so "su root" should be available, but unroot=true overrides it. + EXPECT_THAT(out, StrEq("2000\nstdout\n")); + EXPECT_THAT(err, StrEq("stderr\n")); +} + TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) { EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist")); EXPECT_THAT(out, @@ -534,7 +1040,6 @@ // We don't know the exact duration, so we check the prefix and suffix EXPECT_THAT(out, StartsWith("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No " "such file or directory\n")); - EXPECT_THAT(out, EndsWith("s was the duration of 'Y U NO EXIST?' ------\n")); } TEST_F(DumpstateTest, DumpFileSingleLine) { @@ -574,8 +1079,7 @@ EXPECT_THAT(err, IsEmpty()); EXPECT_THAT( out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath + "single-line.txt:")); - EXPECT_THAT(out, HasSubstr("\n\t(skipped on dry run)\n------")); - EXPECT_THAT(out, EndsWith("s was the duration of 'Might as well dump. Dump!' ------\n")); + EXPECT_THAT(out, HasSubstr("\n\t(skipped on dry run)\n")); } TEST_F(DumpstateTest, DumpFileUpdateProgress) { @@ -585,6 +1089,7 @@ SetProgress(0, 30); EXPECT_CALL(*listener, onProgressUpdated(5)); + EXPECT_CALL(*listener, onProgress(16)); // 5/30 % EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt")); std::string progress_message =
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp index 022f4fc..0bb80dc 100644 --- a/cmds/dumpstate/utils.cpp +++ b/cmds/dumpstate/utils.cpp
@@ -50,8 +50,6 @@ #include <android-base/unique_fd.h> #include <cutils/properties.h> #include <cutils/sockets.h> -#include <debuggerd/client.h> -#include <dumputils/dump_utils.h> #include <log/log.h> #include <private/android_filesystem_config.h> @@ -82,8 +80,12 @@ CommandOptions Dumpstate::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build(); +// TODO(111441001): Default DumpOptions to sensible values. Dumpstate::Dumpstate(const std::string& version) - : pid_(getpid()), version_(version), now_(time(nullptr)) { + : pid_(getpid()), + options_(new Dumpstate::DumpOptions()), + version_(version), + now_(time(nullptr)) { } Dumpstate& Dumpstate::GetInstance() { @@ -91,8 +93,8 @@ return singleton_; } -DurationReporter::DurationReporter(const std::string& title, bool log_only) - : title_(title), log_only_(log_only) { +DurationReporter::DurationReporter(const std::string& title, bool logcat_only) + : title_(title), logcat_only_(logcat_only) { if (!title_.empty()) { started_ = Nanotime(); } @@ -100,14 +102,16 @@ DurationReporter::~DurationReporter() { if (!title_.empty()) { - uint64_t elapsed = Nanotime() - started_; - if (log_only_) { - MYLOGD("Duration of '%s': %.3fs\n", title_.c_str(), (float)elapsed / NANOS_PER_SEC); - } else { - // Use "Yoda grammar" to make it easier to grep|sort sections. - printf("------ %.3fs was the duration of '%s' ------\n", (float)elapsed / NANOS_PER_SEC, - title_.c_str()); + float elapsed = (float)(Nanotime() - started_) / NANOS_PER_SEC; + if (elapsed < .5f) { + return; } + MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed); + if (logcat_only_) { + return; + } + // Use "Yoda grammar" to make it easier to grep|sort sections. + printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str()); } } @@ -225,7 +229,11 @@ } std::string Dumpstate::GetPath(const std::string& suffix) const { - return android::base::StringPrintf("%s/%s-%s%s", bugreport_dir_.c_str(), base_name_.c_str(), + return GetPath(bugreport_internal_dir_, suffix); +} + +std::string Dumpstate::GetPath(const std::string& directory, const std::string& suffix) const { + return android::base::StringPrintf("%s/%s-%s%s", directory.c_str(), base_name_.c_str(), name_.c_str(), suffix.c_str()); } @@ -272,6 +280,12 @@ if (header) printf("\n------ %s ------\n", header); while ((de = readdir(d))) { + if (ds.IsUserConsentDenied()) { + MYLOGE( + "Returning early because user denied consent to share bugreport with calling app."); + closedir(d); + return; + } int pid; int fd; char cmdpath[255]; @@ -344,6 +358,12 @@ func(pid, pid, cmdline); while ((de = readdir(d))) { + if (ds.IsUserConsentDenied()) { + MYLOGE( + "Returning early because user denied consent to share bugreport with calling app."); + closedir(d); + return; + } int tid; int fd; char commpath[255]; @@ -525,13 +545,13 @@ if (PropertiesHelper::IsDryRun()) return; /* Get size of kernel buffer */ - int size = klogctl(KLOG_SIZE_BUFFER, NULL, 0); + int size = klogctl(KLOG_SIZE_BUFFER, nullptr, 0); if (size <= 0) { printf("Unexpected klogctl return value: %d\n\n", size); return; } char *buf = (char *) malloc(size + 1); - if (buf == NULL) { + if (buf == nullptr) { printf("memory allocation failed\n\n"); return; } @@ -598,7 +618,7 @@ DurationReporter duration_reporter(title); DIR *dirp; struct dirent *d; - char *newpath = NULL; + char *newpath = nullptr; const char *slash = "/"; int retval = 0; @@ -611,7 +631,7 @@ ++slash; } dirp = opendir(dir); - if (dirp == NULL) { + if (dirp == nullptr) { retval = -errno; MYLOGE("%s: %s\n", dir, strerror(errno)); return retval; @@ -620,7 +640,7 @@ if (!dump_from_fd) { dump_from_fd = dump_file_from_fd; } - for (; ((d = readdir(dirp))); free(newpath), newpath = NULL) { + for (; ((d = readdir(dirp))); free(newpath), newpath = nullptr) { if ((d->d_name[0] == '.') && (((d->d_name[1] == '.') && (d->d_name[2] == '\0')) || (d->d_name[1] == '\0'))) { @@ -648,7 +668,7 @@ printf("*** %s: %s\n", newpath, strerror(errno)); continue; } - (*dump_from_fd)(NULL, newpath, fd.get()); + (*dump_from_fd)(nullptr, newpath, fd.get()); } closedir(dirp); if (!title.empty()) { @@ -702,12 +722,12 @@ int s = android_get_control_socket(service); if (s < 0) { MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno)); - exit(1); + return -1; } fcntl(s, F_SETFD, FD_CLOEXEC); if (listen(s, 4) < 0) { MYLOGE("listen(control socket): %s\n", strerror(errno)); - exit(1); + return -1; } struct sockaddr addr; @@ -715,18 +735,23 @@ int fd = accept(s, &addr, &alen); if (fd < 0) { MYLOGE("accept(control socket): %s\n", strerror(errno)); - exit(1); + return -1; } return fd; } /* redirect output to a service control socket */ -void redirect_to_socket(FILE *redirect, const char *service) { +bool redirect_to_socket(FILE* redirect, const char* service) { int fd = open_socket(service); + if (fd == -1) { + return false; + } fflush(redirect); - dup2(fd, fileno(redirect)); + // TODO: handle dup2 failure + TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); close(fd); + return true; } // TODO: should call is_valid_output_file and/or be merged into it. @@ -756,7 +781,7 @@ } } -void _redirect_to_file(FILE *redirect, char *path, int truncate_flag) { +bool _redirect_to_file(FILE* redirect, char* path, int truncate_flag) { create_parent_dirs(path); int fd = TEMP_FAILURE_RETRY(open(path, @@ -764,292 +789,20 @@ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); if (fd < 0) { MYLOGE("%s: %s\n", path, strerror(errno)); - exit(1); + return false; } TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); close(fd); + return true; } -void redirect_to_file(FILE *redirect, char *path) { - _redirect_to_file(redirect, path, O_TRUNC); +bool redirect_to_file(FILE* redirect, char* path) { + return _redirect_to_file(redirect, path, O_TRUNC); } -void redirect_to_existing_file(FILE *redirect, char *path) { - _redirect_to_file(redirect, path, O_APPEND); -} - -const char* DumpTraces(const std::string& traces_path); -const char* DumpTracesTombstoned(const std::string& traces_dir); - -/* dump Dalvik and native stack traces, return the trace file location (NULL if none) */ -const char *dump_traces() { - DurationReporter duration_reporter("DUMP TRACES"); - - const std::string traces_dir = android::base::GetProperty("dalvik.vm.stack-trace-dir", ""); - if (!traces_dir.empty()) { - return DumpTracesTombstoned(traces_dir); - } - - const std::string traces_file = android::base::GetProperty("dalvik.vm.stack-trace-file", ""); - if (!traces_file.empty()) { - return DumpTraces(traces_file); - } - - return nullptr; -} - -const char* DumpTracesTombstoned(const std::string& traces_dir) { - const std::string temp_file_pattern = traces_dir + "/dumptrace_XXXXXX"; - - const size_t buf_size = temp_file_pattern.length() + 1; - std::unique_ptr<char[]> file_name_buf(new char[buf_size]); - memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size); - - // Create a new, empty file to receive all trace dumps. - // - // TODO: This can be simplified once we remove support for the old style - // dumps. We can have a file descriptor passed in to dump_traces instead - // of creating a file, closing it and then reopening it again. - android::base::unique_fd fd(mkostemp(file_name_buf.get(), O_APPEND | O_CLOEXEC)); - if (fd < 0) { - MYLOGE("mkostemp on pattern %s: %s\n", file_name_buf.get(), strerror(errno)); - return nullptr; - } - - // Nobody should have access to this temporary file except dumpstate, but we - // temporarily grant 'read' to 'others' here because this file is created - // when tombstoned is still running as root, but dumped after dropping. This - // can go away once support for old style dumping has. - const int chmod_ret = fchmod(fd, 0666); - if (chmod_ret < 0) { - MYLOGE("fchmod on %s failed: %s\n", file_name_buf.get(), strerror(errno)); - return nullptr; - } - - std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir); - if (proc.get() == nullptr) { - MYLOGE("opendir /proc failed: %s\n", strerror(errno)); - return nullptr; - } - - // Number of times process dumping has timed out. If we encounter too many - // failures, we'll give up. - int timeout_failures = 0; - bool dalvik_found = false; - - const std::set<int> hal_pids = get_interesting_hal_pids(); - - struct dirent* d; - while ((d = readdir(proc.get()))) { - int pid = atoi(d->d_name); - if (pid <= 0) { - continue; - } - - const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid); - std::string exe; - if (!android::base::Readlink(link_name, &exe)) { - continue; - } - - bool is_java_process; - if (exe == "/system/bin/app_process32" || exe == "/system/bin/app_process64") { - // Don't bother dumping backtraces for the zygote. - if (IsZygote(pid)) { - continue; - } - - dalvik_found = true; - is_java_process = true; - } else if (should_dump_native_traces(exe.c_str()) || hal_pids.find(pid) != hal_pids.end()) { - is_java_process = false; - } else { - // Probably a native process we don't care about, continue. - continue; - } - - // If 3 backtrace dumps fail in a row, consider debuggerd dead. - if (timeout_failures == 3) { - dprintf(fd, "ERROR: Too many stack dump failures, exiting.\n"); - break; - } - - const uint64_t start = Nanotime(); - const int ret = dump_backtrace_to_file_timeout( - pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace, - is_java_process ? 5 : 20, fd); - - if (ret == -1) { - dprintf(fd, "dumping failed, likely due to a timeout\n"); - timeout_failures++; - continue; - } - - // We've successfully dumped stack traces, reset the failure count - // and write a summary of the elapsed time to the file and continue with the - // next process. - timeout_failures = 0; - - dprintf(fd, "[dump %s stack %d: %.3fs elapsed]\n", is_java_process ? "dalvik" : "native", - pid, (float)(Nanotime() - start) / NANOS_PER_SEC); - } - - if (!dalvik_found) { - MYLOGE("Warning: no Dalvik processes found to dump stacks\n"); - } - - return file_name_buf.release(); -} - -const char* DumpTraces(const std::string& traces_path) { - const char* result = NULL; - /* move the old traces.txt (if any) out of the way temporarily */ - std::string anrtraces_path = traces_path + ".anr"; - if (rename(traces_path.c_str(), anrtraces_path.c_str()) && errno != ENOENT) { - MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), anrtraces_path.c_str(), strerror(errno)); - return nullptr; // Can't rename old traces.txt -- no permission? -- leave it alone instead - } - - /* create a new, empty traces.txt file to receive stack dumps */ - int fd = TEMP_FAILURE_RETRY( - open(traces_path.c_str(), O_CREAT | O_WRONLY | O_APPEND | O_TRUNC | O_NOFOLLOW | O_CLOEXEC, - 0666)); /* -rw-rw-rw- */ - if (fd < 0) { - MYLOGE("%s: %s\n", traces_path.c_str(), strerror(errno)); - return nullptr; - } - int chmod_ret = fchmod(fd, 0666); - if (chmod_ret < 0) { - MYLOGE("fchmod on %s failed: %s\n", traces_path.c_str(), strerror(errno)); - close(fd); - return nullptr; - } - - /* Variables below must be initialized before 'goto' statements */ - int dalvik_found = 0; - int ifd, wfd = -1; - std::set<int> hal_pids = get_interesting_hal_pids(); - - /* walk /proc and kill -QUIT all Dalvik processes */ - DIR *proc = opendir("/proc"); - if (proc == NULL) { - MYLOGE("/proc: %s\n", strerror(errno)); - goto error_close_fd; - } - - /* use inotify to find when processes are done dumping */ - ifd = inotify_init(); - if (ifd < 0) { - MYLOGE("inotify_init: %s\n", strerror(errno)); - goto error_close_fd; - } - - wfd = inotify_add_watch(ifd, traces_path.c_str(), IN_CLOSE_WRITE); - if (wfd < 0) { - MYLOGE("inotify_add_watch(%s): %s\n", traces_path.c_str(), strerror(errno)); - goto error_close_ifd; - } - - struct dirent *d; - while ((d = readdir(proc))) { - int pid = atoi(d->d_name); - if (pid <= 0) continue; - - char path[PATH_MAX]; - char data[PATH_MAX]; - snprintf(path, sizeof(path), "/proc/%d/exe", pid); - ssize_t len = readlink(path, data, sizeof(data) - 1); - if (len <= 0) { - continue; - } - data[len] = '\0'; - - if (!strncmp(data, "/system/bin/app_process", strlen("/system/bin/app_process"))) { - /* skip zygote -- it won't dump its stack anyway */ - snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); - int cfd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC)); - len = read(cfd, data, sizeof(data) - 1); - close(cfd); - if (len <= 0) { - continue; - } - data[len] = '\0'; - if (!strncmp(data, "zygote", strlen("zygote"))) { - continue; - } - - ++dalvik_found; - uint64_t start = Nanotime(); - if (kill(pid, SIGQUIT)) { - MYLOGE("kill(%d, SIGQUIT): %s\n", pid, strerror(errno)); - continue; - } - - /* wait for the writable-close notification from inotify */ - struct pollfd pfd = { ifd, POLLIN, 0 }; - int ret = poll(&pfd, 1, TRACE_DUMP_TIMEOUT_MS); - if (ret < 0) { - MYLOGE("poll: %s\n", strerror(errno)); - } else if (ret == 0) { - MYLOGE("warning: timed out dumping pid %d\n", pid); - } else { - struct inotify_event ie; - read(ifd, &ie, sizeof(ie)); - } - - if (lseek(fd, 0, SEEK_END) < 0) { - MYLOGE("lseek: %s\n", strerror(errno)); - } else { - dprintf(fd, "[dump dalvik stack %d: %.3fs elapsed]\n", pid, - (float)(Nanotime() - start) / NANOS_PER_SEC); - } - } else if (should_dump_native_traces(data) || - hal_pids.find(pid) != hal_pids.end()) { - /* dump native process if appropriate */ - if (lseek(fd, 0, SEEK_END) < 0) { - MYLOGE("lseek: %s\n", strerror(errno)); - } else { - static uint16_t timeout_failures = 0; - uint64_t start = Nanotime(); - - /* If 3 backtrace dumps fail in a row, consider debuggerd dead. */ - if (timeout_failures == 3) { - dprintf(fd, "too many stack dump failures, skipping...\n"); - } else if (dump_backtrace_to_file_timeout( - pid, kDebuggerdNativeBacktrace, 20, fd) == -1) { - dprintf(fd, "dumping failed, likely due to a timeout\n"); - timeout_failures++; - } else { - timeout_failures = 0; - } - dprintf(fd, "[dump native stack %d: %.3fs elapsed]\n", pid, - (float)(Nanotime() - start) / NANOS_PER_SEC); - } - } - } - - if (dalvik_found == 0) { - MYLOGE("Warning: no Dalvik processes found to dump stacks\n"); - } - - static std::string dumptraces_path = android::base::StringPrintf( - "%s/bugreport-%s", dirname(traces_path.c_str()), basename(traces_path.c_str())); - if (rename(traces_path.c_str(), dumptraces_path.c_str())) { - MYLOGE("rename(%s, %s): %s\n", traces_path.c_str(), dumptraces_path.c_str(), - strerror(errno)); - goto error_close_ifd; - } - result = dumptraces_path.c_str(); - - /* replace the saved [ANR] traces.txt file */ - rename(anrtraces_path.c_str(), traces_path.c_str()); - -error_close_ifd: - close(ifd); -error_close_fd: - close(fd); - return result; +bool redirect_to_existing_file(FILE* redirect, char* path) { + return _redirect_to_file(redirect, path, O_APPEND); } void dump_route_tables() { @@ -1084,7 +837,7 @@ bool max_changed = progress_->Inc(delta_sec); // ...but only notifiy listeners when necessary. - if (!update_progress_) return; + if (!options_->do_progress_updates) return; int progress = progress_->Get(); int max = progress_->GetMax(); @@ -1105,16 +858,22 @@ fsync(control_socket_fd_); } + int percent = 100 * progress / max; if (listener_ != nullptr) { - if (progress % 100 == 0) { - // We don't want to spam logcat, so only log multiples of 100. - MYLOGD("Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max); + if (percent % 5 == 0) { + // We don't want to spam logcat, so only log multiples of 5. + MYLOGD("Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), progress, max, + percent); } else { // stderr is ignored on normal invocations, but useful when calling // /system/bin/dumpstate directly for debuggging. - fprintf(stderr, "Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress, max); + fprintf(stderr, "Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), + progress, max, percent); } + // TODO(b/111441001): Remove in favor of onProgress listener_->onProgressUpdated(progress); + + listener_->onProgress(percent); } }
diff --git a/cmds/dumpsys/Android.bp b/cmds/dumpsys/Android.bp index f68b862..f99588f 100644 --- a/cmds/dumpsys/Android.bp +++ b/cmds/dumpsys/Android.bp
@@ -20,8 +20,6 @@ static_libs: [ "libserviceutils", ], - - clang: true, } // @@ -36,7 +34,6 @@ export_include_dirs: ["."], } - // // Executable // @@ -51,4 +48,15 @@ ], } -subdirs = ["tests"] +cc_binary { + name: "dumpsys_vendor", + stem: "dumpsys", + + vendor: true, + + defaults: ["dumpsys_defaults"], + + srcs: [ + "main.cpp", + ], +}
diff --git a/cmds/dumpsys/OWNERS b/cmds/dumpsys/OWNERS new file mode 100644 index 0000000..1ba7cff --- /dev/null +++ b/cmds/dumpsys/OWNERS
@@ -0,0 +1,6 @@ +set noparent + +felipeal@google.com +nandana@google.com +jsharkey@android.com +enh@google.com
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 5412d4d..4811927 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp
@@ -65,7 +65,7 @@ " -l: only list services, do not dump them\n" " -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n" " -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n" - " --proto: filter services that support dumping data in proto format. Dumps" + " --proto: filter services that support dumping data in proto format. Dumps\n" " will be in proto format.\n" " --priority LEVEL: filter services based on specified priority\n" " LEVEL must be one of CRITICAL | HIGH | NORMAL\n" @@ -389,7 +389,7 @@ auto time_left_ms = [end]() { auto now = std::chrono::steady_clock::now(); auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now); - return std::max(diff.count(), 0ll); + return std::max(diff.count(), 0LL); }; int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h index 84f3b02..c48a1e9 100644 --- a/cmds/dumpsys/dumpsys.h +++ b/cmds/dumpsys/dumpsys.h
@@ -26,7 +26,7 @@ class Dumpsys { public: - Dumpsys(android::IServiceManager* sm) : sm_(sm) { + explicit Dumpsys(android::IServiceManager* sm) : sm_(sm) { } /** * Main entry point into dumpsys.
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 5029352..3ada153 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -74,7 +74,7 @@ explicit WriteOnFdAction(const std::string& output) : output_(output) { } virtual Result Perform(const ArgumentTuple& args) { - int fd = ::std::tr1::get<0>(args); + int fd = ::testing::get<0>(args); android::base::WriteStringToFd(output_, fd); } @@ -371,8 +371,8 @@ IServiceManager::DUMP_FLAG_PRIORITY_NORMAL); ExpectCheckService("Locksmith"); ExpectCheckService("Valet"); - ExpectDumpWithArgs("Locksmith", {"-a", "--dump-priority", "NORMAL"}, "dump1"); - ExpectDumpWithArgs("Valet", {"-a", "--dump-priority", "NORMAL"}, "dump2"); + ExpectDumpWithArgs("Locksmith", {"--dump-priority", "NORMAL", "-a"}, "dump1"); + ExpectDumpWithArgs("Valet", {"--dump-priority", "NORMAL", "-a"}, "dump2"); CallMain({"--priority", "NORMAL"});
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp index d5b3372..d398559 100644 --- a/cmds/flatland/GLHelper.cpp +++ b/cmds/flatland/GLHelper.cpp
@@ -29,7 +29,7 @@ mContext(EGL_NO_CONTEXT), mDummySurface(EGL_NO_SURFACE), mConfig(0), - mShaderPrograms(NULL), + mShaderPrograms(nullptr), mDitherTexture(0) { } @@ -101,12 +101,12 @@ } void GLHelper::tearDown() { - if (mShaderPrograms != NULL) { + if (mShaderPrograms != nullptr) { delete[] mShaderPrograms; - mShaderPrograms = NULL; + mShaderPrograms = nullptr; } - if (mSurfaceComposerClient != NULL) { + if (mSurfaceComposerClient != nullptr) { mSurfaceComposerClient->dispose(); mSurfaceComposerClient.clear(); } @@ -210,7 +210,7 @@ glc->setConsumerUsageBits(GRALLOC_USAGE_HW_COMPOSER); sp<ANativeWindow> anw = new Surface(producer); - EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), NULL); + EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), nullptr); if (s == EGL_NO_SURFACE) { fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError()); return false; @@ -222,9 +222,9 @@ } bool GLHelper::computeWindowScale(uint32_t w, uint32_t h, float* scale) { - sp<IBinder> dpy = mSurfaceComposerClient->getBuiltInDisplay(0); - if (dpy == NULL) { - fprintf(stderr, "SurfaceComposer::getBuiltInDisplay failed.\n"); + const sp<IBinder> dpy = mSurfaceComposerClient->getInternalDisplayToken(); + if (dpy == nullptr) { + fprintf(stderr, "SurfaceComposer::getInternalDisplayToken failed.\n"); return false; } @@ -247,7 +247,7 @@ bool result; status_t err; - if (mSurfaceComposerClient == NULL) { + if (mSurfaceComposerClient == nullptr) { mSurfaceComposerClient = new SurfaceComposerClient; } err = mSurfaceComposerClient->initCheck(); @@ -258,7 +258,7 @@ sp<SurfaceControl> sc = mSurfaceComposerClient->createSurface( String8("Benchmark"), w, h, PIXEL_FORMAT_RGBA_8888, 0); - if (sc == NULL || !sc->isValid()) { + if (sc == nullptr || !sc->isValid()) { fprintf(stderr, "Failed to create SurfaceControl.\n"); return false; } @@ -275,7 +275,7 @@ .apply(); sp<ANativeWindow> anw = sc->getSurface(); - EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), NULL); + EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), nullptr); if (s == EGL_NO_SURFACE) { fprintf(stderr, "eglCreateWindowSurface error: %#x\n", eglGetError()); return false; @@ -294,7 +294,7 @@ return false; } - glShaderSource(shader, 1, &src, NULL); + glShaderSource(shader, 1, &src, nullptr); glCompileShader(shader); GLint compiled = 0; @@ -305,7 +305,7 @@ if (infoLen) { char* buf = new char[infoLen]; if (buf) { - glGetShaderInfoLog(shader, infoLen, NULL, buf); + glGetShaderInfoLog(shader, infoLen, nullptr, buf); fprintf(stderr, "Shader compile log:\n%s\n", buf); delete[] buf; } @@ -318,21 +318,21 @@ } static void printShaderSource(const char* const* src) { - for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != NULL; i++) { + for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != nullptr; i++) { fprintf(stderr, "%3zu: %s\n", i+1, src[i]); } } static const char* makeShaderString(const char* const* src) { size_t len = 0; - for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != NULL; i++) { + for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != nullptr; i++) { // The +1 is for the '\n' that will be added. len += strlen(src[i]) + 1; } char* result = new char[len+1]; char* end = result; - for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != NULL; i++) { + for (size_t i = 0; i < MAX_SHADER_LINES && src[i] != nullptr; i++) { strcpy(end, src[i]); end += strlen(src[i]); *end = '\n'; @@ -376,7 +376,7 @@ if (bufLength) { char* buf = new char[bufLength]; if (buf) { - glGetProgramInfoLog(program, bufLength, NULL, buf); + glGetProgramInfoLog(program, bufLength, nullptr, buf); fprintf(stderr, "Program link log:\n%s\n", buf); delete[] buf; }
diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp index 3d7cac0..7ceb397 100644 --- a/cmds/flatland/Main.cpp +++ b/cmds/flatland/Main.cpp
@@ -284,7 +284,7 @@ public: Layer() : - mGLHelper(NULL), + mGLHelper(nullptr), mSurface(EGL_NO_SURFACE) { } @@ -316,23 +316,23 @@ } void tearDown() { - if (mComposer != NULL) { + if (mComposer != nullptr) { mComposer->tearDown(); delete mComposer; - mComposer = NULL; + mComposer = nullptr; } - if (mRenderer != NULL) { + if (mRenderer != nullptr) { mRenderer->tearDown(); delete mRenderer; - mRenderer = NULL; + mRenderer = nullptr; } if (mSurface != EGL_NO_SURFACE) { mGLHelper->destroySurface(&mSurface); mGLConsumer->abandon(); } - mGLHelper = NULL; + mGLHelper = nullptr; mGLConsumer.clear(); } @@ -377,7 +377,7 @@ mDesc(desc), mInstance(instance), mNumLayers(countLayers(desc)), - mGLHelper(NULL), + mGLHelper(nullptr), mSurface(EGL_NO_SURFACE), mWindowSurface(EGL_NO_SURFACE) { } @@ -443,7 +443,7 @@ mLayers[i].tearDown(); } - if (mGLHelper != NULL) { + if (mGLHelper != nullptr) { if (mWindowSurface != EGL_NO_SURFACE) { mGLHelper->destroySurface(&mWindowSurface); } @@ -453,7 +453,7 @@ mSurfaceControl.clear(); mGLHelper->tearDown(); delete mGLHelper; - mGLHelper = NULL; + mGLHelper = nullptr; } } @@ -553,7 +553,7 @@ static size_t countLayers(const BenchmarkDesc& desc) { size_t i; for (i = 0; i < MAX_NUM_LAYERS; i++) { - if (desc.layers[i].rendererFactory == NULL) { + if (desc.layers[i].rendererFactory == nullptr) { break; } }
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index 94c3102..c80ae3b 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp
@@ -14,9 +14,12 @@ "CacheItem.cpp", "CacheTracker.cpp", "InstalldNativeService.cpp", + "QuotaUtils.cpp", "dexopt.cpp", "globals.cpp", "utils.cpp", + "utils_default.cpp", + "view_compiler.cpp", ":installd_aidl", ], header_libs: [ @@ -29,10 +32,27 @@ "libcutils", "liblog", "liblogwrap", + "libprocessgroup", "libselinux", "libutils", + "server_configurable_flags", ], + product_variables: { + arc: { + exclude_srcs: [ + "QuotaUtils.cpp", + ], + static_libs: [ + "libarcdiskquota", + "arc_services_aidl", + ], + cflags: [ + "-DUSE_ARC", + ], + }, + }, + clang: true, tidy: true, @@ -59,6 +79,26 @@ aidl: { export_aidl_headers: true, }, + + product_variables: { + arc: { + exclude_srcs: [ + "QuotaUtils.cpp", + ], + static_libs: [ + "libarcdiskquota", + "arc_services_aidl", + ], + cflags: [ + "-DUSE_ARC", + ], + }, + }, +} + +cc_library_headers { + name: "libinstalld_headers", + export_include_dirs: ["."], } // @@ -73,6 +113,25 @@ static_libs: ["libdiskusage"], init_rc: ["installd.rc"], + + product_variables: { + arc: { + exclude_srcs: [ + "QuotaUtils.cpp", + ], + static_libs: [ + "libarcdiskquota", + "arc_services_aidl", + ], + cflags: [ + "-DUSE_ARC", + ], + }, + }, + + // Needs to be wherever installd is as it's execed by + // installd. + required: [ "migrate_legacy_obb_data.sh" ], } // OTA chroot tool @@ -85,10 +144,26 @@ ], clang: true, - srcs: ["otapreopt_chroot.cpp"], + srcs: [ + "otapreopt_chroot.cpp", + "otapreopt_utils.cpp", + ], shared_libs: [ "libbase", + "libbinder", "liblog", + "libprotobuf-cpp-full", + "libselinux", + "libutils", + "libziparchive", + ], + static_libs: [ + "libapex", + "libapexd", + "lib_apex_manifest_proto", + "libavb", + "libdm", + "libvold_binder", ], } @@ -108,10 +183,8 @@ "-Wall", "-Werror" ], - clang: true, - srcs: [ - "otapreopt_parameters.cpp"], + srcs: ["otapreopt_parameters.cpp"], export_include_dirs: ["."], @@ -119,8 +192,75 @@ "libbase", "libcutils", "liblog", + "libprocessgroup", "libutils", ], } -subdirs = ["tests"] +// +// OTA Executable +// + +cc_binary { + name: "otapreopt", + cflags: [ + "-Wall", + "-Werror" + ], + + srcs: [ + "dexopt.cpp", + "globals.cpp", + "otapreopt.cpp", + "otapreopt_utils.cpp", + "utils.cpp", + "utils_default.cpp", + "view_compiler.cpp", + ], + + header_libs: ["dex2oat_headers"], + + static_libs: [ + "libartimagevalues", + "libdiskusage", + "libotapreoptparameters", + ], + + shared_libs: [ + "libbase", + "libcrypto", + "libcutils", + "liblog", + "liblogwrap", + "libprocessgroup", + "libselinux", + "libutils", + "server_configurable_flags", + ], +} + +// OTA slot script +sh_binary { + name: "otapreopt_slot", + src: "otapreopt_slot.sh", + init_rc: ["otapreopt.rc"], +} + +// OTA postinstall script +sh_binary { + name: "otapreopt_script", + src: "otapreopt_script.sh", + // Let this depend on otapreopt, the chroot tool and the slot script, + // so we just have to mention one in a configuration. + required: [ + "otapreopt", + "otapreopt_chroot", + "otapreopt_slot", + ], +} + +// Script to migrate legacy obb data. +sh_binary { + name: "migrate_legacy_obb_data.sh", + src: "migrate_legacy_obb_data.sh" +}
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk deleted file mode 100644 index a4f95da..0000000 --- a/cmds/installd/Android.mk +++ /dev/null
@@ -1,64 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -# -# OTA Executable -# - -include $(CLEAR_VARS) -LOCAL_MODULE := otapreopt -LOCAL_CFLAGS := -Wall -Werror - -# Base & ASLR boundaries for boot image creation. -ifndef LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA - LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA := -0x1000000 -else - LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA := $(LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA) -endif -ifndef LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA - LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA := 0x1000000 -else - LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA := $(LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA) -endif -LOCAL_CFLAGS += -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) -LOCAL_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA) -LOCAL_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA) - -LOCAL_SRC_FILES := otapreopt.cpp otapreopt_parameters.cpp globals.cpp utils.cpp dexopt.cpp -LOCAL_HEADER_LIBRARIES := dex2oat_headers -LOCAL_SHARED_LIBRARIES := \ - libbase \ - libcrypto \ - libcutils \ - liblog \ - liblogwrap \ - libselinux \ - libutils \ - -LOCAL_STATIC_LIBRARIES := libdiskusage -LOCAL_CLANG := true -include $(BUILD_EXECUTABLE) - -# OTA slot script - -include $(CLEAR_VARS) -LOCAL_MODULE:= otapreopt_slot -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := EXECUTABLES -LOCAL_SRC_FILES := otapreopt_slot.sh -LOCAL_INIT_RC := otapreopt.rc - -include $(BUILD_PREBUILT) - -# OTA postinstall script - -include $(CLEAR_VARS) -LOCAL_MODULE:= otapreopt_script -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := EXECUTABLES -LOCAL_SRC_FILES := otapreopt_script.sh - -# Let this depend on otapreopt, the chroot tool and the slot script, so we just have to mention one -# in a configuration. -LOCAL_REQUIRED_MODULES := otapreopt otapreopt_chroot otapreopt_slot - -include $(BUILD_PREBUILT)
diff --git a/cmds/installd/CacheItem.cpp b/cmds/installd/CacheItem.cpp index 515f915..e29ff4c 100644 --- a/cmds/installd/CacheItem.cpp +++ b/cmds/installd/CacheItem.cpp
@@ -73,7 +73,7 @@ FTS *fts; FTSENT *p; char *argv[] = { (char*) path.c_str(), nullptr }; - if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) { + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { PLOG(WARNING) << "Failed to fts_open " << path; return -1; }
diff --git a/cmds/installd/CacheTracker.cpp b/cmds/installd/CacheTracker.cpp index ea0cd9e..8b868fb 100644 --- a/cmds/installd/CacheTracker.cpp +++ b/cmds/installd/CacheTracker.cpp
@@ -19,13 +19,13 @@ #include "CacheTracker.h" #include <fts.h> -#include <sys/quota.h> #include <sys/xattr.h> #include <utils/Trace.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> +#include "QuotaUtils.h" #include "utils.h" using android::base::StringPrintf; @@ -33,9 +33,13 @@ namespace android { namespace installd { -CacheTracker::CacheTracker(userid_t userId, appid_t appId, const std::string& quotaDevice) : - cacheUsed(0), cacheQuota(0), mUserId(userId), mAppId(appId), mQuotaDevice(quotaDevice), - mItemsLoaded(false) { +CacheTracker::CacheTracker(userid_t userId, appid_t appId, const std::string& uuid) + : cacheUsed(0), + cacheQuota(0), + mUserId(userId), + mAppId(appId), + mItemsLoaded(false), + mUuid(uuid) { } CacheTracker::~CacheTracker() { @@ -72,26 +76,18 @@ bool CacheTracker::loadQuotaStats() { int cacheGid = multiuser_get_cache_gid(mUserId, mAppId); int extCacheGid = multiuser_get_ext_cache_gid(mUserId, mAppId); - if (!mQuotaDevice.empty() && cacheGid != -1 && extCacheGid != -1) { - struct dqblk dq; - if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), mQuotaDevice.c_str(), cacheGid, - reinterpret_cast<char*>(&dq)) != 0) { - if (errno != ESRCH) { - PLOG(ERROR) << "Failed to quotactl " << mQuotaDevice << " for GID " << cacheGid; - } - return false; + if (IsQuotaSupported(mUuid) && cacheGid != -1 && extCacheGid != -1) { + int64_t space; + if ((space = GetOccupiedSpaceForGid(mUuid, cacheGid)) != -1) { + cacheUsed += space; } else { - cacheUsed += dq.dqb_curspace; + return false; } - if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), mQuotaDevice.c_str(), extCacheGid, - reinterpret_cast<char*>(&dq)) != 0) { - if (errno != ESRCH) { - PLOG(ERROR) << "Failed to quotactl " << mQuotaDevice << " for GID " << cacheGid; - } - return false; + if ((space = GetOccupiedSpaceForGid(mUuid, extCacheGid)) != -1) { + cacheUsed += space; } else { - cacheUsed += dq.dqb_curspace; + return false; } return true; } else { @@ -103,7 +99,7 @@ FTS *fts; FTSENT *p; char *argv[] = { (char*) path.c_str(), nullptr }; - if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) { + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { PLOG(WARNING) << "Failed to fts_open " << path; return; }
diff --git a/cmds/installd/CacheTracker.h b/cmds/installd/CacheTracker.h index 44359b4..b0527e7 100644 --- a/cmds/installd/CacheTracker.h +++ b/cmds/installd/CacheTracker.h
@@ -39,7 +39,7 @@ */ class CacheTracker { public: - CacheTracker(userid_t userId, appid_t appId, const std::string& quotaDevice); + CacheTracker(userid_t userId, appid_t appId, const std::string& uuid); ~CacheTracker(); std::string toString(); @@ -61,8 +61,8 @@ private: userid_t mUserId; appid_t mAppId; - std::string mQuotaDevice; bool mItemsLoaded; + const std::string& mUuid; std::vector<std::string> mDataPaths;
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 860a68b..caac2e8 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp
@@ -31,7 +31,6 @@ #include <sys/file.h> #include <sys/ioctl.h> #include <sys/mman.h> -#include <sys/quota.h> #include <sys/resource.h> #include <sys/stat.h> #include <sys/statvfs.h> @@ -40,8 +39,10 @@ #include <sys/xattr.h> #include <unistd.h> +#include <android-base/file.h> #include <android-base/logging.h> #include <android-base/properties.h> +#include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> @@ -61,9 +62,11 @@ #include "installd_deps.h" #include "otapreopt_utils.h" #include "utils.h" +#include "view_compiler.h" #include "CacheTracker.h" #include "MatchExtensionGen.h" +#include "QuotaUtils.h" #ifndef LOG_TAG #define LOG_TAG "installd" @@ -75,9 +78,13 @@ namespace android { namespace installd { +// An uuid used in unit tests. +static constexpr const char* kTestUuid = "TEST"; + +static constexpr const mode_t kRollbackFolderMode = 0700; + static constexpr const char* kCpPath = "/system/bin/cp"; static constexpr const char* kXattrDefault = "user.default"; -static constexpr const char* kPropHasReserved = "vold.has_reserved"; static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M @@ -95,15 +102,6 @@ static constexpr size_t kSha256Size = 32; static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode"; -// NOTE: keep in sync with Installer -static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8; -static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; -static constexpr int FLAG_USE_QUOTA = 1 << 12; -static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13; -static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14; -static constexpr int FLAG_FREE_CACHE_NOOP = 1 << 15; -static constexpr int FLAG_FORCE = 1 << 16; - namespace { constexpr const char* kDump = "android.permission.DUMP"; @@ -163,8 +161,17 @@ } } +binder::Status checkArgumentUuidTestOrNull(const std::unique_ptr<std::string>& uuid) { + if (!uuid || strcmp(uuid->c_str(), kTestUuid) == 0) { + return ok(); + } else { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("UUID must be null or \"%s\", got: %s", kTestUuid, uuid->c_str())); + } +} + binder::Status checkArgumentPackageName(const std::string& packageName) { - if (is_valid_package_name(packageName.c_str())) { + if (is_valid_package_name(packageName)) { return ok(); } else { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, @@ -215,6 +222,13 @@ } \ } +#define CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(uuid) { \ + auto status = checkArgumentUuidTestOrNull(uuid); \ + if (!status.isOk()) { \ + return status; \ + } \ +} \ + #define CHECK_ARGUMENT_PACKAGE_NAME(packageName) { \ binder::Status status = \ checkArgumentPackageName((packageName)); \ @@ -267,11 +281,6 @@ for (const auto& n : mStorageMounts) { out << " " << n.first << " = " << n.second << endl; } - - out << endl << "Quota reverse mounts:" << endl; - for (const auto& n : mQuotaReverseMounts) { - out << " " << n.first << " = " << n.second << endl; - } } { @@ -352,55 +361,6 @@ return 0; } -/** - * Ensure that we have a hard-limit quota to protect against abusive apps; - * they should never use more than 90% of blocks or 50% of inodes. - */ -static int prepare_app_quota(const std::unique_ptr<std::string>& uuid ATTRIBUTE_UNUSED, - const std::string& device, uid_t uid) { - // Skip when reserved blocks are protecting us against abusive apps - if (android::base::GetBoolProperty(kPropHasReserved, false)) return 0; - // Skip when device no quotas present - if (device.empty()) return 0; - - struct dqblk dq; - if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid, - reinterpret_cast<char*>(&dq)) != 0) { - PLOG(WARNING) << "Failed to find quota for " << uid; - return -1; - } - -#if APPLY_HARD_QUOTAS - if ((dq.dqb_bhardlimit == 0) || (dq.dqb_ihardlimit == 0)) { - auto path = create_data_path(uuid ? uuid->c_str() : nullptr); - struct statvfs stat; - if (statvfs(path.c_str(), &stat) != 0) { - PLOG(WARNING) << "Failed to statvfs " << path; - return -1; - } - - dq.dqb_valid = QIF_LIMITS; - dq.dqb_bhardlimit = - (((static_cast<uint64_t>(stat.f_blocks) * stat.f_frsize) / 10) * 9) / QIF_DQBLKSIZE; - dq.dqb_ihardlimit = (stat.f_files / 2); - if (quotactl(QCMD(Q_SETQUOTA, USRQUOTA), device.c_str(), uid, - reinterpret_cast<char*>(&dq)) != 0) { - PLOG(WARNING) << "Failed to set hard quota for " << uid; - return -1; - } else { - LOG(DEBUG) << "Applied hard quotas for " << uid; - return 0; - } - } else { - // Hard quota already set; assume it's reasonable - return 0; - } -#else - // Hard quotas disabled - return 0; -#endif -} - static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) { if (!property_get_bool("dalvik.vm.usejitprofiles", false)) { return true; @@ -515,10 +475,6 @@ return error("Failed to restorecon " + path); } - if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid), uid)) { - return error("Failed to set hard quota " + path); - } - if (!prepare_app_profile_dir(packageName, appId, userId)) { return error("Failed to prepare profiles for " + packageName); } @@ -632,6 +588,41 @@ } } } + if (flags & FLAG_STORAGE_EXTERNAL) { + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + for (const auto& n : mStorageMounts) { + auto extPath = n.second; + if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) { + extPath += StringPrintf("/%d", userId); + } else if (userId != 0) { + // TODO: support devices mounted under secondary users + continue; + } + if (flags & FLAG_CLEAR_CACHE_ONLY) { + // Clear only cached data from shared storage + auto path = StringPrintf("%s/Android/data/%s/cache", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { + // No code cache on shared storage + } else { + // Clear everything on shared storage + auto path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + } + } + } return res; } @@ -693,6 +684,30 @@ // Verify if it's ok to do that. destroy_app_reference_profile(packageName); } + if (flags & FLAG_STORAGE_EXTERNAL) { + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + for (const auto& n : mStorageMounts) { + auto extPath = n.second; + if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) { + extPath += StringPrintf("/%d", userId); + } else if (userId != 0) { + // TODO: support devices mounted under secondary users + continue; + } + auto path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname); + if (delete_dir_contents_and_dir(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname); + if (delete_dir_contents_and_dir(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname); + if (delete_dir_contents_and_dir(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + } + } return res; } @@ -715,7 +730,7 @@ auto ce_path = create_data_user_ce_path(uuid_, user); auto de_path = create_data_user_de_path(uuid_, user); char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), nullptr }; - if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) { + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { return error("Failed to fts_open"); } while ((p = fts_read(fts)) != nullptr) { @@ -775,7 +790,7 @@ PLOG(WARNING) << "Failed to chmod " << p->fts_path; } } - // Intentional fall through to also set GID + [[fallthrough]]; // also set GID case FTS_F: if (chown(p->fts_path, -1, expected) != 0) { PLOG(WARNING) << "Failed to chown " << p->fts_path; @@ -804,6 +819,253 @@ return ok(); } +static int32_t copy_directory_recursive(const char* from, const char* to) { + char *argv[] = { + (char*) kCpPath, + (char*) "-F", /* delete any existing destination file first (--remove-destination) */ + (char*) "-p", /* preserve timestamps, ownership, and permissions */ + (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ + (char*) "-P", /* Do not follow symlinks [default] */ + (char*) "-d", /* don't dereference symlinks */ + (char*) from, + (char*) to + }; + + LOG(DEBUG) << "Copying " << from << " to " << to; + return android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); +} + +binder::Status InstalldNativeService::snapshotAppData( + const std::unique_ptr<std::string>& volumeUuid, + const std::string& packageName, int32_t user, int32_t snapshotId, + int32_t storageFlags, int64_t* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + std::lock_guard<std::recursive_mutex> lock(mLock); + + const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; + const char* package_name = packageName.c_str(); + + binder::Status res = ok(); + // Default result to 0, it will be populated with inode of ce data snapshot + // if FLAG_STORAGE_CE has been passed. + if (_aidl_return != nullptr) *_aidl_return = 0; + + bool clear_ce_on_exit = false; + bool clear_de_on_exit = false; + + auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name, + &snapshotId] { + if (clear_de_on_exit) { + auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, snapshotId, + package_name); + if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { + LOG(WARNING) << "Failed to delete app data snapshot: " << to; + } + } + + if (clear_ce_on_exit) { + auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId, + package_name); + if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { + LOG(WARNING) << "Failed to delete app data snapshot: " << to; + } + } + }; + + auto scope_guard = android::base::make_scope_guard(deleter); + + // The app may not have any data at all, in which case it's OK to skip here. + auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name); + if (access(from_ce.c_str(), F_OK) != 0) { + LOG(INFO) << "Missing source " << from_ce; + return ok(); + } + + // ce_data_inode is not needed when FLAG_CLEAR_CACHE_ONLY is set. + binder::Status clear_cache_result = clearAppData(volumeUuid, packageName, user, + storageFlags | FLAG_CLEAR_CACHE_ONLY, 0); + if (!clear_cache_result.isOk()) { + // It should be fine to continue snapshot if we for some reason failed + // to clear cache. + LOG(WARNING) << "Failed to clear cache of app " << packageName; + } + + // ce_data_inode is not needed when FLAG_CLEAR_CODE_CACHE_ONLY is set. + binder::Status clear_code_cache_result = clearAppData(volumeUuid, packageName, user, + storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY, 0); + if (!clear_code_cache_result.isOk()) { + // It should be fine to continue snapshot if we for some reason failed + // to clear code_cache. + LOG(WARNING) << "Failed to clear code_cache of app " << packageName; + } + + if (storageFlags & FLAG_STORAGE_DE) { + auto from = create_data_user_de_package_path(volume_uuid, user, package_name); + auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId); + auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user, + snapshotId, package_name); + + int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode); + if (rc != 0) { + return error(rc, "Failed to create folder " + to); + } + + rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */); + if (rc != 0) { + return error(rc, "Failed clearing existing snapshot " + rollback_package_path); + } + + rc = copy_directory_recursive(from.c_str(), to.c_str()); + if (rc != 0) { + res = error(rc, "Failed copying " + from + " to " + to); + clear_de_on_exit = true; + return res; + } + } + + if (storageFlags & FLAG_STORAGE_CE) { + auto from = create_data_user_ce_package_path(volume_uuid, user, package_name); + auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId); + auto rollback_package_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, + snapshotId, package_name); + + int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode); + if (rc != 0) { + return error(rc, "Failed to create folder " + to); + } + + rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */); + if (rc != 0) { + return error(rc, "Failed clearing existing snapshot " + rollback_package_path); + } + + rc = copy_directory_recursive(from.c_str(), to.c_str()); + if (rc != 0) { + res = error(rc, "Failed copying " + from + " to " + to); + clear_ce_on_exit = true; + return res; + } + if (_aidl_return != nullptr) { + auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, + snapshotId, package_name); + rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return)); + if (rc != 0) { + res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path); + clear_ce_on_exit = true; + return res; + } + } + } + + return res; +} + +binder::Status InstalldNativeService::restoreAppDataSnapshot( + const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName, + const int32_t appId, const std::string& seInfo, const int32_t user, + const int32_t snapshotId, int32_t storageFlags) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + std::lock_guard<std::recursive_mutex> lock(mLock); + + const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; + const char* package_name = packageName.c_str(); + + auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, + user, snapshotId, package_name); + auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, + user, snapshotId, package_name); + + const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) && + (access(from_ce.c_str(), F_OK) == 0); + const bool needs_de_rollback = (storageFlags & FLAG_STORAGE_DE) && + (access(from_de.c_str(), F_OK) == 0); + + if (!needs_ce_rollback && !needs_de_rollback) { + return ok(); + } + + // We know we're going to rollback one of the CE or DE data, so we clear + // application data first. Note that it's possible that we're asked to + // restore both CE & DE data but that one of the restores fail. Leaving the + // app with no data in those cases is arguably better than leaving the app + // with mismatched / stale data. + LOG(INFO) << "Clearing app data for " << packageName << " to restore snapshot."; + // It's fine to pass 0 as ceDataInode here, because restoreAppDataSnapshot + // can only be called when user unlocks the phone, meaning that CE user data + // is decrypted. + binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, + 0 /* ceDataInode */); + if (!res.isOk()) { + return res; + } + + if (needs_ce_rollback) { + auto to_ce = create_data_user_ce_path(volume_uuid, user); + int rc = copy_directory_recursive(from_ce.c_str(), to_ce.c_str()); + if (rc != 0) { + res = error(rc, "Failed copying " + from_ce + " to " + to_ce); + return res; + } + } + + if (needs_de_rollback) { + auto to_de = create_data_user_de_path(volume_uuid, user); + int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str()); + if (rc != 0) { + if (needs_ce_rollback) { + auto ce_data = create_data_user_ce_package_path(volume_uuid, user, package_name); + LOG(WARNING) << "de_data rollback failed. Erasing rolled back ce_data " << ce_data; + if (delete_dir_contents(ce_data.c_str(), 1, nullptr) != 0) { + LOG(WARNING) << "Failed to delete rolled back ce_data " << ce_data; + } + } + res = error(rc, "Failed copying " + from_de + " to " + to_de); + return res; + } + } + + // Finally, restore the SELinux label on the app data. + return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo); +} + +binder::Status InstalldNativeService::destroyAppDataSnapshot( + const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName, + const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId, + int32_t storageFlags) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + std::lock_guard<std::recursive_mutex> lock(mLock); + + const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; + const char* package_name = packageName.c_str(); + + if (storageFlags & FLAG_STORAGE_DE) { + auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, + user, snapshotId, package_name); + + int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */); + if (res != 0) { + return error(res, "Failed clearing snapshot " + de_snapshot_path); + } + } + + if (storageFlags & FLAG_STORAGE_CE) { + auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, + user, snapshotId, package_name, ceSnapshotInode); + int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */); + if (res != 0) { + return error(res, "Failed clearing snapshot " + ce_snapshot_path); + } + } + return ok(); +} + + binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid, const std::unique_ptr<std::string>& toUuid, const std::string& packageName, const std::string& dataAppName, int32_t appId, const std::string& seInfo, @@ -828,19 +1090,7 @@ auto to = create_data_app_package_path(to_uuid, data_app_name); auto to_parent = create_data_app_path(to_uuid); - char *argv[] = { - (char*) kCpPath, - (char*) "-F", /* delete any existing destination file first (--remove-destination) */ - (char*) "-p", /* preserve timestamps, ownership, and permissions */ - (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ - (char*) "-P", /* Do not follow symlinks [default] */ - (char*) "-d", /* don't dereference symlinks */ - (char*) from.c_str(), - (char*) to_parent.c_str() - }; - - LOG(DEBUG) << "Copying " << from << " to " << to; - int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true); + int rc = copy_directory_recursive(from.c_str(), to_parent.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; @@ -868,25 +1118,11 @@ goto fail; } - char *argv[] = { - (char*) kCpPath, - (char*) "-F", /* delete any existing destination file first (--remove-destination) */ - (char*) "-p", /* preserve timestamps, ownership, and permissions */ - (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ - (char*) "-P", /* Do not follow symlinks [default] */ - (char*) "-d", /* don't dereference symlinks */ - nullptr, - nullptr - }; - { auto from = create_data_user_de_package_path(from_uuid, user, package_name); auto to = create_data_user_de_path(to_uuid, user); - argv[6] = (char*) from.c_str(); - argv[7] = (char*) to.c_str(); - LOG(DEBUG) << "Copying " << from << " to " << to; - int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true); + int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; @@ -895,11 +1131,8 @@ { auto from = create_data_user_ce_package_path(from_uuid, user, package_name); auto to = create_data_user_ce_path(to_uuid, user); - argv[6] = (char*) from.c_str(); - argv[7] = (char*) to.c_str(); - LOG(DEBUG) << "Copying " << from << " to " << to; - int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true); + int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; @@ -922,20 +1155,20 @@ // Nuke everything we might have already copied { auto to = create_data_app_package_path(to_uuid, data_app_name); - if (delete_dir_contents(to.c_str(), 1, NULL) != 0) { + if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to rollback " << to; } } for (auto user : users) { { auto to = create_data_user_de_package_path(to_uuid, user, package_name); - if (delete_dir_contents(to.c_str(), 1, NULL) != 0) { + if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to rollback " << to; } } { auto to = create_data_user_ce_package_path(to_uuid, user, package_name); - if (delete_dir_contents(to.c_str(), 1, NULL) != 0) { + if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to rollback " << to; } } @@ -958,13 +1191,6 @@ } } - // Data under /data/media doesn't have an app, but we still want - // to limit it to prevent abuse. - if (prepare_app_quota(uuid, findQuotaDeviceForUuid(uuid), - multiuser_get_uid(userId, AID_MEDIA_RW))) { - return error("Failed to set hard quota for media_rw"); - } - return ok(); } @@ -1011,9 +1237,9 @@ CHECK_ARGUMENT_UUID(uuid); std::lock_guard<std::recursive_mutex> lock(mLock); + auto uuidString = uuid ? *uuid : ""; const char* uuid_ = uuid ? uuid->c_str() : nullptr; auto data_path = create_data_path(uuid_); - auto device = findQuotaDeviceForUuid(uuid); auto noop = (flags & FLAG_FREE_CACHE_NOOP); int64_t free = data_disk_free(data_path); @@ -1045,10 +1271,10 @@ auto media_path = findDataMediaPath(uuid, user) + "/Android/data/"; char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), (char*) media_path.c_str(), nullptr }; - if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) { + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { return error("Failed to fts_open"); } - while ((p = fts_read(fts)) != NULL) { + while ((p = fts_read(fts)) != nullptr) { if (p->fts_info == FTS_D && p->fts_level == 1) { uid_t uid = p->fts_statp->st_uid; if (multiuser_get_app_id(uid) == AID_MEDIA_RW) { @@ -1060,7 +1286,7 @@ search->second->addDataPath(p->fts_path); } else { auto tracker = std::shared_ptr<CacheTracker>(new CacheTracker( - multiuser_get_user_id(uid), multiuser_get_app_id(uid), device)); + multiuser_get_user_id(uid), multiuser_get_app_id(uid), uuidString)); tracker->addDataPath(p->fts_path); { std::lock_guard<std::recursive_mutex> lock(mQuotasLock); @@ -1225,53 +1451,26 @@ } #endif -static void collectQuotaStats(const std::string& device, int32_t userId, +static void collectQuotaStats(const std::string& uuid, int32_t userId, int32_t appId, struct stats* stats, struct stats* extStats) { - if (device.empty()) return; - - struct dqblk dq; - + int64_t space; if (stats != nullptr) { uid_t uid = multiuser_get_uid(userId, appId); - if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid, - reinterpret_cast<char*>(&dq)) != 0) { - if (errno != ESRCH) { - PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid; - } - } else { -#if MEASURE_DEBUG - LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace; -#endif - stats->dataSize += dq.dqb_curspace; + if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) { + stats->dataSize += space; } int cacheGid = multiuser_get_cache_gid(userId, appId); if (cacheGid != -1) { - if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), cacheGid, - reinterpret_cast<char*>(&dq)) != 0) { - if (errno != ESRCH) { - PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << cacheGid; - } - } else { -#if MEASURE_DEBUG - LOG(DEBUG) << "quotactl() for GID " << cacheGid << " " << dq.dqb_curspace; -#endif - stats->cacheSize += dq.dqb_curspace; + if ((space = GetOccupiedSpaceForGid(uuid, cacheGid)) != -1) { + stats->cacheSize += space; } } int sharedGid = multiuser_get_shared_gid(0, appId); if (sharedGid != -1) { - if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), sharedGid, - reinterpret_cast<char*>(&dq)) != 0) { - if (errno != ESRCH) { - PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << sharedGid; - } - } else { -#if MEASURE_DEBUG - LOG(DEBUG) << "quotactl() for GID " << sharedGid << " " << dq.dqb_curspace; -#endif - stats->codeSize += dq.dqb_curspace; + if ((space = GetOccupiedSpaceForGid(uuid, sharedGid)) != -1) { + stats->codeSize += space; } } } @@ -1279,32 +1478,16 @@ if (extStats != nullptr) { int extGid = multiuser_get_ext_gid(userId, appId); if (extGid != -1) { - if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), extGid, - reinterpret_cast<char*>(&dq)) != 0) { - if (errno != ESRCH) { - PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << extGid; - } - } else { -#if MEASURE_DEBUG - LOG(DEBUG) << "quotactl() for GID " << extGid << " " << dq.dqb_curspace; -#endif - extStats->dataSize += dq.dqb_curspace; + if ((space = GetOccupiedSpaceForGid(uuid, extGid)) != -1) { + extStats->dataSize += space; } } int extCacheGid = multiuser_get_ext_cache_gid(userId, appId); if (extCacheGid != -1) { - if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), extCacheGid, - reinterpret_cast<char*>(&dq)) != 0) { - if (errno != ESRCH) { - PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << extCacheGid; - } - } else { -#if MEASURE_DEBUG - LOG(DEBUG) << "quotactl() for GID " << extCacheGid << " " << dq.dqb_curspace; -#endif - extStats->dataSize += dq.dqb_curspace; - extStats->cacheSize += dq.dqb_curspace; + if ((space = GetOccupiedSpaceForGid(uuid, extCacheGid)) != -1) { + extStats->dataSize += space; + extStats->cacheSize += space; } } } @@ -1398,11 +1581,11 @@ FTS *fts; FTSENT *p; char *argv[] = { (char*) path.c_str(), nullptr }; - if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) { + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { PLOG(ERROR) << "Failed to fts_open " << path; return; } - while ((p = fts_read(fts)) != NULL) { + while ((p = fts_read(fts)) != nullptr) { p->fts_number = p->fts_parent->fts_number; switch (p->fts_info) { case FTS_D: @@ -1412,7 +1595,7 @@ && !strcmp(p->fts_parent->fts_parent->fts_parent->fts_name, "Android")) { p->fts_number = 1; } - // Fall through to count the directory + [[fallthrough]]; // to count the directory case FTS_DEFAULT: case FTS_F: case FTS_SL: @@ -1468,16 +1651,17 @@ memset(&stats, 0, sizeof(stats)); memset(&extStats, 0, sizeof(extStats)); + auto uuidString = uuid ? *uuid : ""; const char* uuid_ = uuid ? uuid->c_str() : nullptr; - auto device = findQuotaDeviceForUuid(uuid); - if (device.empty()) { + if (!IsQuotaSupported(uuidString)) { flags &= ~FLAG_USE_QUOTA; } ATRACE_BEGIN("obb"); for (const auto& packageName : packageNames) { - auto obbCodePath = create_data_media_obb_path(uuid_, packageName.c_str()); + auto obbCodePath = create_data_media_package_path(uuid_, userId, + "obb", packageName.c_str()); calculate_tree_size(obbCodePath, &extStats.codeSize); } ATRACE_END(); @@ -1491,7 +1675,7 @@ ATRACE_END(); ATRACE_BEGIN("quota"); - collectQuotaStats(device, userId, appId, &stats, &extStats); + collectQuotaStats(uuidString, userId, appId, &stats, &extStats); ATRACE_END(); } else { ATRACE_BEGIN("code"); @@ -1574,27 +1758,19 @@ memset(&stats, 0, sizeof(stats)); memset(&extStats, 0, sizeof(extStats)); + auto uuidString = uuid ? *uuid : ""; const char* uuid_ = uuid ? uuid->c_str() : nullptr; - auto device = findQuotaDeviceForUuid(uuid); - if (device.empty()) { + if (!IsQuotaSupported(uuidString)) { flags &= ~FLAG_USE_QUOTA; } if (flags & FLAG_USE_QUOTA) { - struct dqblk dq; + int64_t space; ATRACE_BEGIN("obb"); - if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), AID_MEDIA_OBB, - reinterpret_cast<char*>(&dq)) != 0) { - if (errno != ESRCH) { - PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << AID_MEDIA_OBB; - } - } else { -#if MEASURE_DEBUG - LOG(DEBUG) << "quotactl() for GID " << AID_MEDIA_OBB << " " << dq.dqb_curspace; -#endif - extStats.codeSize += dq.dqb_curspace; + if ((space = GetOccupiedSpaceForGid(uuidString, AID_MEDIA_OBB)) != -1) { + extStats.codeSize += space; } ATRACE_END(); @@ -1620,16 +1796,8 @@ ATRACE_BEGIN("external"); uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW); - if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid, - reinterpret_cast<char*>(&dq)) != 0) { - if (errno != ESRCH) { - PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid; - } - } else { -#if MEASURE_DEBUG - LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace; -#endif - extStats.dataSize += dq.dqb_curspace; + if ((space = GetOccupiedSpaceForUid(uuidString, uid)) != -1) { + extStats.dataSize += space; } ATRACE_END(); @@ -1646,7 +1814,7 @@ int64_t dataSize = extStats.dataSize; for (auto appId : appIds) { if (appId >= AID_APP_START) { - collectQuotaStats(device, userId, appId, &stats, &extStats); + collectQuotaStats(uuidString, userId, appId, &stats, &extStats); #if MEASURE_DEBUG // Sleep to make sure we don't lose logs @@ -1728,6 +1896,7 @@ LOG(INFO) << "Measuring external " << userId; #endif + auto uuidString = uuid ? *uuid : ""; const char* uuid_ = uuid ? uuid->c_str() : nullptr; int64_t totalSize = 0; @@ -1737,58 +1906,33 @@ int64_t appSize = 0; int64_t obbSize = 0; - auto device = findQuotaDeviceForUuid(uuid); - if (device.empty()) { + if (!IsQuotaSupported(uuidString)) { flags &= ~FLAG_USE_QUOTA; } if (flags & FLAG_USE_QUOTA) { - struct dqblk dq; + int64_t space; ATRACE_BEGIN("quota"); uid_t uid = multiuser_get_uid(userId, AID_MEDIA_RW); - if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid, - reinterpret_cast<char*>(&dq)) != 0) { - if (errno != ESRCH) { - PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid; - } - } else { -#if MEASURE_DEBUG - LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace; -#endif - totalSize = dq.dqb_curspace; + if ((space = GetOccupiedSpaceForUid(uuidString, uid)) != -1) { + totalSize = space; } gid_t audioGid = multiuser_get_uid(userId, AID_MEDIA_AUDIO); - if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), audioGid, - reinterpret_cast<char*>(&dq)) == 0) { -#if MEASURE_DEBUG - LOG(DEBUG) << "quotactl() for GID " << audioGid << " " << dq.dqb_curspace; -#endif - audioSize = dq.dqb_curspace; + if ((space = GetOccupiedSpaceForGid(uuidString, audioGid)) != -1) { + audioSize = space; } gid_t videoGid = multiuser_get_uid(userId, AID_MEDIA_VIDEO); - if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), videoGid, - reinterpret_cast<char*>(&dq)) == 0) { -#if MEASURE_DEBUG - LOG(DEBUG) << "quotactl() for GID " << videoGid << " " << dq.dqb_curspace; -#endif - videoSize = dq.dqb_curspace; + if ((space = GetOccupiedSpaceForGid(uuidString, videoGid)) != -1) { + videoSize = space; } gid_t imageGid = multiuser_get_uid(userId, AID_MEDIA_IMAGE); - if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), imageGid, - reinterpret_cast<char*>(&dq)) == 0) { -#if MEASURE_DEBUG - LOG(DEBUG) << "quotactl() for GID " << imageGid << " " << dq.dqb_curspace; -#endif - imageSize = dq.dqb_curspace; + if ((space = GetOccupiedSpaceForGid(uuidString, imageGid)) != -1) { + imageSize = space; } - if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), AID_MEDIA_OBB, - reinterpret_cast<char*>(&dq)) == 0) { -#if MEASURE_DEBUG - LOG(DEBUG) << "quotactl() for GID " << AID_MEDIA_OBB << " " << dq.dqb_curspace; -#endif - obbSize = dq.dqb_curspace; + if ((space = GetOccupiedSpaceForGid(uuidString, AID_MEDIA_OBB)) != -1) { + obbSize = space; } ATRACE_END(); @@ -1797,7 +1941,7 @@ memset(&extStats, 0, sizeof(extStats)); for (auto appId : appIds) { if (appId >= AID_APP_START) { - collectQuotaStats(device, userId, appId, nullptr, &extStats); + collectQuotaStats(uuidString, userId, appId, nullptr, &extStats); } } appSize = extStats.dataSize; @@ -1808,10 +1952,10 @@ FTSENT *p; auto path = create_data_media_path(uuid_, userId); char *argv[] = { (char*) path.c_str(), nullptr }; - if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) { + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { return error("Failed to fts_open " + path); } - while ((p = fts_read(fts)) != NULL) { + while ((p = fts_read(fts)) != nullptr) { char* ext; int64_t size = (p->fts_statp->st_blocks * 512); switch (p->fts_info) { @@ -1827,13 +1971,14 @@ } } } - // Fall through to always count against total + [[fallthrough]]; // always count against total case FTS_D: // Ignore data belonging to specific apps p->fts_number = p->fts_parent->fts_number; if (p->fts_level == 1 && !strcmp(p->fts_name, "Android")) { p->fts_number = 1; } + [[fallthrough]]; // always count against total case FTS_DEFAULT: case FTS_SL: case FTS_SLNONE: @@ -1848,7 +1993,8 @@ ATRACE_END(); ATRACE_BEGIN("obb"); - auto obbPath = create_data_media_obb_path(uuid_, ""); + auto obbPath = StringPrintf("%s/Android/obb", + create_data_media_path(uuid_, userId).c_str()); calculate_tree_size(obbPath, &obbSize); ATRACE_END(); } @@ -1979,6 +2125,17 @@ return res ? error(res, error_msg) : ok(); } +binder::Status InstalldNativeService::compileLayouts(const std::string& apkPath, + const std::string& packageName, + const std ::string& outDexFile, int uid, + bool* _aidl_return) { + const char* apk_path = apkPath.c_str(); + const char* package_name = packageName.c_str(); + const char* out_dex_file = outDexFile.c_str(); + *_aidl_return = android::installd::view_compiler(apk_path, package_name, out_dex_file, uid); + return *_aidl_return ? ok() : error("viewcompiler failed"); +} + binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); std::lock_guard<std::recursive_mutex> lock(mLock); @@ -2024,8 +2181,14 @@ return error("Failed to stat " + _pkgdir); } + char *con = nullptr; + if (lgetfilecon(pkgdir, &con) < 0) { + return error("Failed to lgetfilecon " + _pkgdir); + } + if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) { - return error("Failed to chown " + _pkgdir); + res = error("Failed to chown " + _pkgdir); + goto out; } if (chmod(pkgdir, 0700) < 0) { @@ -2040,7 +2203,7 @@ } } else { if (S_ISDIR(libStat.st_mode)) { - if (delete_dir_contents(libsymlink, 1, NULL) < 0) { + if (delete_dir_contents(libsymlink, 1, nullptr) < 0) { res = error("Failed to delete " + _libsymlink); goto out; } @@ -2057,7 +2220,13 @@ goto out; } + if (lsetfilecon(libsymlink, con) < 0) { + res = error("Failed to lsetfilecon " + _libsymlink); + goto out; + } + out: + free(con); if (chmod(pkgdir, s.st_mode) < 0) { auto msg = "Failed to cleanup chmod " + _pkgdir; if (res.isOk()) { @@ -2082,14 +2251,14 @@ static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd) { execl(kIdMapPath, kIdMapPath, "--fd", target_apk, overlay_apk, - StringPrintf("%d", idmap_fd).c_str(), (char*)NULL); + StringPrintf("%d", idmap_fd).c_str(), (char*)nullptr); PLOG(ERROR) << "execl (" << kIdMapPath << ") failed"; } static void run_verify_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd) { execl(kIdMapPath, kIdMapPath, "--verify", target_apk, overlay_apk, - StringPrintf("%d", idmap_fd).c_str(), (char*)NULL); + StringPrintf("%d", idmap_fd).c_str(), (char*)nullptr); PLOG(ERROR) << "execl (" << kIdMapPath << ") failed"; } @@ -2140,7 +2309,7 @@ static int flatten_path(const char *prefix, const char *suffix, const char *overlay_path, char *idmap_path, size_t N) { - if (overlay_path == NULL || idmap_path == NULL) { + if (overlay_path == nullptr || idmap_path == nullptr) { return -1; } const size_t len_overlay_path = strlen(overlay_path); @@ -2345,7 +2514,7 @@ if (validate_apk_path(packageDir.c_str())) { return error("Invalid path " + packageDir); } - if (delete_dir_contents_and_dir(packageDir) != 0) { + if (rm_package_dir(packageDir) != 0) { return error("Failed to delete " + packageDir); } return ok(); @@ -2481,7 +2650,7 @@ std::to_string(shmSize)); } auto data = std::unique_ptr<void, std::function<void (void *)>>( - mmap(NULL, contentSize, PROT_READ, MAP_SHARED, verityInputAshmem.get(), 0), + mmap(nullptr, contentSize, PROT_READ, MAP_SHARED, verityInputAshmem.get(), 0), [contentSize] (void* ptr) { if (ptr != MAP_FAILED) { munmap(ptr, contentSize); @@ -2584,7 +2753,12 @@ std::lock_guard<std::recursive_mutex> lock(mMountsLock); mStorageMounts.clear(); - mQuotaReverseMounts.clear(); + +#if !BYPASS_QUOTA + if (!InvalidateQuotaMounts()) { + return error("Failed to read mounts"); + } +#endif std::ifstream in("/proc/mounts"); if (!in.is_open()) { @@ -2605,32 +2779,6 @@ mStorageMounts[source] = target; } #endif - -#if !BYPASS_QUOTA - if (source.compare(0, 11, "/dev/block/") == 0) { - struct dqblk dq; - if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0, - reinterpret_cast<char*>(&dq)) == 0) { - LOG(DEBUG) << "Found quota mount " << source << " at " << target; - mQuotaReverseMounts[target] = source; - - // ext4 only enables DQUOT_USAGE_ENABLED by default, so we - // need to kick it again to enable DQUOT_LIMITS_ENABLED. We - // only need hard limits enabled when we're not being protected - // by reserved blocks. - if (!android::base::GetBoolProperty(kPropHasReserved, false)) { - if (quotactl(QCMD(Q_QUOTAON, USRQUOTA), source.c_str(), QFMT_VFS_V1, - nullptr) != 0 && errno != EBUSY) { - PLOG(ERROR) << "Failed to enable USRQUOTA on " << source; - } - if (quotactl(QCMD(Q_QUOTAON, GRPQUOTA), source.c_str(), QFMT_VFS_V1, - nullptr) != 0 && errno != EBUSY) { - PLOG(ERROR) << "Failed to enable GRPQUOTA on " << source; - } - } - } - } -#endif } return ok(); } @@ -2648,16 +2796,10 @@ return StringPrintf("%s/%u", resolved.c_str(), userid); } -std::string InstalldNativeService::findQuotaDeviceForUuid( - const std::unique_ptr<std::string>& uuid) { - std::lock_guard<std::recursive_mutex> lock(mMountsLock); - auto path = create_data_path(uuid ? uuid->c_str() : nullptr); - return mQuotaReverseMounts[path]; -} - binder::Status InstalldNativeService::isQuotaSupported( - const std::unique_ptr<std::string>& volumeUuid, bool* _aidl_return) { - *_aidl_return = !findQuotaDeviceForUuid(volumeUuid).empty(); + const std::unique_ptr<std::string>& uuid, bool* _aidl_return) { + auto uuidString = uuid ? *uuid : ""; + *_aidl_return = IsQuotaSupported(uuidString); return ok(); } @@ -2674,5 +2816,16 @@ return ok(); } +binder::Status InstalldNativeService::migrateLegacyObbData() { + ENFORCE_UID(AID_SYSTEM); + // NOTE: The lint warning doesn't apply to the use of system(3) with + // absolute parse and no command line arguments. + if (system("/system/bin/migrate_legacy_obb_data.sh") != 0) { // NOLINT(cert-env33-c) + LOG(ERROR) << "Unable to migrate legacy obb data"; + } + + return ok(); +} + } // namespace installd } // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index cebd3f9..2b7bf33 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h
@@ -60,6 +60,16 @@ binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags); + binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid, + const std::string& packageName, const int32_t user, const int32_t snapshotId, + int32_t storageFlags, int64_t* _aidl_return); + binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid, + const std::string& packageName, const int32_t appId, const std::string& seInfo, + const int32_t user, const int32_t snapshotId, int32_t storageFlags); + binder::Status destroyAppDataSnapshot(const std::unique_ptr<std::string> &volumeUuid, + const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode, + const int32_t snapshotId, int32_t storageFlags); + binder::Status getAppSize(const std::unique_ptr<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, int32_t flags, int32_t appId, const std::vector<int64_t>& ceDataInodes, @@ -89,6 +99,9 @@ const std::unique_ptr<std::string>& dexMetadataPath, const std::unique_ptr<std::string>& compilationReason); + binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName, + const std::string& outDexFile, int uid, bool* _aidl_return); + binder::Status rmdex(const std::string& codePath, const std::string& instructionSet); binder::Status mergeProfiles(int32_t uid, const std::string& packageName, @@ -142,6 +155,8 @@ const std::string& codePath, const std::unique_ptr<std::string>& dexMetadata, bool* _aidl_return); + binder::Status migrateLegacyObbData(); + private: std::recursive_mutex mLock; @@ -150,14 +165,11 @@ /* Map of all storage mounts from source to target */ std::unordered_map<std::string, std::string> mStorageMounts; - /* Map of all quota mounts from target to source */ - std::unordered_map<std::string, std::string> mQuotaReverseMounts; /* Map from UID to cache quota size */ std::unordered_map<uid_t, int64_t> mCacheQuotas; std::string findDataMediaPath(const std::unique_ptr<std::string>& uuid, userid_t userid); - std::string findQuotaDeviceForUuid(const std::unique_ptr<std::string>& uuid); }; } // namespace installd
diff --git a/cmds/installd/MatchExtensionGen.h b/cmds/installd/MatchExtensionGen.h index fded6b7..35c3889 100644 --- a/cmds/installd/MatchExtensionGen.h +++ b/cmds/installd/MatchExtensionGen.h
@@ -31,6 +31,7 @@ switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; case 'p': case 'P': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; @@ -41,10 +42,15 @@ switch (ext[5]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; } + break; } + break; } + break; case 'a': case 'A': switch (ext[1]) { case 'a': case 'A': @@ -53,7 +59,9 @@ switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; case 'i': case 'I': switch (ext[2]) { case 'f': case 'F': @@ -63,56 +71,73 @@ switch (ext[4]) { case '\0': return AID_MEDIA_AUDIO; } + break; case 'f': case 'F': switch (ext[4]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; } + break; case 'm': case 'M': switch (ext[2]) { case 'r': case 'R': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; case 'r': case 'R': switch (ext[2]) { case 't': case 'T': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; case 'w': case 'W': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 's': case 'S': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; case 'v': case 'V': switch (ext[2]) { case 'i': case 'I': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; case 'w': case 'W': switch (ext[2]) { case 'b': case 'B': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; } + break; case 'b': case 'B': switch (ext[1]) { case 'm': case 'M': @@ -121,8 +146,11 @@ switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; } + break; case 'c': case 'C': switch (ext[1]) { case 'r': case 'R': @@ -131,8 +159,11 @@ switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; } + break; case 'd': case 'D': switch (ext[1]) { case 'i': case 'I': @@ -141,23 +172,30 @@ switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; case 'l': case 'L': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; } + break; case 'n': case 'N': switch (ext[2]) { case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'v': case 'V': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; case 'f': case 'F': switch (ext[1]) { case 'l': case 'L': @@ -168,13 +206,18 @@ switch (ext[4]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; case 'i': case 'I': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; } + break; case 'g': case 'G': switch (ext[1]) { case 'i': case 'I': @@ -183,15 +226,20 @@ switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 's': case 'S': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; } + break; case 'j': case 'J': switch (ext[1]) { case 'n': case 'N': @@ -200,7 +248,9 @@ switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'p': case 'P': switch (ext[2]) { case 'e': case 'E': @@ -210,13 +260,18 @@ switch (ext[4]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; } + break; case 'l': case 'L': switch (ext[1]) { case 's': case 'S': @@ -225,12 +280,16 @@ switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; } + break; case 'm': case 'M': switch (ext[1]) { case '3': @@ -239,36 +298,46 @@ switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; case '4': switch (ext[2]) { case 'a': case 'A': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; case 'v': case 'V': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; case 'k': case 'K': switch (ext[2]) { case 'a': case 'A': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; case 'v': case 'V': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; case 'n': case 'N': switch (ext[2]) { case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; case 'o': case 'O': switch (ext[2]) { case 'v': case 'V': @@ -280,23 +349,30 @@ switch (ext[5]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; } + break; } + break; case 'p': case 'P': switch (ext[2]) { case '2': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; case '3': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; case '4': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; case 'e': case 'E': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; @@ -307,8 +383,11 @@ switch (ext[5]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; } + break; case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; @@ -316,16 +395,22 @@ switch (ext[4]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; } + break; case 'x': case 'X': switch (ext[2]) { case 'u': case 'U': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; } + break; case 'n': case 'N': switch (ext[1]) { case 'e': case 'E': @@ -334,15 +419,20 @@ switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'r': case 'R': switch (ext[2]) { case 'w': case 'W': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; } + break; case 'o': case 'O': switch (ext[1]) { case 'g': case 'G': @@ -351,19 +441,25 @@ switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; case 'r': case 'R': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; } + break; case 'p': case 'P': switch (ext[1]) { case 'b': case 'B': @@ -372,68 +468,88 @@ switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'c': case 'C': switch (ext[2]) { case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'e': case 'E': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'g': case 'G': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'l': case 'L': switch (ext[2]) { case 's': case 'S': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; case 'n': case 'N': switch (ext[2]) { case 'g': case 'G': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'p': case 'P': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 's': case 'S': switch (ext[2]) { case 'd': case 'D': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; } + break; case 'q': case 'Q': switch (ext[1]) { case 't': case 'T': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; case 'r': case 'R': switch (ext[1]) { case 'a': case 'A': @@ -443,30 +559,39 @@ switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; case 's': case 'S': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'g': case 'G': switch (ext[2]) { case 'b': case 'B': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'm': case 'M': switch (ext[2]) { case '\0': return AID_MEDIA_AUDIO; } + break; case 'w': case 'W': switch (ext[2]) { case '2': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; } + break; case 's': case 'S': switch (ext[1]) { case 'd': case 'D': @@ -475,21 +600,27 @@ switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; case 'n': case 'N': switch (ext[2]) { case 'd': case 'D': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; case 'r': case 'R': switch (ext[2]) { case 'w': case 'W': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'v': case 'V': switch (ext[2]) { case 'g': case 'G': @@ -499,9 +630,13 @@ switch (ext[4]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; } + break; } + break; case 't': case 'T': switch (ext[1]) { case 'i': case 'I': @@ -513,13 +648,18 @@ switch (ext[4]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; } + break; case 's': case 'S': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; case 'v': case 'V': switch (ext[1]) { case 'o': case 'O': @@ -528,8 +668,11 @@ switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; } + break; case 'w': case 'W': switch (ext[1]) { case 'a': case 'A': @@ -538,11 +681,14 @@ switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; } + break; case 'b': case 'B': switch (ext[2]) { case 'm': case 'M': @@ -551,8 +697,11 @@ switch (ext[4]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; } + break; case 'e': case 'E': switch (ext[2]) { case 'b': case 'B': @@ -561,12 +710,16 @@ switch (ext[4]) { case '\0': return AID_MEDIA_VIDEO; } + break; case 'p': case 'P': switch (ext[4]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; } + break; case 'm': case 'M': switch (ext[2]) { case '\0': return AID_MEDIA_VIDEO; @@ -574,30 +727,39 @@ switch (ext[3]) { case '\0': return AID_MEDIA_AUDIO; } + break; case 'v': case 'V': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; case 'r': case 'R': switch (ext[2]) { case 'f': case 'F': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; case 'v': case 'V': switch (ext[2]) { case 'x': case 'X': switch (ext[3]) { case '\0': return AID_MEDIA_VIDEO; } + break; } + break; } + break; case 'x': case 'X': switch (ext[1]) { case 'b': case 'B': @@ -606,22 +768,29 @@ switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'p': case 'P': switch (ext[2]) { case 'm': case 'M': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; case 'w': case 'W': switch (ext[2]) { case 'd': case 'D': switch (ext[3]) { case '\0': return AID_MEDIA_IMAGE; } + break; } + break; } + break; } return 0;
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS new file mode 100644 index 0000000..5673918 --- /dev/null +++ b/cmds/installd/OWNERS
@@ -0,0 +1,10 @@ +set noparent + +agampe@google.com +calin@google.com +jsharkey@android.com +maco@google.com +mathieuc@google.com +narayan@google.com +ngeoffray@google.com +toddke@google.com
diff --git a/cmds/installd/QuotaUtils.cpp b/cmds/installd/QuotaUtils.cpp new file mode 100644 index 0000000..b238dd3 --- /dev/null +++ b/cmds/installd/QuotaUtils.cpp
@@ -0,0 +1,122 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QuotaUtils.h" + +#include <fstream> +#include <unordered_map> + +#include <sys/quota.h> + +#include <android-base/logging.h> + +#include "utils.h" + +namespace android { +namespace installd { + +namespace { + +std::recursive_mutex mMountsLock; + +/* Map of all quota mounts from target to source */ +std::unordered_map<std::string, std::string> mQuotaReverseMounts; + +std::string& FindQuotaDeviceForUuid(const std::string& uuid) { + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + auto path = create_data_path(uuid.empty() ? nullptr : uuid.c_str()); + return mQuotaReverseMounts[path]; +} + +} // namespace + +bool InvalidateQuotaMounts() { + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + + mQuotaReverseMounts.clear(); + + std::ifstream in("/proc/mounts"); + if (!in.is_open()) { + return false; + } + + std::string source; + std::string target; + std::string ignored; + while (!in.eof()) { + std::getline(in, source, ' '); + std::getline(in, target, ' '); + std::getline(in, ignored); + + if (source.compare(0, 11, "/dev/block/") == 0) { + struct dqblk dq; + if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0, + reinterpret_cast<char*>(&dq)) == 0) { + LOG(DEBUG) << "Found quota mount " << source << " at " << target; + mQuotaReverseMounts[target] = source; + } + } + } + return true; +} + +bool IsQuotaSupported(const std::string& uuid) { + return !FindQuotaDeviceForUuid(uuid).empty(); +} + +int64_t GetOccupiedSpaceForUid(const std::string& uuid, uid_t uid) { + const std::string device = FindQuotaDeviceForUuid(uuid); + if (device == "") { + return -1; + } + struct dqblk dq; + if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid, + reinterpret_cast<char*>(&dq)) != 0) { + if (errno != ESRCH) { + PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid; + } + return -1; + } else { +#if MEASURE_DEBUG + LOG(DEBUG) << "quotactl() for UID " << uid << " " << dq.dqb_curspace; +#endif + return dq.dqb_curspace; + } +} + +int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid) { + const std::string device = FindQuotaDeviceForUuid(uuid); + if (device == "") { + return -1; + } + struct dqblk dq; + if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), gid, + reinterpret_cast<char*>(&dq)) != 0) { + if (errno != ESRCH) { + PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << gid; + } + return -1; + } else { +#if MEASURE_DEBUG + LOG(DEBUG) << "quotactl() for GID " << gid << " " << dq.dqb_curspace; +#endif + return dq.dqb_curspace; + } + +} + +} // namespace installd +} // namespace android
diff --git a/cmds/installd/QuotaUtils.h b/cmds/installd/QuotaUtils.h new file mode 100644 index 0000000..9ad170f --- /dev/null +++ b/cmds/installd/QuotaUtils.h
@@ -0,0 +1,41 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INSTALLD_QUOTA_UTILS_H_ +#define ANDROID_INSTALLD_QUOTA_UTILS_H_ + +#include <memory> +#include <string> + +namespace android { +namespace installd { + +/* Clear and recompute the reverse mounts map */ +bool InvalidateQuotaMounts(); + +/* Whether quota is supported in the device with the given uuid */ +bool IsQuotaSupported(const std::string& uuid); + +/* Get the current occupied space in bytes for a uid or -1 if fails */ +int64_t GetOccupiedSpaceForUid(const std::string& uuid, uid_t uid); + +/* Get the current occupied space in bytes for a gid or -1 if fails */ +int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid); + +} // namespace installd +} // namespace android + +#endif // ANDROID_INSTALLD_QUOTA_UTILS_H_
diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING new file mode 100644 index 0000000..287f2d9 --- /dev/null +++ b/cmds/installd/TEST_MAPPING
@@ -0,0 +1,28 @@ +{ + "presubmit": [ + { + "name": "installd_cache_test" + }, + { + "name": "installd_dexopt_test" + }, + { + "name": "installd_otapreopt_test" + }, + { + "name": "installd_service_test" + }, + { + "name": "installd_utils_test" + }, + { + "name": "CtsUsesLibraryHostTestCases" + }, + { + "name": "CtsClassloaderSplitsHostTestCases" + }, + { + "name": "CtsCompilationTestCases" + } + ] +}
diff --git a/cmds/installd/art_helper/Android.bp b/cmds/installd/art_helper/Android.bp new file mode 100644 index 0000000..c47dd72 --- /dev/null +++ b/cmds/installd/art_helper/Android.bp
@@ -0,0 +1,12 @@ +// Inherit image values. +art_global_defaults { + name: "libartimagevalues_defaults", +} + +cc_library_static { + name: "libartimagevalues", + defaults: ["libartimagevalues_defaults"], + srcs: ["art_image_values.cpp"], + export_include_dirs: ["."], + cflags: ["-Wconversion"], +}
diff --git a/cmds/installd/art_helper/art_image_values.cpp b/cmds/installd/art_helper/art_image_values.cpp new file mode 100644 index 0000000..a139049 --- /dev/null +++ b/cmds/installd/art_helper/art_image_values.cpp
@@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "art_image_values.h" + +namespace android { +namespace installd { +namespace art { + +uint32_t GetImageBaseAddress() { + return ART_BASE_ADDRESS; +} +int32_t GetImageMinBaseAddressDelta() { + return ART_BASE_ADDRESS_MIN_DELTA; +} +int32_t GetImageMaxBaseAddressDelta() { + return ART_BASE_ADDRESS_MAX_DELTA; +} + +static_assert(ART_BASE_ADDRESS_MIN_DELTA < ART_BASE_ADDRESS_MAX_DELTA, "Inconsistent setup"); + +} // namespace art +} // namespace installd +} // namespace android
diff --git a/cmds/installd/art_helper/art_image_values.h b/cmds/installd/art_helper/art_image_values.h new file mode 100644 index 0000000..20c44c9 --- /dev/null +++ b/cmds/installd/art_helper/art_image_values.h
@@ -0,0 +1,34 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FRAMEWORKS_NATIVE_CMDS_INSTALLD_ART_HELPER_ART_IMAGE_VALUES_H +#define FRAMEWORKS_NATIVE_CMDS_INSTALLD_ART_HELPER_ART_IMAGE_VALUES_H + +#include <cstdint> + +namespace android { +namespace installd { +namespace art { + +uint32_t GetImageBaseAddress(); +int32_t GetImageMinBaseAddressDelta(); +int32_t GetImageMaxBaseAddressDelta(); + +} // namespace art +} // namespace installd +} // namespace android + +#endif // FRAMEWORKS_NATIVE_CMDS_INSTALLD_ART_HELPER_ART_IMAGE_VALUES_H
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 91e20b7..26e9984 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -55,6 +55,8 @@ @nullable @utf8InCpp String profileName, @nullable @utf8InCpp String dexMetadataPath, @nullable @utf8InCpp String compilationReason); + boolean compileLayouts(@utf8InCpp String apkPath, @utf8InCpp String packageName, + @utf8InCpp String outDexFile, int uid); void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet); @@ -102,4 +104,27 @@ boolean prepareAppProfile(@utf8InCpp String packageName, int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath, @nullable @utf8InCpp String dexMetadata); + + long snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, + int userId, int snapshotId, int storageFlags); + void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, + int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags); + void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, + int userId, long ceSnapshotInode, int snapshotId, int storageFlags); + + void migrateLegacyObbData(); + + const int FLAG_STORAGE_DE = 0x1; + const int FLAG_STORAGE_CE = 0x2; + const int FLAG_STORAGE_EXTERNAL = 0x4; + + const int FLAG_CLEAR_CACHE_ONLY = 0x10; + const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20; + + const int FLAG_FREE_CACHE_V2 = 0x100; + const int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 0x200; + const int FLAG_FREE_CACHE_NOOP = 0x400; + + const int FLAG_USE_QUOTA = 0x1000; + const int FLAG_FORCE = 0x2000; }
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index 9615a75..dbb4f22 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp
@@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#define LOG_TAG "installed" +#define LOG_TAG "installd" #include <array> #include <fcntl.h> @@ -43,7 +43,9 @@ #include <log/log.h> // TODO: Move everything to base/logging. #include <openssl/sha.h> #include <private/android_filesystem_config.h> +#include <processgroup/sched_policy.h> #include <selinux/android.h> +#include <server_configurable_flags/get_flags.h> #include <system/thread_defs.h> #include "dexopt.h" @@ -54,6 +56,9 @@ #include "utils.h" using android::base::EndsWith; +using android::base::GetBoolProperty; +using android::base::GetProperty; +using android::base::ReadFdToString; using android::base::ReadFully; using android::base::StringPrintf; using android::base::WriteFully; @@ -181,42 +186,17 @@ return clear_current_profile(package_name, location, user, /*is_secondary_dex*/false); } -static int split_count(const char *str) -{ - char *ctx; - int count = 0; - char buf[kPropertyValueMax]; - - strlcpy(buf, str, sizeof(buf)); - char *pBuf = buf; - - while(strtok_r(pBuf, " ", &ctx) != NULL) { - count++; - pBuf = NULL; - } - - return count; -} - -static int split(char *buf, const char **argv) -{ - char *ctx; - int count = 0; - char *tok; - char *pBuf = buf; - - while((tok = strtok_r(pBuf, " ", &ctx)) != NULL) { - argv[count++] = tok; - pBuf = NULL; - } - - return count; +static std::vector<std::string> SplitBySpaces(const std::string& str) { + if (str.empty()) { + return {}; + } + return android::base::Split(str, " "); } static const char* get_location_from_path(const char* path) { static constexpr char kLocationSeparator = '/'; const char *location = strrchr(path, kLocationSeparator); - if (location == NULL) { + if (location == nullptr) { return path; } else { // Skip the separator character. @@ -224,341 +204,352 @@ } } -[[ noreturn ]] -static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vdex_fd, int image_fd, - const char* input_file_name, const char* output_file_name, int swap_fd, - const char* instruction_set, const char* compiler_filter, - bool debuggable, bool post_bootcomplete, bool background_job_compile, int profile_fd, - const char* class_loader_context, int target_sdk_version, bool enable_hidden_api_checks, - bool generate_compact_dex, int dex_metadata_fd, const char* compilation_reason) { - static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; +// ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations +// need to be performed between the fork and exec. +class ExecVHelper { + public: + // Store a placeholder for the binary name. + ExecVHelper() : args_(1u, std::string()) {} - if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) { - LOG(ERROR) << "Instruction set '" << instruction_set << "' longer than max length of " - << MAX_INSTRUCTION_SET_LEN; - exit(DexoptReturnCodes::kInstructionSetLength); + void PrepareArgs(const std::string& bin) { + CHECK(!args_.empty()); + CHECK(args_[0].empty()); + args_[0] = bin; + // Write char* into array. + for (const std::string& arg : args_) { + argv_.push_back(arg.c_str()); + } + argv_.push_back(nullptr); // Add null terminator. } - // Get the relative path to the input file. - const char* relative_input_file_name = get_location_from_path(input_file_name); - - char dex2oat_Xms_flag[kPropertyValueMax]; - bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0; - - char dex2oat_Xmx_flag[kPropertyValueMax]; - bool have_dex2oat_Xmx_flag = get_property("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0; - - char dex2oat_threads_buf[kPropertyValueMax]; - bool have_dex2oat_threads_flag = get_property(post_bootcomplete - ? "dalvik.vm.dex2oat-threads" - : "dalvik.vm.boot-dex2oat-threads", - dex2oat_threads_buf, - NULL) > 0; - char dex2oat_threads_arg[kPropertyValueMax + 2]; - if (have_dex2oat_threads_flag) { - sprintf(dex2oat_threads_arg, "-j%s", dex2oat_threads_buf); + [[ noreturn ]] + void Exec(int exit_code) { + execv(argv_[0], (char * const *)&argv_[0]); + PLOG(ERROR) << "execv(" << argv_[0] << ") failed"; + exit(exit_code); } - char dex2oat_isa_features_key[kPropertyKeyMax]; - sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set); - char dex2oat_isa_features[kPropertyValueMax]; - bool have_dex2oat_isa_features = get_property(dex2oat_isa_features_key, - dex2oat_isa_features, NULL) > 0; - - char dex2oat_isa_variant_key[kPropertyKeyMax]; - sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set); - char dex2oat_isa_variant[kPropertyValueMax]; - bool have_dex2oat_isa_variant = get_property(dex2oat_isa_variant_key, - dex2oat_isa_variant, NULL) > 0; - - const char *dex2oat_norelocation = "-Xnorelocate"; - bool have_dex2oat_relocation_skip_flag = false; - - char dex2oat_flags[kPropertyValueMax]; - int dex2oat_flags_count = get_property("dalvik.vm.dex2oat-flags", - dex2oat_flags, NULL) <= 0 ? 0 : split_count(dex2oat_flags); - ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags); - - // If we are booting without the real /data, don't spend time compiling. - char vold_decrypt[kPropertyValueMax]; - bool have_vold_decrypt = get_property("vold.decrypt", vold_decrypt, "") > 0; - bool skip_compilation = (have_vold_decrypt && - (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 || - (strcmp(vold_decrypt, "1") == 0))); - - bool generate_debug_info = property_get_bool("debug.generate-debug-info", false); - - char app_image_format[kPropertyValueMax]; - char image_format_arg[strlen("--image-format=") + kPropertyValueMax]; - bool have_app_image_format = - image_fd >= 0 && get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0; - if (have_app_image_format) { - sprintf(image_format_arg, "--image-format=%s", app_image_format); - } - - char dex2oat_large_app_threshold[kPropertyValueMax]; - bool have_dex2oat_large_app_threshold = - get_property("dalvik.vm.dex2oat-very-large", dex2oat_large_app_threshold, NULL) > 0; - char dex2oat_large_app_threshold_arg[strlen("--very-large-app-threshold=") + kPropertyValueMax]; - if (have_dex2oat_large_app_threshold) { - sprintf(dex2oat_large_app_threshold_arg, - "--very-large-app-threshold=%s", - dex2oat_large_app_threshold); - } - - // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat. - const char* dex2oat_bin = "/system/bin/dex2oat"; - constexpr const char* kDex2oatDebugPath = "/system/bin/dex2oatd"; - // Do not use dex2oatd for release candidates (give dex2oat more soak time). - bool is_release = android::base::GetProperty("ro.build.version.codename", "") == "REL"; - if (is_debug_runtime() || (background_job_compile && is_debuggable_build() && !is_release)) { - if (access(kDex2oatDebugPath, X_OK) == 0) { - dex2oat_bin = kDex2oatDebugPath; + // Add an arg if it's not empty. + void AddArg(const std::string& arg) { + if (!arg.empty()) { + args_.push_back(arg); } } - bool generate_minidebug_info = kEnableMinidebugInfo && - android::base::GetBoolProperty(kMinidebugInfoSystemProperty, - kMinidebugInfoSystemPropertyDefault); - - static const char* RUNTIME_ARG = "--runtime-arg"; - - static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig - - // clang FORTIFY doesn't let us use strlen in constant array bounds, so we - // use arraysize instead. - char zip_fd_arg[arraysize("--zip-fd=") + MAX_INT_LEN]; - char zip_location_arg[arraysize("--zip-location=") + PKG_PATH_MAX]; - char input_vdex_fd_arg[arraysize("--input-vdex-fd=") + MAX_INT_LEN]; - char output_vdex_fd_arg[arraysize("--output-vdex-fd=") + MAX_INT_LEN]; - char oat_fd_arg[arraysize("--oat-fd=") + MAX_INT_LEN]; - char oat_location_arg[arraysize("--oat-location=") + PKG_PATH_MAX]; - char instruction_set_arg[arraysize("--instruction-set=") + MAX_INSTRUCTION_SET_LEN]; - char instruction_set_variant_arg[arraysize("--instruction-set-variant=") + kPropertyValueMax]; - char instruction_set_features_arg[arraysize("--instruction-set-features=") + kPropertyValueMax]; - char dex2oat_Xms_arg[arraysize("-Xms") + kPropertyValueMax]; - char dex2oat_Xmx_arg[arraysize("-Xmx") + kPropertyValueMax]; - char dex2oat_compiler_filter_arg[arraysize("--compiler-filter=") + kPropertyValueMax]; - bool have_dex2oat_swap_fd = false; - char dex2oat_swap_fd[arraysize("--swap-fd=") + MAX_INT_LEN]; - bool have_dex2oat_image_fd = false; - char dex2oat_image_fd[arraysize("--app-image-fd=") + MAX_INT_LEN]; - size_t class_loader_context_size = arraysize("--class-loader-context=") + PKG_PATH_MAX; - char target_sdk_version_arg[arraysize("-Xtarget-sdk-version:") + MAX_INT_LEN]; - char class_loader_context_arg[class_loader_context_size]; - if (class_loader_context != nullptr) { - snprintf(class_loader_context_arg, class_loader_context_size, "--class-loader-context=%s", - class_loader_context); - } - - sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd); - sprintf(zip_location_arg, "--zip-location=%s", relative_input_file_name); - sprintf(input_vdex_fd_arg, "--input-vdex-fd=%d", input_vdex_fd); - sprintf(output_vdex_fd_arg, "--output-vdex-fd=%d", output_vdex_fd); - sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd); - sprintf(oat_location_arg, "--oat-location=%s", output_file_name); - sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set); - sprintf(instruction_set_variant_arg, "--instruction-set-variant=%s", dex2oat_isa_variant); - sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features); - if (swap_fd >= 0) { - have_dex2oat_swap_fd = true; - sprintf(dex2oat_swap_fd, "--swap-fd=%d", swap_fd); - } - if (image_fd >= 0) { - have_dex2oat_image_fd = true; - sprintf(dex2oat_image_fd, "--app-image-fd=%d", image_fd); - } - - if (have_dex2oat_Xms_flag) { - sprintf(dex2oat_Xms_arg, "-Xms%s", dex2oat_Xms_flag); - } - if (have_dex2oat_Xmx_flag) { - sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag); - } - sprintf(target_sdk_version_arg, "-Xtarget-sdk-version:%d", target_sdk_version); - - // Compute compiler filter. - - bool have_dex2oat_compiler_filter_flag = false; - if (skip_compilation) { - strlcpy(dex2oat_compiler_filter_arg, "--compiler-filter=extract", - sizeof(dex2oat_compiler_filter_arg)); - have_dex2oat_compiler_filter_flag = true; - have_dex2oat_relocation_skip_flag = true; - } else if (compiler_filter != nullptr) { - if (strlen(compiler_filter) + strlen("--compiler-filter=") < - arraysize(dex2oat_compiler_filter_arg)) { - sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", compiler_filter); - have_dex2oat_compiler_filter_flag = true; - } else { - ALOGW("Compiler filter name '%s' is too large (max characters is %zu)", - compiler_filter, - kPropertyValueMax); + // Add a runtime arg if it's not empty. + void AddRuntimeArg(const std::string& arg) { + if (!arg.empty()) { + args_.push_back("--runtime-arg"); + args_.push_back(arg); } } - if (!have_dex2oat_compiler_filter_flag) { - char dex2oat_compiler_filter_flag[kPropertyValueMax]; - have_dex2oat_compiler_filter_flag = get_property("dalvik.vm.dex2oat-filter", - dex2oat_compiler_filter_flag, NULL) > 0; - if (have_dex2oat_compiler_filter_flag) { - sprintf(dex2oat_compiler_filter_arg, - "--compiler-filter=%s", - dex2oat_compiler_filter_flag); - } - } + protected: + // Holder arrays for backing arg storage. + std::vector<std::string> args_; - // Check whether all apps should be compiled debuggable. - if (!debuggable) { - char prop_buf[kPropertyValueMax]; - debuggable = - (get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) && - (prop_buf[0] == '1'); - } - char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN]; - if (profile_fd != -1) { - sprintf(profile_arg, "--profile-file-fd=%d", profile_fd); - } + // Argument poiners. + std::vector<const char*> argv_; +}; - // Get the directory of the apk to pass as a base classpath directory. - char base_dir[arraysize("--classpath-dir=") + PKG_PATH_MAX]; - std::string apk_dir(input_file_name); - unsigned long dir_index = apk_dir.rfind('/'); - bool has_base_dir = dir_index != std::string::npos; - if (has_base_dir) { - apk_dir = apk_dir.substr(0, dir_index); - sprintf(base_dir, "--classpath-dir=%s", apk_dir.c_str()); - } - - std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd); - - std::string compilation_reason_arg = compilation_reason == nullptr - ? "" - : std::string("--compilation-reason=") + compilation_reason; - - ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name); - - // Disable cdex if update input vdex is true since this combination of options is not - // supported. - const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd); - - const char* argv[9 // program name, mandatory arguments and the final NULL - + (have_dex2oat_isa_variant ? 1 : 0) - + (have_dex2oat_isa_features ? 1 : 0) - + (have_dex2oat_Xms_flag ? 2 : 0) - + (have_dex2oat_Xmx_flag ? 2 : 0) - + (have_dex2oat_compiler_filter_flag ? 1 : 0) - + (have_dex2oat_threads_flag ? 1 : 0) - + (have_dex2oat_swap_fd ? 1 : 0) - + (have_dex2oat_image_fd ? 1 : 0) - + (have_dex2oat_relocation_skip_flag ? 2 : 0) - + (generate_debug_info ? 1 : 0) - + (debuggable ? 1 : 0) - + (have_app_image_format ? 1 : 0) - + dex2oat_flags_count - + (profile_fd == -1 ? 0 : 1) - + (class_loader_context != nullptr ? 1 : 0) - + (has_base_dir ? 1 : 0) - + (have_dex2oat_large_app_threshold ? 1 : 0) - + (disable_cdex ? 1 : 0) - + (generate_minidebug_info ? 1 : 0) - + (target_sdk_version != 0 ? 2 : 0) - + (enable_hidden_api_checks ? 2 : 0) - + (dex_metadata_fd > -1 ? 1 : 0) - + (compilation_reason != nullptr ? 1 : 0)]; - int i = 0; - argv[i++] = dex2oat_bin; - argv[i++] = zip_fd_arg; - argv[i++] = zip_location_arg; - argv[i++] = input_vdex_fd_arg; - argv[i++] = output_vdex_fd_arg; - argv[i++] = oat_fd_arg; - argv[i++] = oat_location_arg; - argv[i++] = instruction_set_arg; - if (have_dex2oat_isa_variant) { - argv[i++] = instruction_set_variant_arg; - } - if (have_dex2oat_isa_features) { - argv[i++] = instruction_set_features_arg; - } - if (have_dex2oat_Xms_flag) { - argv[i++] = RUNTIME_ARG; - argv[i++] = dex2oat_Xms_arg; - } - if (have_dex2oat_Xmx_flag) { - argv[i++] = RUNTIME_ARG; - argv[i++] = dex2oat_Xmx_arg; - } - if (have_dex2oat_compiler_filter_flag) { - argv[i++] = dex2oat_compiler_filter_arg; - } - if (have_dex2oat_threads_flag) { - argv[i++] = dex2oat_threads_arg; - } - if (have_dex2oat_swap_fd) { - argv[i++] = dex2oat_swap_fd; - } - if (have_dex2oat_image_fd) { - argv[i++] = dex2oat_image_fd; - } - if (generate_debug_info) { - argv[i++] = "--generate-debug-info"; - } - if (debuggable) { - argv[i++] = "--debuggable"; - } - if (have_app_image_format) { - argv[i++] = image_format_arg; - } - if (have_dex2oat_large_app_threshold) { - argv[i++] = dex2oat_large_app_threshold_arg; - } - if (dex2oat_flags_count) { - i += split(dex2oat_flags, argv + i); - } - if (have_dex2oat_relocation_skip_flag) { - argv[i++] = RUNTIME_ARG; - argv[i++] = dex2oat_norelocation; - } - if (profile_fd != -1) { - argv[i++] = profile_arg; - } - if (has_base_dir) { - argv[i++] = base_dir; - } - if (class_loader_context != nullptr) { - argv[i++] = class_loader_context_arg; - } - if (generate_minidebug_info) { - argv[i++] = kMinidebugDex2oatFlag; - } - if (disable_cdex) { - argv[i++] = kDisableCompactDexFlag; - } - if (target_sdk_version != 0) { - argv[i++] = RUNTIME_ARG; - argv[i++] = target_sdk_version_arg; - } - if (enable_hidden_api_checks) { - argv[i++] = RUNTIME_ARG; - argv[i++] = "-Xhidden-api-checks"; - } - - if (dex_metadata_fd > -1) { - argv[i++] = dex_metadata_fd_arg.c_str(); - } - - if(compilation_reason != nullptr) { - argv[i++] = compilation_reason_arg.c_str(); - } - // Do not add after dex2oat_flags, they should override others for debugging. - argv[i] = NULL; - - execv(dex2oat_bin, (char * const *)argv); - PLOG(ERROR) << "execv(" << dex2oat_bin << ") failed"; - exit(DexoptReturnCodes::kDex2oatExec); +static std::string MapPropertyToArg(const std::string& property, + const std::string& format, + const std::string& default_value = "") { + std::string prop = GetProperty(property, default_value); + if (!prop.empty()) { + return StringPrintf(format.c_str(), prop.c_str()); + } + return ""; } +// Determines which binary we should use for execution (the debug or non-debug version). +// e.g. dex2oatd vs dex2oat +static const char* select_execution_binary(const char* binary, const char* debug_binary, + bool background_job_compile) { + return select_execution_binary( + binary, + debug_binary, + background_job_compile, + is_debug_runtime(), + (android::base::GetProperty("ro.build.version.codename", "") == "REL"), + is_debuggable_build()); +} + +// Determines which binary we should use for execution (the debug or non-debug version). +// e.g. dex2oatd vs dex2oat +// This is convenient method which is much easier to test because it doesn't read +// system properties. +const char* select_execution_binary( + const char* binary, + const char* debug_binary, + bool background_job_compile, + bool is_debug_runtime, + bool is_release, + bool is_debuggable_build) { + // Do not use debug binaries for release candidates (to give more soak time). + bool is_debug_bg_job = background_job_compile && is_debuggable_build && !is_release; + + // If the runtime was requested to use libartd.so, we'll run the debug version - assuming + // the file is present (it may not be on images with very little space available). + bool useDebug = (is_debug_runtime || is_debug_bg_job) && (access(debug_binary, X_OK) == 0); + + return useDebug ? debug_binary : binary; +} + +// Namespace for Android Runtime flags applied during boot time. +static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot"; +// Feature flag name for running the JIT in Zygote experiment, b/119800099. +static const char* ENABLE_APEX_IMAGE = "enable_apex_image"; +// Location of the apex image. +static const char* kApexImage = "/system/framework/apex.art"; + +class RunDex2Oat : public ExecVHelper { + public: + RunDex2Oat(int zip_fd, + int oat_fd, + int input_vdex_fd, + int output_vdex_fd, + int image_fd, + const char* input_file_name, + const char* output_file_name, + int swap_fd, + const char* instruction_set, + const char* compiler_filter, + bool debuggable, + bool post_bootcomplete, + bool background_job_compile, + int profile_fd, + const char* class_loader_context, + const std::string& class_loader_context_fds, + int target_sdk_version, + bool enable_hidden_api_checks, + bool generate_compact_dex, + int dex_metadata_fd, + const char* compilation_reason) { + // Get the relative path to the input file. + const char* relative_input_file_name = get_location_from_path(input_file_name); + + std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s"); + std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s"); + + const char* threads_property = post_bootcomplete + ? "dalvik.vm.dex2oat-threads" + : "dalvik.vm.boot-dex2oat-threads"; + std::string dex2oat_threads_arg = MapPropertyToArg(threads_property, "-j%s"); + + std::string bootclasspath; + char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH"); + if (dex2oat_bootclasspath != nullptr) { + bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath); + } + // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query + // BOOTCLASSPATH. + + const std::string dex2oat_isa_features_key = + StringPrintf("dalvik.vm.isa.%s.features", instruction_set); + std::string instruction_set_features_arg = + MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s"); + + const std::string dex2oat_isa_variant_key = + StringPrintf("dalvik.vm.isa.%s.variant", instruction_set); + std::string instruction_set_variant_arg = + MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s"); + + const char* dex2oat_norelocation = "-Xnorelocate"; + + const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", ""); + std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags); + ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str()); + + // If we are booting without the real /data, don't spend time compiling. + std::string vold_decrypt = GetProperty("vold.decrypt", ""); + bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" || + vold_decrypt == "1"; + + std::string resolve_startup_string_arg = + MapPropertyToArg("persist.device_config.runtime.dex2oat_resolve_startup_strings", + "--resolve-startup-const-strings=%s"); + if (resolve_startup_string_arg.empty()) { + // If empty, fall back to system property. + resolve_startup_string_arg = + MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings", + "--resolve-startup-const-strings=%s"); + } + + const std::string image_block_size_arg = + MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size", + "--max-image-block-size=%s"); + + const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false); + + std::string image_format_arg; + if (image_fd >= 0) { + image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s"); + } + + std::string dex2oat_large_app_threshold_arg = + MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s"); + + + const char* dex2oat_bin = select_execution_binary( + kDex2oatPath, kDex2oatDebugPath, background_job_compile); + + bool generate_minidebug_info = kEnableMinidebugInfo && + GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault); + + std::string boot_image; + std::string use_apex_image = + server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE, + ENABLE_APEX_IMAGE, + /*default_value=*/ ""); + if (use_apex_image == "true") { + boot_image = StringPrintf("-Ximage:%s", kApexImage); + } else { + boot_image = MapPropertyToArg("dalvik.vm.boot-image", "-Ximage:%s"); + } + + // clang FORTIFY doesn't let us use strlen in constant array bounds, so we + // use arraysize instead. + std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd); + std::string zip_location_arg = StringPrintf("--zip-location=%s", relative_input_file_name); + std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd); + std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd); + std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd); + std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name); + std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set); + std::string dex2oat_compiler_filter_arg; + std::string dex2oat_swap_fd; + std::string dex2oat_image_fd; + std::string target_sdk_version_arg; + if (target_sdk_version != 0) { + target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version); + } + std::string class_loader_context_arg; + std::string class_loader_context_fds_arg; + if (class_loader_context != nullptr) { + class_loader_context_arg = StringPrintf("--class-loader-context=%s", + class_loader_context); + if (!class_loader_context_fds.empty()) { + class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s", + class_loader_context_fds.c_str()); + } + } + + if (swap_fd >= 0) { + dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd); + } + if (image_fd >= 0) { + dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd); + } + + // Compute compiler filter. + bool have_dex2oat_relocation_skip_flag = false; + if (skip_compilation) { + dex2oat_compiler_filter_arg = "--compiler-filter=extract"; + have_dex2oat_relocation_skip_flag = true; + } else if (compiler_filter != nullptr) { + dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter); + } + + if (dex2oat_compiler_filter_arg.empty()) { + dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter", + "--compiler-filter=%s"); + } + + // Check whether all apps should be compiled debuggable. + if (!debuggable) { + debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1"; + } + std::string profile_arg; + if (profile_fd != -1) { + profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd); + } + + // Get the directory of the apk to pass as a base classpath directory. + std::string base_dir; + std::string apk_dir(input_file_name); + unsigned long dir_index = apk_dir.rfind('/'); + bool has_base_dir = dir_index != std::string::npos; + if (has_base_dir) { + apk_dir = apk_dir.substr(0, dir_index); + base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str()); + } + + std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd); + + std::string compilation_reason_arg = compilation_reason == nullptr + ? "" + : std::string("--compilation-reason=") + compilation_reason; + + ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name); + + // Disable cdex if update input vdex is true since this combination of options is not + // supported. + const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd); + + AddArg(zip_fd_arg); + AddArg(zip_location_arg); + AddArg(input_vdex_fd_arg); + AddArg(output_vdex_fd_arg); + AddArg(oat_fd_arg); + AddArg(oat_location_arg); + AddArg(instruction_set_arg); + + AddArg(instruction_set_variant_arg); + AddArg(instruction_set_features_arg); + + AddRuntimeArg(boot_image); + AddRuntimeArg(bootclasspath); + AddRuntimeArg(dex2oat_Xms_arg); + AddRuntimeArg(dex2oat_Xmx_arg); + + AddArg(resolve_startup_string_arg); + AddArg(image_block_size_arg); + AddArg(dex2oat_compiler_filter_arg); + AddArg(dex2oat_threads_arg); + AddArg(dex2oat_swap_fd); + AddArg(dex2oat_image_fd); + + if (generate_debug_info) { + AddArg("--generate-debug-info"); + } + if (debuggable) { + AddArg("--debuggable"); + } + AddArg(image_format_arg); + AddArg(dex2oat_large_app_threshold_arg); + + if (have_dex2oat_relocation_skip_flag) { + AddRuntimeArg(dex2oat_norelocation); + } + AddArg(profile_arg); + AddArg(base_dir); + AddArg(class_loader_context_arg); + AddArg(class_loader_context_fds_arg); + if (generate_minidebug_info) { + AddArg(kMinidebugDex2oatFlag); + } + if (disable_cdex) { + AddArg(kDisableCompactDexFlag); + } + AddRuntimeArg(target_sdk_version_arg); + if (enable_hidden_api_checks) { + AddRuntimeArg("-Xhidden-api-policy:enabled"); + } + + if (dex_metadata_fd > -1) { + AddArg(dex_metadata_fd_arg); + } + + AddArg(compilation_reason_arg); + + // Do not add args after dex2oat_flags, they should override others for debugging. + args_.insert(args_.end(), dex2oat_flags_args.begin(), dex2oat_flags_args.end()); + + PrepareArgs(dex2oat_bin); + } +}; + /* * Whether dexopt should use a swap file when compiling an APK. * @@ -580,13 +571,9 @@ } // Check the "override" property. If it exists, return value == "true". - char dex2oat_prop_buf[kPropertyValueMax]; - if (get_property("dalvik.vm.dex2oat-swap", dex2oat_prop_buf, "") > 0) { - if (strcmp(dex2oat_prop_buf, "true") == 0) { - return true; - } else { - return false; - } + std::string dex2oat_prop_buf = GetProperty("dalvik.vm.dex2oat-swap", ""); + if (!dex2oat_prop_buf.empty()) { + return dex2oat_prop_buf == "true"; } // Shortcut for default value. This is an implementation optimization for the process sketched @@ -596,8 +583,7 @@ return true; } - bool is_low_mem = property_get_bool("ro.config.low_ram", false); - if (is_low_mem) { + if (GetBoolProperty("ro.config.low_ram", false)) { return true; } @@ -630,7 +616,7 @@ // the app uid. If we cannot do that, there's no point in returning the fd // since dex2oat/profman will fail with SElinux denials. if (fchown(fd.get(), uid, uid) < 0) { - PLOG(ERROR) << "Could not chwon profile " << profile; + PLOG(ERROR) << "Could not chown profile " << profile; return invalid_unique_fd(); } return fd; @@ -711,117 +697,116 @@ } } -static void drop_capabilities(uid_t uid) { - if (setgid(uid) != 0) { - PLOG(ERROR) << "setgid(" << uid << ") failed in installd during dexopt"; - exit(DexoptReturnCodes::kSetGid); - } - if (setuid(uid) != 0) { - PLOG(ERROR) << "setuid(" << uid << ") failed in installd during dexopt"; - exit(DexoptReturnCodes::kSetUid); - } - // drop capabilities - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata[2]; - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - capheader.version = _LINUX_CAPABILITY_VERSION_3; - if (capset(&capheader, &capdata[0]) < 0) { - PLOG(ERROR) << "capset failed"; - exit(DexoptReturnCodes::kCapSet); - } -} - static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0; static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1; static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2; static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3; static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4; -[[ noreturn ]] -static void run_profman(const std::vector<unique_fd>& profile_fds, - const unique_fd& reference_profile_fd, - const std::vector<unique_fd>* apk_fds, - const std::vector<std::string>* dex_locations, - bool copy_and_update) { - const char* profman_bin = is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman"; +class RunProfman : public ExecVHelper { + public: + void SetupArgs(const std::vector<unique_fd>& profile_fds, + const unique_fd& reference_profile_fd, + const std::vector<unique_fd>& apk_fds, + const std::vector<std::string>& dex_locations, + bool copy_and_update, + bool store_aggregation_counters) { - if (copy_and_update) { - CHECK_EQ(1u, profile_fds.size()); - CHECK(apk_fds != nullptr); - CHECK_EQ(1u, apk_fds->size()); - } - std::vector<std::string> profile_args(profile_fds.size()); - for (size_t k = 0; k < profile_fds.size(); k++) { - profile_args[k] = "--profile-file-fd=" + std::to_string(profile_fds[k].get()); - } - std::string reference_profile_arg = "--reference-profile-file-fd=" - + std::to_string(reference_profile_fd.get()); + // TODO(calin): Assume for now we run in the bg compile job (which is in + // most of the invocation). With the current data flow, is not very easy or + // clean to discover this in RunProfman (it will require quite a messy refactoring). + const char* profman_bin = select_execution_binary( + kProfmanPath, kProfmanDebugPath, /*background_job_compile=*/ true); - std::vector<std::string> apk_args; - if (apk_fds != nullptr) { - for (size_t k = 0; k < apk_fds->size(); k++) { - apk_args.push_back("--apk-fd=" + std::to_string((*apk_fds)[k].get())); + if (copy_and_update) { + CHECK_EQ(1u, profile_fds.size()); + CHECK_EQ(1u, apk_fds.size()); } - } - - std::vector<std::string> dex_location_args; - if (dex_locations != nullptr) { - for (size_t k = 0; k < dex_locations->size(); k++) { - dex_location_args.push_back("--dex-location=" + (*dex_locations)[k]); + if (reference_profile_fd != -1) { + AddArg("--reference-profile-file-fd=" + std::to_string(reference_profile_fd.get())); } + + for (const unique_fd& fd : profile_fds) { + AddArg("--profile-file-fd=" + std::to_string(fd.get())); + } + + for (const unique_fd& fd : apk_fds) { + AddArg("--apk-fd=" + std::to_string(fd.get())); + } + + for (const std::string& dex_location : dex_locations) { + AddArg("--dex-location=" + dex_location); + } + + if (copy_and_update) { + AddArg("--copy-and-update-profile-key"); + } + + if (store_aggregation_counters) { + AddArg("--store-aggregation-counters"); + } + + // Do not add after dex2oat_flags, they should override others for debugging. + PrepareArgs(profman_bin); } - // program name, reference profile fd, the final NULL and the profile fds - const char* argv[3 + profile_args.size() + apk_args.size() + (copy_and_update ? 1 : 0)]; - int i = 0; - argv[i++] = profman_bin; - argv[i++] = reference_profile_arg.c_str(); - for (size_t k = 0; k < profile_args.size(); k++) { - argv[i++] = profile_args[k].c_str(); - } - for (size_t k = 0; k < apk_args.size(); k++) { - argv[i++] = apk_args[k].c_str(); - } - for (size_t k = 0; k < dex_location_args.size(); k++) { - argv[i++] = dex_location_args[k].c_str(); - } - if (copy_and_update) { - argv[i++] = "--copy-and-update-profile-key"; + void SetupMerge(const std::vector<unique_fd>& profiles_fd, + const unique_fd& reference_profile_fd, + const std::vector<unique_fd>& apk_fds = std::vector<unique_fd>(), + const std::vector<std::string>& dex_locations = std::vector<std::string>(), + bool store_aggregation_counters = false) { + SetupArgs(profiles_fd, + reference_profile_fd, + apk_fds, + dex_locations, + /*copy_and_update=*/false, + store_aggregation_counters); } - // Do not add after dex2oat_flags, they should override others for debugging. - argv[i] = NULL; + void SetupCopyAndUpdate(unique_fd&& profile_fd, + unique_fd&& reference_profile_fd, + unique_fd&& apk_fd, + const std::string& dex_location) { + // The fds need to stay open longer than the scope of the function, so put them into a local + // variable vector. + profiles_fd_.push_back(std::move(profile_fd)); + apk_fds_.push_back(std::move(apk_fd)); + reference_profile_fd_ = std::move(reference_profile_fd); + std::vector<std::string> dex_locations = {dex_location}; + SetupArgs(profiles_fd_, + reference_profile_fd_, + apk_fds_, + dex_locations, + /*copy_and_update=*/true, + /*store_aggregation_counters=*/false); + } - execv(profman_bin, (char * const *)argv); - PLOG(ERROR) << "execv(" << profman_bin << ") failed"; - exit(DexoptReturnCodes::kProfmanExec); /* only get here on exec failure */ -} + void SetupDump(const std::vector<unique_fd>& profiles_fd, + const unique_fd& reference_profile_fd, + const std::vector<std::string>& dex_locations, + const std::vector<unique_fd>& apk_fds, + const unique_fd& output_fd) { + AddArg("--dump-only"); + AddArg(StringPrintf("--dump-output-to-fd=%d", output_fd.get())); + SetupArgs(profiles_fd, + reference_profile_fd, + apk_fds, + dex_locations, + /*copy_and_update=*/false, + /*store_aggregation_counters=*/false); + } -[[ noreturn ]] -static void run_profman_merge(const std::vector<unique_fd>& profiles_fd, - const unique_fd& reference_profile_fd, - const std::vector<unique_fd>* apk_fds = nullptr, - const std::vector<std::string>* dex_locations = nullptr) { - run_profman(profiles_fd, reference_profile_fd, apk_fds, dex_locations, - /*copy_and_update*/false); -} + void Exec() { + ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec); + } -[[ noreturn ]] -static void run_profman_copy_and_update(unique_fd&& profile_fd, - unique_fd&& reference_profile_fd, - unique_fd&& apk_fd, - const std::string& dex_location) { - std::vector<unique_fd> profiles_fd; - profiles_fd.push_back(std::move(profile_fd)); - std::vector<unique_fd> apk_fds; - apk_fds.push_back(std::move(apk_fd)); - std::vector<std::string> dex_locations; - dex_locations.push_back(dex_location); + private: + unique_fd reference_profile_fd_; + std::vector<unique_fd> profiles_fd_; + std::vector<unique_fd> apk_fds_; +}; - run_profman(profiles_fd, reference_profile_fd, &apk_fds, &dex_locations, - /*copy_and_update*/true); -} + // Decides if profile guided compilation is needed or not based on existing profiles. // The location is the package name for primary apks or the dex path for secondary dex files. @@ -841,11 +826,13 @@ return false; } + RunProfman profman_merge; + profman_merge.SetupMerge(profiles_fd, reference_profile_fd); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(uid); - run_profman_merge(profiles_fd, reference_profile_fd); + profman_merge.Exec(); } /* parent */ int return_code = wait_child(pid); @@ -918,42 +905,6 @@ return analyze_profiles(uid, package_name, profile_name, /*is_secondary_dex*/false); } -[[ noreturn ]] -static void run_profman_dump(const std::vector<unique_fd>& profile_fds, - const unique_fd& reference_profile_fd, - const std::vector<std::string>& dex_locations, - const std::vector<unique_fd>& apk_fds, - const unique_fd& output_fd) { - std::vector<std::string> profman_args; - static const char* PROFMAN_BIN = "/system/bin/profman"; - profman_args.push_back(PROFMAN_BIN); - profman_args.push_back("--dump-only"); - profman_args.push_back(StringPrintf("--dump-output-to-fd=%d", output_fd.get())); - if (reference_profile_fd != -1) { - profman_args.push_back(StringPrintf("--reference-profile-file-fd=%d", - reference_profile_fd.get())); - } - for (size_t i = 0; i < profile_fds.size(); i++) { - profman_args.push_back(StringPrintf("--profile-file-fd=%d", profile_fds[i].get())); - } - for (const std::string& dex_location : dex_locations) { - profman_args.push_back(StringPrintf("--dex-location=%s", dex_location.c_str())); - } - for (size_t i = 0; i < apk_fds.size(); i++) { - profman_args.push_back(StringPrintf("--apk-fd=%d", apk_fds[i].get())); - } - const char **argv = new const char*[profman_args.size() + 1]; - size_t i = 0; - for (const std::string& profman_arg : profman_args) { - argv[i++] = profman_arg.c_str(); - } - argv[i] = NULL; - - execv(PROFMAN_BIN, (char * const *)argv); - PLOG(ERROR) << "execv(" << PROFMAN_BIN << ") failed"; - exit(DexoptReturnCodes::kProfmanExec); /* only get here on exec failure */ -} - bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& profile_name, const std::string& code_path) { std::vector<unique_fd> profile_fds; @@ -990,12 +941,13 @@ apk_fds.push_back(std::move(apk_fd)); + RunProfman profman_dump; + profman_dump.SetupDump(profile_fds, reference_profile_fd, dex_locations, apk_fds, output_fd); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(uid); - run_profman_dump(profile_fds, reference_profile_fd, dex_locations, - apk_fds, output_fd); + profman_dump.Exec(); } /* parent */ int return_code = wait_child(pid); @@ -1305,10 +1257,8 @@ if (!generate_app_image) { return Dex2oatFileWrapper(); } - char app_image_format[kPropertyValueMax]; - bool have_app_image_format = - get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0; - if (!have_app_image_format) { + std::string app_image_format = GetProperty("dalvik.vm.appimageformat", ""); + if (app_image_format.empty()) { return Dex2oatFileWrapper(); } // Recreate is true since we do not want to modify a mapped image. If the app is @@ -1569,70 +1519,82 @@ // The analyzer will check if the dex_file needs to be (re)compiled to match the compiler_filter. // If this is for a profile guided compilation, profile_was_updated will tell whether or not // the profile has changed. -static void exec_dexoptanalyzer(const std::string& dex_file, int vdex_fd, int oat_fd, - int zip_fd, const std::string& instruction_set, const std::string& compiler_filter, - bool profile_was_updated, bool downgrade, - const char* class_loader_context) { - CHECK_GE(zip_fd, 0); - const char* dexoptanalyzer_bin = - is_debug_runtime() - ? "/system/bin/dexoptanalyzerd" - : "/system/bin/dexoptanalyzer"; - static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; +class RunDexoptAnalyzer : public ExecVHelper { + public: + RunDexoptAnalyzer(const std::string& dex_file, + int vdex_fd, + int oat_fd, + int zip_fd, + const std::string& instruction_set, + const std::string& compiler_filter, + bool profile_was_updated, + bool downgrade, + const char* class_loader_context, + const std::string& class_loader_context_fds) { + CHECK_GE(zip_fd, 0); - if (instruction_set.size() >= MAX_INSTRUCTION_SET_LEN) { - LOG(ERROR) << "Instruction set " << instruction_set - << " longer than max length of " << MAX_INSTRUCTION_SET_LEN; - return; + // We always run the analyzer in the background job. + const char* dexoptanalyzer_bin = select_execution_binary( + kDexoptanalyzerPath, kDexoptanalyzerDebugPath, /*background_job_compile=*/ true); + + std::string dex_file_arg = "--dex-file=" + dex_file; + std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd); + std::string vdex_fd_arg = "--vdex-fd=" + std::to_string(vdex_fd); + std::string zip_fd_arg = "--zip-fd=" + std::to_string(zip_fd); + std::string isa_arg = "--isa=" + instruction_set; + std::string compiler_filter_arg = "--compiler-filter=" + compiler_filter; + const char* assume_profile_changed = "--assume-profile-changed"; + const char* downgrade_flag = "--downgrade"; + std::string class_loader_context_arg = "--class-loader-context="; + if (class_loader_context != nullptr) { + class_loader_context_arg += class_loader_context; + } + std::string class_loader_context_fds_arg = "--class-loader-context-fds="; + if (!class_loader_context_fds.empty()) { + class_loader_context_fds_arg += class_loader_context_fds; + } + + // program name, dex file, isa, filter + AddArg(dex_file_arg); + AddArg(isa_arg); + AddArg(compiler_filter_arg); + if (oat_fd >= 0) { + AddArg(oat_fd_arg); + } + if (vdex_fd >= 0) { + AddArg(vdex_fd_arg); + } + AddArg(zip_fd_arg); + if (profile_was_updated) { + AddArg(assume_profile_changed); + } + if (downgrade) { + AddArg(downgrade_flag); + } + if (class_loader_context != nullptr) { + AddArg(class_loader_context_arg); + if (!class_loader_context_fds.empty()) { + AddArg(class_loader_context_fds_arg); + } + } + + PrepareArgs(dexoptanalyzer_bin); } - std::string dex_file_arg = "--dex-file=" + dex_file; - std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd); - std::string vdex_fd_arg = "--vdex-fd=" + std::to_string(vdex_fd); - std::string zip_fd_arg = "--zip-fd=" + std::to_string(zip_fd); - std::string isa_arg = "--isa=" + instruction_set; - std::string compiler_filter_arg = "--compiler-filter=" + compiler_filter; - const char* assume_profile_changed = "--assume-profile-changed"; - const char* downgrade_flag = "--downgrade"; - std::string class_loader_context_arg = "--class-loader-context="; - if (class_loader_context != nullptr) { - class_loader_context_arg += class_loader_context; - } + // Dexoptanalyzer mode which flattens the given class loader context and + // prints a list of its dex files in that flattened order. + RunDexoptAnalyzer(const char* class_loader_context) { + CHECK(class_loader_context != nullptr); - // program name, dex file, isa, filter, the final NULL - const int argc = 6 + - (profile_was_updated ? 1 : 0) + - (vdex_fd >= 0 ? 1 : 0) + - (oat_fd >= 0 ? 1 : 0) + - (downgrade ? 1 : 0) + - (class_loader_context != nullptr ? 1 : 0); - const char* argv[argc]; - int i = 0; - argv[i++] = dexoptanalyzer_bin; - argv[i++] = dex_file_arg.c_str(); - argv[i++] = isa_arg.c_str(); - argv[i++] = compiler_filter_arg.c_str(); - if (oat_fd >= 0) { - argv[i++] = oat_fd_arg.c_str(); - } - if (vdex_fd >= 0) { - argv[i++] = vdex_fd_arg.c_str(); - } - argv[i++] = zip_fd_arg.c_str(); - if (profile_was_updated) { - argv[i++] = assume_profile_changed; - } - if (downgrade) { - argv[i++] = downgrade_flag; - } - if (class_loader_context != nullptr) { - argv[i++] = class_loader_context_arg.c_str(); - } - argv[i] = NULL; + // We always run the analyzer in the background job. + const char* dexoptanalyzer_bin = select_execution_binary( + kDexoptanalyzerPath, kDexoptanalyzerDebugPath, /*background_job_compile=*/ true); - execv(dexoptanalyzer_bin, (char * const *)argv); - ALOGE("execv(%s) failed: %s\n", dexoptanalyzer_bin, strerror(errno)); -} + AddArg("--flatten-class-loader-context"); + AddArg(std::string("--class-loader-context=") + class_loader_context); + PrepareArgs(dexoptanalyzer_bin); + } +}; // Prepares the oat dir for the secondary dex files. static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid, @@ -1691,15 +1653,12 @@ *dexopt_needed_out = NO_DEXOPT_NEEDED; return true; case 1: // dexoptanalyzer: dex2oat_from_scratch *dexopt_needed_out = DEX2OAT_FROM_SCRATCH; return true; - case 5: // dexoptanalyzer: dex2oat_for_bootimage_odex + case 4: // dexoptanalyzer: dex2oat_for_bootimage_odex *dexopt_needed_out = -DEX2OAT_FOR_BOOT_IMAGE; return true; - case 6: // dexoptanalyzer: dex2oat_for_filter_odex + case 5: // dexoptanalyzer: dex2oat_for_filter_odex *dexopt_needed_out = -DEX2OAT_FOR_FILTER; return true; - case 7: // dexoptanalyzer: dex2oat_for_relocation_odex - *dexopt_needed_out = -DEX2OAT_FOR_RELOCATION; return true; case 2: // dexoptanalyzer: dex2oat_for_bootimage_oat case 3: // dexoptanalyzer: dex2oat_for_filter_oat - case 4: // dexoptanalyzer: dex2oat_for_relocation_oat *error_msg = StringPrintf("Dexoptanalyzer return the status of an oat file." " Expected odex file status for secondary dex %s" " : dexoptanalyzer result=%d", @@ -1814,6 +1773,95 @@ return true; } +static bool get_class_loader_context_dex_paths(const char* class_loader_context, int uid, + /* out */ std::vector<std::string>* context_dex_paths) { + if (class_loader_context == nullptr) { + return true; + } + + LOG(DEBUG) << "Getting dex paths for context " << class_loader_context; + + // Pipe to get the hash result back from our child process. + unique_fd pipe_read, pipe_write; + if (!Pipe(&pipe_read, &pipe_write)) { + PLOG(ERROR) << "Failed to create pipe"; + return false; + } + + pid_t pid = fork(); + if (pid == 0) { + // child -- drop privileges before continuing. + drop_capabilities(uid); + + // Route stdout to `pipe_write` + while ((dup2(pipe_write, STDOUT_FILENO) == -1) && (errno == EINTR)) {} + pipe_write.reset(); + pipe_read.reset(); + + RunDexoptAnalyzer run_dexopt_analyzer(class_loader_context); + run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec); + } + + /* parent */ + pipe_write.reset(); + + std::string str_dex_paths; + if (!ReadFdToString(pipe_read, &str_dex_paths)) { + PLOG(ERROR) << "Failed to read from pipe"; + return false; + } + pipe_read.reset(); + + int return_code = wait_child(pid); + if (!WIFEXITED(return_code)) { + PLOG(ERROR) << "Error waiting for child dexoptanalyzer process"; + return false; + } + + constexpr int kFlattenClassLoaderContextSuccess = 50; + return_code = WEXITSTATUS(return_code); + if (return_code != kFlattenClassLoaderContextSuccess) { + LOG(ERROR) << "Dexoptanalyzer could not flatten class loader context, code=" << return_code; + return false; + } + + if (!str_dex_paths.empty()) { + *context_dex_paths = android::base::Split(str_dex_paths, ":"); + } + return true; +} + +static int open_dex_paths(const std::vector<std::string>& dex_paths, + /* out */ std::vector<unique_fd>* zip_fds, /* out */ std::string* error_msg) { + for (const std::string& dex_path : dex_paths) { + zip_fds->emplace_back(open(dex_path.c_str(), O_RDONLY)); + if (zip_fds->back().get() < 0) { + *error_msg = StringPrintf( + "installd cannot open '%s' for input during dexopt", dex_path.c_str()); + if (errno == ENOENT) { + return kSecondaryDexDexoptAnalyzerSkippedNoFile; + } else { + return kSecondaryDexDexoptAnalyzerSkippedOpenZip; + } + } + } + return 0; +} + +static std::string join_fds(const std::vector<unique_fd>& fds) { + std::stringstream ss; + bool is_first = true; + for (const unique_fd& fd : fds) { + if (is_first) { + is_first = false; + } else { + ss << ":"; + } + ss << fd.get(); + } + return ss.str(); +} + // Processes the dex_path as a secondary dex files and return true if the path dex file should // be compiled. Returns false for errors (logged) or true if the secondary dex path was process // successfully. @@ -1825,7 +1873,7 @@ int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set, const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out, std::string* oat_dir_out, bool downgrade, const char* class_loader_context, - /* out */ std::string* error_msg) { + const std::vector<std::string>& context_dex_paths, /* out */ std::string* error_msg) { LOG(DEBUG) << "Processing secondary dex path " << dex_path; int storage_flag; if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag, error_msg)) { @@ -1865,6 +1913,13 @@ } } + // Open class loader context dex files. + std::vector<unique_fd> context_zip_fds; + int open_dex_paths_rc = open_dex_paths(context_dex_paths, &context_zip_fds, error_msg); + if (open_dex_paths_rc != 0) { + _exit(open_dex_paths_rc); + } + // Prepare the oat directories. if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set)) { _exit(kSecondaryDexDexoptAnalyzerSkippedPrepareDir); @@ -1887,16 +1942,18 @@ /*is_secondary_dex*/true); // Run dexoptanalyzer to get dexopt_needed code. This is not expected to return. - exec_dexoptanalyzer(dex_path, - vdex_file_fd.get(), - oat_file_fd.get(), - zip_fd.get(), - instruction_set, - compiler_filter, profile_was_updated, - downgrade, - class_loader_context); - PLOG(ERROR) << "Failed to exec dexoptanalyzer"; - _exit(kSecondaryDexDexoptAnalyzerSkippedFailExec); + // Note that we do not do it before the fork since opening the files is required to happen + // after forking. + RunDexoptAnalyzer run_dexopt_analyzer(dex_path, + vdex_file_fd.get(), + oat_file_fd.get(), + zip_fd.get(), + instruction_set, + compiler_filter, profile_was_updated, + downgrade, + class_loader_context, + join_fds(context_zip_fds)); + run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec); } /* parent */ @@ -1983,10 +2040,16 @@ // Check if we're dealing with a secondary dex file and if we need to compile it. std::string oat_dir_str; + std::vector<std::string> context_dex_paths; if (is_secondary_dex) { + if (!get_class_loader_context_dex_paths(class_loader_context, uid, &context_dex_paths)) { + *error_msg = "Failed acquiring context dex paths"; + return -1; // We had an error, logged in the process method. + } + if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid, instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str, - downgrade, class_loader_context, error_msg)) { + downgrade, class_loader_context, context_dex_paths, error_msg)) { oat_dir = oat_dir_str.c_str(); if (dexopt_needed == NO_DEXOPT_NEEDED) { return 0; // Nothing to do, report success. @@ -1998,7 +2061,7 @@ return -1; // We had an error, logged in the process method. } } else { - // Currently these flags are only use for secondary dex files. + // Currently these flags are only used for secondary dex files. // Verify that they are not set for primary apks. CHECK((dexopt_flags & DEXOPT_STORAGE_CE) == 0); CHECK((dexopt_flags & DEXOPT_STORAGE_DE) == 0); @@ -2012,6 +2075,13 @@ return -1; } + // Open class loader context dex files. + std::vector<unique_fd> context_input_fds; + if (open_dex_paths(context_dex_paths, &context_input_fds, error_msg) != 0) { + LOG(ERROR) << *error_msg; + return -1; + } + // Create the output OAT file. char out_oat_path[PKG_PATH_MAX]; Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid, @@ -2065,6 +2135,28 @@ LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---"; + RunDex2Oat runner(input_fd.get(), + out_oat_fd.get(), + in_vdex_fd.get(), + out_vdex_fd.get(), + image_fd.get(), + dex_path, + out_oat_path, + swap_fd.get(), + instruction_set, + compiler_filter, + debuggable, + boot_complete, + background_job_compile, + reference_profile_fd.get(), + class_loader_context, + join_fds(context_input_fds), + target_sdk_version, + enable_hidden_api_checks, + generate_compact_dex, + dex_metadata_fd.get(), + compilation_reason); + pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ @@ -2076,26 +2168,7 @@ _exit(DexoptReturnCodes::kFlock); } - run_dex2oat(input_fd.get(), - out_oat_fd.get(), - in_vdex_fd.get(), - out_vdex_fd.get(), - image_fd.get(), - dex_path, - out_oat_path, - swap_fd.get(), - instruction_set, - compiler_filter, - debuggable, - boot_complete, - background_job_compile, - reference_profile_fd.get(), - class_loader_context, - target_sdk_version, - enable_hidden_api_checks, - generate_compact_dex, - dex_metadata_fd.get(), - compilation_reason); + runner.Exec(DexoptReturnCodes::kDex2oatExec); } else { int res = wait_child(pid); if (res == 0) { @@ -2186,7 +2259,7 @@ drop_capabilities(uid); const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str(); - if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr, + if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr, uid, storage_flag)) { LOG(ERROR) << "Could not validate secondary dex path " << dex_path; _exit(kReconcileSecondaryDexValidationError); @@ -2423,18 +2496,14 @@ bool move_ab(const char* apk_path, const char* instruction_set, const char* oat_dir) { // Get the current slot suffix. No suffix, no A/B. - std::string slot_suffix; - { - char buf[kPropertyValueMax]; - if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) { - return false; - } - slot_suffix = buf; + const std::string slot_suffix = GetProperty("ro.boot.slot_suffix", ""); + if (slot_suffix.empty()) { + return false; + } - if (!ValidateTargetSlotSuffix(slot_suffix)) { - LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix; - return false; - } + if (!ValidateTargetSlotSuffix(slot_suffix)) { + LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix; + return false; } // Validate other inputs. @@ -2663,11 +2732,13 @@ return false; } + RunProfman args; + args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ drop_capabilities(app_shared_gid); - run_profman_merge(profiles_fd, snapshot_fd, &apk_fds, &dex_locations); + args.Exec(); } /* parent */ @@ -2691,6 +2762,13 @@ return false; } + // Return false for empty class path since it may otherwise return true below if profiles is + // empty. + if (classpath.empty()) { + PLOG(ERROR) << "Class path is empty"; + return false; + } + // Open and create the snapshot profile. unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); @@ -2740,6 +2818,12 @@ profiles_fd.push_back(std::move(fd)); } } + RunProfman args; + args.SetupMerge(profiles_fd, + snapshot_fd, + apk_fds, + dex_locations, + /*store_aggregation_counters=*/true); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ @@ -2747,7 +2831,7 @@ // The introduction of new access flags into boot jars causes them to // fail dex file verification. - run_profman_merge(profiles_fd, snapshot_fd, &apk_fds, &dex_locations); + args.Exec(); } /* parent */ @@ -2801,6 +2885,11 @@ return false; } + RunProfman args; + args.SetupCopyAndUpdate(std::move(dex_metadata_fd), + std::move(ref_profile_fd), + std::move(apk_fd), + code_path); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ @@ -2808,10 +2897,7 @@ drop_capabilities(app_shared_gid); // The copy and update takes ownership over the fds. - run_profman_copy_and_update(std::move(dex_metadata_fd), - std::move(ref_profile_fd), - std::move(apk_fd), - code_path); + args.Exec(); } /* parent */
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index bb6fab3..a8c48c5 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h
@@ -31,7 +31,16 @@ static constexpr int DEX2OAT_FROM_SCRATCH = 1; static constexpr int DEX2OAT_FOR_BOOT_IMAGE = 2; static constexpr int DEX2OAT_FOR_FILTER = 3; -static constexpr int DEX2OAT_FOR_RELOCATION = 4; + +#define ANDROID_RUNTIME_APEX_BIN "/apex/com.android.runtime/bin" +// Location of binaries in the Android Runtime APEX. +static constexpr const char* kDex2oatPath = ANDROID_RUNTIME_APEX_BIN "/dex2oat"; +static constexpr const char* kDex2oatDebugPath = ANDROID_RUNTIME_APEX_BIN "/dex2oatd"; +static constexpr const char* kProfmanPath = ANDROID_RUNTIME_APEX_BIN "/profman"; +static constexpr const char* kProfmanDebugPath = ANDROID_RUNTIME_APEX_BIN "/profmand"; +static constexpr const char* kDexoptanalyzerPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzer"; +static constexpr const char* kDexoptanalyzerDebugPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzerd"; +#undef ANDROID_RUNTIME_APEX_BIN // Clear the reference profile identified by the given profile name. bool clear_primary_reference_profile(const std::string& pkgname, const std::string& profile_name); @@ -119,6 +128,14 @@ bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path); +const char* select_execution_binary( + const char* binary, + const char* debug_binary, + bool background_job_compile, + bool is_debug_runtime, + bool is_release, + bool is_debuggable_build); + } // namespace installd } // namespace android
diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp index b3a6daf..1394701 100644 --- a/cmds/installd/globals.cpp +++ b/cmds/installd/globals.cpp
@@ -44,6 +44,8 @@ static constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under // ANDROID_DATA +static constexpr const char* STAGING_SUBDIR = "app-staging/"; // sub-directory under ANDROID_DATA + std::string android_app_dir; std::string android_app_ephemeral_dir; std::string android_app_lib_dir; @@ -54,6 +56,7 @@ std::string android_mnt_expand_dir; std::string android_profiles_dir; std::string android_root_dir; +std::string android_staging_dir; std::vector<std::string> android_system_dirs; @@ -110,6 +113,9 @@ // Get the android profiles directory. android_profiles_dir = android_data_dir + PROFILES_SUBDIR; + // Get the android session staging directory. + android_staging_dir = android_data_dir + STAGING_SUBDIR; + // Take note of the system and vendor directories. android_system_dirs.clear(); android_system_dirs.push_back(android_root_dir + APP_SUBDIR);
diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h index 633e33b..a88a86e 100644 --- a/cmds/installd/globals.h +++ b/cmds/installd/globals.h
@@ -38,6 +38,7 @@ extern std::string android_mnt_expand_dir; extern std::string android_profiles_dir; extern std::string android_root_dir; +extern std::string android_staging_dir; extern std::vector<std::string> android_system_dirs;
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp index 95ed2ff..673ff0d 100644 --- a/cmds/installd/installd.cpp +++ b/cmds/installd/installd.cpp
@@ -107,7 +107,7 @@ DIR *dir; struct dirent *dirent; dir = opendir("/data/user"); - if (dir != NULL) { + if (dir != nullptr) { while ((dirent = readdir(dir))) { const char *name = dirent->d_name; @@ -146,10 +146,10 @@ closedir(dir); if (access(keychain_added_dir, F_OK) == 0) { - delete_dir_contents(keychain_added_dir, 1, 0); + delete_dir_contents(keychain_added_dir, 1, nullptr); } if (access(keychain_removed_dir, F_OK) == 0) { - delete_dir_contents(keychain_removed_dir, 1, 0); + delete_dir_contents(keychain_removed_dir, 1, nullptr); } }
diff --git a/cmds/installd/matchgen.py b/cmds/installd/matchgen.py index 131487d..42ce82b 100644 --- a/cmds/installd/matchgen.py +++ b/cmds/installd/matchgen.py
@@ -84,6 +84,8 @@ print "%scase '%s':" % (prefix, k) dump(target[k], index + 1) print "%s}" % (prefix) + if index > 0: + print "%sbreak;" % (prefix) dump(trie, 0)
diff --git a/cmds/installd/migrate_legacy_obb_data.sh b/cmds/installd/migrate_legacy_obb_data.sh new file mode 100644 index 0000000..1075688 --- /dev/null +++ b/cmds/installd/migrate_legacy_obb_data.sh
@@ -0,0 +1,40 @@ +#!/system/bin/sh + +# +# 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. + +rm -rf /sdcard/Android/obb/test_probe +mkdir -p /sdcard/Android/obb/ +touch /sdcard/Android/obb/test_probe +if ! test -f /data/media/0/Android/obb/test_probe ; then + log -p i -t migrate_legacy_obb_data "No support for 'unshared_obb'. Not migrating" + rm -rf /sdcard/Android/obb/test_probe + exit 0 +fi + +# Delete the test file, and remove the obb folder if it is empty +rm -rf /sdcard/Android/obb/test_probe +rmdir /data/media/obb + +if ! test -d /data/media/obb ; then + log -p i -t migrate_legacy_obb_data "No legacy obb data to migrate." + exit 0 +fi + +log -p i -t migrate_legacy_obb_data "Migrating legacy obb data." +rm -rf /data/media/0/Android/obb +cp -F -p -R -P -d /data/media/obb /data/media/0/Android +rm -rf /data/media/obb +log -p i -t migrate_legacy_obb_data "Done."
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index 96d8c47..de7b249 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp
@@ -26,12 +26,12 @@ #include <sys/capability.h> #include <sys/prctl.h> #include <sys/stat.h> -#include <sys/wait.h> #include <android-base/logging.h> #include <android-base/macros.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> +#include <art_image_values.h> #include <cutils/fs.h> #include <cutils/properties.h> #include <dex2oat_return_codes.h> @@ -57,7 +57,6 @@ #define REPLY_MAX 256 /* largest reply allowed */ using android::base::EndsWith; -using android::base::Join; using android::base::Split; using android::base::StartsWith; using android::base::StringPrintf; @@ -88,6 +87,12 @@ "DEXOPT_MASK unexpected."); +template<typename T> +static constexpr bool IsPowerOfTwo(T x) { + static_assert(std::is_integral<T>::value, "T must be integral"); + // TODO: assert unsigned. There is currently many uses with signed values. + return (x & (x - 1)) == 0; +} template<typename T> static constexpr T RoundDown(T x, typename std::decay<T>::type n) { @@ -315,21 +320,8 @@ return false; } const char* isa = parameters_.instruction_set; - - // Check whether the file exists where expected. std::string dalvik_cache = GetOTADataDirectory() + "/" + DALVIK_CACHE; std::string isa_path = dalvik_cache + "/" + isa; - std::string art_path = isa_path + "/system@framework@boot.art"; - std::string oat_path = isa_path + "/system@framework@boot.oat"; - bool cleared = false; - if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) { - // Files exist, assume everything is alright if not forced. Otherwise clean up. - if (!force) { - return true; - } - ClearDirectory(isa_path); - cleared = true; - } // Reset umask in otapreopt, so that we control the the access for the files we create. umask(0); @@ -348,18 +340,34 @@ } } - // Prepare to create. + // Check whether we have files in /data. + // TODO: check that the files are correct wrt/ jars. + std::string art_path = isa_path + "/system@framework@boot.art"; + std::string oat_path = isa_path + "/system@framework@boot.oat"; + bool cleared = false; + if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) { + // Files exist, assume everything is alright if not forced. Otherwise clean up. + if (!force) { + return true; + } + ClearDirectory(isa_path); + cleared = true; + } + + // Check whether we have an image in /system. + // TODO: check that the files are correct wrt/ jars. + std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa); + if (access(preopted_boot_art_path.c_str(), F_OK) == 0) { + // Note: we ignore |force| here. + return true; + } + + if (!cleared) { ClearDirectory(isa_path); } - std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa); - if (access(preopted_boot_art_path.c_str(), F_OK) == 0) { - return PatchoatBootImage(isa_path, isa); - } else { - // No preopted boot image. Try to compile. - return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa); - } + return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa); } static bool CreatePath(const std::string& path) { @@ -424,45 +432,22 @@ CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory."; } - bool PatchoatBootImage(const std::string& output_dir, const char* isa) const { - // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc. - - std::vector<std::string> cmd; - cmd.push_back("/system/bin/patchoat"); - - cmd.push_back("--input-image-location=/system/framework/boot.art"); - cmd.push_back(StringPrintf("--output-image-directory=%s", output_dir.c_str())); - - cmd.push_back(StringPrintf("--instruction-set=%s", isa)); - - int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA, - ART_BASE_ADDRESS_MAX_DELTA); - cmd.push_back(StringPrintf("--base-offset-delta=%d", base_offset)); - - std::string error_msg; - bool result = Exec(cmd, &error_msg); - if (!result) { - LOG(ERROR) << "Could not generate boot image: " << error_msg; - } - return result; - } - bool Dex2oatBootImage(const std::string& boot_cp, const std::string& art_path, const std::string& oat_path, const char* isa) const { // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc. std::vector<std::string> cmd; - cmd.push_back("/system/bin/dex2oat"); + cmd.push_back(kDex2oatPath); cmd.push_back(StringPrintf("--image=%s", art_path.c_str())); for (const std::string& boot_part : Split(boot_cp, ":")) { cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str())); } cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str())); - int32_t base_offset = ChooseRelocationOffsetDelta(ART_BASE_ADDRESS_MIN_DELTA, - ART_BASE_ADDRESS_MAX_DELTA); - cmd.push_back(StringPrintf("--base=0x%x", ART_BASE_ADDRESS + base_offset)); + int32_t base_offset = ChooseRelocationOffsetDelta(art::GetImageMinBaseAddressDelta(), + art::GetImageMaxBaseAddressDelta()); + cmd.push_back(StringPrintf("--base=0x%x", art::GetImageBaseAddress() + base_offset)); cmd.push_back(StringPrintf("--instruction-set=%s", isa)); @@ -604,7 +589,7 @@ // If the dexopt failed, we may have a stale boot image from a previous OTA run. // Then regenerate and retry. if (WEXITSTATUS(dexopt_result) == - static_cast<int>(art::dex2oat::ReturnCode::kCreateRuntime)) { + static_cast<int>(::art::dex2oat::ReturnCode::kCreateRuntime)) { if (!PrepareBootImage(/* force */ true)) { LOG(ERROR) << "Forced boot image creating failed. Original error return was " << dexopt_result; @@ -632,61 +617,6 @@ // Helpers, mostly taken from ART // //////////////////////////////////// - // Wrapper on fork/execv to run a command in a subprocess. - static bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) { - const std::string command_line = Join(arg_vector, ' '); - - CHECK_GE(arg_vector.size(), 1U) << command_line; - - // Convert the args to char pointers. - const char* program = arg_vector[0].c_str(); - std::vector<char*> args; - for (size_t i = 0; i < arg_vector.size(); ++i) { - const std::string& arg = arg_vector[i]; - char* arg_str = const_cast<char*>(arg.c_str()); - CHECK(arg_str != nullptr) << i; - args.push_back(arg_str); - } - args.push_back(nullptr); - - // Fork and exec. - pid_t pid = fork(); - if (pid == 0) { - // No allocation allowed between fork and exec. - - // Change process groups, so we don't get reaped by ProcessManager. - setpgid(0, 0); - - execv(program, &args[0]); - - PLOG(ERROR) << "Failed to execv(" << command_line << ")"; - // _exit to avoid atexit handlers in child. - _exit(1); - } else { - if (pid == -1) { - *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s", - command_line.c_str(), strerror(errno)); - return false; - } - - // wait for subprocess to finish - int status; - pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); - if (got_pid != pid) { - *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: " - "wanted %d, got %d: %s", - command_line.c_str(), pid, got_pid, strerror(errno)); - return false; - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", - command_line.c_str()); - return false; - } - } - return true; - } - // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc. static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) { constexpr size_t kPageSize = PAGE_SIZE;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index e90cf3b..2e2cc18 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp
@@ -17,6 +17,7 @@ #include <fcntl.h> #include <linux/unistd.h> #include <sys/mount.h> +#include <sys/stat.h> #include <sys/wait.h> #include <sstream> @@ -24,6 +25,10 @@ #include <android-base/logging.h> #include <android-base/macros.h> #include <android-base/stringprintf.h> +#include <libdm/dm.h> +#include <selinux/android.h> + +#include <apexd.h> #include "installd_constants.h" #include "otapreopt_utils.h" @@ -54,6 +59,56 @@ } } +static std::vector<apex::ApexFile> ActivateApexPackages() { + // The logic here is (partially) copied and adapted from + // system/apex/apexd/apexd_main.cpp. + // + // Only scan the APEX directory under /system (within the chroot dir). + apex::scanPackagesDirAndActivate(apex::kApexPackageSystemDir); + return apex::getActivePackages(); +} + +static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_packages) { + for (const apex::ApexFile& apex_file : active_packages) { + const std::string& package_path = apex_file.GetPath(); + apex::Status status = apex::deactivatePackage(package_path); + if (!status.Ok()) { + LOG(ERROR) << "Failed to deactivate " << package_path << ": " << status.ErrorMessage(); + } + } +} + +static void TryExtraMount(const char* name, const char* slot, const char* target) { + std::string partition_name = StringPrintf("%s%s", name, slot); + + // See whether update_engine mounted a logical partition. + { + auto& dm = dm::DeviceMapper::Instance(); + if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) { + std::string path; + if (dm.GetDmDevicePathByName(partition_name, &path)) { + int mount_result = mount(path.c_str(), + target, + "ext4", + MS_RDONLY, + /* data */ nullptr); + if (mount_result == 0) { + return; + } + } + } + } + + // Fall back and attempt a direct mount. + std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str()); + int mount_result = mount(block_device.c_str(), + target, + "ext4", + MS_RDONLY, + /* data */ nullptr); + UNUSED(mount_result); +} + // Entry for otapreopt_chroot. Expected parameters are: // [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params] // The file descriptor denoted by status-fd will be closed. The rest of the parameters will @@ -114,28 +169,48 @@ LOG(ERROR) << "Target slot suffix not legal: " << arg[2]; exit(207); } - { - std::string vendor_partition = StringPrintf("/dev/block/by-name/vendor%s", - arg[2]); - int vendor_result = mount(vendor_partition.c_str(), - "/postinstall/vendor", - "ext4", - MS_RDONLY, - /* data */ nullptr); - UNUSED(vendor_result); - } + TryExtraMount("vendor", arg[2], "/postinstall/vendor"); // Try to mount the product partition. update_engine doesn't do this for us, but we // want it for product APKs. Same notes as vendor above. - { - std::string product_partition = StringPrintf("/dev/block/by-name/product%s", - arg[2]); - int product_result = mount(product_partition.c_str(), - "/postinstall/product", - "ext4", - MS_RDONLY, - /* data */ nullptr); - UNUSED(product_result); + TryExtraMount("product", arg[2], "/postinstall/product"); + + // Setup APEX mount point and its security context. + static constexpr const char* kPostinstallApexDir = "/postinstall/apex"; + // The following logic is similar to the one in system/core/rootdir/init.rc: + // + // mount tmpfs tmpfs /apex nodev noexec nosuid + // chmod 0755 /apex + // chown root root /apex + // restorecon /apex + // + // except we perform the `restorecon` step just after mounting the tmpfs + // filesystem in /postinstall/apex, so that this directory is correctly + // labeled (with type `postinstall_apex_mnt_dir`) and may be manipulated in + // following operations (`chmod`, `chown`, etc.) following policies + // restricted to `postinstall_apex_mnt_dir`: + // + // mount tmpfs tmpfs /postinstall/apex nodev noexec nosuid + // restorecon /postinstall/apex + // chmod 0755 /postinstall/apex + // chown root root /postinstall/apex + // + if (mount("tmpfs", kPostinstallApexDir, "tmpfs", MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr) + != 0) { + PLOG(ERROR) << "Failed to mount tmpfs in " << kPostinstallApexDir; + exit(209); + } + if (selinux_android_restorecon(kPostinstallApexDir, 0) < 0) { + PLOG(ERROR) << "Failed to restorecon " << kPostinstallApexDir; + exit(214); + } + if (chmod(kPostinstallApexDir, 0755) != 0) { + PLOG(ERROR) << "Failed to chmod " << kPostinstallApexDir << " to 0755"; + exit(210); + } + if (chown(kPostinstallApexDir, 0, 0) != 0) { + PLOG(ERROR) << "Failed to chown " << kPostinstallApexDir << " to root:root"; + exit(211); } // Chdir into /postinstall. @@ -155,22 +230,38 @@ exit(205); } + // Try to mount APEX packages in "/apex" in the chroot dir. We need at least + // the Android Runtime APEX, as it is required by otapreopt to run dex2oat. + std::vector<apex::ApexFile> active_packages = ActivateApexPackages(); + // Now go on and run otapreopt. - // Incoming: cmd + status-fd + target-slot + cmd... + null | Incoming | = argc + 1 - // Outgoing: cmd + target-slot + cmd... + null | Outgoing | = argc - const char** argv = new const char*[argc]; - - argv[0] = "/system/bin/otapreopt"; + // Incoming: cmd + status-fd + target-slot + cmd... | Incoming | = argc + // Outgoing: cmd + target-slot + cmd... | Outgoing | = argc - 1 + std::vector<std::string> cmd; + cmd.reserve(argc); + cmd.push_back("/system/bin/otapreopt"); // The first parameter is the status file descriptor, skip. - for (size_t i = 2; i <= static_cast<size_t>(argc); ++i) { - argv[i - 1] = arg[i]; + for (size_t i = 2; i < static_cast<size_t>(argc); ++i) { + cmd.push_back(arg[i]); } - execv(argv[0], static_cast<char * const *>(const_cast<char**>(argv))); - PLOG(ERROR) << "execv(OTAPREOPT) failed."; - exit(99); + // Fork and execute otapreopt in its own process. + std::string error_msg; + bool exec_result = Exec(cmd, &error_msg); + if (!exec_result) { + LOG(ERROR) << "Running otapreopt failed: " << error_msg; + } + + // Tear down the work down by the apexd logic. (i.e. deactivate packages). + DeactivateApexPackages(active_packages); + + if (!exec_result) { + exit(213); + } + + return 0; } } // namespace installd
diff --git a/cmds/installd/otapreopt_parameters.cpp b/cmds/installd/otapreopt_parameters.cpp index cf3de01..b1ad8db 100644 --- a/cmds/installd/otapreopt_parameters.cpp +++ b/cmds/installd/otapreopt_parameters.cpp
@@ -16,6 +16,8 @@ #include "otapreopt_parameters.h" +#include <cstring> + #include <android-base/logging.h> #include "dexopt.h" @@ -248,6 +250,8 @@ case 8: num_args_expected = 16; break; // Version 9 adds a new dexopt flag: DEXOPT_GENERATE_APP_IMAGE case 9: num_args_expected = 16; break; + // Version 10 is a compatibility bump. + case 10: num_args_expected = 16; break; default: LOG(ERROR) << "Don't know how to read arguments for version " << version; return false; @@ -360,6 +364,15 @@ } } + if (version < 10) { + // Do not accept '&' as shared libraries from versions prior to 10. These may lead + // to runtime crashes. The server side of version 10+ should send the correct + // context in almost all cases (e.g., only for actual shared packages). + if (shared_libraries != nullptr && std::string("&") == shared_libraries) { + return false; + } + } + return true; }
diff --git a/cmds/installd/otapreopt_utils.cpp b/cmds/installd/otapreopt_utils.cpp new file mode 100644 index 0000000..124f726 --- /dev/null +++ b/cmds/installd/otapreopt_utils.cpp
@@ -0,0 +1,88 @@ +/* + ** Copyright 2019, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include "otapreopt_utils.h" + +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> + +using android::base::Join; +using android::base::StringPrintf; + +namespace android { +namespace installd { + +bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) { + const std::string command_line = Join(arg_vector, ' '); + + CHECK_GE(arg_vector.size(), 1U) << command_line; + + // Convert the args to char pointers. + const char* program = arg_vector[0].c_str(); + std::vector<char*> args; + for (size_t i = 0; i < arg_vector.size(); ++i) { + const std::string& arg = arg_vector[i]; + char* arg_str = const_cast<char*>(arg.c_str()); + CHECK(arg_str != nullptr) << i; + args.push_back(arg_str); + } + args.push_back(nullptr); + + // Fork and exec. + pid_t pid = fork(); + if (pid == 0) { + // No allocation allowed between fork and exec. + + // Change process groups, so we don't get reaped by ProcessManager. + setpgid(0, 0); + + execv(program, &args[0]); + + PLOG(ERROR) << "Failed to execv(" << command_line << ")"; + // _exit to avoid atexit handlers in child. + _exit(1); + } else { + if (pid == -1) { + *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s", + command_line.c_str(), strerror(errno)); + return false; + } + + // wait for subprocess to finish + int status; + pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); + if (got_pid != pid) { + *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: " + "wanted %d, got %d: %s", + command_line.c_str(), pid, got_pid, strerror(errno)); + return false; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status", + command_line.c_str()); + return false; + } + } + return true; +} + +} // namespace installd +} // namespace android
diff --git a/cmds/installd/otapreopt_utils.h b/cmds/installd/otapreopt_utils.h index 436e554..03a6d87 100644 --- a/cmds/installd/otapreopt_utils.h +++ b/cmds/installd/otapreopt_utils.h
@@ -18,6 +18,8 @@ #define OTAPREOPT_UTILS_H_ #include <regex> +#include <string> +#include <vector> namespace android { namespace installd { @@ -28,6 +30,9 @@ return std::regex_match(input, slot_suffix_match, slot_suffix_regex); } +// Wrapper on fork/execv to run a command in a subprocess. +bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg); + } // namespace installd } // namespace android
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 7438d3d..aa79fdc 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp
@@ -1,6 +1,7 @@ // Build the unit tests for installd cc_test { name: "installd_utils_test", + test_suites: ["device-tests"], clang: true, srcs: ["installd_utils_test.cpp"], cflags: ["-Wall", "-Werror"], @@ -14,10 +15,12 @@ "libinstalld", "liblog", ], + test_config: "installd_utils_test.xml", } cc_test { name: "installd_cache_test", + test_suites: ["device-tests"], clang: true, srcs: ["installd_cache_test.cpp"], cflags: ["-Wall", "-Werror"], @@ -26,8 +29,10 @@ "libbinder", "libcrypto", "libcutils", + "libprocessgroup", "libselinux", "libutils", + "server_configurable_flags", ], static_libs: [ "libdiskusage", @@ -35,10 +40,12 @@ "liblog", "liblogwrap", ], + test_config: "installd_cache_test.xml", } cc_test { name: "installd_service_test", + test_suites: ["device-tests"], clang: true, srcs: ["installd_service_test.cpp"], cflags: ["-Wall", "-Werror"], @@ -47,8 +54,10 @@ "libbinder", "libcrypto", "libcutils", + "libprocessgroup", "libselinux", "libutils", + "server_configurable_flags", ], static_libs: [ "libdiskusage", @@ -56,10 +65,12 @@ "liblog", "liblogwrap", ], + test_config: "installd_service_test.xml", } cc_test { name: "installd_dexopt_test", + test_suites: ["device-tests"], clang: true, srcs: ["installd_dexopt_test.cpp"], cflags: ["-Wall", "-Werror"], @@ -68,8 +79,10 @@ "libbinder", "libcrypto", "libcutils", + "libprocessgroup", "libselinux", "libutils", + "server_configurable_flags", ], static_libs: [ "libdiskusage", @@ -77,10 +90,12 @@ "liblog", "liblogwrap", ], + test_config: "installd_dexopt_test.xml", } cc_test { name: "installd_otapreopt_test", + test_suites: ["device-tests"], clang: true, srcs: ["installd_otapreopt_test.cpp"], cflags: ["-Wall", "-Werror"], @@ -88,6 +103,7 @@ "libbase", "libcutils", "libutils", + "server_configurable_flags", ], static_libs: [ "liblog",
diff --git a/cmds/installd/tests/binder_test_utils.h b/cmds/installd/tests/binder_test_utils.h new file mode 100644 index 0000000..efd1391 --- /dev/null +++ b/cmds/installd/tests/binder_test_utils.h
@@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/Status.h> +#include <gtest/gtest.h> +#include <utils/String8.h> + +#define ASSERT_BINDER_SUCCESS(expr) \ + ({ \ + binder::Status expect_status = (expr); \ + ASSERT_TRUE(expect_status.isOk()) << expect_status.toString8().c_str(); \ + expect_status; \ + }) +#define ASSERT_BINDER_FAIL(expr) \ + ({ \ + binder::Status expect_status = (expr); \ + ASSERT_FALSE(expect_status.isOk()); \ + expect_status; \ + }) +#define EXPECT_BINDER_SUCCESS(expr) \ + ({ \ + binder::Status expect_status = (expr); \ + EXPECT_TRUE(expect_status.isOk()) << expect_status.toString8().c_str(); \ + expect_status; \ + }) +#define EXPECT_BINDER_FAIL(expr) \ + ({ \ + binder::Status expect_status = (expr); \ + EXPECT_FALSE(expect_status.isOk()); \ + expect_status; \ + })
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp index 2d58515..db09070 100644 --- a/cmds/installd/tests/installd_cache_test.cpp +++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -40,8 +40,8 @@ constexpr int64_t kGbInBytes = 1024 * kMbInBytes; constexpr int64_t kTbInBytes = 1024 * kGbInBytes; -static constexpr int FLAG_FREE_CACHE_V2 = 1 << 13; -static constexpr int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 1 << 14; +#define FLAG_FREE_CACHE_V2 InstalldNativeService::FLAG_FREE_CACHE_V2 +#define FLAG_FREE_CACHE_V2_DEFY_QUOTA InstalldNativeService::FLAG_FREE_CACHE_V2_DEFY_QUOTA int get_property(const char *key, char *value, const char *default_value) { return property_get(key, value, default_value);
diff --git a/cmds/installd/tests/installd_cache_test.xml b/cmds/installd/tests/installd_cache_test.xml new file mode 100644 index 0000000..97af514 --- /dev/null +++ b/cmds/installd/tests/installd_cache_test.xml
@@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<!-- Note: this is derived from the autogenerated configuration. We require + root support. --> +<configuration description="Runs installd_cache_test."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="installd_cache_test->/data/local/tmp/installd_cache_test" /> + </target_preparer> + + <!-- The test requires root for file access. --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="installd_cache_test" /> + </test> +</configuration>
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index 279bce8..fa2b0d9 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -23,9 +23,11 @@ #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <android-base/unique_fd.h> - +#include <binder/Status.h> #include <cutils/properties.h> #include <gtest/gtest.h> @@ -33,6 +35,7 @@ #include <selinux/android.h> #include <selinux/avc.h> +#include "binder_test_utils.h" #include "dexopt.h" #include "InstalldNativeService.h" #include "globals.h" @@ -84,10 +87,33 @@ system(cmd.c_str()); } -static void mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) { - ::mkdir(path.c_str(), mode); - ::chown(path.c_str(), owner, group); - ::chmod(path.c_str(), mode); +template <typename Visitor> +static void run_cmd_and_process_output(const std::string& cmd, const Visitor& visitor) { + FILE* file = popen(cmd.c_str(), "r"); + CHECK(file != nullptr) << "Failed to ptrace " << cmd; + char* line = nullptr; + while (true) { + size_t n = 0u; + ssize_t value = getline(&line, &n, file); + if (value == -1) { + break; + } + visitor(line); + } + free(line); + fclose(file); +} + +static int mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) { + int ret = ::mkdir(path.c_str(), mode); + if (ret != 0) { + return ret; + } + ret = ::chown(path.c_str(), owner, group); + if (ret != 0) { + return ret; + } + return ::chmod(path.c_str(), mode); } static int log_callback(int type, const char *fmt, ...) { // NOLINT @@ -137,6 +163,20 @@ "AAAACADojmFLPcugSwoBAAAUAgAACwAYAAAAAAAAAAAAoIEAAAAAY2xhc3Nlcy5kZXhVVAUAA/Ns" "+ll1eAsAAQQj5QIABIgTAABQSwUGAAAAAAEAAQBRAAAATwEAAAAA"; +class DexoptTestEnvTest : public testing::Test { +}; + +TEST_F(DexoptTestEnvTest, CheckSelinux) { + ASSERT_EQ(1, is_selinux_enabled()); + + // Crude cutout for virtual devices. +#if !defined(__i386__) && !defined(__x86_64__) + constexpr bool kIsX86 = false; +#else + constexpr bool kIsX86 = true; +#endif + ASSERT_TRUE(1 == security_getenforce() || kIsX86 || true /* b/119032200 */); +} class DexoptTest : public testing::Test { protected: @@ -184,7 +224,7 @@ se_info_ = "default"; app_apk_dir_ = android_app_dir + package_name_; - create_mock_app(); + ASSERT_TRUE(create_mock_app()); } virtual void TearDown() { @@ -198,33 +238,57 @@ delete service_; } - void create_mock_app() { + ::testing::AssertionResult create_mock_app() { // Create the oat dir. app_oat_dir_ = app_apk_dir_ + "/oat"; - mkdir(app_apk_dir_, kSystemUid, kSystemGid, 0755); - service_->createOatDir(app_oat_dir_, kRuntimeIsa); + // For debug mode, the directory might already exist. Avoid erroring out. + if (mkdir(app_apk_dir_, kSystemUid, kSystemGid, 0755) != 0 && !kDebug) { + return ::testing::AssertionFailure() << "Could not create app dir " << app_apk_dir_ + << " : " << strerror(errno); + } + binder::Status status = service_->createOatDir(app_oat_dir_, kRuntimeIsa); + if (!status.isOk()) { + return ::testing::AssertionFailure() << "Could not create oat dir: " + << status.toString8().c_str(); + } // Copy the primary apk. apk_path_ = app_apk_dir_ + "/base.jar"; - ASSERT_TRUE(WriteBase64ToFile(kDexFile, apk_path_, kSystemUid, kSystemGid, 0644)); + std::string error_msg; + if (!WriteBase64ToFile(kDexFile, apk_path_, kSystemUid, kSystemGid, 0644, &error_msg)) { + return ::testing::AssertionFailure() << "Could not write base64 file to " << apk_path_ + << " : " << error_msg; + } // Create the app user data. - ASSERT_TRUE(service_->createAppData( - volume_uuid_, - package_name_, - kTestUserId, - kAppDataFlags, - kTestAppUid, - se_info_, - kOSdkVersion, - &ce_data_inode_).isOk()); + status = service_->createAppData( + volume_uuid_, + package_name_, + kTestUserId, + kAppDataFlags, + kTestAppUid, + se_info_, + kOSdkVersion, + &ce_data_inode_); + if (!status.isOk()) { + return ::testing::AssertionFailure() << "Could not create app data: " + << status.toString8().c_str(); + } // Create a secondary dex file on CE storage const char* volume_uuid_cstr = volume_uuid_ == nullptr ? nullptr : volume_uuid_->c_str(); app_private_dir_ce_ = create_data_user_ce_package_path( volume_uuid_cstr, kTestUserId, package_name_.c_str()); secondary_dex_ce_ = app_private_dir_ce_ + "/secondary_ce.jar"; - ASSERT_TRUE(WriteBase64ToFile(kDexFile, secondary_dex_ce_, kTestAppUid, kTestAppGid, 0600)); + if (!WriteBase64ToFile(kDexFile, + secondary_dex_ce_, + kTestAppUid, + kTestAppGid, + 0600, + &error_msg)) { + return ::testing::AssertionFailure() << "Could not write base64 file to " + << secondary_dex_ce_ << " : " << error_msg; + } std::string app_private_dir_ce_link = create_data_user_ce_package_path_as_user_link( volume_uuid_cstr, kTestUserId, package_name_.c_str()); secondary_dex_ce_link_ = app_private_dir_ce_link + "/secondary_ce.jar"; @@ -233,10 +297,24 @@ app_private_dir_de_ = create_data_user_de_package_path( volume_uuid_cstr, kTestUserId, package_name_.c_str()); secondary_dex_de_ = app_private_dir_de_ + "/secondary_de.jar"; - ASSERT_TRUE(WriteBase64ToFile(kDexFile, secondary_dex_de_, kTestAppUid, kTestAppGid, 0600)); + if (!WriteBase64ToFile(kDexFile, + secondary_dex_de_, + kTestAppUid, + kTestAppGid, + 0600, + &error_msg)) { + return ::testing::AssertionFailure() << "Could not write base64 file to " + << secondary_dex_de_ << " : " << error_msg; + } // Fix app data uid. - ASSERT_TRUE(service_->fixupAppData(volume_uuid_, kTestUserId).isOk()); + status = service_->fixupAppData(volume_uuid_, kTestUserId); + if (!status.isOk()) { + return ::testing::AssertionFailure() << "Could not fixup app data: " + << status.toString8().c_str(); + } + + return ::testing::AssertionSuccess(); } @@ -249,16 +327,21 @@ void CompileSecondaryDex(const std::string& path, int32_t dex_storage_flag, bool should_binder_call_succeed, bool should_dex_be_compiled = true, - /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1) { + /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1, + const char* class_loader_context = nullptr) { if (uid == -1) { uid = kTestAppUid; } + if (class_loader_context == nullptr) { + class_loader_context = "&"; + } std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_)); int32_t dexopt_needed = 0; // does not matter; std::unique_ptr<std::string> out_path = nullptr; // does not matter int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag; std::string compiler_filter = "speed-profile"; - std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&")); + std::unique_ptr<std::string> class_loader_context_ptr( + new std::string(class_loader_context)); std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_)); bool downgrade = false; int32_t target_sdk_version = 0; // default @@ -394,11 +477,9 @@ std::unique_ptr<std::string> compilation_reason_ptr(new std::string("test-reason")); bool prof_result; - binder::Status prof_binder_result = service_->prepareAppProfile( + ASSERT_BINDER_SUCCESS(service_->prepareAppProfile( package_name_, kTestUserId, kTestAppId, *profile_name_ptr, apk_path_, - /*dex_metadata*/ nullptr, &prof_result); - - ASSERT_TRUE(prof_binder_result.isOk()) << prof_binder_result.toString8().c_str(); + /*dex_metadata*/ nullptr, &prof_result)); ASSERT_TRUE(prof_result); binder::Status result = service_->dexopt(apk_path_, @@ -479,12 +560,26 @@ /*binder_ok*/ true, /*compile_ok*/ true); } +TEST_F(DexoptTest, DexoptSecondaryCeWithContext) { + LOG(INFO) << "DexoptSecondaryCeWithContext"; + std::string class_loader_context = "PCL[" + secondary_dex_ce_ + "]"; + CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE, + /*binder_ok*/ true, /*compile_ok*/ true, nullptr, -1, class_loader_context.c_str()); +} + TEST_F(DexoptTest, DexoptSecondaryDe) { LOG(INFO) << "DexoptSecondaryDe"; CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE, /*binder_ok*/ true, /*compile_ok*/ true); } +TEST_F(DexoptTest, DexoptSecondaryDeWithContext) { + LOG(INFO) << "DexoptSecondaryDeWithContext"; + std::string class_loader_context = "PCL[" + secondary_dex_de_ + "]"; + CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE, + /*binder_ok*/ true, /*compile_ok*/ true, nullptr, -1, class_loader_context.c_str()); +} + TEST_F(DexoptTest, DexoptSecondaryDoesNotExist) { LOG(INFO) << "DexoptSecondaryDoesNotExist"; // If the file validates but does not exist we do not treat it as an error. @@ -500,7 +595,7 @@ CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_DE, /*binder_ok*/ false, /*compile_ok*/ false, &status); EXPECT_STREQ(status.toString8().c_str(), - "Status(-8): '-1: Dexoptanalyzer path validation failed'"); + "Status(-8, EX_SERVICE_SPECIFIC): '-1: Dexoptanalyzer path validation failed'"); } TEST_F(DexoptTest, DexoptSecondaryAppOwnershipValidationError) { @@ -509,7 +604,7 @@ CompileSecondaryDex("/data/data/random.app/secondary.jar", DEXOPT_STORAGE_CE, /*binder_ok*/ false, /*compile_ok*/ false, &status); EXPECT_STREQ(status.toString8().c_str(), - "Status(-8): '-1: Dexoptanalyzer path validation failed'"); + "Status(-8, EX_SERVICE_SPECIFIC): '-1: Dexoptanalyzer path validation failed'"); } TEST_F(DexoptTest, DexoptSecondaryAcessViaDifferentUidError) { @@ -517,7 +612,8 @@ binder::Status status; CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE, /*binder_ok*/ false, /*compile_ok*/ false, &status, kSystemUid); - EXPECT_STREQ(status.toString8().c_str(), "Status(-8): '-1: Dexoptanalyzer open zip failed'"); + EXPECT_STREQ(status.toString8().c_str(), + "Status(-8, EX_SERVICE_SPECIFIC): '-1: Dexoptanalyzer open zip failed'"); } TEST_F(DexoptTest, DexoptPrimaryPublic) { @@ -539,7 +635,7 @@ DEX2OAT_FROM_SCRATCH, &status); EXPECT_STREQ(status.toString8().c_str(), - "Status(-8): \'256: Dex2oat invocation for " + "Status(-8, EX_SERVICE_SPECIFIC): \'256: Dex2oat invocation for " "/data/app/com.installd.test.dexopt/base.jar failed: unspecified dex2oat error'"); } @@ -572,6 +668,50 @@ DEX2OAT_FROM_SCRATCH); } +TEST_F(DexoptTest, ResolveStartupConstStrings) { + LOG(INFO) << "DexoptDex2oatResolveStartupStrings"; + const std::string property = "persist.device_config.runtime.dex2oat_resolve_startup_strings"; + const std::string previous_value = android::base::GetProperty(property, ""); + auto restore_property = android::base::make_scope_guard([=]() { + android::base::SetProperty(property, previous_value); + }); + std::string odex = GetPrimaryDexArtifact(app_oat_dir_.c_str(), apk_path_, "odex"); + // Disable the property to start. + bool found_disable = false; + ASSERT_TRUE(android::base::SetProperty(property, "false")) << property; + CompilePrimaryDexOk("speed-profile", + DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PROFILE_GUIDED | + DEXOPT_GENERATE_APP_IMAGE, + app_oat_dir_.c_str(), + kTestAppGid, + DEX2OAT_FROM_SCRATCH); + run_cmd_and_process_output( + "oatdump --header-only --oat-file=" + odex, + [&](const std::string& line) { + if (line.find("--resolve-startup-const-strings=false") != std::string::npos) { + found_disable = true; + } + }); + EXPECT_TRUE(found_disable); + // Enable the property and inspect that .art artifact is larger. + bool found_enable = false; + ASSERT_TRUE(android::base::SetProperty(property, "true")) << property; + CompilePrimaryDexOk("speed-profile", + DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PROFILE_GUIDED | + DEXOPT_GENERATE_APP_IMAGE, + app_oat_dir_.c_str(), + kTestAppGid, + DEX2OAT_FROM_SCRATCH); + run_cmd_and_process_output( + "oatdump --header-only --oat-file=" + odex, + [&](const std::string& line) { + if (line.find("--resolve-startup-const-strings=true") != std::string::npos) { + found_enable = true; + } + }); + EXPECT_TRUE(found_enable); +} + class PrimaryDexReCompilationTest : public DexoptTest { public: virtual void SetUp() { @@ -703,9 +843,8 @@ void createProfileSnapshot(int32_t appid, const std::string& package_name, bool expected_result) { bool result; - binder::Status binder_result = service_->createProfileSnapshot( - appid, package_name, kPrimaryProfile, apk_path_, &result); - ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str(); + ASSERT_BINDER_SUCCESS(service_->createProfileSnapshot( + appid, package_name, kPrimaryProfile, apk_path_, &result)); ASSERT_EQ(expected_result, result); if (!expected_result) { @@ -745,9 +884,8 @@ const std::string& code_path, bool expected_result) { bool result; - binder::Status binder_result = service_->mergeProfiles( - kTestAppUid, package_name, code_path, &result); - ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str(); + ASSERT_BINDER_SUCCESS(service_->mergeProfiles( + kTestAppUid, package_name, code_path, &result)); ASSERT_EQ(expected_result, result); if (!expected_result) { @@ -773,10 +911,9 @@ void preparePackageProfile(const std::string& package_name, const std::string& profile_name, bool expected_result) { bool result; - binder::Status binder_result = service_->prepareAppProfile( + ASSERT_BINDER_SUCCESS(service_->prepareAppProfile( package_name, kTestUserId, kTestAppId, profile_name, apk_path_, - /*dex_metadata*/ nullptr, &result); - ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str(); + /*dex_metadata*/ nullptr, &result)); ASSERT_EQ(expected_result, result); if (!expected_result) { @@ -799,8 +936,7 @@ protected: void TransitionToSystemServer() { ASSERT_TRUE(DropCapabilities(kSystemUid, kSystemGid)); - int32_t res = selinux_android_setcontext( - kSystemUid, true, se_info_.c_str(), "system_server"); + int32_t res = selinux_android_setcon("u:r:system_server:s0"); ASSERT_EQ(0, res) << "Failed to setcon " << strerror(errno); } @@ -862,8 +998,7 @@ SetupProfiles(/*setup_ref*/ true); createProfileSnapshot(kTestAppId, package_name_, /*expected_result*/ true); - binder::Status binder_result = service_->destroyProfileSnapshot(package_name_, kPrimaryProfile); - ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str(); + ASSERT_BINDER_SUCCESS(service_->destroyProfileSnapshot(package_name_, kPrimaryProfile)); struct stat st; ASSERT_EQ(-1, stat(snap_profile_.c_str(), &st)); ASSERT_EQ(ENOENT, errno); @@ -921,7 +1056,7 @@ ASSERT_EQ(0, chmod(ref_profile_dir.c_str(), 0700)); // Run createAppData again which will offer to fix-up the profile directories. - ASSERT_TRUE(service_->createAppData( + ASSERT_BINDER_SUCCESS(service_->createAppData( volume_uuid_, package_name_, kTestUserId, @@ -929,7 +1064,7 @@ kTestAppUid, se_info_, kOSdkVersion, - &ce_data_inode_).isOk()); + &ce_data_inode_)); // Check the file access. CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR); @@ -975,9 +1110,8 @@ void createBootImageProfileSnapshot(const std::string& classpath, bool expected_result) { bool result; - binder::Status binder_result = service_->createProfileSnapshot( - -1, "android", "android.prof", classpath, &result); - ASSERT_TRUE(binder_result.isOk()); + ASSERT_BINDER_SUCCESS(service_->createProfileSnapshot( + -1, "android", "android.prof", classpath, &result)); ASSERT_EQ(expected_result, result); if (!expected_result) { @@ -1060,5 +1194,64 @@ ASSERT_TRUE(std::find(profiles.begin(), profiles.end(), ref_prof) != profiles.end()); } +TEST_F(DexoptTest, select_execution_binary) { + LOG(INFO) << "DexoptTestselect_execution_binary"; + + std::string release_str = app_private_dir_ce_ + "/release"; + std::string debug_str = app_private_dir_ce_ + "/debug"; + + // Setup the binaries. Note that we only need executable files to actually + // test the execution binary selection + run_cmd("touch " + release_str); + run_cmd("touch " + debug_str); + run_cmd("chmod 777 " + release_str); + run_cmd("chmod 777 " + debug_str); + + const char* release = release_str.c_str(); + const char* debug = debug_str.c_str(); + + ASSERT_STREQ(release, select_execution_binary( + release, + debug, + /*background_job_compile=*/ false, + /*is_debug_runtime=*/ false, + /*is_release=*/ false, + /*is_debuggable_build=*/ false)); + + ASSERT_STREQ(release, select_execution_binary( + release, + debug, + /*background_job_compile=*/ true, + /*is_debug_runtime=*/ false, + /*is_release=*/ true, + /*is_debuggable_build=*/ true)); + + ASSERT_STREQ(debug, select_execution_binary( + release, + debug, + /*background_job_compile=*/ false, + /*is_debug_runtime=*/ true, + /*is_release=*/ false, + /*is_debuggable_build=*/ false)); + + ASSERT_STREQ(debug, select_execution_binary( + release, + debug, + /*background_job_compile=*/ true, + /*is_debug_runtime=*/ false, + /*is_release=*/ false, + /*is_debuggable_build=*/ true)); + + + // Select the release when the debug file is not there. + ASSERT_STREQ(release, select_execution_binary( + release, + "does_not_exist", + /*background_job_compile=*/ false, + /*is_debug_runtime=*/ true, + /*is_release=*/ false, + /*is_debuggable_build=*/ false)); +} + } // namespace installd } // namespace android
diff --git a/cmds/installd/tests/installd_dexopt_test.xml b/cmds/installd/tests/installd_dexopt_test.xml new file mode 100644 index 0000000..24526cc --- /dev/null +++ b/cmds/installd/tests/installd_dexopt_test.xml
@@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<!-- Note: this is derived from the autogenerated configuration. We require + root support. --> +<configuration description="Runs installd_dexopt_test."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="installd_dexopt_test->/data/local/tmp/installd_dexopt_test" /> + </target_preparer> + + <!-- The test runs as root to prepare the temporary directory, make selinux adjustments + and so on. --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="installd_dexopt_test" /> + </test> +</configuration>
diff --git a/cmds/installd/tests/installd_otapreopt_test.cpp b/cmds/installd/tests/installd_otapreopt_test.cpp index b518507..66dd51e 100644 --- a/cmds/installd/tests/installd_otapreopt_test.cpp +++ b/cmds/installd/tests/installd_otapreopt_test.cpp
@@ -114,11 +114,14 @@ case 7: return "7"; case 8: return "8"; case 9: return "9"; + case 10: return "10"; } return nullptr; } - std::vector<const char*> getArgs(uint32_t version, bool versioned) { + std::vector<const char*> getArgs(uint32_t version, + bool versioned, + const char* shared_libs = "shared.lib") { std::vector<const char*> args; args.push_back("otapreopt"); // "otapreopt" args.push_back("a"); // slot @@ -135,7 +138,7 @@ args.push_back("0"); // dexopt_flags args.push_back("speed"); // filter args.push_back("!"); // volume - args.push_back("shared.lib"); // libs + args.push_back(shared_libs); // libs if (version > 1) { args.push_back("!"); // seinfo @@ -159,9 +162,11 @@ return args; } - void VerifyReadArguments(uint32_t version, bool versioned) { + void VerifyReadArguments(uint32_t version, + bool versioned, + const char* shared_libs = "shared.lib") { OTAPreoptParameters params; - std::vector<const char*> args = getArgs(version, versioned); + std::vector<const char*> args = getArgs(version, versioned, shared_libs); ASSERT_TRUE(params.ReadArguments(args.size() - 1, args.data())); verifyPackageParameters(params, version, versioned, args.data()); } @@ -199,6 +204,18 @@ VerifyReadArguments(7, true); } +TEST_F(OTAPreoptTest, ReadArgumentsV9SharedLibsAmpersand) { + OTAPreoptParameters params; + std::vector<const char*> args = getArgs(9, true, "&"); + ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data())); +} + +TEST_F(OTAPreoptTest, ReadArgumentsV10SharedLibsAmpersand) { + OTAPreoptParameters params; + std::vector<const char*> args = getArgs(10, true, "&"); + ASSERT_TRUE(params.ReadArguments(args.size() - 1, args.data())); +} + TEST_F(OTAPreoptTest, ReadArgumentsFailToManyArgs) { OTAPreoptParameters params; std::vector<const char*> args = getArgs(5, true);
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index a5af5d7..a31d510 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp
@@ -15,16 +15,24 @@ */ #include <sstream> +#include <string> + +#include <fcntl.h> #include <stdlib.h> #include <string.h> #include <sys/statvfs.h> +#include <sys/stat.h> #include <sys/xattr.h> +#include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <cutils/properties.h> #include <gtest/gtest.h> +#include "binder_test_utils.h" #include "InstalldNativeService.h" #include "dexopt.h" #include "globals.h" @@ -37,7 +45,7 @@ constexpr const char* kTestUuid = "TEST"; -static constexpr int FLAG_FORCE = 1 << 16; +#define FLAG_FORCE InstalldNativeService::FLAG_FORCE int get_property(const char *key, char *value, const char *default_value) { return property_get(key, value, default_value); @@ -63,27 +71,28 @@ static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) { const std::string fullPath = get_full_path(path); - ::mkdir(fullPath.c_str(), mode); - ::chown(fullPath.c_str(), owner, group); - ::chmod(fullPath.c_str(), mode); + EXPECT_EQ(::mkdir(fullPath.c_str(), mode), 0); + EXPECT_EQ(::chown(fullPath.c_str(), owner, group), 0); + EXPECT_EQ(::chmod(fullPath.c_str(), mode), 0); } static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) { int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode); - ::fchown(fd, owner, group); - ::fchmod(fd, mode); - ::close(fd); + EXPECT_NE(fd, -1); + EXPECT_EQ(::fchown(fd, owner, group), 0); + EXPECT_EQ(::fchmod(fd, mode), 0); + EXPECT_EQ(::close(fd), 0); } static int stat_gid(const char* path) { struct stat buf; - ::stat(get_full_path(path).c_str(), &buf); + EXPECT_EQ(::stat(get_full_path(path).c_str(), &buf), 0); return buf.st_gid; } static int stat_mode(const char* path) { struct stat buf; - ::stat(get_full_path(path).c_str(), &buf); + EXPECT_EQ(::stat(get_full_path(path).c_str(), &buf), 0); return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID); } @@ -162,8 +171,8 @@ std::vector<uint8_t> result; std::string dexPath = get_full_path("com.example/foo/file"); - EXPECT_TRUE(service->hashSecondaryDexFile( - dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk()); + EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile( + dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result)); EXPECT_EQ(result.size(), 32U); @@ -182,8 +191,8 @@ std::vector<uint8_t> result; std::string dexPath = get_full_path("com.example/foo/file"); - EXPECT_TRUE(service->hashSecondaryDexFile( - dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk()); + EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile( + dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result)); EXPECT_EQ(result.size(), 0U); } @@ -197,8 +206,8 @@ std::vector<uint8_t> result; std::string dexPath = get_full_path("com.example/foo/file"); - EXPECT_TRUE(service->hashSecondaryDexFile( - dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk()); + EXPECT_BINDER_SUCCESS(service->hashSecondaryDexFile( + dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result)); EXPECT_EQ(result.size(), 0U); } @@ -212,8 +221,8 @@ std::vector<uint8_t> result; std::string dexPath = get_full_path("com.example/foo/file"); - EXPECT_FALSE(service->hashSecondaryDexFile( - dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk()); + EXPECT_BINDER_FAIL(service->hashSecondaryDexFile( + dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result)); } TEST_F(ServiceTest, CalculateOat) { @@ -240,5 +249,410 @@ EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf)); } +static bool mkdirs(const std::string& path, mode_t mode) { + struct stat sb; + if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) { + return true; + } + + if (!mkdirs(android::base::Dirname(path), mode)) { + return false; + } + + if (::mkdir(path.c_str(), mode) != 0) { + PLOG(DEBUG) << "Failed to create folder " << path; + return false; + } + return true; +} + +class AppDataSnapshotTest : public testing::Test { +private: + std::string rollback_ce_base_dir; + std::string rollback_de_base_dir; + +protected: + InstalldNativeService* service; + + std::string fake_package_ce_path; + std::string fake_package_de_path; + + virtual void SetUp() { + setenv("ANDROID_LOG_TAGS", "*:v", 1); + android::base::InitLogging(nullptr); + + service = new InstalldNativeService(); + ASSERT_TRUE(mkdirs("/data/local/tmp/user/0", 0700)); + + init_globals_from_data_and_root(); + + rollback_ce_base_dir = create_data_misc_ce_rollback_base_path("TEST", 0); + rollback_de_base_dir = create_data_misc_de_rollback_base_path("TEST", 0); + + fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); + fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); + + ASSERT_TRUE(mkdirs(rollback_ce_base_dir, 0700)); + ASSERT_TRUE(mkdirs(rollback_de_base_dir, 0700)); + ASSERT_TRUE(mkdirs(fake_package_ce_path, 0700)); + ASSERT_TRUE(mkdirs(fake_package_de_path, 0700)); + } + + virtual void TearDown() { + ASSERT_EQ(0, delete_dir_contents_and_dir(rollback_ce_base_dir, true)); + ASSERT_EQ(0, delete_dir_contents_and_dir(rollback_de_base_dir, true)); + ASSERT_EQ(0, delete_dir_contents(fake_package_ce_path, true)); + ASSERT_EQ(0, delete_dir_contents(fake_package_de_path, true)); + + delete service; + ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user/0", true)); + } +}; + +TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 37); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 37); + + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_CE", fake_package_ce_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_DE", fake_package_de_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + // Request a snapshot of the CE content but not the DE content. + int64_t ce_snapshot_inode; + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, 37, FLAG_STORAGE_CE, &ce_snapshot_inode)); + struct stat buf; + memset(&buf, 0, sizeof(buf)); + ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &buf)); + ASSERT_EQ(ce_snapshot_inode, (int64_t) buf.st_ino); + + std::string ce_content, de_content; + // At this point, we should have the CE content but not the DE content. + ASSERT_TRUE(android::base::ReadFileToString( + rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); + ASSERT_FALSE(android::base::ReadFileToString( + rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); + ASSERT_EQ("TEST_CONTENT_CE", ce_content); + + // Modify the CE content, so we can assert later that it's reflected + // in the snapshot. + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_CE_MODIFIED", fake_package_ce_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + // Request a snapshot of the DE content but not the CE content. + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, 37, FLAG_STORAGE_DE, &ce_snapshot_inode)); + // Only DE content snapshot was requested. + ASSERT_EQ(ce_snapshot_inode, 0); + + // At this point, both the CE as well as the DE content should be fully + // populated. + ASSERT_TRUE(android::base::ReadFileToString( + rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::ReadFileToString( + rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); + ASSERT_EQ("TEST_CONTENT_CE", ce_content); + ASSERT_EQ("TEST_CONTENT_DE", de_content); + + // Modify the DE content, so we can assert later that it's reflected + // in our final snapshot. + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_DE_MODIFIED", fake_package_de_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + // Request a snapshot of both the CE as well as the DE content. + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, 37, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); + + ASSERT_TRUE(android::base::ReadFileToString( + rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::ReadFileToString( + rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); + ASSERT_EQ("TEST_CONTENT_CE_MODIFIED", ce_content); + ASSERT_EQ("TEST_CONTENT_DE_MODIFIED", de_content); +} + +TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_TwoSnapshotsWithTheSameId) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 67); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 67); + + auto another_fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.bar"); + auto another_fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.bar"); + + // Since this test sets up data for another package, some bookkeeping is required. + auto deleter = [&]() { + ASSERT_EQ(0, delete_dir_contents_and_dir(another_fake_package_ce_path, true)); + ASSERT_EQ(0, delete_dir_contents_and_dir(another_fake_package_de_path, true)); + }; + auto scope_guard = android::base::make_scope_guard(deleter); + + ASSERT_TRUE(mkdirs(another_fake_package_ce_path, 0700)); + ASSERT_TRUE(mkdirs(another_fake_package_de_path, 0700)); + + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_CE", fake_package_ce_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_DE", fake_package_de_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "ANOTHER_TEST_CONTENT_CE", another_fake_package_ce_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "ANOTHER_TEST_CONTENT_DE", another_fake_package_de_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + // Request snapshot for the package com.foo. + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); + // Now request snapshot with the same id for the package com.bar + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.bar", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); + + // Check that both snapshots have correct data in them. + std::string com_foo_ce_content, com_foo_de_content; + std::string com_bar_ce_content, com_bar_de_content; + ASSERT_TRUE(android::base::ReadFileToString( + rollback_ce_dir + "/com.foo/file1", &com_foo_ce_content, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::ReadFileToString( + rollback_de_dir + "/com.foo/file1", &com_foo_de_content, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::ReadFileToString( + rollback_ce_dir + "/com.bar/file1", &com_bar_ce_content, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::ReadFileToString( + rollback_de_dir + "/com.bar/file1", &com_bar_de_content, false /* follow_symlinks */)); + ASSERT_EQ("TEST_CONTENT_CE", com_foo_ce_content); + ASSERT_EQ("TEST_CONTENT_DE", com_foo_de_content); + ASSERT_EQ("ANOTHER_TEST_CONTENT_CE", com_bar_ce_content); + ASSERT_EQ("ANOTHER_TEST_CONTENT_DE", com_bar_de_content); +} + +TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_AppDataAbsent) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 73); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 73); + + // Similuating app data absence. + ASSERT_EQ(0, delete_dir_contents_and_dir(fake_package_ce_path, true)); + ASSERT_EQ(0, delete_dir_contents_and_dir(fake_package_de_path, true)); + + int64_t ce_snapshot_inode; + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, 73, FLAG_STORAGE_CE, &ce_snapshot_inode)); + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, 73, FLAG_STORAGE_DE, nullptr)); + // No CE content snapshot was performed. + ASSERT_EQ(ce_snapshot_inode, 0); + + // The snapshot calls must succeed but there should be no snapshot + // created. + struct stat sb; + ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); + ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); +} + +TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_ClearsExistingSnapshot) { + auto rollback_ce_dir = create_data_misc_ce_rollback_package_path("TEST", 0, 13, "com.foo"); + auto rollback_de_dir = create_data_misc_de_rollback_package_path("TEST", 0, 13, "com.foo"); + + ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); + + // Simulate presence of an existing snapshot + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_CE", rollback_ce_dir + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_DE", rollback_de_dir + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + // Create app data. + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_2_CE", fake_package_ce_path + "/file2", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_2_DE", fake_package_de_path + "/file2", + 0700, 10000, 20000, false /* follow_symlinks */)); + + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, 13, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); + + // Previous snapshot (with data for file1) must be cleared. + struct stat sb; + ASSERT_EQ(-1, stat((rollback_ce_dir + "/file1").c_str(), &sb)); + ASSERT_EQ(-1, stat((rollback_de_dir + "/file1").c_str(), &sb)); + // New snapshot (with data for file2) must be present. + ASSERT_NE(-1, stat((rollback_ce_dir + "/file2").c_str(), &sb)); + ASSERT_NE(-1, stat((rollback_de_dir + "/file2").c_str(), &sb)); +} + +TEST_F(AppDataSnapshotTest, SnapshotAppData_WrongVolumeUuid) { + // Setup rollback folders to make sure that fails due to wrong volumeUuid being + // passed, not because of some other reason. + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 17); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 17); + + ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); + + EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique<std::string>("FOO"), + "com.foo", 0, 17, FLAG_STORAGE_DE, nullptr)); +} + +TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_ClearsCache) { + auto fake_package_ce_cache_path = fake_package_ce_path + "/cache"; + auto fake_package_ce_code_cache_path = fake_package_ce_path + "/code_cache"; + auto fake_package_de_cache_path = fake_package_de_path + "/cache"; + auto fake_package_de_code_cache_path = fake_package_de_path + "/code_cache"; + + ASSERT_TRUE(mkdirs(fake_package_ce_cache_path, 0700)); + ASSERT_TRUE(mkdirs(fake_package_ce_code_cache_path, 0700)); + ASSERT_TRUE(mkdirs(fake_package_de_cache_path, 0700)); + ASSERT_TRUE(mkdirs(fake_package_de_code_cache_path, 0700)); + + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_CE", fake_package_ce_cache_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_CE", fake_package_ce_code_cache_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_DE", fake_package_de_cache_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, 23, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr)); + // The snapshot call must clear cache. + struct stat sb; + ASSERT_EQ(-1, stat((fake_package_ce_cache_path + "/file1").c_str(), &sb)); + ASSERT_EQ(-1, stat((fake_package_ce_code_cache_path + "/file1").c_str(), &sb)); + ASSERT_EQ(-1, stat((fake_package_de_cache_path + "/file1").c_str(), &sb)); + ASSERT_EQ(-1, stat((fake_package_de_code_cache_path + "/file1").c_str(), &sb)); +} + +TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 239); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 239); + + ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); + + // Write contents to the rollback location. We'll write the same files to the + // app data location and make sure the restore has overwritten them. + ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 0700)); + ASSERT_TRUE(android::base::WriteStringToFile( + "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_CE", fake_package_ce_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_DE", fake_package_de_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"), + "com.foo", 10000, "", 0, 239, FLAG_STORAGE_DE | FLAG_STORAGE_CE)); + + std::string ce_content, de_content; + ASSERT_TRUE(android::base::ReadFileToString( + fake_package_ce_path + "/file1", &ce_content, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::ReadFileToString( + fake_package_de_path + "/file1", &de_content, false /* follow_symlinks */)); + ASSERT_EQ("CE_RESTORE_CONTENT", ce_content); + ASSERT_EQ("DE_RESTORE_CONTENT", de_content); +} + +TEST_F(AppDataSnapshotTest, CreateSnapshotThenDestroyIt) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 57); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 57); + + // Prepare data for snapshot. + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_CE", fake_package_ce_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_DE", fake_package_de_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + int64_t ce_snapshot_inode; + // Request a snapshot of both the CE as well as the DE content. + ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk()); + // Because CE data snapshot was requested, ce_snapshot_inode can't be null. + ASSERT_NE(0, ce_snapshot_inode); + // Check snapshot is there. + struct stat sb; + ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); + ASSERT_EQ(0, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); + + + ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), + "com.foo", 0, ce_snapshot_inode, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); + // Check snapshot is deleted. + ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); + ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); +} + +TEST_F(AppDataSnapshotTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 1543); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 1543); + + // Create a snapshot + ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 0700)); + ASSERT_TRUE(android::base::WriteStringToFile( + "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), + "com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); + + // Check snapshot is deleted. + struct stat sb; + ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); + ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); + + // Check that deleting already deleted snapshot is no-op. + ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), + "com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); +} + +TEST_F(AppDataSnapshotTest, DestroyAppDataSnapshot_WrongVolumeUuid) { + // Setup rollback data to make sure that test fails due to wrong volumeUuid + // being passed, not because of some other reason. + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 43); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 43); + + ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); + + ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique<std::string>("BAR"), + "com.foo", 0, 0, 43, FLAG_STORAGE_DE).isOk()); +} + +TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot_WrongVolumeUuid) { + // Setup rollback data to make sure that fails due to wrong volumeUuid being + // passed, not because of some other reason. + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 41); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 41); + + ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); + + EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_unique<std::string>("BAR"), + "com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE)); +} + } // namespace installd } // namespace android
diff --git a/cmds/installd/tests/installd_service_test.xml b/cmds/installd/tests/installd_service_test.xml new file mode 100644 index 0000000..b838f4f --- /dev/null +++ b/cmds/installd/tests/installd_service_test.xml
@@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<!-- Note: this is derived from the autogenerated configuration. We require + root support. --> +<configuration description="Runs installd_service_test."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="installd_service_test->/data/local/tmp/installd_service_test" /> + </target_preparer> + + <!-- The test requires root for file access. --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="installd_service_test" /> + </test> +</configuration>
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index bcdd03e..e61eb6e 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -18,9 +18,11 @@ #include <string.h> #include <android-base/logging.h> +#include <android-base/scopeguard.h> #include <gtest/gtest.h> #include "InstalldNativeService.h" +#include "MatchExtensionGen.h" #include "globals.h" #include "utils.h" @@ -529,5 +531,102 @@ EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file")); } +TEST_F(UtilsTest, MatchExtension_Valid) { + EXPECT_EQ(AID_MEDIA_VIDEO, MatchExtension("mpg")); + EXPECT_EQ(AID_MEDIA_VIDEO, MatchExtension("mpeg")); + EXPECT_EQ(AID_MEDIA_VIDEO, MatchExtension("mPeG")); + EXPECT_EQ(AID_MEDIA_VIDEO, MatchExtension("MPEG")); +} + +TEST_F(UtilsTest, MatchExtension_Invalid) { + EXPECT_EQ(0, MatchExtension("log")); + EXPECT_EQ(0, MatchExtension("3amp")); + EXPECT_EQ(0, MatchExtension("fpe")); + EXPECT_EQ(0, MatchExtension("docx")); +} + +TEST_F(UtilsTest, TestRollbackPaths) { + EXPECT_EQ("/data/misc_ce/0/rollback/239/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 0, 239, "com.foo")); + EXPECT_EQ("/data/misc_ce/10/rollback/37/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 10, 37, "com.foo")); + + EXPECT_EQ("/data/misc_de/0/rollback/73/com.foo", + create_data_misc_de_rollback_package_path(nullptr, 0, 73, "com.foo")); + EXPECT_EQ("/data/misc_de/10/rollback/13/com.foo", + create_data_misc_de_rollback_package_path(nullptr, 10, 13, "com.foo")); + + EXPECT_EQ("/data/misc_ce/0/rollback/57", + create_data_misc_ce_rollback_path(nullptr, 0, 57)); + EXPECT_EQ("/data/misc_ce/10/rollback/1543", + create_data_misc_ce_rollback_path(nullptr, 10, 1543)); + + EXPECT_EQ("/data/misc_de/0/rollback/43", + create_data_misc_de_rollback_path(nullptr, 0, 43)); + EXPECT_EQ("/data/misc_de/10/rollback/41", + create_data_misc_de_rollback_path(nullptr, 10, 41)); + + EXPECT_EQ("/data/misc_ce/0/rollback/17/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 0, 17, "com.foo", 0)); + EXPECT_EQ("/data/misc_ce/0/rollback/19/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 0, 19, "com.foo", 239)); + + auto rollback_ce_path = create_data_misc_ce_rollback_path(nullptr, 0, 53); + auto rollback_ce_package_path = create_data_misc_ce_rollback_package_path(nullptr, 0, 53, + "com.foo"); + auto deleter = [&rollback_ce_path]() { + delete_dir_contents_and_dir(rollback_ce_path, true /* ignore_if_missing */); + }; + auto scope_guard = android::base::make_scope_guard(deleter); + + EXPECT_NE(-1, mkdir(rollback_ce_path.c_str(), 700)); + EXPECT_NE(-1, mkdir(rollback_ce_package_path.c_str(), 700)); + + ino_t ce_data_inode; + EXPECT_EQ(0, get_path_inode(rollback_ce_package_path, &ce_data_inode)); + + EXPECT_EQ("/data/misc_ce/0/rollback/53/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 0, 53, "com.foo", ce_data_inode)); + // Check that path defined by inode is picked even if it's not the same as + // the fallback one. + EXPECT_EQ("/data/misc_ce/0/rollback/53/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 0, 53, "com.bar", ce_data_inode)); + + // These last couple of cases are never exercised in production because we + // only snapshot apps in the primary data partition. Exercise them here for + // the sake of completeness. + EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_ce/0/rollback/7/com.example", + create_data_misc_ce_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, 7, + "com.example")); + EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_de/0/rollback/11/com.example", + create_data_misc_de_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, 11, + "com.example")); +} + +TEST_F(UtilsTest, TestCreateDirIfNeeded) { + system("mkdir -p /data/local/tmp/user/0"); + + auto deleter = [&]() { + delete_dir_contents_and_dir("/data/local/tmp/user/0", true /* ignore_if_missing */); + }; + auto scope_guard = android::base::make_scope_guard(deleter); + + // Create folder and check it's permissions. + ASSERT_EQ(0, create_dir_if_needed("/data/local/tmp/user/0/foo", 0700)); + struct stat st; + ASSERT_EQ(0, stat("/data/local/tmp/user/0/foo", &st)); + ASSERT_EQ(0700, st.st_mode & ALLPERMS); + + // Check that create_dir_if_needed is no-op if folder already exists with + // correct permissions. + ASSERT_EQ(0, create_dir_if_needed("/data/local/tmp/user/0/foo", 0700)); + + // Check -1 is returned if folder exists but with different permissions. + ASSERT_EQ(-1, create_dir_if_needed("/data/local/tmp/user/0/foo", 0750)); + + // Check that call fails if parent doesn't exist. + ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700)); +} + } // namespace installd } // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.xml b/cmds/installd/tests/installd_utils_test.xml new file mode 100644 index 0000000..92aba50 --- /dev/null +++ b/cmds/installd/tests/installd_utils_test.xml
@@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<!-- Note: this is derived from the autogenerated configuration. We require + root support. --> +<configuration description="Runs installd_utils_test."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="installd_utils_test->/data/local/tmp/installd_utils_test" /> + </target_preparer> + + <!-- The test requires root for file access (rollback paths). --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="installd_utils_test" /> + </test> +</configuration>
diff --git a/cmds/installd/tests/test_utils.h b/cmds/installd/tests/test_utils.h index b8785c6..70eefe2 100644 --- a/cmds/installd/tests/test_utils.h +++ b/cmds/installd/tests/test_utils.h
@@ -1,8 +1,27 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + #include <stdlib.h> #include <string.h> #include <sys/capability.h> #include <android-base/logging.h> +#include <android-base/stringprintf.h> #include <selinux/android.h> uint8_t kBase64Map[256] = { @@ -74,7 +93,7 @@ } bool WriteBase64ToFile(const char* base64, const std::string& file, - uid_t uid, gid_t gid, int mode) { + uid_t uid, gid_t gid, int mode, std::string* error_msg) { CHECK(base64 != nullptr); size_t length; std::unique_ptr<uint8_t[]> bytes(DecodeBase64(base64, &length)); @@ -83,8 +102,10 @@ int fd = open(file.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + using android::base::StringPrintf; + if (fd < 0) { - PLOG(ERROR) << "Could not open file " << file; + *error_msg = StringPrintf("Could not open file %s: %s", file.c_str(), strerror(errno)); return false; } @@ -92,18 +113,18 @@ while (wrote < length) { ssize_t cur = write(fd, bytes.get() + wrote, length - wrote); if (cur == -1) { - PLOG(ERROR) << "Could not write file " << file; + *error_msg = StringPrintf("Could not write file %s: %s", file.c_str(), strerror(errno)); return false; } wrote += cur; } if (::chown(file.c_str(), uid, gid) != 0) { - PLOG(ERROR) << "Could not chown file " << file; + *error_msg = StringPrintf("Could not chown file %s: %s", file.c_str(), strerror(errno)); return false; } if (::chmod(file.c_str(), mode) != 0) { - PLOG(ERROR) << "Could not chmod file " << file; + *error_msg = StringPrintf("Could not chmod file %s: %s", file.c_str(), strerror(errno)); return false; } return true;
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 1ff45e4..4eb1df0 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp
@@ -20,6 +20,7 @@ #include <fcntl.h> #include <fts.h> #include <stdlib.h> +#include <sys/capability.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/xattr.h> @@ -34,6 +35,7 @@ #include <log/log.h> #include <private/android_filesystem_config.h> +#include "dexopt_return_codes.h" #include "globals.h" // extern variables. #ifndef LOG_TAG @@ -43,6 +45,7 @@ #define DEBUG_XATTRS 0 using android::base::EndsWith; +using android::base::Fdopendir; using android::base::StringPrintf; using android::base::unique_fd; @@ -67,6 +70,35 @@ CHECK(is_valid_package_name(package_name)); } +static std::string resolve_ce_path_by_inode_or_fallback(const std::string& root_path, + ino_t ce_data_inode, const std::string& fallback) { + if (ce_data_inode != 0) { + DIR* dir = opendir(root_path.c_str()); + if (dir == nullptr) { + PLOG(ERROR) << "Failed to opendir " << root_path; + return fallback; + } + + struct dirent* ent; + while ((ent = readdir(dir))) { + if (ent->d_ino == ce_data_inode) { + auto resolved = StringPrintf("%s/%s", root_path.c_str(), ent->d_name); + if (resolved != fallback) { + LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode + << " instead of " << fallback; + } + closedir(dir); + return resolved; + } + } + LOG(WARNING) << "Failed to resolve inode " << ce_data_inode << "; using " << fallback; + closedir(dir); + return fallback; + } else { + return fallback; + } +} + /** * Create the path name where package app contents should be stored for * the given volume UUID and package name. An empty UUID is assumed to @@ -110,34 +142,8 @@ // For testing purposes, rely on the inode when defined; this could be // optimized to use access() in the future. auto fallback = create_data_user_ce_package_path(volume_uuid, user, package_name); - if (ce_data_inode != 0) { - auto user_path = create_data_user_ce_path(volume_uuid, user); - DIR* dir = opendir(user_path.c_str()); - if (dir == nullptr) { - PLOG(ERROR) << "Failed to opendir " << user_path; - return fallback; - } - - struct dirent* ent; - while ((ent = readdir(dir))) { - if (ent->d_ino == ce_data_inode) { - auto resolved = StringPrintf("%s/%s", user_path.c_str(), ent->d_name); -#if DEBUG_XATTRS - if (resolved != fallback) { - LOG(DEBUG) << "Resolved path " << resolved << " for inode " << ce_data_inode - << " instead of " << fallback; - } -#endif - closedir(dir); - return resolved; - } - } - LOG(WARNING) << "Failed to resolve inode " << ce_data_inode << "; using " << fallback; - closedir(dir); - return fallback; - } else { - return fallback; - } + auto user_path = create_data_user_ce_path(volume_uuid, user); + return resolve_ce_path_by_inode_or_fallback(user_path, ce_data_inode, fallback); } std::string create_data_user_de_package_path(const char* volume_uuid, @@ -191,6 +197,46 @@ return StringPrintf("%s/user_de/%u", data.c_str(), userid); } +std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) { + return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user); +} + +std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user) { + return StringPrintf("%s/misc_de/%u/rollback", create_data_path(volume_uuid).c_str(), user); +} + +std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user, + int32_t snapshot_id) { + return StringPrintf("%s/%d", create_data_misc_ce_rollback_base_path(volume_uuid, user).c_str(), + snapshot_id); +} + +std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user, + int32_t snapshot_id) { + return StringPrintf("%s/%d", create_data_misc_de_rollback_base_path(volume_uuid, user).c_str(), + snapshot_id); +} + +std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, + userid_t user, int32_t snapshot_id, const char* package_name) { + return StringPrintf("%s/%s", + create_data_misc_ce_rollback_path(volume_uuid, user, snapshot_id).c_str(), package_name); +} + +std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, + userid_t user, int32_t snapshot_id, const char* package_name, ino_t ce_rollback_inode) { + auto fallback = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshot_id, + package_name); + auto user_path = create_data_misc_ce_rollback_path(volume_uuid, user, snapshot_id); + return resolve_ce_path_by_inode_or_fallback(user_path, ce_rollback_inode, fallback); +} + +std::string create_data_misc_de_rollback_package_path(const char* volume_uuid, + userid_t user, int32_t snapshot_id, const char* package_name) { + return StringPrintf("%s/%s", + create_data_misc_de_rollback_path(volume_uuid, user, snapshot_id).c_str(), package_name); +} + /** * Create the path name for media for a certain userid. */ @@ -198,10 +244,6 @@ return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid); } -std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name) { - return StringPrintf("%s/media/obb/%s", create_data_path(volume_uuid).c_str(), package_name); -} - std::string create_data_media_package_path(const char* volume_uuid, userid_t userid, const char* data_type, const char* package_name) { return StringPrintf("%s/Android/%s/%s", create_data_media_path(volume_uuid, userid).c_str(), @@ -310,7 +352,7 @@ std::string path(create_data_path(volume_uuid) + "/" + SECONDARY_USER_PREFIX); DIR* dir = opendir(path.c_str()); - if (dir == NULL) { + if (dir == nullptr) { // Unable to discover other users, but at least return owner PLOG(ERROR) << "Failed to opendir " << path; return users; @@ -340,13 +382,13 @@ FTSENT *p; int64_t matchedSize = 0; char *argv[] = { (char*) path.c_str(), nullptr }; - if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) { + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { if (errno != ENOENT) { PLOG(ERROR) << "Failed to fts_open " << path; } return -1; } - while ((p = fts_read(fts)) != NULL) { + while ((p = fts_read(fts)) != nullptr) { switch (p->fts_info) { case FTS_D: case FTS_DEFAULT: @@ -469,7 +511,7 @@ continue; } subdir = fdopendir(subfd); - if (subdir == NULL) { + if (subdir == nullptr) { ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); close(subfd); result = -1; @@ -494,12 +536,36 @@ return result; } +int create_dir_if_needed(const std::string& pathname, mode_t perms) { + struct stat st; + + int rc; + if ((rc = stat(pathname.c_str(), &st)) != 0) { + if (errno == ENOENT) { + return mkdir(pathname.c_str(), perms); + } else { + return rc; + } + } else if (!S_ISDIR(st.st_mode)) { + LOG(DEBUG) << pathname << " is not a folder"; + return -1; + } + + mode_t actual_perms = st.st_mode & ALLPERMS; + if (actual_perms != perms) { + LOG(WARNING) << pathname << " permissions " << actual_perms << " expected " << perms; + return -1; + } + + return 0; +} + int delete_dir_contents(const std::string& pathname, bool ignore_if_missing) { - return delete_dir_contents(pathname.c_str(), 0, NULL, ignore_if_missing); + return delete_dir_contents(pathname.c_str(), 0, nullptr, ignore_if_missing); } int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing) { - return delete_dir_contents(pathname.c_str(), 1, NULL, ignore_if_missing); + return delete_dir_contents(pathname.c_str(), 1, nullptr, ignore_if_missing); } int delete_dir_contents(const char *pathname, @@ -511,7 +577,7 @@ DIR *d; d = opendir(pathname); - if (d == NULL) { + if (d == nullptr) { if (ignore_if_missing && (errno == ENOENT)) { return 0; } @@ -540,12 +606,12 @@ return -1; } d = fdopendir(fd); - if (d == NULL) { + if (d == nullptr) { ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); close(fd); return -1; } - res = _delete_dir_contents(d, 0); + res = _delete_dir_contents(d, nullptr); closedir(d); return res; } @@ -573,7 +639,7 @@ } DIR *ds = fdopendir(sdfd); - if (ds == NULL) { + if (ds == nullptr) { ALOGE("Couldn't fdopendir: %s\n", strerror(errno)); return -1; } @@ -619,18 +685,18 @@ uid_t group) { int res = 0; - DIR *ds = NULL; - DIR *dd = NULL; + DIR *ds = nullptr; + DIR *dd = nullptr; ds = opendir(srcname); - if (ds == NULL) { + if (ds == nullptr) { ALOGE("Couldn't opendir %s: %s\n", srcname, strerror(errno)); return -errno; } mkdir(dstname, 0600); dd = opendir(dstname); - if (dd == NULL) { + if (dd == nullptr) { ALOGE("Couldn't opendir %s: %s\n", dstname, strerror(errno)); closedir(ds); return -errno; @@ -858,6 +924,8 @@ static int validate_apk_path_internal(const std::string& path, int maxSubdirs) { if (validate_path(android_app_dir, path, maxSubdirs) == 0) { return 0; + } else if (validate_path(android_staging_dir, path, maxSubdirs) == 0) { + return 0; } else if (validate_path(android_app_private_dir, path, maxSubdirs) == 0) { return 0; } else if (validate_path(android_app_ephemeral_dir, path, maxSubdirs) == 0) { @@ -964,17 +1032,17 @@ FTS *fts; FTSENT *p; char *argv[] = { (char*) path.c_str(), nullptr }; - if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL))) { + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { PLOG(ERROR) << "Failed to fts_open " << path; return -1; } - while ((p = fts_read(fts)) != NULL) { + while ((p = fts_read(fts)) != nullptr) { switch (p->fts_info) { case FTS_DP: if (chmod(p->fts_path, target_mode) != 0) { PLOG(WARNING) << "Failed to chmod " << p->fts_path; } - // Intentional fall through to also set GID + [[fallthrough]]; // to also set GID case FTS_F: if (chown(p->fts_path, -1, gid) != 0) { PLOG(WARNING) << "Failed to chown " << p->fts_path; @@ -1036,8 +1104,8 @@ continue; } - DIR* subdir = fdopendir(subdir_fd); - if (subdir == NULL) { + DIR* subdir = Fdopendir(std::move(subdir_fd)); + if (subdir == nullptr) { PLOG(WARNING) << "Could not open dir path " << local_path; result = false; continue; @@ -1055,12 +1123,33 @@ bool collect_profiles(std::vector<std::string>* profiles_paths) { DIR* d = opendir(android_profiles_dir.c_str()); - if (d == NULL) { + if (d == nullptr) { return false; } else { return collect_profiles(d, android_profiles_dir, profiles_paths); } } +void drop_capabilities(uid_t uid) { + if (setgid(uid) != 0) { + PLOG(ERROR) << "setgid(" << uid << ") failed in installd during dexopt"; + exit(DexoptReturnCodes::kSetGid); + } + if (setuid(uid) != 0) { + PLOG(ERROR) << "setuid(" << uid << ") failed in installd during dexopt"; + exit(DexoptReturnCodes::kSetUid); + } + // drop capabilities + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata[2]; + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + capheader.version = _LINUX_CAPABILITY_VERSION_3; + if (capset(&capheader, &capdata[0]) < 0) { + PLOG(ERROR) << "capset failed"; + exit(DexoptReturnCodes::kCapSet); + } +} + } // namespace installd } // namespace android
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 5829c4f..6a42026 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h
@@ -36,8 +36,6 @@ #define BYPASS_QUOTA 0 #define BYPASS_SDCARDFS 0 -#define APPLY_HARD_QUOTAS 0 - namespace android { namespace installd { @@ -63,8 +61,20 @@ std::string create_data_user_ce_package_path_as_user_link( const char* volume_uuid, userid_t userid, const char* package_name); +std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user); +std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user); +std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user, + int32_t snapshot_id); +std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user, + int32_t snapshot_id); +std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, + userid_t user, int32_t snapshot_id, const char* package_name); +std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, + userid_t user, int32_t snapshot_id, const char* package_name, ino_t ce_rollback_inode); +std::string create_data_misc_de_rollback_package_path(const char* volume_uuid, + userid_t user, int32_t snapshot_id, const char* package_name); + std::string create_data_media_path(const char* volume_uuid, userid_t userid); -std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name); std::string create_data_media_package_path(const char* volume_uuid, userid_t userid, const char* data_type, const char* package_name); @@ -102,6 +112,8 @@ bool is_valid_filename(const std::string& name); bool is_valid_package_name(const std::string& packageName); +int create_dir_if_needed(const std::string& pathname, mode_t mode); + int delete_dir_contents(const std::string& pathname, bool ignore_if_missing = false); int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = false); @@ -112,6 +124,8 @@ int delete_dir_contents_fd(int dfd, const char *name); +int rm_package_dir(const std::string& package_dir); + int copy_dir_files(const char *srcname, const char *dstname, uid_t owner, gid_t group); int64_t data_disk_free(const std::string& data_path); @@ -142,6 +156,8 @@ // It returns true if there were no errors at all, and false otherwise. bool collect_profiles(std::vector<std::string>* profiles_paths); +void drop_capabilities(uid_t uid); + } // namespace installd } // namespace android
diff --git a/cmds/installd/utils_default.cpp b/cmds/installd/utils_default.cpp new file mode 100644 index 0000000..a6025e6 --- /dev/null +++ b/cmds/installd/utils_default.cpp
@@ -0,0 +1,30 @@ +/* +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "utils.h" + +namespace android { +namespace installd { + +// In this file are default definitions of the functions that may contain +// platform dependent logic. + +int rm_package_dir(const std::string& package_dir) { + return delete_dir_contents_and_dir(package_dir); +} + +} // namespace installd +} // namespace android
diff --git a/cmds/installd/view_compiler.cpp b/cmds/installd/view_compiler.cpp new file mode 100644 index 0000000..60d6492 --- /dev/null +++ b/cmds/installd/view_compiler.cpp
@@ -0,0 +1,95 @@ +/* + * 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. + */ + +#include "view_compiler.h" + +#include <string> + +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "utils.h" + +#include "android-base/logging.h" +#include "android-base/stringprintf.h" +#include "android-base/unique_fd.h" + +namespace android { +namespace installd { + +using base::unique_fd; + +bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file, + int uid) { + CHECK(apk_path != nullptr); + CHECK(package_name != nullptr); + CHECK(out_dex_file != nullptr); + + // viewcompiler won't have permission to open anything, so we have to open the files first + // and pass file descriptors. + + // Open input file + unique_fd infd{open(apk_path, O_RDONLY)}; // NOLINT(android-cloexec-open) + if (infd.get() < 0) { + PLOG(ERROR) << "Could not open input file: " << apk_path; + return false; + } + + // Set up output file. viewcompiler can't open outputs by fd, but it can write to stdout, so + // we close stdout and open it towards the right output. + unique_fd outfd{open(out_dex_file, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644)}; + if (outfd.get() < 0) { + PLOG(ERROR) << "Could not open output file: " << out_dex_file; + return false; + } + if (fchmod(outfd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0) { + PLOG(ERROR) << "Could not change output file permissions"; + return false; + } + if (dup2(outfd, STDOUT_FILENO) < 0) { + PLOG(ERROR) << "Could not duplicate output file descriptor"; + return false; + } + + // Prepare command line arguments for viewcompiler + std::string args[] = {"/system/bin/viewcompiler", + "--apk", + "--infd", + android::base::StringPrintf("%d", infd.get()), + "--dex", + "--package", + package_name}; + char* const argv[] = {const_cast<char*>(args[0].c_str()), const_cast<char*>(args[1].c_str()), + const_cast<char*>(args[2].c_str()), const_cast<char*>(args[3].c_str()), + const_cast<char*>(args[4].c_str()), const_cast<char*>(args[5].c_str()), + const_cast<char*>(args[6].c_str()), nullptr}; + + pid_t pid = fork(); + if (pid == 0) { + // Now that we've opened the files we need, drop privileges. + drop_capabilities(uid); + execv("/system/bin/viewcompiler", argv); + _exit(1); + } + + return wait_child(pid) == 0; +} + +} // namespace installd +} // namespace android
diff --git a/cmds/installd/view_compiler.h b/cmds/installd/view_compiler.h new file mode 100644 index 0000000..aa141ca --- /dev/null +++ b/cmds/installd/view_compiler.h
@@ -0,0 +1,29 @@ +/* + * 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. + */ + +#ifndef VIEW_COMPILER_H_ +#define VIEW_COMPILER_H_ + +namespace android { +namespace installd { + +bool view_compiler(const char* apk_path, const char* package_name, const char* out_dex_file, + int uid); + +} // namespace installd +} // namespace android + +#endif // VIEW_COMPILER_H_
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c index 3b8955b..71f0837 100644 --- a/cmds/ip-up-vpn/ip-up-vpn.c +++ b/cmds/ip-up-vpn/ip-up-vpn.c
@@ -95,6 +95,7 @@ strncpy(ifr.ifr_name, interface, IFNAMSIZ); if (ioctl(s, SIOCSIFFLAGS, &ifr)) { ALOGE("Cannot bring up %s: %s", interface, strerror(errno)); + fclose(state); return 1; } @@ -102,6 +103,7 @@ if (!set_address(&ifr.ifr_addr, address) || ioctl(s, SIOCSIFADDR, &ifr)) { ALOGE("Cannot set address: %s", strerror(errno)); + fclose(state); return 1; } @@ -109,6 +111,7 @@ if (set_address(&ifr.ifr_netmask, env("INTERNAL_NETMASK4"))) { if (ioctl(s, SIOCSIFNETMASK, &ifr)) { ALOGE("Cannot set netmask: %s", strerror(errno)); + fclose(state); return 1; } } @@ -123,6 +126,7 @@ fprintf(state, "%s\n", env("REMOTE_ADDR")); } else { ALOGE("Cannot parse parameters"); + fclose(state); return 1; }
diff --git a/cmds/lshal/Command.h b/cmds/lshal/Command.h index 4f128ab..e19e3f7 100644 --- a/cmds/lshal/Command.h +++ b/cmds/lshal/Command.h
@@ -27,7 +27,7 @@ // Base class for all *Commands class Command { public: - Command(Lshal& lshal) : mLshal(lshal) {} + explicit Command(Lshal& lshal) : mLshal(lshal) {} virtual ~Command() = default; // Expect optind to be set by Lshal::main and points to the next argument // to process.
diff --git a/cmds/lshal/DebugCommand.cpp b/cmds/lshal/DebugCommand.cpp index dd8812d..0952db6 100644 --- a/cmds/lshal/DebugCommand.cpp +++ b/cmds/lshal/DebugCommand.cpp
@@ -59,8 +59,8 @@ auto pair = splitFirst(mInterfaceName, '/'); - FQName fqName(pair.first); - if (!fqName.isValid() || fqName.isIdentifier() || !fqName.isFullyQualified()) { + FQName fqName; + if (!FQName::parse(pair.first, &fqName) || fqName.isIdentifier() || !fqName.isFullyQualified()) { mLshal.err() << "Invalid fully-qualified name '" << pair.first << "'\n\n"; return USAGE; }
diff --git a/cmds/lshal/DebugCommand.h b/cmds/lshal/DebugCommand.h index 6e12008..3c3f56f 100644 --- a/cmds/lshal/DebugCommand.h +++ b/cmds/lshal/DebugCommand.h
@@ -31,7 +31,7 @@ class DebugCommand : public Command { public: - DebugCommand(Lshal &lshal) : Command(lshal) {} + explicit DebugCommand(Lshal &lshal) : Command(lshal) {} ~DebugCommand() = default; Status main(const Arg &arg) override; void usage() const override;
diff --git a/cmds/lshal/HelpCommand.h b/cmds/lshal/HelpCommand.h index cc709f8..da0cba6 100644 --- a/cmds/lshal/HelpCommand.h +++ b/cmds/lshal/HelpCommand.h
@@ -31,7 +31,7 @@ class HelpCommand : public Command { public: - HelpCommand(Lshal &lshal) : Command(lshal) {} + explicit HelpCommand(Lshal &lshal) : Command(lshal) {} ~HelpCommand() = default; Status main(const Arg &arg) override; void usage() const override;
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index ff22048..c706d91 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp
@@ -18,14 +18,17 @@ #include <getopt.h> +#include <algorithm> #include <fstream> +#include <functional> #include <iomanip> #include <iostream> #include <map> -#include <sstream> #include <regex> +#include <sstream> #include <android-base/file.h> +#include <android-base/logging.h> #include <android-base/parseint.h> #include <android/hidl/manager/1.0/IServiceManager.h> #include <hidl-hash/Hash.h> @@ -54,6 +57,19 @@ return (p == Partition::SYSTEM) ? vintf::SchemaType::FRAMEWORK : vintf::SchemaType::DEVICE; } +Partition toPartition(vintf::SchemaType t) { + switch (t) { + case vintf::SchemaType::FRAMEWORK: return Partition::SYSTEM; + // TODO(b/71555570): Device manifest does not distinguish HALs from vendor or ODM. + case vintf::SchemaType::DEVICE: return Partition::VENDOR; + } + return Partition::UNKNOWN; +} + +std::string getPackageAndVersion(const std::string& fqInstance) { + return splitFirst(fqInstance, ':').first; +} + NullableOStream<std::ostream> ListCommand::out() const { return mLshal.out(); } @@ -74,6 +90,8 @@ } const std::string &ListCommand::getCmdline(pid_t pid) { + static const std::string kEmptyString{}; + if (pid == NO_PID) return kEmptyString; auto pair = mCmdlines.find(pid); if (pair != mCmdlines.end()) { return pair->second; @@ -90,6 +108,7 @@ } Partition ListCommand::getPartition(pid_t pid) { + if (pid == NO_PID) return Partition::UNKNOWN; auto it = mPartitions.find(pid); if (it != mPartitions.end()) { return it->second; @@ -101,21 +120,19 @@ // Give sensible defaults when nothing can be inferred from runtime. // process: Partition inferred from executable location or cmdline. -Partition ListCommand::resolvePartition(Partition process, const FQName& fqName) const { - if (fqName.inPackage("vendor") || - fqName.inPackage("com")) { +Partition ListCommand::resolvePartition(Partition process, const FqInstance& fqInstance) const { + if (fqInstance.inPackage("vendor") || fqInstance.inPackage("com")) { return Partition::VENDOR; } - if (fqName.inPackage("android.frameworks") || - fqName.inPackage("android.system") || - fqName.inPackage("android.hidl")) { + if (fqInstance.inPackage("android.frameworks") || fqInstance.inPackage("android.system") || + fqInstance.inPackage("android.hidl")) { return Partition::SYSTEM; } // Some android.hardware HALs are served from system. Check the value from executable // location / cmdline first. - if (fqName.inPackage("android.hardware")) { + if (fqInstance.inPackage("android.hardware")) { if (process != Partition::UNKNOWN) { return process; } @@ -125,6 +142,67 @@ return process; } +bool match(const vintf::ManifestInstance& instance, const FqInstance& fqInstance, + vintf::TransportArch ta) { + // For hwbinder libs, allow missing arch in manifest. + // For passthrough libs, allow missing interface/instance in table. + return (ta.transport == instance.transport()) && + (ta.transport == vintf::Transport::HWBINDER || + vintf::contains(instance.arch(), ta.arch)) && + (!fqInstance.hasInterface() || fqInstance.getInterface() == instance.interface()) && + (!fqInstance.hasInstance() || fqInstance.getInstance() == instance.instance()); +} + +bool match(const vintf::MatrixInstance& instance, const FqInstance& fqInstance, + vintf::TransportArch /* ta */) { + return (!fqInstance.hasInterface() || fqInstance.getInterface() == instance.interface()) && + (!fqInstance.hasInstance() || instance.matchInstance(fqInstance.getInstance())); +} + +template <typename ObjectType> +VintfInfo getVintfInfo(const std::shared_ptr<const ObjectType>& object, + const FqInstance& fqInstance, vintf::TransportArch ta, VintfInfo value) { + bool found = false; + (void)object->forEachInstanceOfVersion(fqInstance.getPackage(), fqInstance.getVersion(), + [&](const auto& instance) { + found = match(instance, fqInstance, ta); + return !found; // continue if not found + }); + return found ? value : VINTF_INFO_EMPTY; +} + +std::shared_ptr<const vintf::HalManifest> ListCommand::getDeviceManifest() const { + return vintf::VintfObject::GetDeviceHalManifest(); +} + +std::shared_ptr<const vintf::CompatibilityMatrix> ListCommand::getDeviceMatrix() const { + return vintf::VintfObject::GetDeviceCompatibilityMatrix(); +} + +std::shared_ptr<const vintf::HalManifest> ListCommand::getFrameworkManifest() const { + return vintf::VintfObject::GetFrameworkHalManifest(); +} + +std::shared_ptr<const vintf::CompatibilityMatrix> ListCommand::getFrameworkMatrix() const { + return vintf::VintfObject::GetFrameworkCompatibilityMatrix(); +} + +VintfInfo ListCommand::getVintfInfo(const std::string& fqInstanceName, + vintf::TransportArch ta) const { + FqInstance fqInstance; + if (!fqInstance.setTo(fqInstanceName) && + // Ignore interface / instance for passthrough libs + !fqInstance.setTo(getPackageAndVersion(fqInstanceName))) { + err() << "Warning: Cannot parse '" << fqInstanceName << "'; no VINTF info." << std::endl; + return VINTF_INFO_EMPTY; + } + + return lshal::getVintfInfo(getDeviceManifest(), fqInstance, ta, DEVICE_MANIFEST) | + lshal::getVintfInfo(getFrameworkManifest(), fqInstance, ta, FRAMEWORK_MANIFEST) | + lshal::getVintfInfo(getDeviceMatrix(), fqInstance, ta, DEVICE_MATRIX) | + lshal::getVintfInfo(getFrameworkMatrix(), fqInstance, ta, FRAMEWORK_MATRIX); +} + static bool scanBinderContext(pid_t pid, const std::string &contextName, std::function<void(const std::string&)> eachLine) { @@ -221,16 +299,40 @@ return &pair.first->second; } -// Must process hwbinder services first, then passthrough services. +bool ListCommand::shouldFetchHalType(const HalType &type) const { + return (std::find(mFetchTypes.begin(), mFetchTypes.end(), type) != mFetchTypes.end()); +} + +Table* ListCommand::tableForType(HalType type) { + switch (type) { + case HalType::BINDERIZED_SERVICES: + return &mServicesTable; + case HalType::PASSTHROUGH_CLIENTS: + return &mPassthroughRefTable; + case HalType::PASSTHROUGH_LIBRARIES: + return &mImplementationsTable; + case HalType::VINTF_MANIFEST: + return &mManifestHalsTable; + case HalType::LAZY_HALS: + return &mLazyHalsTable; + default: + LOG(FATAL) << "Unknown HAL type " << static_cast<int64_t>(type); + return nullptr; + } +} +const Table* ListCommand::tableForType(HalType type) const { + return const_cast<ListCommand*>(this)->tableForType(type); +} + void ListCommand::forEachTable(const std::function<void(Table &)> &f) { - f(mServicesTable); - f(mPassthroughRefTable); - f(mImplementationsTable); + for (const auto& type : mListTypes) { + f(*tableForType(type)); + } } void ListCommand::forEachTable(const std::function<void(const Table &)> &f) const { - f(mServicesTable); - f(mPassthroughRefTable); - f(mImplementationsTable); + for (const auto& type : mListTypes) { + f(*tableForType(type)); + } } void ListCommand::postprocess() { @@ -246,22 +348,25 @@ } } for (TableEntry& entry : table) { - entry.partition = getPartition(entry.serverPid); + if (entry.partition == Partition::UNKNOWN) { + entry.partition = getPartition(entry.serverPid); + } + entry.vintfInfo = getVintfInfo(entry.interfaceName, {entry.transport, entry.arch}); } }); // use a double for loop here because lshal doesn't care about efficiency. for (TableEntry &packageEntry : mImplementationsTable) { std::string packageName = packageEntry.interfaceName; - FQName fqPackageName{packageName.substr(0, packageName.find("::"))}; - if (!fqPackageName.isValid()) { + FQName fqPackageName; + if (!FQName::parse(packageName.substr(0, packageName.find("::")), &fqPackageName)) { continue; } for (TableEntry &interfaceEntry : mPassthroughRefTable) { - if (interfaceEntry.arch != ARCH_UNKNOWN) { + if (interfaceEntry.arch != vintf::Arch::ARCH_EMPTY) { continue; } - FQName interfaceName{splitFirst(interfaceEntry.interfaceName, '/').first}; - if (!interfaceName.isValid()) { + FQName interfaceName; + if (!FQName::parse(splitFirst(interfaceEntry.interfaceName, '/').first, &interfaceName)) { continue; } if (interfaceName.getPackageAndVersion() == fqPackageName) { @@ -271,169 +376,167 @@ } mServicesTable.setDescription( - "All binderized services (registered services through hwservicemanager)"); + "| All binderized services (registered with hwservicemanager)"); mPassthroughRefTable.setDescription( - "All interfaces that getService() has ever return as a passthrough interface;\n" - "PIDs / processes shown below might be inaccurate because the process\n" - "might have relinquished the interface or might have died.\n" - "The Server / Server CMD column can be ignored.\n" - "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n" - "the library and successfully fetched the passthrough implementation."); + "| All interfaces that getService() has ever returned as a passthrough interface;\n" + "| PIDs / processes shown below might be inaccurate because the process\n" + "| might have relinquished the interface or might have died.\n" + "| The Server / Server CMD column can be ignored.\n" + "| The Clients / Clients CMD column shows all process that have ever dlopen'ed \n" + "| the library and successfully fetched the passthrough implementation."); mImplementationsTable.setDescription( - "All available passthrough implementations (all -impl.so files).\n" - "These may return subclasses through their respective HIDL_FETCH_I* functions."); + "| All available passthrough implementations (all -impl.so files).\n" + "| These may return subclasses through their respective HIDL_FETCH_I* functions."); + mManifestHalsTable.setDescription( + "| All HALs that are in VINTF manifest."); + mLazyHalsTable.setDescription( + "| All HALs that are declared in VINTF manifest:\n" + "| - as hwbinder HALs but are not registered to hwservicemanager, and\n" + "| - as hwbinder/passthrough HALs with no implementation."); } -static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) { - for (vintf::Version& v : hal->versions) { - if (v.majorVer == version.majorVer) { - v.minorVer = std::max(v.minorVer, version.minorVer); - return true; - } +bool ListCommand::addEntryWithInstance(const TableEntry& entry, + vintf::HalManifest* manifest) const { + FqInstance fqInstance; + if (!fqInstance.setTo(entry.interfaceName)) { + err() << "Warning: '" << entry.interfaceName << "' is not a valid FqInstance." << std::endl; + return false; } - return false; + + if (fqInstance.getPackage() == gIBaseFqName.package()) { + return true; // always remove IBase from manifest + } + + Partition partition = resolvePartition(entry.partition, fqInstance); + + if (partition == Partition::UNKNOWN) { + err() << "Warning: Cannot guess the partition of FqInstance " << fqInstance.string() + << std::endl; + return false; + } + + if (partition != mVintfPartition) { + return true; // strip out instances that is in a different partition. + } + + vintf::Arch arch; + if (entry.transport == vintf::Transport::HWBINDER) { + arch = vintf::Arch::ARCH_EMPTY; // no need to specify arch in manifest + } else if (entry.transport == vintf::Transport::PASSTHROUGH) { + if (entry.arch == vintf::Arch::ARCH_EMPTY) { + err() << "Warning: '" << entry.interfaceName << "' doesn't have bitness info."; + return false; + } + arch = entry.arch; + } else { + err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl; + return false; + } + + std::string e; + if (!manifest->insertInstance(fqInstance, entry.transport, arch, vintf::HalFormat::HIDL, &e)) { + err() << "Warning: Cannot insert '" << fqInstance.string() << ": " << e << std::endl; + return false; + } + return true; +} + +bool ListCommand::addEntryWithoutInstance(const TableEntry& entry, + const vintf::HalManifest* manifest) const { + const auto& packageAndVersion = splitFirst(getPackageAndVersion(entry.interfaceName), '@'); + const auto& package = packageAndVersion.first; + vintf::Version version; + if (!vintf::parse(packageAndVersion.second, &version)) { + err() << "Warning: Cannot parse version '" << packageAndVersion.second << "' for entry '" + << entry.interfaceName << "'" << std::endl; + return false; + } + + bool found = false; + (void)manifest->forEachInstanceOfVersion(package, version, [&found](const auto&) { + found = true; + return false; // break + }); + return found; } void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const { using vintf::operator|=; using vintf::operator<<; + using namespace std::placeholders; vintf::HalManifest manifest; manifest.setType(toSchemaType(mVintfPartition)); - forEachTable([this, &manifest] (const Table &table) { - for (const TableEntry &entry : table) { - std::string fqInstanceName = entry.interfaceName; + std::vector<std::string> error; + for (const TableEntry& entry : mServicesTable) + if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); + for (const TableEntry& entry : mPassthroughRefTable) + if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); + for (const TableEntry& entry : mManifestHalsTable) + if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); - if (&table == &mImplementationsTable) { - // Quick hack to work around *'s - replaceAll(&fqInstanceName, '*', 'D'); - } - auto splittedFqInstanceName = splitFirst(fqInstanceName, '/'); - FQName fqName(splittedFqInstanceName.first); - if (!fqName.isValid()) { - err() << "Warning: '" << splittedFqInstanceName.first - << "' is not a valid FQName." << std::endl; - continue; - } + std::vector<std::string> passthrough; + for (const TableEntry& entry : mImplementationsTable) + if (!addEntryWithoutInstance(entry, &manifest)) passthrough.push_back(entry.interfaceName); - if (fqName.package() == gIBaseFqName.package()) { - continue; // always remove IBase from manifest - } - - Partition partition = resolvePartition(entry.partition, fqName); - - if (partition == Partition::UNKNOWN) { - err() << "Warning: Cannot guess the partition of instance " << fqInstanceName - << ". It is removed from the generated manifest." << std::endl; - continue; - } - - if (partition != mVintfPartition) { - continue; // strip out instances that is in a different partition. - } - - std::string interfaceName = - &table == &mImplementationsTable ? "" : fqName.name(); - std::string instanceName = - &table == &mImplementationsTable ? "" : splittedFqInstanceName.second; - - vintf::Version version{fqName.getPackageMajorVersion(), - fqName.getPackageMinorVersion()}; - vintf::Transport transport; - vintf::Arch arch; - if (entry.transport == "hwbinder") { - transport = vintf::Transport::HWBINDER; - arch = vintf::Arch::ARCH_EMPTY; - } else if (entry.transport == "passthrough") { - transport = vintf::Transport::PASSTHROUGH; - switch (entry.arch) { - case lshal::ARCH32: - arch = vintf::Arch::ARCH_32; break; - case lshal::ARCH64: - arch = vintf::Arch::ARCH_64; break; - case lshal::ARCH_BOTH: - arch = vintf::Arch::ARCH_32_64; break; - case lshal::ARCH_UNKNOWN: // fallthrough - default: - err() << "Warning: '" << fqName.package() - << "' doesn't have bitness info, assuming 32+64." << std::endl; - arch = vintf::Arch::ARCH_32_64; - } - } else { - err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl; - continue; - } - - bool done = false; - for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) { - if (hal->transport() != transport) { - if (transport != vintf::Transport::PASSTHROUGH) { - err() << "Fatal: should not reach here. Generated result may be wrong for '" - << hal->name << "'." - << std::endl; - } - done = true; - break; - } - if (findAndBumpVersion(hal, version)) { - if (&table != &mImplementationsTable) { - hal->insertLegacyInstance(interfaceName, instanceName); - } - hal->transportArch.arch |= arch; - done = true; - break; - } - } - if (done) { - continue; // to next TableEntry - } - vintf::ManifestHal manifestHal{ - vintf::HalFormat::HIDL, - std::string{fqName.package()}, - {version}, - {transport, arch}, - {}}; - if (&table != &mImplementationsTable) { - manifestHal.insertLegacyInstance(interfaceName, instanceName); - } - if (!manifest.add(std::move(manifestHal))) { - err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl; - } - } - }); out << "<!-- " << std::endl - << " This is a skeleton " << manifest.type() << " manifest. Notes: " << std::endl - << INIT_VINTF_NOTES - << "-->" << std::endl; - out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlag::HALS_NO_FQNAME); + << " This is a skeleton " << manifest.type() << " manifest. Notes: " << std::endl + << INIT_VINTF_NOTES; + if (!error.empty()) { + out << std::endl << " The following HALs are not added; see warnings." << std::endl; + for (const auto& e : error) { + out << " " << e << std::endl; + } + } + if (!passthrough.empty()) { + out << std::endl + << " The following HALs are passthrough and no interface or instance " << std::endl + << " names can be inferred." << std::endl; + for (const auto& e : passthrough) { + out << " " << e << std::endl; + } + } + out << "-->" << std::endl; + out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlags::HALS_ONLY); } std::string ListCommand::INIT_VINTF_NOTES{ - " 1. If a HAL is supported in both hwbinder and passthrough transport, \n" + " 1. If a HAL is supported in both hwbinder and passthrough transport,\n" " only hwbinder is shown.\n" " 2. It is likely that HALs in passthrough transport does not have\n" " <interface> declared; users will have to write them by hand.\n" " 3. A HAL with lower minor version can be overridden by a HAL with\n" " higher minor version if they have the same name and major version.\n" + " 4. This output is intended for launch devices.\n" + " Upgrading devices should not use this tool to generate device\n" + " manifest and replace the existing manifest directly, but should\n" + " edit the existing manifest manually.\n" + " Specifically, devices which launched at Android O-MR1 or earlier\n" + " should not use the 'fqname' format for required HAL entries and\n" + " should instead use the legacy package, name, instance-name format\n" + " until they are updated.\n" }; -static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) { +static vintf::Arch fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) { switch (a) { case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT: - return ARCH64; + return vintf::Arch::ARCH_64; case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT: - return ARCH32; + return vintf::Arch::ARCH_32; case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough default: - return ARCH_UNKNOWN; + return vintf::Arch::ARCH_EMPTY; } } void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const { if (mNeat) { - MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable}) - .createTextTable().dump(out.buf()); + std::vector<const Table*> tables; + forEachTable([&tables](const Table &table) { + tables.push_back(&table); + }); + MergedTable(std::move(tables)).createTextTable().dump(out.buf()); return; } @@ -481,24 +584,13 @@ return OK; } -void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) { - Table *table = nullptr; - switch (source) { - case HWSERVICEMANAGER_LIST : - table = &mServicesTable; break; - case PTSERVICEMANAGER_REG_CLIENT : - table = &mPassthroughRefTable; break; - case LIST_DLLIB : - table = &mImplementationsTable; break; - default: - err() << "Error: Unknown source of entry " << source << std::endl; - } - if (table) { - table->add(std::forward<TableEntry>(entry)); - } +void ListCommand::putEntry(HalType type, TableEntry &&entry) { + tableForType(type)->add(std::forward<TableEntry>(entry)); } Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) { + if (!shouldFetchHalType(HalType::PASSTHROUGH_LIBRARIES)) { return OK; } + using namespace ::android::hardware; using namespace ::android::hidl::manager::V1_0; using namespace ::android::hidl::base::V1_0; @@ -510,12 +602,12 @@ std::string{info.instanceName.c_str()}; entries.emplace(interfaceName, TableEntry{ .interfaceName = interfaceName, - .transport = "passthrough", + .transport = vintf::Transport::PASSTHROUGH, .clientPids = info.clientPids, }).first->second.arch |= fromBaseArchitecture(info.arch); } for (auto &&pair : entries) { - putEntry(LIST_DLLIB, std::move(pair.second)); + putEntry(HalType::PASSTHROUGH_LIBRARIES, std::move(pair.second)); } }); if (!ret.isOk()) { @@ -527,6 +619,8 @@ } Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) { + if (!shouldFetchHalType(HalType::PASSTHROUGH_CLIENTS)) { return OK; } + using namespace ::android::hardware; using namespace ::android::hardware::details; using namespace ::android::hidl::manager::V1_0; @@ -536,11 +630,11 @@ if (info.clientPids.size() <= 0) { continue; } - putEntry(PTSERVICEMANAGER_REG_CLIENT, { + putEntry(HalType::PASSTHROUGH_CLIENTS, { .interfaceName = std::string{info.interfaceName.c_str()} + "/" + std::string{info.instanceName.c_str()}, - .transport = "passthrough", + .transport = vintf::Transport::PASSTHROUGH, .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID, .clientPids = info.clientPids, .arch = fromBaseArchitecture(info.arch) @@ -556,8 +650,11 @@ } Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { - const std::string mode = "hwbinder"; + using vintf::operator<<; + if (!shouldFetchHalType(HalType::BINDERIZED_SERVICES)) { return OK; } + + const vintf::Transport mode = vintf::Transport::HWBINDER; hidl_vec<hidl_string> fqInstanceNames; // copying out for timeoutIPC auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) { @@ -576,12 +673,13 @@ TableEntry& entry = allTableEntries[fqInstanceName]; entry.interfaceName = fqInstanceName; entry.transport = mode; + entry.serviceStatus = ServiceStatus::NON_RESPONSIVE; status |= fetchBinderizedEntry(manager, &entry); } for (auto& pair : allTableEntries) { - putEntry(HWSERVICEMANAGER_LIST, std::move(pair.second)); + putEntry(HalType::BINDERIZED_SERVICES, std::move(pair.second)); } return status; } @@ -681,9 +779,100 @@ handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description()); } } while (0); + if (status == OK) { + entry->serviceStatus = ServiceStatus::ALIVE; + } return status; } +Status ListCommand::fetchManifestHals() { + if (!shouldFetchHalType(HalType::VINTF_MANIFEST)) { return OK; } + Status status = OK; + + for (auto manifest : {getDeviceManifest(), getFrameworkManifest()}) { + if (manifest == nullptr) { + status |= VINTF_ERROR; + continue; + } + + std::map<std::string, TableEntry> entries; + + manifest->forEachInstance([&] (const vintf::ManifestInstance& manifestInstance) { + TableEntry entry{ + .interfaceName = manifestInstance.getFqInstance().string(), + .transport = manifestInstance.transport(), + .arch = manifestInstance.arch(), + // TODO(b/71555570): Device manifest does not distinguish HALs from vendor or ODM. + .partition = toPartition(manifest->type()), + .serviceStatus = ServiceStatus::DECLARED}; + std::string key = entry.interfaceName; + entries.emplace(std::move(key), std::move(entry)); + return true; + }); + + for (auto&& pair : entries) + mManifestHalsTable.add(std::move(pair.second)); + } + return status; +} + +Status ListCommand::fetchLazyHals() { + using vintf::operator<<; + + if (!shouldFetchHalType(HalType::LAZY_HALS)) { return OK; } + Status status = OK; + + for (const TableEntry& manifestEntry : mManifestHalsTable) { + if (manifestEntry.transport == vintf::Transport::HWBINDER) { + if (!hasHwbinderEntry(manifestEntry)) { + mLazyHalsTable.add(TableEntry(manifestEntry)); + } + continue; + } + if (manifestEntry.transport == vintf::Transport::PASSTHROUGH) { + if (!hasPassthroughEntry(manifestEntry)) { + mLazyHalsTable.add(TableEntry(manifestEntry)); + } + continue; + } + err() << "Warning: unrecognized transport in VINTF manifest: " + << manifestEntry.transport; + status |= VINTF_ERROR; + } + return status; +} + +bool ListCommand::hasHwbinderEntry(const TableEntry& entry) const { + for (const TableEntry& existing : mServicesTable) { + if (existing.interfaceName == entry.interfaceName) { + return true; + } + } + return false; +} + +bool ListCommand::hasPassthroughEntry(const TableEntry& entry) const { + FqInstance entryFqInstance; + if (!entryFqInstance.setTo(entry.interfaceName)) { + return false; // cannot parse, so add it anyway. + } + for (const TableEntry& existing : mImplementationsTable) { + FqInstance existingFqInstance; + if (!existingFqInstance.setTo(getPackageAndVersion(existing.interfaceName))) { + continue; + } + + // For example, manifest may say graphics.mapper@2.1 but passthroughServiceManager + // can only list graphics.mapper@2.0. + if (entryFqInstance.getPackage() == existingFqInstance.getPackage() && + vintf::Version{entryFqInstance.getVersion()} + .minorAtLeast(vintf::Version{existingFqInstance.getVersion()})) { + return true; + } + } + return false; +} + Status ListCommand::fetch() { Status status = OK; auto bManager = mLshal.serviceManager(); @@ -703,9 +892,27 @@ } else { status |= fetchAllLibraries(pManager); } + status |= fetchManifestHals(); + status |= fetchLazyHals(); return status; } +void ListCommand::initFetchTypes() { + // TODO: refactor to do polymorphism on each table (so that dependency graph is not hardcoded). + static const std::map<HalType, std::set<HalType>> kDependencyGraph{ + {HalType::LAZY_HALS, {HalType::BINDERIZED_SERVICES, + HalType::PASSTHROUGH_LIBRARIES, + HalType::VINTF_MANIFEST}}, + }; + mFetchTypes.insert(mListTypes.begin(), mListTypes.end()); + for (HalType listType : mListTypes) { + auto it = kDependencyGraph.find(listType); + if (it != kDependencyGraph.end()) { + mFetchTypes.insert(it->second.begin(), it->second.end()); + } + } +} + void ListCommand::registerAllOptions() { int v = mOptions.size(); // A list of acceptable command line options @@ -721,7 +928,7 @@ mOptions.push_back({'l', "released", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::RELEASED); return OK; - }, "print the 'is released?' column\n(Y=released, empty=unreleased or unknown)"}); + }, "print the 'is released?' column\n(Y=released, N=unreleased, ?=unknown)"}); mOptions.push_back({'t', "transport", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::TRANSPORT); return OK; @@ -761,6 +968,23 @@ }, "Emit debug info from\nIBase::debug with empty options. Cannot be used with --neat.\n" "Writes to specified file if 'arg' is provided, otherwise stdout."}); + mOptions.push_back({'V', "vintf", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::VINTF); + return OK; + }, "print VINTF info. This column contains a comma-separated list of:\n" + " - DM: if the HAL is in the device manifest\n" + " - DC: if the HAL is in the device compatibility matrix\n" + " - FM: if the HAL is in the framework manifest\n" + " - FC: if the HAL is in the framework compatibility matrix"}); + mOptions.push_back({'S', "service-status", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::SERVICE_STATUS); + return OK; + }, "print service status column. Possible values are:\n" + " - alive: alive and running hwbinder service;\n" + " - registered;dead: registered to hwservicemanager but is not responsive;\n" + " - declared: only declared in VINTF manifest but is not registered to hwservicemanager;\n" + " - N/A: no information for passthrough HALs."}); + // long options without short alternatives mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) { thiz->mVintf = true; @@ -791,6 +1015,46 @@ thiz->mNeat = true; return OK; }, "output is machine parsable (no explanatory text).\nCannot be used with --debug."}); + mOptions.push_back({'\0', "types", required_argument, v++, [](ListCommand* thiz, const char* arg) { + if (!arg) { return USAGE; } + + static const std::map<std::string, HalType> kHalTypeMap { + {"binderized", HalType::BINDERIZED_SERVICES}, + {"b", HalType::BINDERIZED_SERVICES}, + {"passthrough_clients", HalType::PASSTHROUGH_CLIENTS}, + {"c", HalType::PASSTHROUGH_CLIENTS}, + {"passthrough_libs", HalType::PASSTHROUGH_LIBRARIES}, + {"l", HalType::PASSTHROUGH_LIBRARIES}, + {"vintf", HalType::VINTF_MANIFEST}, + {"v", HalType::VINTF_MANIFEST}, + {"lazy", HalType::LAZY_HALS}, + {"z", HalType::LAZY_HALS}, + }; + + std::vector<std::string> halTypesArgs = split(std::string(arg), ','); + for (const auto& halTypeArg : halTypesArgs) { + if (halTypeArg.empty()) continue; + + const auto& halTypeIter = kHalTypeMap.find(halTypeArg); + if (halTypeIter == kHalTypeMap.end()) { + + thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl; + return USAGE; + } + + // Append unique (non-repeated) HAL types to the reporting list + HalType halType = halTypeIter->second; + if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) == + thiz->mListTypes.end()) { + thiz->mListTypes.push_back(halType); + } + } + + if (thiz->mListTypes.empty()) { return USAGE; } + return OK; + }, "comma-separated list of one or more sections.\nThe output is restricted to the selected " + "section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|" + "passthrough_libs), (v|vintf), and (z|lazy).\nDefault is `bcl`."}); } // Create 'longopts' argument to getopt_long. Caller is responsible for maintaining @@ -809,9 +1073,9 @@ i++; } // getopt_long last option has all zeros - ret[i].name = NULL; + ret[i].name = nullptr; ret[i].has_arg = 0; - ret[i].flag = NULL; + ret[i].flag = nullptr; ret[i].val = 0; return ret; @@ -829,6 +1093,7 @@ } Status ListCommand::parseArgs(const Arg &arg) { + mListTypes.clear(); if (mOptions.empty()) { registerAllOptions(); @@ -885,7 +1150,7 @@ } if (mSelectedColumns.empty()) { - mSelectedColumns = {TableColumnType::RELEASED, + mSelectedColumns = {TableColumnType::VINTF, TableColumnType::RELEASED, TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS}; } @@ -901,6 +1166,13 @@ } } + // By default, list all HAL types + if (mListTypes.empty()) { + mListTypes = {HalType::BINDERIZED_SERVICES, HalType::PASSTHROUGH_CLIENTS, + HalType::PASSTHROUGH_LIBRARIES}; + } + initFetchTypes(); + forEachTable([this] (Table& table) { table.setSelectedColumns(this->mSelectedColumns); }); @@ -919,22 +1191,6 @@ return status; } -static std::vector<std::string> splitString(const std::string &s, char c) { - std::vector<std::string> components; - - size_t startPos = 0; - size_t matchPos; - while ((matchPos = s.find(c, startPos)) != std::string::npos) { - components.push_back(s.substr(startPos, matchPos - startPos)); - startPos = matchPos + 1; - } - - if (startPos <= s.length()) { - components.push_back(s.substr(startPos)); - } - return components; -} - const std::string& ListCommand::RegisteredOption::getHelpMessageForArgument() const { static const std::string empty{}; static const std::string optional{"[=<arg>]"}; @@ -954,7 +1210,7 @@ err() << "list:" << std::endl << " lshal" << std::endl << " lshal list" << std::endl - << " List all hals with default ordering and columns (`lshal list -riepc`)" << std::endl + << " List all hals with default ordering and columns (`lshal list -Vliepc`)" << std::endl << " lshal list [-h|--help]" << std::endl << " -h, --help: Print help message for list (`lshal help list`)" << std::endl << " lshal [list] [OPTIONS...]" << std::endl; @@ -970,7 +1226,7 @@ if (!e.longOption.empty()) err() << "--" << e.longOption << e.getHelpMessageForArgument(); err() << ": "; - std::vector<std::string> lines = splitString(e.help, '\n'); + std::vector<std::string> lines = split(e.help, '\n'); for (const auto& line : lines) { if (&line != &lines.front()) err() << " ";
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h index 1e85ea0..85195fc 100644 --- a/cmds/lshal/ListCommand.h +++ b/cmds/lshal/ListCommand.h
@@ -26,7 +26,9 @@ #include <android-base/macros.h> #include <android/hidl/manager/1.0/IServiceManager.h> -#include <hidl-util/FQName.h> +#include <hidl-util/FqInstance.h> +#include <vintf/HalManifest.h> +#include <vintf/VintfObject.h> #include "Command.h" #include "NullableOStream.h" @@ -45,9 +47,17 @@ uint32_t threadCount; // number of threads total }; +enum class HalType { + BINDERIZED_SERVICES = 0, + PASSTHROUGH_CLIENTS, + PASSTHROUGH_LIBRARIES, + VINTF_MANIFEST, + LAZY_HALS, +}; + class ListCommand : public Command { public: - ListCommand(Lshal &lshal) : Command(lshal) {} + explicit ListCommand(Lshal &lshal) : Command(lshal) {} virtual ~ListCommand() = default; Status main(const Arg &arg) override; void usage() const override; @@ -80,13 +90,17 @@ protected: Status parseArgs(const Arg &arg); + // Retrieve first-hand information Status fetch(); + // Retrieve derived information base on existing table virtual void postprocess(); Status dump(); - void putEntry(TableEntrySource source, TableEntry &&entry); + void putEntry(HalType type, TableEntry &&entry); Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); Status fetchBinderized(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); + Status fetchManifestHals(); + Status fetchLazyHals(); Status fetchBinderizedEntry(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager, TableEntry *entry); @@ -113,19 +127,43 @@ void removeDeadProcesses(Pids *pids); virtual Partition getPartition(pid_t pid); - Partition resolvePartition(Partition processPartition, const FQName& fqName) const; + Partition resolvePartition(Partition processPartition, const FqInstance &fqInstance) const; + + VintfInfo getVintfInfo(const std::string &fqInstanceName, vintf::TransportArch ta) const; + // Allow to mock these functions for testing. + virtual std::shared_ptr<const vintf::HalManifest> getDeviceManifest() const; + virtual std::shared_ptr<const vintf::CompatibilityMatrix> getDeviceMatrix() const; + virtual std::shared_ptr<const vintf::HalManifest> getFrameworkManifest() const; + virtual std::shared_ptr<const vintf::CompatibilityMatrix> getFrameworkMatrix() const; void forEachTable(const std::function<void(Table &)> &f); void forEachTable(const std::function<void(const Table &)> &f) const; + Table* tableForType(HalType type); + const Table* tableForType(HalType type) const; NullableOStream<std::ostream> err() const; NullableOStream<std::ostream> out() const; void registerAllOptions(); + // helper functions to dumpVintf. + bool addEntryWithInstance(const TableEntry &entry, vintf::HalManifest *manifest) const; + bool addEntryWithoutInstance(const TableEntry &entry, const vintf::HalManifest *manifest) const; + + // Helper function. Whether to fetch entries corresponding to a given HAL type. + bool shouldFetchHalType(const HalType &type) const; + + void initFetchTypes(); + + // Helper functions ti add HALs that are listed in VINTF manifest to LAZY_HALS table. + bool hasHwbinderEntry(const TableEntry& entry) const; + bool hasPassthroughEntry(const TableEntry& entry) const; + Table mServicesTable{}; Table mPassthroughRefTable{}; Table mImplementationsTable{}; + Table mManifestHalsTable{}; + Table mLazyHalsTable{}; std::string mFileOutputPath; TableEntryCompare mSortColumn = nullptr; @@ -139,6 +177,11 @@ // If true, explanatory text are not emitted. bool mNeat = false; + // Type(s) of HAL associations to list. + std::vector<HalType> mListTypes{}; + // Type(s) of HAL associations to fetch. + std::set<HalType> mFetchTypes{}; + // If an entry does not exist, need to ask /proc/{pid}/cmdline to get it. // If an entry exist but is an empty string, process might have died. // If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline.
diff --git a/cmds/lshal/NullableOStream.h b/cmds/lshal/NullableOStream.h index ab37a04..737d3a2 100644 --- a/cmds/lshal/NullableOStream.h +++ b/cmds/lshal/NullableOStream.h
@@ -25,8 +25,8 @@ template<typename S> class NullableOStream { public: - NullableOStream(S &os) : mOs(&os) {} - NullableOStream(S *os) : mOs(os) {} + explicit NullableOStream(S &os) : mOs(&os) {} + explicit NullableOStream(S *os) : mOs(os) {} NullableOStream &operator=(S &os) { mOs = &os; return *this; @@ -57,7 +57,7 @@ S& buf() const { return *mOs; } - operator bool() const { + operator bool() const { // NOLINT(google-explicit-constructor) return mOs != nullptr; } private:
diff --git a/cmds/lshal/OWNERS b/cmds/lshal/OWNERS new file mode 100644 index 0000000..60038da --- /dev/null +++ b/cmds/lshal/OWNERS
@@ -0,0 +1,2 @@ +elsk@google.com +smoreland@google.com
diff --git a/cmds/lshal/PipeRelay.cpp b/cmds/lshal/PipeRelay.cpp index fc40749..820679f 100644 --- a/cmds/lshal/PipeRelay.cpp +++ b/cmds/lshal/PipeRelay.cpp
@@ -16,34 +16,75 @@ #include "PipeRelay.h" -#include <sys/socket.h> +#include <sys/select.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <atomic> + +#include <android-base/logging.h> #include <utils/Thread.h> namespace android { namespace lshal { +static constexpr struct timeval READ_TIMEOUT { .tv_sec = 1, .tv_usec = 0 }; + struct PipeRelay::RelayThread : public Thread { explicit RelayThread(int fd, std::ostream &os); bool threadLoop() override; + void setFinished(); private: int mFd; std::ostream &mOutStream; + // If we were to use requestExit() and exitPending() instead, threadLoop() + // may not run at all by the time ~PipeRelay is called (i.e. debug() has + // returned from HAL). By using our own flag, we ensure that select() and + // read() are executed until data are drained. + std::atomic_bool mFinished; + DISALLOW_COPY_AND_ASSIGN(RelayThread); }; //////////////////////////////////////////////////////////////////////////////// PipeRelay::RelayThread::RelayThread(int fd, std::ostream &os) - : mFd(fd), - mOutStream(os) { -} + : mFd(fd), mOutStream(os), mFinished(false) {} bool PipeRelay::RelayThread::threadLoop() { char buffer[1024]; - ssize_t n = read(mFd, buffer, sizeof(buffer)); + + fd_set set; + FD_ZERO(&set); + FD_SET(mFd, &set); + + struct timeval timeout = READ_TIMEOUT; + + int res = TEMP_FAILURE_RETRY(select(mFd + 1, &set, nullptr, nullptr, &timeout)); + if (res < 0) { + PLOG(INFO) << "select() failed"; + return false; + } + + if (res == 0 || !FD_ISSET(mFd, &set)) { + if (mFinished) { + LOG(WARNING) << "debug: timeout reading from pipe, output may be truncated."; + return false; + } + // timeout, but debug() has not returned, so wait for HAL to finish. + return true; + } + + // FD_ISSET(mFd, &set) == true. Data available, start reading + ssize_t n = TEMP_FAILURE_RETRY(read(mFd, buffer, sizeof(buffer))); + + if (n < 0) { + PLOG(ERROR) << "read() failed"; + } if (n <= 0) { return false; @@ -54,11 +95,15 @@ return true; } +void PipeRelay::RelayThread::setFinished() { + mFinished = true; +} + //////////////////////////////////////////////////////////////////////////////// PipeRelay::PipeRelay(std::ostream &os) : mInitCheck(NO_INIT) { - int res = socketpair(AF_UNIX, SOCK_STREAM, 0 /* protocol */, mFds); + int res = pipe(mFds); if (res < 0) { mInitCheck = -errno; @@ -77,20 +122,14 @@ } PipeRelay::~PipeRelay() { - if (mFds[1] >= 0) { - shutdown(mFds[1], SHUT_WR); - } + CloseFd(&mFds[1]); - if (mFds[0] >= 0) { - shutdown(mFds[0], SHUT_RD); - } - - if (mThread != NULL) { + if (mThread != nullptr) { + mThread->setFinished(); mThread->join(); mThread.clear(); } - CloseFd(&mFds[1]); CloseFd(&mFds[0]); }
diff --git a/cmds/lshal/TableEntry.cpp b/cmds/lshal/TableEntry.cpp index e8792a4..8e21975 100644 --- a/cmds/lshal/TableEntry.cpp +++ b/cmds/lshal/TableEntry.cpp
@@ -16,7 +16,11 @@ #define LOG_TAG "lshal" #include <android-base/logging.h> +#include <map> + +#include <android-base/strings.h> #include <hidl-hash/Hash.h> +#include <vintf/parse_string.h> #include "TableEntry.h" @@ -26,19 +30,19 @@ namespace android { namespace lshal { -static const std::string &getArchString(Architecture arch) { +static const std::string &getArchString(vintf::Arch arch) { static const std::string sStr64 = "64"; static const std::string sStr32 = "32"; static const std::string sStrBoth = "32+64"; - static const std::string sStrUnknown = ""; + static const std::string sStrUnknown = "?"; switch (arch) { - case ARCH64: + case vintf::Arch::ARCH_64: return sStr64; - case ARCH32: + case vintf::Arch::ARCH_32: return sStr32; - case ARCH_BOTH: + case vintf::Arch::ARCH_32_64: return sStrBoth; - case ARCH_UNKNOWN: // fall through + case vintf::Arch::ARCH_EMPTY: // fall through default: return sStrUnknown; } @@ -57,6 +61,8 @@ case TableColumnType::THREADS: return "Thread Use"; case TableColumnType::RELEASED: return "R"; case TableColumnType::HASH: return "Hash"; + case TableColumnType::VINTF: return "VINTF"; + case TableColumnType::SERVICE_STATUS: return "Status"; default: LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type); return ""; @@ -68,7 +74,7 @@ case TableColumnType::INTERFACE_NAME: return interfaceName; case TableColumnType::TRANSPORT: - return transport; + return vintf::to_string(transport); case TableColumnType::SERVER_PID: return serverPid == NO_PID ? "N/A" : std::to_string(serverPid); case TableColumnType::SERVER_CMD: @@ -87,6 +93,10 @@ return isReleased(); case TableColumnType::HASH: return hash; + case TableColumnType::VINTF: + return getVintfInfo(); + case TableColumnType::SERVICE_STATUS: + return lshal::to_string(serviceStatus); default: LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type); return ""; @@ -96,12 +106,44 @@ std::string TableEntry::isReleased() const { static const std::string unreleased = Hash::hexString(Hash::kEmptyHash); - if (hash.empty() || hash == unreleased) { - return " "; // unknown or unreleased + if (hash.empty()) { + return "?"; + } + if (hash == unreleased) { + return "N"; // unknown or unreleased } return "Y"; // released } +std::string TableEntry::getVintfInfo() const { + static const std::map<VintfInfo, std::string> values{ + {DEVICE_MANIFEST, "DM"}, + {DEVICE_MATRIX, "DC"}, + {FRAMEWORK_MANIFEST, "FM"}, + {FRAMEWORK_MATRIX, "FC"}, + }; + std::vector<std::string> ret; + for (const auto& pair : values) { + if (vintfInfo & pair.first) { + ret.push_back(pair.second); + } + } + auto joined = base::Join(ret, ','); + return joined.empty() ? "X" : joined; +} + +std::string to_string(ServiceStatus s) { + switch (s) { + case ServiceStatus::ALIVE: return "alive"; + case ServiceStatus::NON_RESPONSIVE: return "non-responsive"; + case ServiceStatus::DECLARED: return "declared"; + case ServiceStatus::UNKNOWN: return "N/A"; + } + + LOG(FATAL) << __func__ << "Should not reach here." << static_cast<int>(s); + return ""; +} + TextTable Table::createTextTable(bool neat, const std::function<std::string(const std::string&)>& emitDebugInfo) const { @@ -152,6 +194,7 @@ } std::string TableEntry::to_string() const { + using vintf::operator<<; std::stringstream ss; ss << "name=" << interfaceName << ";transport=" << transport << ";thread=" << getThreadUsage() << ";server=" << serverPid
diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h index 24ea438..601b7e2 100644 --- a/cmds/lshal/TableEntry.h +++ b/cmds/lshal/TableEntry.h
@@ -24,6 +24,8 @@ #include <iostream> #include <procpartition/procpartition.h> +#include <vintf/Arch.h> +#include <vintf/Transport.h> #include "TextTable.h" @@ -33,21 +35,6 @@ using android::procpartition::Partition; using Pids = std::vector<int32_t>; -enum : unsigned int { - HWSERVICEMANAGER_LIST, // through defaultServiceManager()->list() - PTSERVICEMANAGER_REG_CLIENT, // through registerPassthroughClient - LIST_DLLIB, // through listing dynamic libraries -}; -using TableEntrySource = unsigned int; - -enum : unsigned int { - ARCH_UNKNOWN = 0, - ARCH32 = 1 << 0, - ARCH64 = 1 << 1, - ARCH_BOTH = ARCH32 | ARCH64 -}; -using Architecture = unsigned int; - enum class TableColumnType : unsigned int { INTERFACE_NAME, TRANSPORT, @@ -60,16 +47,35 @@ THREADS, RELEASED, HASH, + VINTF, + SERVICE_STATUS, }; +enum : unsigned int { + VINTF_INFO_EMPTY = 0, + DEVICE_MANIFEST = 1 << 0, + DEVICE_MATRIX = 1 << 1, + FRAMEWORK_MANIFEST = 1 << 2, + FRAMEWORK_MATRIX = 1 << 3, +}; +using VintfInfo = unsigned int; + enum { NO_PID = -1, NO_PTR = 0 }; +enum class ServiceStatus { + UNKNOWN, // For passthrough + ALIVE, + NON_RESPONSIVE, // registered but not respond to calls + DECLARED, // in VINTF manifest +}; +std::string to_string(ServiceStatus s); + struct TableEntry { std::string interfaceName{}; - std::string transport{}; + vintf::Transport transport{vintf::Transport::EMPTY}; int32_t serverPid{NO_PID}; uint32_t threadUsage{0}; uint32_t threadCount{0}; @@ -77,10 +83,13 @@ uint64_t serverObjectAddress{NO_PTR}; Pids clientPids{}; std::vector<std::string> clientCmdlines{}; - Architecture arch{ARCH_UNKNOWN}; + vintf::Arch arch{vintf::Arch::ARCH_EMPTY}; // empty: unknown, all zeros: unreleased, otherwise: released std::string hash{}; Partition partition{Partition::UNKNOWN}; + VintfInfo vintfInfo{VINTF_INFO_EMPTY}; + // true iff hwbinder and service started + ServiceStatus serviceStatus{ServiceStatus::UNKNOWN}; static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) { return a.interfaceName < b.interfaceName; @@ -99,6 +108,8 @@ std::string isReleased() const; + std::string getVintfInfo() const; + std::string getField(TableColumnType type) const; bool operator==(const TableEntry& other) const; @@ -138,7 +149,7 @@ class MergedTable { public: - MergedTable(std::vector<const Table*>&& tables) : mTables(std::move(tables)) {} + explicit MergedTable(std::vector<const Table*>&& tables) : mTables(std::move(tables)) {} TextTable createTextTable(); private: std::vector<const Table*> mTables;
diff --git a/cmds/lshal/TextTable.h b/cmds/lshal/TextTable.h index 91d522a..301b4bd 100644 --- a/cmds/lshal/TextTable.h +++ b/cmds/lshal/TextTable.h
@@ -33,11 +33,11 @@ TextTableRow() {} // A row of cells. - TextTableRow(std::vector<std::string>&& v) : mFields(std::move(v)) {} + explicit TextTableRow(std::vector<std::string>&& v) : mFields(std::move(v)) {} // A single comment string. - TextTableRow(std::string&& s) : mLine(std::move(s)) {} - TextTableRow(const std::string& s) : mLine(s) {} + explicit TextTableRow(std::string&& s) : mLine(std::move(s)) {} + explicit TextTableRow(const std::string& s) : mLine(s) {} // Whether this row is an actual row of cells. bool isRow() const { return !fields().empty(); }
diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h index c940404..46d8177 100644 --- a/cmds/lshal/Timeout.h +++ b/cmds/lshal/Timeout.h
@@ -29,7 +29,7 @@ class BackgroundTaskState { public: - BackgroundTaskState(std::function<void(void)> &&func) + explicit BackgroundTaskState(std::function<void(void)> &&func) : mFunc(std::forward<decltype(func)>(func)) {} void notify() { std::unique_lock<std::mutex> lock(mMutex); @@ -57,7 +57,7 @@ BackgroundTaskState &state = *static_cast<BackgroundTaskState *>(data); state(); state.notify(); - return NULL; + return nullptr; } template<class R, class P> @@ -65,7 +65,7 @@ auto now = std::chrono::system_clock::now(); BackgroundTaskState state{std::forward<decltype(func)>(func)}; pthread_t thread; - if (pthread_create(&thread, NULL, callAndNotify, &state)) { + if (pthread_create(&thread, nullptr, callAndNotify, &state)) { std::cerr << "FATAL: could not create background thread." << std::endl; return false; } @@ -73,7 +73,7 @@ if (!success) { pthread_kill(thread, SIGINT); } - pthread_join(thread, NULL); + pthread_join(thread, nullptr); return success; }
diff --git a/cmds/lshal/libprocpartition/procpartition.cpp b/cmds/lshal/libprocpartition/procpartition.cpp index 8ca458a..9645f3a 100644 --- a/cmds/lshal/libprocpartition/procpartition.cpp +++ b/cmds/lshal/libprocpartition/procpartition.cpp
@@ -50,7 +50,7 @@ false /* follow symlinks */)) { return ""; } - return content; + return std::string{content.c_str()}; } Partition parsePartition(const std::string& s) {
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp index 4fa941e..fc8d58b 100644 --- a/cmds/lshal/test.cpp +++ b/cmds/lshal/test.cpp
@@ -44,6 +44,13 @@ using ::android::hardware::hidl_handle; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; +using android::vintf::Arch; +using android::vintf::CompatibilityMatrix; +using android::vintf::gCompatibilityMatrixConverter; +using android::vintf::gHalManifestConverter; +using android::vintf::HalManifest; +using android::vintf::Transport; +using android::vintf::VintfObject; using InstanceDebugInfo = IServiceManager::InstanceDebugInfo; @@ -184,7 +191,7 @@ // expose protected fields and methods for ListCommand class MockListCommand : public ListCommand { public: - MockListCommand(Lshal* lshal) : ListCommand(*lshal) {} + explicit MockListCommand(Lshal* lshal) : ListCommand(*lshal) {} Status parseArgs(const Arg& arg) { return ListCommand::parseArgs(arg); } Status main(const Arg& arg) { return ListCommand::main(arg); } @@ -207,6 +214,11 @@ MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*)); MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t)); MOCK_METHOD1(getPartition, Partition(pid_t)); + + MOCK_CONST_METHOD0(getDeviceManifest, std::shared_ptr<const vintf::HalManifest>()); + MOCK_CONST_METHOD0(getDeviceMatrix, std::shared_ptr<const vintf::CompatibilityMatrix>()); + MOCK_CONST_METHOD0(getFrameworkManifest, std::shared_ptr<const vintf::HalManifest>()); + MOCK_CONST_METHOD0(getFrameworkMatrix, std::shared_ptr<const vintf::CompatibilityMatrix>()); }; class ListParseArgsTest : public ::testing::Test { @@ -214,12 +226,13 @@ void SetUp() override { mockLshal = std::make_unique<NiceMock<MockLshal>>(); mockList = std::make_unique<MockListCommand>(mockLshal.get()); + ON_CALL(*mockLshal, err()).WillByDefault(Return(NullableOStream<std::ostream>(err))); // ListCommand::parseArgs should parse arguments from the second element optind = 1; } std::unique_ptr<MockLshal> mockLshal; std::unique_ptr<MockListCommand> mockList; - std::stringstream output; + std::stringstream err; }; TEST_F(ListParseArgsTest, Args) { @@ -229,6 +242,7 @@ TableColumnType::SERVER_ADDR, TableColumnType::CLIENT_PIDS}), table.getSelectedColumns()); }); + EXPECT_EQ("", err.str()); } TEST_F(ListParseArgsTest, Cmds) { @@ -243,12 +257,12 @@ EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::CLIENT_CMDS)) << "should print client cmds with -m"; }); + EXPECT_EQ("", err.str()); } TEST_F(ListParseArgsTest, DebugAndNeat) { - ON_CALL(*mockLshal, err()).WillByDefault(Return(NullableOStream<std::ostream>(output))); EXPECT_NE(0u, mockList->parseArgs(createArg({"lshal", "--neat", "-d"}))); - EXPECT_THAT(output.str(), StrNe("")); + EXPECT_THAT(err.str(), HasSubstr("--neat should not be used with --debug.")); } /// Fetch Test @@ -294,7 +308,7 @@ // Fake service returned by mocked IServiceManager::get. class TestService : public IBase { public: - TestService(pid_t id) : mId(id) {} + explicit TestService(pid_t id) : mId(id) {} hardware::Return<void> getDebugInfo(getDebugInfo_cb cb) override { cb({ mId /* pid */, getPtr(mId), DebugInfo::Architecture::IS_64BIT }); return hardware::Void(); @@ -313,7 +327,7 @@ class ListTest : public ::testing::Test { public: - void SetUp() override { + virtual void SetUp() override { initMockServiceManager(); lshal = std::make_unique<Lshal>(out, err, serviceManager, passthruManager); initMockList(); @@ -335,6 +349,15 @@ }); })); ON_CALL(*mockList, getPartition(_)).WillByDefault(Return(Partition::VENDOR)); + + ON_CALL(*mockList, getDeviceManifest()) + .WillByDefault(Return(std::make_shared<HalManifest>())); + ON_CALL(*mockList, getDeviceMatrix()) + .WillByDefault(Return(std::make_shared<CompatibilityMatrix>())); + ON_CALL(*mockList, getFrameworkManifest()) + .WillByDefault(Return(std::make_shared<HalManifest>())); + ON_CALL(*mockList, getFrameworkMatrix()) + .WillByDefault(Return(std::make_shared<CompatibilityMatrix>())); } void initMockServiceManager() { @@ -388,92 +411,76 @@ } TEST_F(ListTest, Fetch) { - EXPECT_EQ(0u, mockList->fetch()); - std::array<std::string, 6> transports{{"hwbinder", "hwbinder", "passthrough", - "passthrough", "passthrough", "passthrough"}}; - std::array<Architecture, 6> archs{{ARCH64, ARCH64, ARCH32, ARCH32, ARCH32, ARCH32}}; - int id = 1; + optind = 1; // mimic Lshal::parseArg() + ASSERT_EQ(0u, mockList->parseArgs(createArg({"lshal"}))); + ASSERT_EQ(0u, mockList->fetch()); + vintf::TransportArch hwbinder{Transport::HWBINDER, Arch::ARCH_64}; + vintf::TransportArch passthrough{Transport::PASSTHROUGH, Arch::ARCH_32}; + std::array<vintf::TransportArch, 6> transportArchs{{hwbinder, hwbinder, passthrough, + passthrough, passthrough, passthrough}}; + int i = 0; mockList->forEachTable([&](const Table& table) { - ASSERT_EQ(2u, table.size()); for (const auto& entry : table) { - const auto& transport = transports[id - 1]; + if (i >= transportArchs.size()) { + break; + } + + int id = i + 1; + auto transport = transportArchs.at(i).transport; TableEntry expected{ .interfaceName = getFqInstanceName(id), .transport = transport, - .serverPid = transport == "hwbinder" ? id : NO_PID, - .threadUsage = transport == "hwbinder" ? getPidInfoFromId(id).threadUsage : 0, - .threadCount = transport == "hwbinder" ? getPidInfoFromId(id).threadCount : 0, + .serverPid = transport == Transport::HWBINDER ? id : NO_PID, + .threadUsage = + transport == Transport::HWBINDER ? getPidInfoFromId(id).threadUsage : 0, + .threadCount = + transport == Transport::HWBINDER ? getPidInfoFromId(id).threadCount : 0, .serverCmdline = {}, - .serverObjectAddress = transport == "hwbinder" ? getPtr(id) : NO_PTR, + .serverObjectAddress = transport == Transport::HWBINDER ? getPtr(id) : NO_PTR, .clientPids = getClients(id), .clientCmdlines = {}, - .arch = archs[id - 1], + .arch = transportArchs.at(i).arch, }; EXPECT_EQ(expected, entry) << expected.to_string() << " vs. " << entry.to_string(); - ++id; + ++i; } }); + EXPECT_EQ(transportArchs.size(), i) << "Not all entries are tested."; + } TEST_F(ListTest, DumpVintf) { - const std::string expected = - "<!-- \n" - " This is a skeleton device manifest. Notes: \n" + ListCommand::INIT_VINTF_NOTES + - "-->\n" - "<manifest version=\"1.0\" type=\"device\">\n" - " <hal format=\"hidl\">\n" - " <name>a.h.foo1</name>\n" - " <transport>hwbinder</transport>\n" - " <version>1.0</version>\n" - " <interface>\n" - " <name>IFoo</name>\n" - " <instance>1</instance>\n" - " </interface>\n" - " </hal>\n" - " <hal format=\"hidl\">\n" - " <name>a.h.foo2</name>\n" - " <transport>hwbinder</transport>\n" - " <version>2.0</version>\n" - " <interface>\n" - " <name>IFoo</name>\n" - " <instance>2</instance>\n" - " </interface>\n" - " </hal>\n" - " <hal format=\"hidl\">\n" - " <name>a.h.foo3</name>\n" - " <transport arch=\"32\">passthrough</transport>\n" - " <version>3.0</version>\n" - " <interface>\n" - " <name>IFoo</name>\n" - " <instance>3</instance>\n" - " </interface>\n" - " </hal>\n" - " <hal format=\"hidl\">\n" - " <name>a.h.foo4</name>\n" - " <transport arch=\"32\">passthrough</transport>\n" - " <version>4.0</version>\n" - " <interface>\n" - " <name>IFoo</name>\n" - " <instance>4</instance>\n" - " </interface>\n" - " </hal>\n" - " <hal format=\"hidl\">\n" - " <name>a.h.foo5</name>\n" - " <transport arch=\"32\">passthrough</transport>\n" - " <version>5.0</version>\n" - " </hal>\n" - " <hal format=\"hidl\">\n" - " <name>a.h.foo6</name>\n" - " <transport arch=\"32\">passthrough</transport>\n" - " <version>6.0</version>\n" - " </hal>\n" - "</manifest>\n"; + const std::string expected = "<manifest version=\"1.0\" type=\"device\">\n" + " <hal format=\"hidl\">\n" + " <name>a.h.foo1</name>\n" + " <transport>hwbinder</transport>\n" + " <fqname>@1.0::IFoo/1</fqname>\n" + " </hal>\n" + " <hal format=\"hidl\">\n" + " <name>a.h.foo2</name>\n" + " <transport>hwbinder</transport>\n" + " <fqname>@2.0::IFoo/2</fqname>\n" + " </hal>\n" + " <hal format=\"hidl\">\n" + " <name>a.h.foo3</name>\n" + " <transport arch=\"32\">passthrough</transport>\n" + " <fqname>@3.0::IFoo/3</fqname>\n" + " </hal>\n" + " <hal format=\"hidl\">\n" + " <name>a.h.foo4</name>\n" + " <transport arch=\"32\">passthrough</transport>\n" + " <fqname>@4.0::IFoo/4</fqname>\n" + " </hal>\n" + "</manifest>"; optind = 1; // mimic Lshal::parseArg() EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--init-vintf"}))); - EXPECT_EQ(expected, out.str()); + auto output = out.str(); + EXPECT_THAT(output, HasSubstr(expected)); + EXPECT_THAT(output, HasSubstr("a.h.foo5@5.0::IFoo/5")); + EXPECT_THAT(output, HasSubstr("a.h.foo6@6.0::IFoo/6")); EXPECT_EQ("", err.str()); vintf::HalManifest m; @@ -487,18 +494,18 @@ const std::string expected = "[fake description 0]\n" "R Interface Thread Use Server Clients\n" - " a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n" + "N a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n" "Y a.h.foo2@2.0::IFoo/2 12/22 2 3 5\n" "\n" "[fake description 1]\n" "R Interface Thread Use Server Clients\n" - " a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n" - " a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n" + "? a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n" + "? a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n" "\n" "[fake description 2]\n" "R Interface Thread Use Server Clients\n" - " a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n" - " a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n" + "? a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n" + "? a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n" "\n"; optind = 1; // mimic Lshal::parseArg() @@ -511,18 +518,18 @@ const std::string expected = "[fake description 0]\n" "Interface R Hash\n" - "a.h.foo1@1.0::IFoo/1 0000000000000000000000000000000000000000000000000000000000000000\n" + "a.h.foo1@1.0::IFoo/1 N 0000000000000000000000000000000000000000000000000000000000000000\n" "a.h.foo2@2.0::IFoo/2 Y 0202020202020202020202020202020202020202020202020202020202020202\n" "\n" "[fake description 1]\n" "Interface R Hash\n" - "a.h.foo3@3.0::IFoo/3 \n" - "a.h.foo4@4.0::IFoo/4 \n" + "a.h.foo3@3.0::IFoo/3 ? \n" + "a.h.foo4@4.0::IFoo/4 ? \n" "\n" "[fake description 2]\n" "Interface R Hash\n" - "a.h.foo5@5.0::IFoo/5 \n" - "a.h.foo6@6.0::IFoo/6 \n" + "a.h.foo5@5.0::IFoo/5 ? \n" + "a.h.foo6@6.0::IFoo/6 ? \n" "\n"; optind = 1; // mimic Lshal::parseArg() @@ -594,6 +601,225 @@ EXPECT_EQ("", err.str()); } +TEST_F(ListTest, DumpSingleHalType) { + const std::string expected = + "[fake description 0]\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 1 0000000000002711 2 4\n" + "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 2 0000000000002712 3 5\n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac", "--types=binderized"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, DumpReorderedHalTypes) { + const std::string expected = + "[fake description 0]\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n" + "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n" + "\n" + "[fake description 1]\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n" + "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n" + "\n" + "[fake description 2]\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 1 0000000000002711 2 4\n" + "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 2 0000000000002712 3 5\n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac", "--types=passthrough_clients", + "--types=passthrough_libs", "--types=binderized"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, DumpAbbreviatedHalTypes) { + const std::string expected = + "[fake description 0]\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n" + "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n" + "\n" + "[fake description 1]\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n" + "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,l"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, DumpEmptyAndDuplicateHalTypes) { + const std::string expected = + "[fake description 0]\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n" + "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n" + "\n" + "[fake description 1]\n" + "Interface Transport Arch Thread Use Server PTR Clients\n" + "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n" + "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,l,,,l,l,c,", + "--types=passthrough_libs,passthrough_clients"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, UnknownHalType) { + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,a"}))); + EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: a")); +} + +TEST_F(ListTest, Vintf) { + std::string deviceManifestXml = + "<manifest version=\"1.0\" type=\"device\">\n" + " <hal>\n" + " <name>a.h.foo1</name>\n" + " <transport>hwbinder</transport>\n" + " <fqname>@1.0::IFoo/1</fqname>\n" + " </hal>\n" + " <hal>\n" + " <name>a.h.foo3</name>\n" + " <transport arch=\"32+64\">passthrough</transport>\n" + " <fqname>@3.0::IFoo/3</fqname>\n" + " </hal>\n" + "</manifest>\n"; + std::string frameworkManifestXml = + "<manifest version=\"1.0\" type=\"framework\">\n" + " <hal>\n" + " <name>a.h.foo5</name>\n" + " <transport arch=\"32\">passthrough</transport>\n" + " <fqname>@5.0::IFoo/5</fqname>\n" + " </hal>\n" + "</manifest>\n"; + std::string deviceMatrixXml = + "<compatibility-matrix version=\"1.0\" type=\"device\">\n" + " <hal>\n" + " <name>a.h.foo5</name>\n" + " <version>5.0</version>\n" + " <interface>\n" + " <name>IFoo</name>\n" + " <instance>5</instance>\n" + " </interface>\n" + " </hal>\n" + "</compatibility-matrix>\n"; + std::string frameworkMatrixXml = + "<compatibility-matrix version=\"1.0\" type=\"framework\">\n" + " <hal>\n" + " <name>a.h.foo1</name>\n" + " <version>1.0</version>\n" + " <interface>\n" + " <name>IFoo</name>\n" + " <instance>1</instance>\n" + " </interface>\n" + " </hal>\n" + " <hal>\n" + " <name>a.h.foo3</name>\n" + " <version>3.0</version>\n" + " <interface>\n" + " <name>IFoo</name>\n" + " <instance>3</instance>\n" + " </interface>\n" + " </hal>\n" + "</compatibility-matrix>\n"; + + std::string expected = "DM,FC a.h.foo1@1.0::IFoo/1\n" + "X a.h.foo2@2.0::IFoo/2\n" + "DM,FC a.h.foo3@3.0::IFoo/3\n" + "X a.h.foo4@4.0::IFoo/4\n" + "DC,FM a.h.foo5@5.0::IFoo/5\n" + "X a.h.foo6@6.0::IFoo/6\n"; + + auto deviceManifest = std::make_shared<HalManifest>(); + auto frameworkManifest = std::make_shared<HalManifest>(); + auto deviceMatrix = std::make_shared<CompatibilityMatrix>(); + auto frameworkMatrix = std::make_shared<CompatibilityMatrix>(); + + ASSERT_TRUE(gHalManifestConverter(deviceManifest.get(), deviceManifestXml)); + ASSERT_TRUE(gHalManifestConverter(frameworkManifest.get(), frameworkManifestXml)); + ASSERT_TRUE(gCompatibilityMatrixConverter(deviceMatrix.get(), deviceMatrixXml)); + ASSERT_TRUE(gCompatibilityMatrixConverter(frameworkMatrix.get(), frameworkMatrixXml)); + + ON_CALL(*mockList, getDeviceManifest()).WillByDefault(Return(deviceManifest)); + ON_CALL(*mockList, getDeviceMatrix()).WillByDefault(Return(deviceMatrix)); + ON_CALL(*mockList, getFrameworkManifest()).WillByDefault(Return(frameworkManifest)); + ON_CALL(*mockList, getFrameworkMatrix()).WillByDefault(Return(frameworkMatrix)); + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-Vi", "--neat"}))); + EXPECT_THAT(out.str(), HasSubstr(expected)); + EXPECT_EQ("", err.str()); +} + +class ListVintfTest : public ListTest { +public: + virtual void SetUp() override { + ListTest::SetUp(); + const std::string mockManifestXml = + "<manifest version=\"1.0\" type=\"device\">\n" + " <hal format=\"hidl\">\n" + " <name>a.h.foo1</name>\n" + " <transport>hwbinder</transport>\n" + " <fqname>@1.0::IFoo/1</fqname>\n" + " </hal>\n" + " <hal format=\"hidl\">\n" + " <name>a.h.bar1</name>\n" + " <transport>hwbinder</transport>\n" + " <fqname>@1.0::IBar/1</fqname>\n" + " </hal>\n" + " <hal format=\"hidl\">\n" + " <name>a.h.bar2</name>\n" + " <transport arch=\"32+64\">passthrough</transport>\n" + " <fqname>@2.0::IBar/2</fqname>\n" + " </hal>\n" + "</manifest>"; + auto manifest = std::make_shared<HalManifest>(); + EXPECT_TRUE(gHalManifestConverter(manifest.get(), mockManifestXml)); + EXPECT_CALL(*mockList, getDeviceManifest()) + .Times(AnyNumber()) + .WillRepeatedly(Return(manifest)); + } +}; + +TEST_F(ListVintfTest, ManifestHals) { + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iStr", "--types=v", "--neat"}))); + EXPECT_THAT(out.str(), HasSubstr("a.h.bar1@1.0::IBar/1 declared hwbinder ?")); + EXPECT_THAT(out.str(), HasSubstr("a.h.bar2@2.0::IBar/2 declared passthrough 32+64")); + EXPECT_THAT(out.str(), HasSubstr("a.h.foo1@1.0::IFoo/1 declared hwbinder ?")); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListVintfTest, Lazy) { + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iStr", "--types=z", "--neat"}))); + EXPECT_THAT(out.str(), HasSubstr("a.h.bar1@1.0::IBar/1 declared hwbinder ?")); + EXPECT_THAT(out.str(), HasSubstr("a.h.bar2@2.0::IBar/2 declared passthrough 32+64")); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListVintfTest, NoLazy) { + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iStr", "--types=b,c,l", "--neat"}))); + EXPECT_THAT(out.str(), Not(HasSubstr("IBar"))); + EXPECT_EQ("", err.str()); +} + class HelpTest : public ::testing::Test { public: void SetUp() override {
diff --git a/cmds/lshal/utils.h b/cmds/lshal/utils.h index c09e8b1..240155e 100644 --- a/cmds/lshal/utils.h +++ b/cmds/lshal/utils.h
@@ -46,6 +46,8 @@ TRANSACTION_ERROR = 1 << 8, // No transaction error, but return value is unexpected. BAD_IMPL = 1 << 9, + // Cannot fetch VINTF data. + VINTF_ERROR = 1 << 10, }; using Status = unsigned int;
diff --git a/cmds/rawbu/Android.bp b/cmds/rawbu/Android.bp new file mode 100644 index 0000000..363ffc1 --- /dev/null +++ b/cmds/rawbu/Android.bp
@@ -0,0 +1,14 @@ +// Copyright 2009 The Android Open Source Project + +cc_binary { + name: "rawbu", + + srcs: ["backup.cpp"], + + cflags: [ + "-Wall", + "-Werror", + ], + + shared_libs: ["libcutils"], +}
diff --git a/cmds/rawbu/Android.mk b/cmds/rawbu/Android.mk deleted file mode 100644 index 9322151..0000000 --- a/cmds/rawbu/Android.mk +++ /dev/null
@@ -1,17 +0,0 @@ -# Copyright 2009 The Android Open Source Project - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= backup.cpp - -LOCAL_CFLAGS := -Wall -Werror - -LOCAL_SHARED_LIBRARIES := libcutils libc - -LOCAL_MODULE:= rawbu - -LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) -LOCAL_MODULE_TAGS := debug - -include $(BUILD_EXECUTABLE)
diff --git a/cmds/rawbu/backup.cpp b/cmds/rawbu/backup.cpp index 0072281..8b20e3e 100644 --- a/cmds/rawbu/backup.cpp +++ b/cmds/rawbu/backup.cpp
@@ -38,7 +38,7 @@ static struct stat statBuffer; static char copyBuffer[8192]; -static char *backupFilePath = NULL; +static char *backupFilePath = nullptr; static uint32_t inputFileVersion; @@ -58,7 +58,7 @@ { "/data/system/batterystats.bin", SPECIAL_NO_TOUCH }, { "/data/system/location", SPECIAL_NO_TOUCH }, { "/data/dalvik-cache", SPECIAL_NO_BACKUP }, - { NULL, 0 }, + { nullptr, 0 }, }; /* This is just copied from the shell's built-in wipe command. */ @@ -71,7 +71,7 @@ dir = opendir(path); - if (dir == NULL) { + if (dir == nullptr) { fprintf (stderr, "Error opendir'ing %s: %s\n", path, strerror(errno)); return 0; @@ -87,7 +87,7 @@ for (;;) { de = readdir(dir); - if (de == NULL) { + if (de == nullptr) { break; } @@ -115,7 +115,7 @@ } } - if (!noBackup && SKIP_PATHS[i].path != NULL) { + if (!noBackup && SKIP_PATHS[i].path != nullptr) { // This is a SPECIAL_NO_TOUCH directory. continue; } @@ -203,7 +203,7 @@ int amt = size > (off_t)sizeof(copyBuffer) ? sizeof(copyBuffer) : (int)size; int readLen = fread(copyBuffer, 1, amt, src); if (readLen <= 0) { - if (srcName != NULL) { + if (srcName != nullptr) { fprintf(stderr, "unable to read source (%d of %ld bytes) file '%s': %s\n", amt, origSize, srcName, errno != 0 ? strerror(errno) : "unexpected EOF"); } else { @@ -214,7 +214,7 @@ } int writeLen = fwrite(copyBuffer, 1, readLen, dest); if (writeLen != readLen) { - if (destName != NULL) { + if (destName != nullptr) { fprintf(stderr, "unable to write file (%d of %d bytes) '%s': '%s'\n", writeLen, readLen, destName, strerror(errno)); } else { @@ -256,14 +256,14 @@ { DIR *dir; struct dirent *de; - char* fullPath = NULL; + char* fullPath = nullptr; int srcLen = strlen(srcPath); int result = 1; int i; dir = opendir(srcPath); - if (dir == NULL) { + if (dir == nullptr) { fprintf (stderr, "error opendir'ing '%s': %s\n", srcPath, strerror(errno)); return 0; @@ -272,7 +272,7 @@ for (;;) { de = readdir(dir); - if (de == NULL) { + if (de == nullptr) { break; } @@ -283,7 +283,7 @@ continue; } - if (fullPath != NULL) { + if (fullPath != nullptr) { free(fullPath); } fullPath = (char*)malloc(srcLen + strlen(de->d_name) + 2); @@ -298,7 +298,7 @@ break; } } - if (SKIP_PATHS[i].path != NULL) { + if (SKIP_PATHS[i].path != nullptr) { continue; } } @@ -343,14 +343,14 @@ } FILE* src = fopen(fullPath, "r"); - if (src == NULL) { + if (src == nullptr) { fprintf(stderr, "unable to open source file '%s': %s\n", fullPath, strerror(errno)); result = 0; goto done; } - int copyres = copy_file(fh, src, size, NULL, fullPath); + int copyres = copy_file(fh, src, size, nullptr, fullPath); fclose(src); if (!copyres) { result = 0; @@ -360,7 +360,7 @@ } done: - if (fullPath != NULL) { + if (fullPath != nullptr) { free(fullPath); } @@ -374,7 +374,7 @@ int res = -1; FILE* fh = fopen(destPath, "w"); - if (fh == NULL) { + if (fh == nullptr) { fprintf(stderr, "unable to open destination '%s': %s\n", destPath, strerror(errno)); return -1; @@ -504,7 +504,7 @@ int res = -1; FILE* fh = fopen(srcPath, "r"); - if (fh == NULL) { + if (fh == nullptr) { fprintf(stderr, "Unable to open source '%s': %s\n", srcPath, strerror(errno)); return -1; @@ -534,7 +534,7 @@ while (1) { int type; - char* path = NULL; + char* path = nullptr; if (read_header(fh, &type, &path, &statBuffer) == 0) { goto done; } @@ -570,14 +570,14 @@ printf("Restoring file %s...\n", path); FILE* dest = fopen(path, "w"); - if (dest == NULL) { + if (dest == nullptr) { fprintf(stderr, "unable to open destination file '%s': %s\n", path, strerror(errno)); free(path); goto done; } - int copyres = copy_file(dest, fh, size, path, NULL); + int copyres = copy_file(dest, fh, size, path, nullptr); fclose(dest); if (!copyres) { free(path);
diff --git a/cmds/rss_hwm_reset/Android.bp b/cmds/rss_hwm_reset/Android.bp new file mode 100644 index 0000000..15f10ef --- /dev/null +++ b/cmds/rss_hwm_reset/Android.bp
@@ -0,0 +1,28 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://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_binary { + name: "rss_hwm_reset", + + srcs: [ + "rss_hwm_reset.cc", + ], + + shared_libs: [ + "libbase", + "liblog", + ], + + init_rc: ["rss_hwm_reset.rc"], +}
diff --git a/cmds/rss_hwm_reset/rss_hwm_reset.cc b/cmds/rss_hwm_reset/rss_hwm_reset.cc new file mode 100644 index 0000000..1626e7e --- /dev/null +++ b/cmds/rss_hwm_reset/rss_hwm_reset.cc
@@ -0,0 +1,72 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://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. + */ + + /* + * rss_hwm_reset clears the RSS high-water mark counters for all currently + * running processes. It writes "5" to /proc/PID/clear_refs for every PID. + * + * It runs in its own process becuase dac_override capability is required + * in order to write to other processes' clear_refs. + * + * It is invoked from a system service by flipping sys.rss_hwm_reset.on + * property to "1". + */ + +#define LOG_TAG "rss_hwm_reset" + +#include <dirent.h> + +#include <string> + +#include <android-base/file.h> +#include <android-base/stringprintf.h> +#include <log/log.h> + +namespace { +// Resets RSS HWM counter for the selected process by writing 5 to +// /proc/PID/clear_refs. +void reset_rss_hwm(const char* pid) { + std::string clear_refs_path = + ::android::base::StringPrintf("/proc/%s/clear_refs", pid); + ::android::base::WriteStringToFile("5", clear_refs_path); +} +} + +// Clears RSS HWM counters for all currently running processes. +int main(int /* argc */, char** /* argv[] */) { + DIR* dirp = opendir("/proc"); + if (dirp == nullptr) { + ALOGE("unable to read /proc"); + return 1; + } + struct dirent* entry; + while ((entry = readdir(dirp)) != nullptr) { + // Skip entries that are not directories. + if (entry->d_type != DT_DIR) continue; + // Skip entries that do not contain only numbers. + const char* pid = entry->d_name; + while (*pid) { + if (*pid < '0' || *pid > '9') break; + pid++; + } + if (*pid != 0) continue; + + pid = entry->d_name; + reset_rss_hwm(pid); + } + closedir(dirp); + return 0; +}
diff --git a/cmds/rss_hwm_reset/rss_hwm_reset.rc b/cmds/rss_hwm_reset/rss_hwm_reset.rc new file mode 100644 index 0000000..fbbc820 --- /dev/null +++ b/cmds/rss_hwm_reset/rss_hwm_reset.rc
@@ -0,0 +1,26 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://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. + +service rss_hwm_reset /system/bin/rss_hwm_reset + class late_start + disabled + oneshot + user nobody + group nobody readproc + writepid /dev/cpuset/system-background/tasks + capabilities DAC_OVERRIDE + +on property:sys.rss_hwm_reset.on=1 + start rss_hwm_reset + setprop sys.rss_hwm_reset.on 0
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp index bc11256..d5dc6b7 100644 --- a/cmds/service/service.cpp +++ b/cmds/service/service.cpp
@@ -30,7 +30,7 @@ void writeString16(Parcel& parcel, const char* string) { - if (string != NULL) + if (string != nullptr) { parcel.writeString16(String16(string)); } @@ -43,7 +43,7 @@ // get the name of the generic interface we hold a reference to static String16 get_interface_name(sp<IBinder> service) { - if (service != NULL) { + if (service != nullptr) { Parcel data, reply; status_t err = service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply); if (err == NO_ERROR) { @@ -93,7 +93,7 @@ #endif sp<IServiceManager> sm = defaultServiceManager(); fflush(stdout); - if (sm == NULL) { + if (sm == nullptr) { aerr << "service: Unable to get default service manager!" << endl; return 20; } @@ -106,7 +106,7 @@ if (optind < argc) { sp<IBinder> service = sm->checkService(String16(argv[optind])); aout << "Service " << argv[optind] << - (service == NULL ? ": not found" : ": found") << endl; + (service == nullptr ? ": not found" : ": found") << endl; } else { aerr << "service: No service specified for check" << endl; wantsUsage = true; @@ -131,7 +131,7 @@ sp<IBinder> service = sm->checkService(String16(argv[optind++])); String16 ifName = get_interface_name(service); int32_t code = atoi(argv[optind++]); - if (service != NULL && ifName.size() > 0) { + if (service != nullptr && ifName.size() > 0) { Parcel data, reply; // the interface name is first @@ -186,28 +186,28 @@ data.writeDouble(atof(argv[optind++])); } else if (strcmp(argv[optind], "null") == 0) { optind++; - data.writeStrongBinder(NULL); + data.writeStrongBinder(nullptr); } else if (strcmp(argv[optind], "intent") == 0) { - char* action = NULL; - char* dataArg = NULL; - char* type = NULL; + char* action = nullptr; + char* dataArg = nullptr; + char* type = nullptr; int launchFlags = 0; - char* component = NULL; + char* component = nullptr; int categoryCount = 0; char* categories[16]; - char* context1 = NULL; + char* context1 = nullptr; optind++; while (optind < argc) { char* key = strtok_r(argv[optind], "=", &context1); - char* value = strtok_r(NULL, "=", &context1); + char* value = strtok_r(nullptr, "=", &context1); // we have reached the end of the XXX=XXX args. - if (key == NULL) break; + if (key == nullptr) break; if (strcmp(key, "action") == 0) { @@ -231,14 +231,13 @@ } else if (strcmp(key, "categories") == 0) { - char* context2 = NULL; - int categoryCount = 0; + char* context2 = nullptr; categories[categoryCount] = strtok_r(value, ",", &context2); - while (categories[categoryCount] != NULL) + while (categories[categoryCount] != nullptr) { categoryCount++; - categories[categoryCount] = strtok_r(NULL, ",", &context2); + categories[categoryCount] = strtok_r(nullptr, ",", &context2); } }
diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c index fade8cf..cf3b172 100644 --- a/cmds/servicemanager/binder.c +++ b/cmds/servicemanager/binder.c
@@ -146,7 +146,19 @@ int binder_become_context_manager(struct binder_state *bs) { - return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0); + struct flat_binder_object obj; + memset(&obj, 0, sizeof(obj)); + obj.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX; + + int result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR_EXT, &obj); + + // fallback to original method + if (result != 0) { + android_errorWriteLog(0x534e4554, "121035042"); + + result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0); + } + return result; } int binder_write(struct binder_state *bs, void *data, size_t len) @@ -240,13 +252,28 @@ #endif ptr += sizeof(struct binder_ptr_cookie); break; + case BR_TRANSACTION_SEC_CTX: case BR_TRANSACTION: { - struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr; - if ((end - ptr) < sizeof(*txn)) { - ALOGE("parse: txn too small!\n"); - return -1; + struct binder_transaction_data_secctx txn; + if (cmd == BR_TRANSACTION_SEC_CTX) { + if ((end - ptr) < sizeof(struct binder_transaction_data_secctx)) { + ALOGE("parse: txn too small (binder_transaction_data_secctx)!\n"); + return -1; + } + memcpy(&txn, (void*) ptr, sizeof(struct binder_transaction_data_secctx)); + ptr += sizeof(struct binder_transaction_data_secctx); + } else /* BR_TRANSACTION */ { + if ((end - ptr) < sizeof(struct binder_transaction_data)) { + ALOGE("parse: txn too small (binder_transaction_data)!\n"); + return -1; + } + memcpy(&txn.transaction_data, (void*) ptr, sizeof(struct binder_transaction_data)); + ptr += sizeof(struct binder_transaction_data); + + txn.secctx = 0; } - binder_dump_txn(txn); + + binder_dump_txn(&txn.transaction_data); if (func) { unsigned rdata[256/4]; struct binder_io msg; @@ -254,15 +281,14 @@ int res; bio_init(&reply, rdata, sizeof(rdata), 4); - bio_init_from_txn(&msg, txn); - res = func(bs, txn, &msg, &reply); - if (txn->flags & TF_ONE_WAY) { - binder_free_buffer(bs, txn->data.ptr.buffer); + bio_init_from_txn(&msg, &txn.transaction_data); + res = func(bs, &txn, &msg, &reply); + if (txn.transaction_data.flags & TF_ONE_WAY) { + binder_free_buffer(bs, txn.transaction_data.data.ptr.buffer); } else { - binder_send_reply(bs, &reply, txn->data.ptr.buffer, res); + binder_send_reply(bs, &reply, txn.transaction_data.data.ptr.buffer, res); } } - ptr += sizeof(*txn); break; } case BR_REPLY: {
diff --git a/cmds/servicemanager/binder.h b/cmds/servicemanager/binder.h index c95b33f..a9ccc74 100644 --- a/cmds/servicemanager/binder.h +++ b/cmds/servicemanager/binder.h
@@ -4,8 +4,8 @@ #ifndef _BINDER_H_ #define _BINDER_H_ -#include <sys/ioctl.h> #include <linux/android/binder.h> +#include <sys/ioctl.h> struct binder_state; @@ -42,7 +42,7 @@ }; typedef int (*binder_handler)(struct binder_state *bs, - struct binder_transaction_data *txn, + struct binder_transaction_data_secctx *txn, struct binder_io *msg, struct binder_io *reply);
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index 6b340a8..ec3fac5 100644 --- a/cmds/servicemanager/service_manager.c +++ b/cmds/servicemanager/service_manager.c
@@ -61,14 +61,14 @@ static char *service_manager_context; static struct selabel_handle* sehandle; -static bool check_mac_perms(pid_t spid, uid_t uid, const char *tctx, const char *perm, const char *name) +static bool check_mac_perms(pid_t spid, const char* sid, uid_t uid, const char *tctx, const char *perm, const char *name) { - char *sctx = NULL; + char *lookup_sid = NULL; const char *class = "service_manager"; bool allowed; struct audit_data ad; - if (getpidcon(spid, &sctx) < 0) { + if (sid == NULL && getpidcon(spid, &lookup_sid) < 0) { ALOGE("SELinux: getpidcon(pid=%d) failed to retrieve pid context.\n", spid); return false; } @@ -77,19 +77,23 @@ ad.uid = uid; ad.name = name; - int result = selinux_check_access(sctx, tctx, class, perm, (void *) &ad); + if (sid == NULL) { + android_errorWriteLog(0x534e4554, "121035042"); + } + + int result = selinux_check_access(sid ? sid : lookup_sid, tctx, class, perm, (void *) &ad); allowed = (result == 0); - freecon(sctx); + freecon(lookup_sid); return allowed; } -static bool check_mac_perms_from_getcon(pid_t spid, uid_t uid, const char *perm) +static bool check_mac_perms_from_getcon(pid_t spid, const char* sid, uid_t uid, const char *perm) { - return check_mac_perms(spid, uid, service_manager_context, perm, NULL); + return check_mac_perms(spid, sid, uid, service_manager_context, perm, NULL); } -static bool check_mac_perms_from_lookup(pid_t spid, uid_t uid, const char *perm, const char *name) +static bool check_mac_perms_from_lookup(pid_t spid, const char* sid, uid_t uid, const char *perm, const char *name) { bool allowed; char *tctx = NULL; @@ -104,12 +108,12 @@ return false; } - allowed = check_mac_perms(spid, uid, tctx, perm, name); + allowed = check_mac_perms(spid, sid, uid, tctx, perm, name); freecon(tctx); return allowed; } -static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid) +static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid, const char* sid, uid_t uid) { const char *perm = "add"; @@ -117,19 +121,19 @@ return 0; /* Don't allow apps to register services */ } - return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0; + return check_mac_perms_from_lookup(spid, sid, uid, perm, str8(name, name_len)) ? 1 : 0; } -static int svc_can_list(pid_t spid, uid_t uid) +static int svc_can_list(pid_t spid, const char* sid, uid_t uid) { const char *perm = "list"; - return check_mac_perms_from_getcon(spid, uid, perm) ? 1 : 0; + return check_mac_perms_from_getcon(spid, sid, uid, perm) ? 1 : 0; } -static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid) +static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, const char* sid, uid_t uid) { const char *perm = "find"; - return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0; + return check_mac_perms_from_lookup(spid, sid, uid, perm, str8(name, name_len)) ? 1 : 0; } struct svcinfo @@ -175,7 +179,7 @@ }; -uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid) +uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid, const char* sid) { struct svcinfo *si = find_svc(s, len); @@ -192,7 +196,7 @@ } } - if (!svc_can_find(s, len, spid, uid)) { + if (!svc_can_find(s, len, spid, sid, uid)) { return 0; } @@ -200,7 +204,7 @@ } int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle, - uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid) { + uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid, const char* sid) { struct svcinfo *si; //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle, @@ -209,7 +213,7 @@ if (!handle || (len == 0) || (len > 127)) return -1; - if (!svc_can_register(s, len, spid, uid)) { + if (!svc_can_register(s, len, spid, sid, uid)) { ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n", str8(s, len), handle, uid); return -1; @@ -248,7 +252,7 @@ } int svcmgr_handler(struct binder_state *bs, - struct binder_transaction_data *txn, + struct binder_transaction_data_secctx *txn_secctx, struct binder_io *msg, struct binder_io *reply) { @@ -260,6 +264,8 @@ int allow_isolated; uint32_t dumpsys_priority; + struct binder_transaction_data *txn = &txn_secctx->transaction_data; + //ALOGI("target=%p code=%d pid=%d uid=%d\n", // (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid); @@ -274,6 +280,7 @@ // Note that we ignore the strict_policy and don't propagate it // further (since we do no outbound RPCs anyway). strict_policy = bio_get_uint32(msg); + bio_get_uint32(msg); // Ignore worksource header. s = bio_get_string16(msg, &len); if (s == NULL) { return -1; @@ -304,7 +311,8 @@ if (s == NULL) { return -1; } - handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid); + handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid, + (const char*) txn_secctx->secctx); if (!handle) break; bio_put_ref(reply, handle); @@ -319,7 +327,7 @@ allow_isolated = bio_get_uint32(msg) ? 1 : 0; dumpsys_priority = bio_get_uint32(msg); if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority, - txn->sender_pid)) + txn->sender_pid, (const char*) txn_secctx->secctx)) return -1; break; @@ -327,7 +335,7 @@ uint32_t n = bio_get_uint32(msg); uint32_t req_dumpsys_priority = bio_get_uint32(msg); - if (!svc_can_list(txn->sender_pid, txn->sender_euid)) { + if (!svc_can_list(txn->sender_pid, (const char*) txn_secctx->secctx, txn->sender_euid)) { ALOGE("list_service() uid=%d - PERMISSION DENIED\n", txn->sender_euid); return -1; @@ -403,7 +411,11 @@ cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); +#ifdef VENDORSERVICEMANAGER + cb.func_log = selinux_vendor_log_callback; +#else cb.func_log = selinux_log_callback; +#endif selinux_set_callback(SELINUX_CB_LOG, cb); #ifdef VENDORSERVICEMANAGER
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc index 4d93cb4..152ac28 100644 --- a/cmds/servicemanager/servicemanager.rc +++ b/cmds/servicemanager/servicemanager.rc
@@ -13,5 +13,6 @@ onrestart restart cameraserver onrestart restart keystore onrestart restart gatekeeperd + onrestart restart thermalservice writepid /dev/cpuset/system-background/tasks shutdown critical
diff --git a/cmds/surfacereplayer/OWNERS b/cmds/surfacereplayer/OWNERS new file mode 100644 index 0000000..cc4c842 --- /dev/null +++ b/cmds/surfacereplayer/OWNERS
@@ -0,0 +1,2 @@ +mathias@google.com +racarr@google.com
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto index 0bc08a9..c70bc3e 100644 --- a/cmds/surfacereplayer/proto/src/trace.proto +++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -30,14 +30,13 @@ message SurfaceChange { required int32 id = 1; - + reserved 7; oneof SurfaceChange { PositionChange position = 2; SizeChange size = 3; AlphaChange alpha = 4; LayerChange layer = 5; CropChange crop = 6; - FinalCropChange final_crop = 7; MatrixChange matrix = 8; OverrideScalingModeChange override_scaling_mode = 9; TransparentRegionHintChange transparent_region_hint = 10; @@ -46,6 +45,7 @@ OpaqueFlagChange opaque_flag = 13; SecureFlagChange secure_flag = 14; DeferredTransactionChange deferred_transaction = 15; + CornerRadiusChange corner_radius = 16; } } @@ -63,6 +63,10 @@ required float alpha = 1; } +message CornerRadiusChange { + required float corner_radius = 1; +} + message LayerChange { required uint32 layer = 1; } @@ -71,10 +75,6 @@ required Rectangle rectangle = 1; } -message FinalCropChange { - required Rectangle rectangle = 1; -} - message MatrixChange { required float dsdx = 1; required float dtdx = 2; @@ -165,7 +165,7 @@ message DisplayCreation { required int32 id = 1; required string name = 2; - required int32 type = 3; + optional uint64 display_id = 3; required bool is_secure = 4; }
diff --git a/cmds/surfacereplayer/replayer/Android.bp b/cmds/surfacereplayer/replayer/Android.bp index 5caceec..7632311 100644 --- a/cmds/surfacereplayer/replayer/Android.bp +++ b/cmds/surfacereplayer/replayer/Android.bp
@@ -1,6 +1,5 @@ cc_library_shared { name: "libsurfacereplayer", - clang: true, srcs: [ "BufferQueueScheduler.cpp", "Event.cpp", @@ -16,7 +15,6 @@ "-Wno-float-equal", "-Wno-sign-conversion", "-Wno-padded", - "-std=c++14", ], static_libs: [ "libtrace_proto", @@ -41,7 +39,6 @@ cc_binary { name: "surfacereplayer", - clang: true, srcs: [ "Main.cpp", ], @@ -61,6 +58,5 @@ "-Wno-float-conversion", "-Wno-disabled-macro-expansion", "-Wno-float-equal", - "-std=c++14", ], }
diff --git a/cmds/surfacereplayer/replayer/Main.cpp b/cmds/surfacereplayer/replayer/Main.cpp index 7090bdb..fbfcacf 100644 --- a/cmds/surfacereplayer/replayer/Main.cpp +++ b/cmds/surfacereplayer/replayer/Main.cpp
@@ -94,7 +94,7 @@ } char** input = argv + optind; - if (input[0] == NULL) { + if (input[0] == nullptr) { std::cerr << "No trace file provided...exiting" << std::endl; abort(); }
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index 4140f40..34886a9 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -286,10 +286,6 @@ std::thread(&Replayer::createSurfaceControl, this, increment.surface_creation(), event) .detach(); } break; - case increment.kSurfaceDeletion: { - std::thread(&Replayer::deleteSurfaceControl, this, increment.surface_deletion(), event) - .detach(); - } break; case increment.kBufferUpdate: { std::lock_guard<std::mutex> lock1(mLayerLock); std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock); @@ -385,12 +381,12 @@ case SurfaceChange::SurfaceChangeCase::kCrop: setCrop(transaction, change.id(), change.crop()); break; + case SurfaceChange::SurfaceChangeCase::kCornerRadius: + setCornerRadius(transaction, change.id(), change.corner_radius()); + break; case SurfaceChange::SurfaceChangeCase::kMatrix: setMatrix(transaction, change.id(), change.matrix()); break; - case SurfaceChange::SurfaceChangeCase::kFinalCrop: - setFinalCrop(transaction, change.id(), change.final_crop()); - break; case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode: setOverrideScalingMode(transaction, change.id(), change.override_scaling_mode()); @@ -489,17 +485,14 @@ Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(), cc.rectangle().bottom()); - t.setCrop(mLayers[id], r); + t.setCrop_legacy(mLayers[id], r); } -void Replayer::setFinalCrop(SurfaceComposerClient::Transaction& t, - layer_id id, const FinalCropChange& fcc) { - ALOGV("Layer %d: Setting Final Crop -- left=%d, top=%d, right=%d, bottom=%d", id, - fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(), - fcc.rectangle().bottom()); - Rect r = Rect(fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(), - fcc.rectangle().bottom()); - t.setFinalCrop(mLayers[id], r); +void Replayer::setCornerRadius(SurfaceComposerClient::Transaction& t, + layer_id id, const CornerRadiusChange& cc) { + ALOGV("Layer %d: Setting Corner Radius -- cornerRadius=%d", id, cc.corner_radius()); + + t.setCornerRadius(mLayers[id], cc.corner_radius()); } void Replayer::setMatrix(SurfaceComposerClient::Transaction& t, @@ -570,7 +563,7 @@ auto handle = mLayers[dtc.layer_id()]->getHandle(); - t.deferTransactionUntil(mLayers[id], handle, dtc.frame_number()); + t.deferTransactionUntil_legacy(mLayers[id], handle, dtc.frame_number()); } void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t, @@ -631,47 +624,10 @@ return NO_ERROR; } -status_t Replayer::deleteSurfaceControl( - const SurfaceDeletion& delete_, const std::shared_ptr<Event>& event) { - ALOGV("Deleting %d Surface Control", delete_.id()); - event->readyToExecute(); - - std::lock_guard<std::mutex> lock1(mPendingLayersLock); - - mLayersPendingRemoval.push_back(delete_.id()); - - const auto& iterator = mBufferQueueSchedulers.find(delete_.id()); - if (iterator != mBufferQueueSchedulers.end()) { - (*iterator).second->stopScheduling(); - } - - std::lock_guard<std::mutex> lock2(mLayerLock); - if (mLayers[delete_.id()] != nullptr) { - mComposerClient->destroySurface(mLayers[delete_.id()]->getHandle()); - } - - return NO_ERROR; -} - -void Replayer::doDeleteSurfaceControls() { - std::lock_guard<std::mutex> lock1(mPendingLayersLock); - std::lock_guard<std::mutex> lock2(mLayerLock); - if (!mLayersPendingRemoval.empty()) { - for (int id : mLayersPendingRemoval) { - mLayers.erase(id); - mColors.erase(id); - mBufferQueueSchedulers.erase(id); - } - mLayersPendingRemoval.clear(); - } -} - status_t Replayer::injectVSyncEvent( const VSyncEvent& vSyncEvent, const std::shared_ptr<Event>& event) { ALOGV("Injecting VSync Event"); - doDeleteSurfaceControls(); - event->readyToExecute(); SurfaceComposerClient::injectVSync(vSyncEvent.when());
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h index 295403e..ad807ee 100644 --- a/cmds/surfacereplayer/replayer/Replayer.h +++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -70,8 +70,6 @@ status_t doTransaction(const Transaction& transaction, const std::shared_ptr<Event>& event); status_t createSurfaceControl(const SurfaceCreation& create, const std::shared_ptr<Event>& event); - status_t deleteSurfaceControl(const SurfaceDeletion& delete_, - const std::shared_ptr<Event>& event); status_t injectVSyncEvent(const VSyncEvent& vsyncEvent, const std::shared_ptr<Event>& event); void createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event); void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event); @@ -92,8 +90,8 @@ layer_id id, const LayerChange& lc); void setCrop(SurfaceComposerClient::Transaction& t, layer_id id, const CropChange& cc); - void setFinalCrop(SurfaceComposerClient::Transaction& t, - layer_id id, const FinalCropChange& fcc); + void setCornerRadius(SurfaceComposerClient::Transaction& t, + layer_id id, const CornerRadiusChange& cc); void setMatrix(SurfaceComposerClient::Transaction& t, layer_id id, const MatrixChange& mc); void setOverrideScalingMode(SurfaceComposerClient::Transaction& t, @@ -120,7 +118,6 @@ void setDisplayProjection(SurfaceComposerClient::Transaction& t, display_id id, const ProjectionChange& pc); - void doDeleteSurfaceControls(); void waitUntilTimestamp(int64_t timestamp); void waitUntilDeferredTransactionLayerExists( const DeferredTransactionChange& dtc, std::unique_lock<std::mutex>& lock);
diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py index a892e46..d63d97f 100644 --- a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py +++ b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py
@@ -172,7 +172,7 @@ def display_create(increment): increment.display_creation.id = int(input("Enter id: ")) increment.display_creation.name = str(raw_input("Enter name: ")) - increment.display_creation.type = int(input("Enter type: ")) + increment.display_creation.display_id = int(input("Enter display ID: ")) increment.display_creation.is_secure = bool(input("Enter if secure: ")) def display_delete(increment):
diff --git a/data/etc/android.hardware.biometrics.face.xml b/data/etc/android.hardware.biometrics.face.xml new file mode 100644 index 0000000..7fa0bf9 --- /dev/null +++ b/data/etc/android.hardware.biometrics.face.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard set of features for a biometric face authentication sensor. --> +<permissions> + <feature name="android.hardware.biometrics.face" /> +</permissions>
diff --git a/data/etc/android.hardware.nfc.ese.xml b/data/etc/android.hardware.nfc.ese.xml new file mode 100644 index 0000000..6642bb2 --- /dev/null +++ b/data/etc/android.hardware.nfc.ese.xml
@@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- This feature indicates that the device supports eSE-based NFC card + emulation --> +<permissions> + <feature name="android.hardware.nfc.ese" /> +</permissions>
diff --git a/data/etc/android.hardware.nfc.uicc.xml b/data/etc/android.hardware.nfc.uicc.xml new file mode 100644 index 0000000..4f12de4 --- /dev/null +++ b/data/etc/android.hardware.nfc.uicc.xml
@@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- This feature indicates that the device supports uicc-based NFC card + emulation --> +<permissions> + <feature name="android.hardware.nfc.uicc" /> +</permissions>
diff --git a/data/etc/android.hardware.telephony.ims.xml b/data/etc/android.hardware.telephony.ims.xml new file mode 100644 index 0000000..eeb7b00 --- /dev/null +++ b/data/etc/android.hardware.telephony.ims.xml
@@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Feature for devices that support IMS via ImsService APIs. --> +<permissions> + <feature name="android.hardware.telephony.ims" /> +</permissions>
diff --git a/data/etc/android.hardware.usb.accessory.xml b/data/etc/android.hardware.usb.accessory.xml index 80a0904..29df966 100644 --- a/data/etc/android.hardware.usb.accessory.xml +++ b/data/etc/android.hardware.usb.accessory.xml
@@ -17,6 +17,4 @@ <!-- This is the standard feature indicating that the device supports USB accessories. --> <permissions> <feature name="android.hardware.usb.accessory" /> - <library name="com.android.future.usb.accessory" - file="/system/framework/com.android.future.usb.accessory.jar" /> </permissions>
diff --git a/data/etc/android.software.ipsec_tunnels.xml b/data/etc/android.software.ipsec_tunnels.xml new file mode 100644 index 0000000..f7ffc02 --- /dev/null +++ b/data/etc/android.software.ipsec_tunnels.xml
@@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- + This is the feature indicating that the device has support for multinetworking-capable IPsec + tunnels +--> + +<permissions> + <feature name="android.software.ipsec_tunnels" /> +</permissions>
diff --git a/data/etc/android.software.secure_lock_screen.xml b/data/etc/android.software.secure_lock_screen.xml new file mode 100644 index 0000000..3464487 --- /dev/null +++ b/data/etc/android.software.secure_lock_screen.xml
@@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<permissions> + <feature name="android.software.secure_lock_screen" /> +</permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml index 561f5ba..ad7791e 100644 --- a/data/etc/car_core_hardware.xml +++ b/data/etc/car_core_hardware.xml
@@ -36,18 +36,15 @@ <feature name="android.hardware.type.automotive" /> <!-- basic system services --> - <feature name="android.software.app_widgets" /> <feature name="android.software.connectionservice" /> <feature name="android.software.voice_recognizers" notLowRam="true" /> <feature name="android.software.backup" /> <feature name="android.software.home_screen" /> <feature name="android.software.print" /> - - <!-- Feature to specify if the device supports adding device admins. --> - <feature name="android.software.device_admin" /> - - <!-- Feature to specify if the device support managed users. --> - <feature name="android.software.managed_users" /> + <feature name="android.software.companion_device_setup" /> + <feature name="android.software.autofill" /> + <feature name="android.software.cant_save_state" /> + <feature name="android.software.secure_lock_screen" /> <!-- devices with GPS must include android.hardware.location.gps.xml --> <!-- devices with an autofocus camera and/or flash must include either
diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml index 8b5a461..915e579 100644 --- a/data/etc/go_handheld_core_hardware.xml +++ b/data/etc/go_handheld_core_hardware.xml
@@ -43,6 +43,7 @@ <feature name="android.software.companion_device_setup" /> <feature name="android.software.autofill" /> <feature name="android.software.cant_save_state" /> + <feature name="android.software.secure_lock_screen" /> <!-- Feature to specify if the device supports adding device admins. --> <feature name="android.software.device_admin" />
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml index 060a334..619d017 100644 --- a/data/etc/handheld_core_hardware.xml +++ b/data/etc/handheld_core_hardware.xml
@@ -51,6 +51,7 @@ <feature name="android.software.companion_device_setup" /> <feature name="android.software.autofill" /> <feature name="android.software.cant_save_state" /> + <feature name="android.software.secure_lock_screen" /> <!-- Feature to specify if the device supports adding device admins. --> <feature name="android.software.device_admin" />
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml index 6db2627..52524ca 100644 --- a/data/etc/tablet_core_hardware.xml +++ b/data/etc/tablet_core_hardware.xml
@@ -51,6 +51,7 @@ <feature name="android.software.companion_device_setup" /> <feature name="android.software.autofill" /> <feature name="android.software.cant_save_state" /> + <feature name="android.software.secure_lock_screen" /> <!-- Feature to specify if the device supports adding device admins. --> <feature name="android.software.device_admin" />
diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml index e2ab71a..0f364c1 100644 --- a/data/etc/wearable_core_hardware.xml +++ b/data/etc/wearable_core_hardware.xml
@@ -35,6 +35,7 @@ <!-- basic system services --> <feature name="android.software.home_screen" /> + <feature name="android.software.secure_lock_screen" /> <!-- input management and third-party input method editors --> <feature name="android.software.input_methods" />
diff --git a/headers/media_plugin/media/arcvideobridge/IArcVideoBridge.h b/headers/media_plugin/media/arcvideobridge/IArcVideoBridge.h deleted file mode 100644 index b32c92e..0000000 --- a/headers/media_plugin/media/arcvideobridge/IArcVideoBridge.h +++ /dev/null
@@ -1,46 +0,0 @@ -/* - * Copyright 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_IARC_VIDEO_BRIDGE_H -#define ANDROID_IARC_VIDEO_BRIDGE_H - -#include <arc/IArcBridgeService.h> -#include <binder/IInterface.h> -#include <utils/Errors.h> - -namespace android { - -class IArcVideoBridge : public IInterface { -public: - DECLARE_META_INTERFACE(ArcVideoBridge); - - // Returns MojoBootstrapResult for creating mojo ipc channel of - // VideoAcceleratorFactory. - virtual ::arc::MojoBootstrapResult bootstrapVideoAcceleratorFactory() = 0; - - // Get the version of the remote VideoHost on Chromium side. - virtual int32_t hostVersion() = 0; -}; - -class BnArcVideoBridge : public BnInterface<IArcVideoBridge> { -public: - virtual status_t onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); -}; - -}; // namespace android - -#endif // ANDROID_IARC_VIDEO_BRIDGE_H
diff --git a/headers/media_plugin/media/cas/CasAPI.h b/headers/media_plugin/media/cas/CasAPI.h index 4de314d..c87ee56 100644 --- a/headers/media_plugin/media/cas/CasAPI.h +++ b/headers/media_plugin/media/cas/CasAPI.h
@@ -48,6 +48,14 @@ uint8_t *data, size_t size); +typedef void (*CasPluginCallbackExt)( + void *appData, + int32_t event, + int32_t arg, + uint8_t *data, + size_t size, + const CasSessionId *sessionId); + struct CasFactory { CasFactory() {} virtual ~CasFactory() {} @@ -67,6 +75,13 @@ CasPluginCallback callback, CasPlugin **plugin) = 0; + // Construct a new extend instance of a CasPlugin given a CA_system_id + virtual status_t createPlugin( + int32_t CA_system_id, + void *appData, + CasPluginCallbackExt callback, + CasPlugin **plugin) = 0; + private: CasFactory(const CasFactory &); CasFactory &operator=(const CasFactory &); /* NOLINT */ @@ -110,7 +125,15 @@ int32_t arg, const CasData &eventData) = 0; - // Native implementation of the MediaCas Java API provision method. + // Deliver an session event to the CasPlugin. The format of the event is + // specific to the CA scheme and is opaque to the framework. + virtual status_t sendSessionEvent( + const CasSessionId &sessionId, + int32_t event, + int32_t arg, + const CasData &eventData) = 0; + + // Native implementation of the MediaCas Java API provision method. virtual status_t provision( const String8 &provisionString) = 0;
diff --git a/headers/media_plugin/media/cas/DescramblerAPI.h b/headers/media_plugin/media/cas/DescramblerAPI.h index 033c8ce..c57f606 100644 --- a/headers/media_plugin/media/cas/DescramblerAPI.h +++ b/headers/media_plugin/media/cas/DescramblerAPI.h
@@ -72,12 +72,12 @@ // associated MediaCas session is used to load decryption keys // into the crypto/cas plugin. The keys are then referenced by key-id // in the 'key' parameter to the decrypt() method. - // Should return NO_ERROR on success, ERROR_DRM_SESSION_NOT_OPENED if + // Should return NO_ERROR on success, ERROR_CAS_SESSION_NOT_OPENED if // the session is not opened and a code from MediaErrors.h otherwise. virtual status_t setMediaCasSession(const CasSessionId& sessionId) = 0; // If the error returned falls into the range - // ERROR_DRM_VENDOR_MIN..ERROR_DRM_VENDOR_MAX, errorDetailMsg should be + // ERROR_CAS_VENDOR_MIN..ERROR_CAS_VENDOR_MAX, errorDetailMsg should be // filled in with an appropriate string. // At the java level these special errors will then trigger a // MediaCodec.CryptoException that gives clients access to both
diff --git a/headers/media_plugin/media/drm/DrmAPI.h b/headers/media_plugin/media/drm/DrmAPI.h index c44a1f6..17b9993 100644 --- a/headers/media_plugin/media/drm/DrmAPI.h +++ b/headers/media_plugin/media/drm/DrmAPI.h
@@ -84,6 +84,7 @@ kDrmPluginEventSessionReclaimed, kDrmPluginEventExpirationUpdate, kDrmPluginEventKeysChange, + kDrmPluginEventSessionLostState, }; // Drm keys can be for offline content or for online streaming. @@ -114,7 +115,8 @@ kKeyStatusType_Expired, kKeyStatusType_OutputNotAllowed, kKeyStatusType_StatusPending, - kKeyStatusType_InternalError + kKeyStatusType_InternalError, + kKeyStatusType_UsableInFuture }; // Used by sendKeysChange to report the usability status of each @@ -139,6 +141,8 @@ kHdcpV2_1, // HDCP version 2.2 Type 1. kHdcpV2_2, + // HDCP version 2.3 Type 1. + kHdcpV2_3, // No digital output, implicitly secure kHdcpNoOutput = 0x7fff }; @@ -167,6 +171,25 @@ kSecurityLevelHwSecureAll }; + // An offline license may be usable or inactive. The keys in a + // usable offline license are available for decryption. When + // the offline license state is inactive, the keys have been + // marked for release using getKeyRequest with + // kKeyType_Release but the key response has not been + // received. The keys in an inactive offline license are not + // usable for decryption. + + enum OfflineLicenseState { + // The offline license state is unknown due to an error + kOfflineLicenseStateUnknown, + // Offline license state is usable, the keys may be used for decryption. + kOfflineLicenseStateUsable, + // Offline license state is released, the keys have been marked for + // release using getKeyRequest() with kKeyType_Release but the + // key response has not been received. + kOfflineLicenseStateReleased + }; + DrmPlugin() {} virtual ~DrmPlugin() {}
diff --git a/headers/media_plugin/media/hardware/HardwareAPI.h b/headers/media_plugin/media/hardware/HardwareAPI.h index 6c1ba3d..ae0220a 100644 --- a/headers/media_plugin/media/hardware/HardwareAPI.h +++ b/headers/media_plugin/media/hardware/HardwareAPI.h
@@ -425,7 +425,7 @@ // HDR color description parameters. // This is passed via OMX_SetConfig or OMX_GetConfig to video encoders and decoders when the -// 'OMX.google.android.index.describeHDRColorInfo' extension is given and an HDR stream +// 'OMX.google.android.index.describeHDRStaticInfo' extension is given and an HDR stream // is detected. Component SHALL behave as described below if it supports this extension. // // Currently, only Static Metadata Descriptor Type 1 support is required. @@ -496,6 +496,64 @@ HDRStaticInfo sInfo; // IN/OUT }; +// HDR10+ metadata configuration. +// +// nParamSize: size of the storage starting at nValue (must be at least 1 and at most +// MAX_HDR10PLUSINFO_SIZE). This field must not be modified by the component. +// nParamSizeUsed: size of the actual HDR10+ metadata starting at nValue. For OMX_SetConfig, +// it must not be modified by the component. For OMX_GetConfig, the component +// should put the actual size of the retrieved config in this field (and in +// case where nParamSize is smaller than nParamSizeUsed, the component should +// still update nParamSizeUsed without actually copying the metadata to nValue). +// nValue: storage of the HDR10+ metadata conforming to the user_data_registered_itu_t_t35() +// syntax of SEI message for ST 2094-40. +// +// This is passed via OMX_SetConfig or OMX_GetConfig to video encoders and decoders when the +// 'OMX.google.android.index.describeHDR10PlusInfo' extension is given. In general, this config +// is associated with a particular frame. A typical sequence of usage is as follows: +// +// a) OMX_SetConfig associates the config with the next input buffer sent in OMX_EmptyThisBuffer +// (input A); +// b) The component sends OMX_EventConfigUpdate to notify the client that there is a config +// update on the output port that is associated with the next output buffer that's about to +// be sent via FillBufferDone callback (output A); +// c) The client, upon receiving the OMX_EventConfigUpdate, calls OMX_GetConfig to retrieve +// the config and associates it with output A. +// +// All config updates will be retrieved in the order reported, and the client is required to +// call OMX_GetConfig for each OMX_EventConfigUpdate for this config. Note that the order of +// OMX_EventConfigUpdate relative to FillBufferDone callback determines which output frame +// the config should be associated with, the actual OMX_GetConfig for the config could happen +// before or after the component calls the FillBufferDone callback. +// +// Depending on the video codec type (in particular, whether the codec uses in-band or out-of- +// band HDR10+ metadata), the component shall behave as detailed below: +// +// VIDEO DECODERS: +// 1) If the codec utilizes out-of-band HDR10+ metadata, the decoder must support the sequence +// a) ~ c) outlined above; +// 2) If the codec utilizes in-band HDR10+ metadata, OMX_SetConfig for this config should be +// ignored (as the metadata is embedded in the input buffer), while the notification and +// retrieval of the config on the output as outlined in b) & c) must be supported. +// +// VIDEO ENCODERS: +// 1) If the codec utilizes out-of-band HDR10+ metadata, the decoder must support the sequence +// a) ~ c) outlined above; +// 2) If the codec utilizes in-band HDR10+ metadata, OMX_SetConfig for this config outlined in +// a) must be supported. The notification as outlined in b) must not be sent, and the +// retrieval of the config via OMX_GetConfig should be ignored (as the metadata is embedded +// in the output buffer). + +#define MAX_HDR10PLUSINFO_SIZE 1024 +struct DescribeHDR10PlusInfoParams { + OMX_U32 nSize; // IN + OMX_VERSIONTYPE nVersion; // IN + OMX_U32 nPortIndex; // IN + OMX_U32 nParamSize; // IN + OMX_U32 nParamSizeUsed; // IN/OUT + OMX_U8 nValue[1]; // IN/OUT +}; + } // namespace android extern android::OMXPluginBase *createOMXPlugin();
diff --git a/headers/media_plugin/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h index dc25ded..ce30b41 100644 --- a/headers/media_plugin/media/openmax/OMX_AsString.h +++ b/headers/media_plugin/media/openmax/OMX_AsString.h
@@ -188,7 +188,9 @@ inline static const char *asString(OMX_AUDIO_CODINGEXTTYPE i, const char *def = "??") { switch (i) { case OMX_AUDIO_CodingAndroidAC3: return "AndroidAC3"; + case OMX_AUDIO_CodingAndroidEAC3: return "AndroidEAC3"; case OMX_AUDIO_CodingAndroidOPUS: return "AndroidOPUS"; + case OMX_AUDIO_CodingAndroidAC4: return "AndroidAC4"; default: return asString((OMX_AUDIO_CODINGTYPE)i, def); } } @@ -533,9 +535,11 @@ // case OMX_IndexConfigCommit: return "ConfigCommit"; case OMX_IndexConfigAndroidVendorExtension: return "ConfigAndroidVendorExtension"; case OMX_IndexParamAudioAndroidAc3: return "ParamAudioAndroidAc3"; + case OMX_IndexConfigAudioPresentation: return "ConfigAudioPresentation"; case OMX_IndexParamAudioAndroidOpus: return "ParamAudioAndroidOpus"; case OMX_IndexParamAudioAndroidAacPresentation: return "ParamAudioAndroidAacPresentation"; case OMX_IndexParamAudioAndroidEac3: return "ParamAudioAndroidEac3"; + case OMX_IndexParamAudioAndroidAc4: return "ParamAudioAndroidAc4"; case OMX_IndexParamAudioProfileQuerySupported: return "ParamAudioProfileQuerySupported"; // case OMX_IndexParamNalStreamFormatSupported: return "ParamNalStreamFormatSupported"; // case OMX_IndexParamNalStreamFormat: return "ParamNalStreamFormat"; @@ -907,6 +911,9 @@ case OMX_VIDEO_AVCLevel5: return "Level5"; case OMX_VIDEO_AVCLevel51: return "Level51"; case OMX_VIDEO_AVCLevel52: return "Level52"; + case OMX_VIDEO_AVCLevel6: return "Level6"; + case OMX_VIDEO_AVCLevel61: return "Level61"; + case OMX_VIDEO_AVCLevel62: return "Level62"; default: return def; } }
diff --git a/headers/media_plugin/media/openmax/OMX_AudioExt.h b/headers/media_plugin/media/openmax/OMX_AudioExt.h index 8409553..477faed 100644 --- a/headers/media_plugin/media/openmax/OMX_AudioExt.h +++ b/headers/media_plugin/media/openmax/OMX_AudioExt.h
@@ -48,6 +48,7 @@ OMX_AUDIO_CodingAndroidAC3, /**< AC3 encoded data */ OMX_AUDIO_CodingAndroidOPUS, /**< OPUS encoded data */ OMX_AUDIO_CodingAndroidEAC3, /**< EAC3 encoded data */ + OMX_AUDIO_CodingAndroidAC4, /**< AC4 encoded data */ } OMX_AUDIO_CODINGEXTTYPE; typedef struct OMX_AUDIO_PARAM_ANDROID_AC3TYPE { @@ -68,6 +69,15 @@ variable or unknown sampling rate. */ } OMX_AUDIO_PARAM_ANDROID_EAC3TYPE; +typedef struct OMX_AUDIO_PARAM_ANDROID_AC4TYPE { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_U32 nPortIndex; /**< port that this structure applies to */ + OMX_U32 nChannels; /**< Number of channels */ + OMX_U32 nSampleRate; /**< Sampling rate of the source data. Use 0 for + variable or unknown sampling rate. */ +} OMX_AUDIO_PARAM_ANDROID_AC4TYPE; + typedef struct OMX_AUDIO_PARAM_ANDROID_OPUSTYPE { OMX_U32 nSize; /**< size of the structure in bytes */ OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ @@ -117,6 +127,13 @@ OMX_U32 nProfileIndex; /**< Used to query for individual profile support information */ } OMX_AUDIO_PARAM_ANDROID_PROFILETYPE; +typedef struct OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION { + OMX_U32 nSize; /**< size of the structure in bytes */ + OMX_VERSIONTYPE nVersion; /**< OMX specification version information */ + OMX_S32 nPresentationId; /**< presentation id */ + OMX_S32 nProgramId; /**< program id */ +} OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION; + #ifdef __cplusplus } #endif /* __cplusplus */
diff --git a/headers/media_plugin/media/openmax/OMX_Core.h b/headers/media_plugin/media/openmax/OMX_Core.h index bb974b3..9ff934e 100644 --- a/headers/media_plugin/media/openmax/OMX_Core.h +++ b/headers/media_plugin/media/openmax/OMX_Core.h
@@ -542,6 +542,20 @@ * fool-proof way to do that for video encoders. */ OMX_EventDataSpaceChanged, + + /** + * Event when a component has an updated configuration on output for the client to retrieve. + * |arg1| contains the port index (currently only output port is valid). |arg2| contains the + * index of the updated config. + * + * For config updates that's associated with one frame, the update should be applied to the + * next output frame that comes in EmptyBufferDone callback. + * + * Upon receiving this event, the client must call the corresponding OMX_GetConfig to retrieve + * the config update. + */ + OMX_EventConfigUpdate, + OMX_EventMax = 0x7FFFFFFF } OMX_EVENTTYPE;
diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h index 716d959..479e9b8 100644 --- a/headers/media_plugin/media/openmax/OMX_IndexExt.h +++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h
@@ -64,6 +64,8 @@ OMX_IndexParamAudioAndroidEac3, /**< reference: OMX_AUDIO_PARAM_ANDROID_EAC3TYPE */ OMX_IndexParamAudioProfileQuerySupported, /**< reference: OMX_AUDIO_PARAM_ANDROID_PROFILETYPE */ OMX_IndexParamAudioAndroidAacDrcPresentation, /**< reference: OMX_AUDIO_PARAM_ANDROID_AACDRCPRESENTATIONTYPE */ + OMX_IndexParamAudioAndroidAc4, /**< reference: OMX_AUDIO_PARAM_ANDROID_AC4TYPE */ + OMX_IndexConfigAudioPresentation, /**< reference: OMX_AUDIO_CONFIG_ANDROID_AUDIOPRESENTATION */ OMX_IndexExtAudioEndUnused, /* Image parameters and configurations */
diff --git a/headers/media_plugin/media/openmax/OMX_Video.h b/headers/media_plugin/media/openmax/OMX_Video.h index 9fd2fd2..b6edaa9 100644 --- a/headers/media_plugin/media/openmax/OMX_Video.h +++ b/headers/media_plugin/media/openmax/OMX_Video.h
@@ -832,6 +832,9 @@ OMX_VIDEO_AVCLevel5 = 0x4000, /**< Level 5 */ OMX_VIDEO_AVCLevel51 = 0x8000, /**< Level 5.1 */ OMX_VIDEO_AVCLevel52 = 0x10000, /**< Level 5.2 */ + OMX_VIDEO_AVCLevel6 = 0x20000, /**< Level 6 */ + OMX_VIDEO_AVCLevel61 = 0x40000, /**< Level 6.1 */ + OMX_VIDEO_AVCLevel62 = 0x80000, /**< Level 6.2 */ OMX_VIDEO_AVCLevelKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_AVCLevelVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_AVCLevelMax = 0x7FFFFFFF
diff --git a/headers/media_plugin/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h index bbf157b..435fcc8 100644 --- a/headers/media_plugin/media/openmax/OMX_VideoExt.h +++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h
@@ -164,6 +164,8 @@ // HDR profiles also support passing HDR metadata OMX_VIDEO_VP9Profile2HDR = 0x1000, OMX_VIDEO_VP9Profile3HDR = 0x2000, + OMX_VIDEO_VP9Profile2HDR10Plus = 0x4000, + OMX_VIDEO_VP9Profile3HDR10Plus = 0x8000, OMX_VIDEO_VP9ProfileUnknown = 0x6EFFFFFF, OMX_VIDEO_VP9ProfileMax = 0x7FFFFFFF } OMX_VIDEO_VP9PROFILETYPE; @@ -216,6 +218,7 @@ OMX_VIDEO_HEVCProfileMainStill = 0x4, // Main10 profile with HDR SEI support. OMX_VIDEO_HEVCProfileMain10HDR10 = 0x1000, + OMX_VIDEO_HEVCProfileMain10HDR10Plus = 0x2000, OMX_VIDEO_HEVCProfileMax = 0x7FFFFFFF } OMX_VIDEO_HEVCPROFILETYPE;
diff --git a/include/OWNERS b/include/OWNERS new file mode 100644 index 0000000..db52850 --- /dev/null +++ b/include/OWNERS
@@ -0,0 +1,16 @@ +alexeykuzmin@google.com +dangittik@google.com +jreck@google.com +lajos@google.com +mathias@google.com +michaelwr@google.com +nona@google.com +racarr@google.com +romainguy@android.com +santoscordon@google.com +stoza@google.com +svv@google.com + +# For multinetwork.h only. +lorenzo@google.com +
diff --git a/include/android/choreographer.h b/include/android/choreographer.h index 43346fe..44883cc 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h
@@ -26,12 +26,11 @@ #ifndef ANDROID_CHOREOGRAPHER_H #define ANDROID_CHOREOGRAPHER_H +#include <stdint.h> #include <sys/cdefs.h> __BEGIN_DECLS -#if __ANDROID_API__ >= 24 - struct AChoreographer; typedef struct AChoreographer AChoreographer; @@ -46,26 +45,56 @@ typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data); /** + * Prototype of the function that is called when a new frame is being rendered. + * It's passed the time that the frame is being rendered as nanoseconds in the + * CLOCK_MONOTONIC time base, as well as the data pointer provided by the + * application that registered a callback. All callbacks that run as part of + * rendering a frame will observe the same frame time, so it should be used + * whenever events need to be synchronized (e.g. animations). + */ +typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data); + +#if __ANDROID_API__ >= 24 + +/** * Get the AChoreographer instance for the current thread. This must be called * on an ALooper thread. */ -AChoreographer* AChoreographer_getInstance(); +AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24); /** - * Post a callback to be run on the next frame. The data pointer provided will - * be passed to the callback function when it's called. + * Deprecated: Use AChoreographer_postFrameCallback64 instead. */ void AChoreographer_postFrameCallback(AChoreographer* choreographer, - AChoreographer_frameCallback callback, void* data); + AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29); + /** - * Post a callback to be run on the frame following the specified delay. The + * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead. + */ +void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, + AChoreographer_frameCallback callback, void* data, + long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29); + +#endif /* __ANDROID_API__ >= 24 */ + +#if __ANDROID_API__ >= 29 + +/** + * Power a callback to be run on the next frame. The data pointer provided will + * be passed to the callback function when it's called. + */ +void AChoreographer_postFrameCallback64(AChoreographer* chroreographer, + AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29); + +/** + * Post a callback to be run on the frame following the specified delay. The * data pointer provided will be passed to the callback function when it's * called. */ -void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, - AChoreographer_frameCallback callback, void* data, long delayMillis); +void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, + AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29); -#endif /* __ANDROID_API__ >= 24 */ +#endif /* __ANDROID_API__ >= 29 */ __END_DECLS
diff --git a/include/android/configuration.h b/include/android/configuration.h index 6287332..ef6c5a2 100644 --- a/include/android/configuration.h +++ b/include/android/configuration.h
@@ -30,6 +30,10 @@ #include <android/asset_manager.h> +#if !defined(__INTRODUCED_IN) +#define __INTRODUCED_IN(__api_level) /* nothing */ +#endif + #ifdef __cplusplus extern "C" { #endif @@ -676,34 +680,34 @@ * Return the current configuration screen width in dp units, or * ACONFIGURATION_SCREEN_WIDTH_DP_ANY if not set. */ -int32_t AConfiguration_getScreenWidthDp(AConfiguration* config); +int32_t AConfiguration_getScreenWidthDp(AConfiguration* config) __INTRODUCED_IN(13); /** * Set the configuration's current screen width in dp units. */ -void AConfiguration_setScreenWidthDp(AConfiguration* config, int32_t value); +void AConfiguration_setScreenWidthDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13); /** * Return the current configuration screen height in dp units, or * ACONFIGURATION_SCREEN_HEIGHT_DP_ANY if not set. */ -int32_t AConfiguration_getScreenHeightDp(AConfiguration* config); +int32_t AConfiguration_getScreenHeightDp(AConfiguration* config) __INTRODUCED_IN(13); /** * Set the configuration's current screen width in dp units. */ -void AConfiguration_setScreenHeightDp(AConfiguration* config, int32_t value); +void AConfiguration_setScreenHeightDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13); /** * Return the configuration's smallest screen width in dp units, or * ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY if not set. */ -int32_t AConfiguration_getSmallestScreenWidthDp(AConfiguration* config); +int32_t AConfiguration_getSmallestScreenWidthDp(AConfiguration* config) __INTRODUCED_IN(13); /** * Set the configuration's smallest screen width in dp units. */ -void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t value); +void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13); #endif /* __ANDROID_API__ >= 13 */ #if __ANDROID_API__ >= 17 @@ -711,12 +715,12 @@ * Return the configuration's layout direction, or * ACONFIGURATION_LAYOUTDIR_ANY if not set. */ -int32_t AConfiguration_getLayoutDirection(AConfiguration* config); +int32_t AConfiguration_getLayoutDirection(AConfiguration* config) __INTRODUCED_IN(17); /** * Set the configuration's layout direction. */ -void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value); +void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value) __INTRODUCED_IN(17); #endif /* __ANDROID_API__ >= 17 */ /**
diff --git a/include/android/font.h b/include/android/font.h new file mode 100644 index 0000000..435a573 --- /dev/null +++ b/include/android/font.h
@@ -0,0 +1,288 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup Font + * { + */ + +/** + * @file font.h + * @brief Provides some constants used in system_fonts.h or fonts_matcher.h + * + * Available since API level 29. + */ + +#ifndef ANDROID_FONT_H +#define ANDROID_FONT_H + +#include <stdbool.h> +#include <stddef.h> +#include <sys/cdefs.h> + +/****************************************************************** + * + * IMPORTANT NOTICE: + * + * This file is part of Android's set of stable system headers + * exposed by the Android NDK (Native Development Kit). + * + * Third-party source AND binary code relies on the definitions + * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. + * + * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) + * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS + * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY + * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES + */ + +__BEGIN_DECLS + +#if __ANDROID_API__ >= 29 + +enum { + /** The minimum value fot the font weight value. */ + AFONT_WEIGHT_MIN = 0, + + /** A font weight value for the thin weight. */ + AFONT_WEIGHT_THIN = 100, + + /** A font weight value for the extra-light weight. */ + AFONT_WEIGHT_EXTRA_LIGHT = 200, + + /** A font weight value for the light weight. */ + AFONT_WEIGHT_LIGHT = 300, + + /** A font weight value for the normal weight. */ + AFONT_WEIGHT_NORMAL = 400, + + /** A font weight value for the medium weight. */ + AFONT_WEIGHT_MEDIUM = 500, + + /** A font weight value for the semi-bold weight. */ + AFONT_WEIGHT_SEMI_BOLD = 600, + + /** A font weight value for the bold weight. */ + AFONT_WEIGHT_BOLD = 700, + + /** A font weight value for the extra-bold weight. */ + AFONT_WEIGHT_EXTRA_BOLD = 800, + + /** A font weight value for the black weight. */ + AFONT_WEIGHT_BLACK = 900, + + /** The maximum value for the font weight value. */ + AFONT_WEIGHT_MAX = 1000 +}; + +/** + * AFont provides information of the single font configuration. + */ +struct AFont; + +/** + * Close an AFont. + * + * \param font a font returned by ASystemFontIterator_next or AFontMatchert_match. + * Do nothing if NULL is passed. + */ +void AFont_close(AFont* _Nullable font) __INTRODUCED_IN(29); + +/** + * Return an absolute path to the current font file. + * + * Here is a list of font formats returned by this method: + * <ul> + * <li>OpenType</li> + * <li>OpenType Font Collection</li> + * <li>TrueType</li> + * <li>TrueType Collection</li> + * </ul> + * The file extension could be one of *.otf, *.ttf, *.otc or *.ttc. + * + * The font file returned is guaranteed to be opend with O_RDONLY. + * Note that the returned pointer is valid until AFont_close() is called for the given font. + * + * \param font a font object. Passing NULL is not allowed. + * \return a string of the font file path. + */ +const char* _Nonnull AFont_getFontFilePath(const AFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return a weight value associated with the current font. + * + * The weight values are positive and less than or equal to 1000. + * Here are pairs of the common names and their values. + * <p> + * <table> + * <tr> + * <th align="center">Value</th> + * <th align="center">Name</th> + * <th align="center">NDK Definition</th> + * </tr> + * <tr> + * <td align="center">100</td> + * <td align="center">Thin</td> + * <td align="center">{@link AFONT_WEIGHT_THIN}</td> + * </tr> + * <tr> + * <td align="center">200</td> + * <td align="center">Extra Light (Ultra Light)</td> + * <td align="center">{@link AFONT_WEIGHT_EXTRA_LIGHT}</td> + * </tr> + * <tr> + * <td align="center">300</td> + * <td align="center">Light</td> + * <td align="center">{@link AFONT_WEIGHT_LIGHT}</td> + * </tr> + * <tr> + * <td align="center">400</td> + * <td align="center">Normal (Regular)</td> + * <td align="center">{@link AFONT_WEIGHT_NORMAL}</td> + * </tr> + * <tr> + * <td align="center">500</td> + * <td align="center">Medium</td> + * <td align="center">{@link AFONT_WEIGHT_MEDIUM}</td> + * </tr> + * <tr> + * <td align="center">600</td> + * <td align="center">Semi Bold (Demi Bold)</td> + * <td align="center">{@link AFONT_WEIGHT_SEMI_BOLD}</td> + * </tr> + * <tr> + * <td align="center">700</td> + * <td align="center">Bold</td> + * <td align="center">{@link AFONT_WEIGHT_BOLD}</td> + * </tr> + * <tr> + * <td align="center">800</td> + * <td align="center">Extra Bold (Ultra Bold)</td> + * <td align="center">{@link AFONT_WEIGHT_EXTRA_BOLD}</td> + * </tr> + * <tr> + * <td align="center">900</td> + * <td align="center">Black (Heavy)</td> + * <td align="center">{@link AFONT_WEIGHT_BLACK}</td> + * </tr> + * </table> + * </p> + * Note that the weight value may fall in between above values, e.g. 250 weight. + * + * For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass) + * + * \param font a font object. Passing NULL is not allowed. + * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned. + */ +uint16_t AFont_getWeight(const AFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return true if the current font is italic, otherwise returns false. + * + * \param font a font object. Passing NULL is not allowed. + * \return true if italic, otherwise false. + */ +bool AFont_isItalic(const AFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return a IETF BCP47 compliant language tag associated with the current font. + * + * For information about IETF BCP47, read [Locale.forLanguageTag(java.lang.String)](https://developer.android.com/reference/java/util/Locale.html#forLanguageTag(java.lang.String)") + * + * Note that the returned pointer is valid until AFont_close() is called. + * + * \param font a font object. Passing NULL is not allowed. + * \return a IETF BCP47 compliant language tag or nullptr if not available. + */ +const char* _Nullable AFont_getLocale(const AFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return a font collection index value associated with the current font. + * + * In case the target font file is a font collection (e.g. .ttc or .otc), this + * returns a non-negative value as an font offset in the collection. This + * always returns 0 if the target font file is a regular font. + * + * \param font a font object. Passing NULL is not allowed. + * \return a font collection index. + */ +size_t AFont_getCollectionIndex(const AFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return a count of font variation settings associated with the current font + * + * The font variation settings are provided as multiple tag-values pairs. + * + * For example, bold italic font may have following font variation settings: + * 'wght' 700, 'slnt' -12 + * In this case, AFont_getAxisCount returns 2 and AFont_getAxisTag + * and AFont_getAxisValue will return following values. + * \code{.cpp} + * AFont* font = AFontIterator_next(ite); + * + * // Returns the number of axes + * AFont_getAxisCount(font); // Returns 2 + * + * // Returns the tag-value pair for the first axis. + * AFont_getAxisTag(font, 0); // Returns 'wght'(0x77676874) + * AFont_getAxisValue(font, 0); // Returns 700.0 + * + * // Returns the tag-value pair for the second axis. + * AFont_getAxisTag(font, 1); // Returns 'slnt'(0x736c6e74) + * AFont_getAxisValue(font, 1); // Returns -12.0 + * \endcode + * + * For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar) + * + * \param font a font object. Passing NULL is not allowed. + * \return a number of font variation settings. + */ +size_t AFont_getAxisCount(const AFont* _Nonnull font) __INTRODUCED_IN(29); + + +/** + * Return an OpenType axis tag associated with the current font. + * + * See AFont_getAxisCount for more details. + * + * \param font a font object. Passing NULL is not allowed. + * \param axisIndex an index to the font variation settings. Passing value larger than or + * equal to {@link AFont_getAxisCount} is not allowed. + * \return an OpenType axis tag value for the given font variation setting. + */ +uint32_t AFont_getAxisTag(const AFont* _Nonnull font, uint32_t axisIndex) + __INTRODUCED_IN(29); + +/** + * Return an OpenType axis value associated with the current font. + * + * See AFont_getAxisCount for more details. + * + * \param font a font object. Passing NULL is not allowed. + * \param axisIndex an index to the font variation settings. Passing value larger than or + * equal to {@link ASYstemFont_getAxisCount} is not allwed. + * \return a float value for the given font variation setting. + */ +float AFont_getAxisValue(const AFont* _Nonnull font, uint32_t axisIndex) + __INTRODUCED_IN(29); + +#endif // __ANDROID_API__ >= 29 + +__END_DECLS + +#endif // ANDROID_FONT_H + +/** @} */
diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h new file mode 100644 index 0000000..e286a4c --- /dev/null +++ b/include/android/font_matcher.h
@@ -0,0 +1,214 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup Font + * { + */ + +/** + * @file font_matcher.h + * @brief Provides the font matching logic with various inputs. + * + * You can use this class for deciding what font is to be used for drawing text. + * + * A matcher is created from text style, locales and UI compatibility. The match function for + * matcher object can be called multiple times until close function is called. + * + * Even if no font can render the given text, the match function will return a non-null result for + * drawing Tofu character. + * + * Examples: + * \code{.cpp} + * // Simple font query for the ASCII character. + * std::vector<uint16_t> text = { 'A' }; + * AFontMatcher* matcher = AFontMatcher_create("sans-serif"); + * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength); + * // runLength will be 1 and the font will points a valid font file. + * AFontMatcher_destroy(matcher); + * + * // Querying font for CJK characters + * std::vector<uint16_t> text = { 0x9AA8 }; + * AFontMatcher* matcher = AFontMatcher_create("sans-serif"); + * AFontMatcher_setLocales(matcher, "zh-CN,ja-JP"); + * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength); + * // runLength will be 1 and the font will points a Simplified Chinese font. + * AFontMatcher_setLocales(matcher, "ja-JP,zh-CN"); + * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength); + * // runLength will be 1 and the font will points a Japanese font. + * AFontMatcher_destroy(matcher); + * + * // Querying font for text/color emoji + * std::vector<uint16_t> text = { 0xD83D, 0xDC68, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 }; + * AFontMatcher* matcher = AFontMatcher_create("sans-serif"); + * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength); + * // runLength will be 8 and the font will points a color emoji font. + * AFontMatcher_destroy(matcher); + * + * // Mixture of multiple script of characters. + * // 0x05D0 is a Hebrew character and 0x0E01 is a Thai character. + * std::vector<uint16_t> text = { 0x05D0, 0x0E01 }; + * AFontMatcher* matcher = AFontMatcher_create("sans-serif"); + * ASystemFont* font = AFontMatcher_match(text.data(), text.length(), &runLength); + * // runLength will be 1 and the font will points a Hebrew font. + * AFontMatcher_destroy(matcher); + * \endcode + * + * Available since API level 29. + */ + +#ifndef ANDROID_FONT_MATCHER_H +#define ANDROID_FONT_MATCHER_H + +#include <stdbool.h> +#include <stddef.h> +#include <sys/cdefs.h> + +#include <android/font.h> + +/****************************************************************** + * + * IMPORTANT NOTICE: + * + * This file is part of Android's set of stable system headers + * exposed by the Android NDK (Native Development Kit). + * + * Third-party source AND binary code relies on the definitions + * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. + * + * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) + * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS + * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY + * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES + */ + +__BEGIN_DECLS + +#if __ANDROID_API__ >= 29 + +enum { + /** A family variant value for the system default variant. */ + AFAMILY_VARIANT_DEFAULT = 0, + + /** + * A family variant value for the compact font family variant. + * + * The compact font family has Latin-based vertical metrics. + */ + AFAMILY_VARIANT_COMPACT = 1, + + /** + * A family variant value for the elegant font family variant. + * + * The elegant font family may have larger vertical metrics than Latin font. + */ + AFAMILY_VARIANT_ELEGANT = 2, +}; + +/** + * AFontMatcher performs match operation on given parameters and available font files. + * This matcher is not a thread-safe object. Do not pass this matcher to other threads. + */ +struct AFontMatcher; + +/** + * Select the best font from given parameters. + * + */ + +/** + * Creates a new AFontMatcher object + */ +AFontMatcher* _Nonnull AFontMatcher_create() __INTRODUCED_IN(29); + +/** + * Destroy the matcher object. + * + * \param matcher a matcher object. Passing NULL is not allowed. + */ +void AFontMatcher_destroy(AFontMatcher* _Nonnull matcher) __INTRODUCED_IN(29); + +/** + * Set font style to matcher. + * + * If this function is not called, the matcher performs with {@link ASYSTEM_FONT_WEIGHT_NORMAL} + * with non-italic style. + * + * \param matcher a matcher object. Passing NULL is not allowed. + * \param weight a font weight value. Only from 0 to 1000 value is valid + * \param italic true if italic, otherwise false. + */ +void AFontMatcher_setStyle( + AFontMatcher* _Nonnull matcher, + uint16_t weight, + bool italic) __INTRODUCED_IN(29); + +/** + * Set font locales to matcher. + * + * If this function is not called, the matcher performs with empty locale list. + * + * \param matcher a matcher object. Passing NULL is not allowed. + * \param languageTags a null character terminated comma separated IETF BCP47 compliant language + * tags. + */ +void AFontMatcher_setLocales( + AFontMatcher* _Nonnull matcher, + const char* _Nonnull languageTags) __INTRODUCED_IN(29); + +/** + * Set family variant to matcher. + * + * If this function is not called, the matcher performs with {@link AFAMILY_VARIANT_DEFAULT}. + * + * \param matcher a matcher object. Passing NULL is not allowed. + * \param familyVariant Must be one of {@link AFAMILY_VARIANT_DEFAULT}, + * {@link AFAMILY_VARIANT_COMPACT} or {@link AFAMILY_VARIANT_ELEGANT} is valid. + */ +void AFontMatcher_setFamilyVariant( + AFontMatcher* _Nonnull matcher, + uint32_t familyVariant) __INTRODUCED_IN(29); + +/** + * Performs the matching from the generic font family for the text and select one font. + * + * For more information about generic font families, read [W3C spec](https://www.w3.org/TR/css-fonts-4/#generic-font-families) + * + * Even if no font can render the given text, this function will return a non-null result for + * drawing Tofu character. + * + * \param matcher a matcher object. Passing NULL is not allowed. + * \param familyName a null character terminated font family name + * \param text a UTF-16 encoded text buffer to be rendered. Do not pass empty string. + * \param textLength a length of the given text buffer. This must not be zero. + * \param runLengthOut if not null, the font run length will be filled. + * \return a font to be used for given text and params. You need to release the returned font by + * ASystemFont_close when it is no longer needed. + */ +AFont* _Nonnull AFontMatcher_match( + const AFontMatcher* _Nonnull matcher, + const char* _Nonnull familyName, + const uint16_t* _Nonnull text, + const uint32_t textLength, + uint32_t* _Nullable runLengthOut) __INTRODUCED_IN(29); + +#endif // __ANDROID_API__ >= 29 + +__END_DECLS + +#endif // ANDROID_FONT_MATCHER_H + +/** @} */
diff --git a/include/android/hardware_buffer_jni.h b/include/android/hardware_buffer_jni.h index 7c4be24..aedf369 100644 --- a/include/android/hardware_buffer_jni.h +++ b/include/android/hardware_buffer_jni.h
@@ -15,7 +15,13 @@ */ /** + * @addtogroup AHardwareBuffer + * @{ + */ + +/** * @file hardware_buffer_jni.h + * @brief JNI glue for native hardware buffers. */ #ifndef ANDROID_HARDWARE_BUFFER_JNI_H @@ -30,23 +36,25 @@ __BEGIN_DECLS /** - * Return the AHardwareBuffer associated with a Java HardwareBuffer object, - * for interacting with it through native code. This method does not acquire any - * additional reference to the AHardwareBuffer that is returned. To keep the - * AHardwareBuffer live after the Java HardwareBuffer object got garbage - * collected, be sure to use AHardwareBuffer_acquire() to acquire an additional - * reference. + * Return the AHardwareBuffer wrapped by a Java HardwareBuffer object. + * + * This method does not acquire any additional reference to the AHardwareBuffer + * that is returned. To keep the AHardwareBuffer live after the Java + * HardwareBuffer object got garbage collected, be sure to use AHardwareBuffer_acquire() + * to acquire an additional reference. */ AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env, - jobject hardwareBufferObj); + jobject hardwareBufferObj) __INTRODUCED_IN(26); /** * Return a new Java HardwareBuffer object that wraps the passed native * AHardwareBuffer object. */ jobject AHardwareBuffer_toHardwareBuffer(JNIEnv* env, - AHardwareBuffer* hardwareBuffer); + AHardwareBuffer* hardwareBuffer) __INTRODUCED_IN(26); __END_DECLS #endif // ANDROID_HARDWARE_BUFFER_JNI_H + +/** @} */
diff --git a/include/android/input.h b/include/android/input.h index 0829989..cfade6c 100644 --- a/include/android/input.h +++ b/include/android/input.h
@@ -56,6 +56,10 @@ #include <android/keycodes.h> #include <android/looper.h> +#if !defined(__INTRODUCED_IN) +#define __INTRODUCED_IN(__api_level) /* nothing */ +#endif + #ifdef __cplusplus extern "C" { #endif @@ -79,7 +83,7 @@ }; /** - * Meta key / modifer state. + * Meta key / modifier state. */ enum { /** No meta keys are pressed. */ @@ -984,7 +988,7 @@ #if __ANDROID_API__ >= 14 /** Get the button state of all buttons that are pressed. */ -int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event); +int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event) __INTRODUCED_IN(14); #endif /** @@ -1056,7 +1060,7 @@ * The tool type indicates the type of tool used to make contact such as a * finger or stylus, if known. */ -int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index); +int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index) __INTRODUCED_IN(14); #endif /** @@ -1150,7 +1154,7 @@ #if __ANDROID_API__ >= 13 /** Get the value of the request axis for the given pointer index. */ float AMotionEvent_getAxisValue(const AInputEvent* motion_event, - int32_t axis, size_t pointer_index); + int32_t axis, size_t pointer_index) __INTRODUCED_IN(13); #endif /** @@ -1288,7 +1292,7 @@ * that occurred between this event and the previous motion event. */ float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, - int32_t axis, size_t pointer_index, size_t history_index); + int32_t axis, size_t pointer_index, size_t history_index) __INTRODUCED_IN(13); #endif
diff --git a/include/android/keycodes.h b/include/android/keycodes.h index 59d67f3..214559d 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h
@@ -769,7 +769,14 @@ /** all apps */ AKEYCODE_ALL_APPS = 284, /** refresh key */ - AKEYCODE_REFRESH = 285 + AKEYCODE_REFRESH = 285, + /** Thumbs up key. Apps can use this to let user upvote content. */ + AKEYCODE_THUMBS_UP = 286, + /** Thumbs down key. Apps can use this to let user downvote content. */ + AKEYCODE_THUMBS_DOWN = 287, + /** Used to switch current account that is consuming content. + * May be consumed by system to switch current viewer profile. */ + AKEYCODE_PROFILE_SWITCH = 288 // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h index 97892f8..d31d1f1 100644 --- a/include/android/multinetwork.h +++ b/include/android/multinetwork.h
@@ -14,6 +14,15 @@ * limitations under the License. */ +/** + * @addtogroup Networking + * @{ + */ + +/** + * @file multinetwork.h + */ + #ifndef ANDROID_MULTINETWORK_H #define ANDROID_MULTINETWORK_H @@ -51,7 +60,7 @@ * on failure with an appropriate errno value set. */ -#if __ANDROID_API__ >= 24 +#if __ANDROID_API__ >= 23 /** * Set the network to be used by the given socket file descriptor. @@ -61,7 +70,7 @@ * This is the equivalent of: [android.net.Network#bindSocket()](https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.Socket)) * */ -int android_setsocknetwork(net_handle_t network, int fd); +int android_setsocknetwork(net_handle_t network, int fd) __INTRODUCED_IN(23); /** @@ -78,7 +87,7 @@ * This is the equivalent of: [android.net.ConnectivityManager#setProcessDefaultNetwork()](https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network)) * */ -int android_setprocnetwork(net_handle_t network); +int android_setprocnetwork(net_handle_t network) __INTRODUCED_IN(23); /** @@ -97,10 +106,80 @@ */ int android_getaddrinfofornetwork(net_handle_t network, const char *node, const char *service, - const struct addrinfo *hints, struct addrinfo **res); + const struct addrinfo *hints, struct addrinfo **res) __INTRODUCED_IN(23); -#endif /* __ANDROID_API__ >= 24 */ +#endif /* __ANDROID_API__ >= 23 */ + +#if __ANDROID_API__ >= 29 + +/** + * Possible values of the flags argument to android_res_nsend and android_res_nquery. + * Values are ORed together. + */ +enum ResNsendFlags : uint32_t { + /** + * Send a single request to a single resolver and fail on timeout or network errors + */ + ANDROID_RESOLV_NO_RETRY = 1 << 0, + + /** + * Do not cache the result of the lookup. The lookup may return a result that is already + * in the cache, unless the ANDROID_RESOLV_NO_CACHE_LOOKUP flag is also specified. + */ + ANDROID_RESOLV_NO_CACHE_STORE = 1 << 1, + + /** + * Don't lookup the request in cache. + */ + ANDROID_RESOLV_NO_CACHE_LOOKUP = 1 << 2, +}; + +/** + * Look up the {|ns_class|, |ns_type|} Resource Record (RR) associated + * with Domain Name |dname| on the given |network|. + * The typical value for |ns_class| is ns_c_in, while |type| can be any + * record type (for instance, ns_t_aaaa or ns_t_txt). + * |flags| is a additional config to control actual querying behavior, see + * ResNsendFlags for detail. + * + * Returns a file descriptor to watch for read events, or a negative + * POSIX error code (see errno.h) if an immediate error occurs. + */ +int android_res_nquery(net_handle_t network, + const char *dname, int ns_class, int ns_type, uint32_t flags) __INTRODUCED_IN(29); + +/** + * Issue the query |msg| on the given |network|. + * |flags| is a additional config to control actual querying behavior, see + * ResNsendFlags for detail. + * + * Returns a file descriptor to watch for read events, or a negative + * POSIX error code (see errno.h) if an immediate error occurs. + */ +int android_res_nsend(net_handle_t network, + const uint8_t *msg, size_t msglen, uint32_t flags) __INTRODUCED_IN(29); + +/** + * Read a result for the query associated with the |fd| descriptor. + * Closes |fd| before returning. + * + * Returns: + * < 0: negative POSIX error code (see errno.h for possible values). |rcode| is not set. + * >= 0: length of |answer|. |rcode| is the resolver return code (e.g., ns_r_nxdomain) + */ +int android_res_nresult(int fd, + int *rcode, uint8_t *answer, size_t anslen) __INTRODUCED_IN(29); + +/** + * Attempts to cancel the in-progress query associated with the |nsend_fd| + * descriptor. + */ +void android_res_cancel(int nsend_fd) __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ __END_DECLS #endif // ANDROID_MULTINETWORK_H + +/** @} */
diff --git a/include/android/native_window_jni.h b/include/android/native_window_jni.h index 23b39aa..0c196b9 100644 --- a/include/android/native_window_jni.h +++ b/include/android/native_window_jni.h
@@ -44,16 +44,6 @@ */ ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface); -#if __ANDROID_API__ >= 13 -/** - * Return the ANativeWindow associated with a Java SurfaceTexture object, - * for interacting with it through native code. This acquires a reference - * on the ANativeWindow that is returned; be sure to use ANativeWindow_release() - * when done with it so that it doesn't leak. - */ -ANativeWindow* ANativeWindow_fromSurfaceTexture(JNIEnv* env, jobject surfaceTexture); -#endif - #if __ANDROID_API__ >= 26 /** * Return a Java Surface object derived from the ANativeWindow, for interacting @@ -62,7 +52,7 @@ * and will automatically release the reference when the Java object gets garbage * collected. */ -jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window); +jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window) __INTRODUCED_IN(26); #endif #ifdef __cplusplus
diff --git a/include/android/sensor.h b/include/android/sensor.h index 191777c..e9d5c16 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h
@@ -64,6 +64,7 @@ #define ASENSOR_RESOLUTION_INVALID (nanf("")) #define ASENSOR_FIFO_COUNT_INVALID (-1) #define ASENSOR_DELAY_INVALID INT32_MIN +#define ASENSOR_INVALID (-1) /* (Keep in sync with hardware/sensors-base.h and Sensor.java.) */ @@ -208,6 +209,35 @@ */ ASENSOR_TYPE_HEART_BEAT = 31, /** + * This sensor type is for delivering additional sensor information aside + * from sensor event data. + * + * Additional information may include: + * - {@link ASENSOR_ADDITIONAL_INFO_INTERNAL_TEMPERATURE} + * - {@link ASENSOR_ADDITIONAL_INFO_SAMPLING} + * - {@link ASENSOR_ADDITIONAL_INFO_SENSOR_PLACEMENT} + * - {@link ASENSOR_ADDITIONAL_INFO_UNTRACKED_DELAY} + * - {@link ASENSOR_ADDITIONAL_INFO_VEC3_CALIBRATION} + * + * This type will never bind to a sensor. In other words, no sensor in the + * sensor list can have the type {@link ASENSOR_TYPE_ADDITIONAL_INFO}. + * + * If a device supports the sensor additional information feature, it will + * report additional information events via {@link ASensorEvent} and will + * have {@link ASensorEvent#type} set to + * {@link ASENSOR_TYPE_ADDITIONAL_INFO} and {@link ASensorEvent#sensor} set + * to the handle of the reporting sensor. + * + * Additional information reports consist of multiple frames ordered by + * {@link ASensorEvent#timestamp}. The first frame in the report will have + * a {@link AAdditionalInfoEvent#type} of + * {@link ASENSOR_ADDITIONAL_INFO_BEGIN}, and the last frame in the report + * will have a {@link AAdditionalInfoEvent#type} of + * {@link ASENSOR_ADDITIONAL_INFO_END}. + * + */ + ASENSOR_TYPE_ADDITIONAL_INFO = 33, + /** * {@link ASENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT} */ ASENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT = 34, @@ -273,6 +303,51 @@ ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER = 2 }; +/** + * Sensor Additional Info Types. + * + * Used to populate {@link AAdditionalInfoEvent#type}. + */ +enum { + /** Marks the beginning of additional information frames */ + ASENSOR_ADDITIONAL_INFO_BEGIN = 0, + + /** Marks the end of additional information frames */ + ASENSOR_ADDITIONAL_INFO_END = 1, + + /** + * Estimation of the delay that is not tracked by sensor timestamps. This + * includes delay introduced by sensor front-end filtering, data transport, + * etc. + * float[2]: delay in seconds, standard deviation of estimated value + */ + ASENSOR_ADDITIONAL_INFO_UNTRACKED_DELAY = 0x10000, + + /** float: Celsius temperature */ + ASENSOR_ADDITIONAL_INFO_INTERNAL_TEMPERATURE, + + /** + * First three rows of a homogeneous matrix, which represents calibration to + * a three-element vector raw sensor reading. + * float[12]: 3x4 matrix in row major order + */ + ASENSOR_ADDITIONAL_INFO_VEC3_CALIBRATION, + + /** + * Location and orientation of sensor element in the device frame: origin is + * the geometric center of the mobile device screen surface; the axis + * definition corresponds to Android sensor definitions. + * float[12]: 3x4 matrix in row major order + */ + ASENSOR_ADDITIONAL_INFO_SENSOR_PLACEMENT, + + /** + * float[2]: raw sample period in seconds, + * standard deviation of sampling period + */ + ASENSOR_ADDITIONAL_INFO_SAMPLING, +}; + /* * A few useful constants */ @@ -341,7 +416,7 @@ int32_t handle; } ADynamicSensorEvent; -typedef struct { +typedef struct AAdditionalInfoEvent { int32_t type; int32_t serial; union { @@ -420,6 +495,7 @@ * - ASensorEventQueue_hasEvents() * - ASensorEventQueue_getEvents() * - ASensorEventQueue_setEventRate() + * - ASensorEventQueue_requestAdditionalInfoEvents() */ typedef struct ASensorEventQueue ASensorEventQueue; @@ -444,6 +520,7 @@ * - ASensor_getStringType() * - ASensor_getReportingMode() * - ASensor_isWakeUpSensor() + * - ASensor_getHandle() */ typedef struct ASensor ASensor; /** @@ -472,13 +549,13 @@ * ASensorManager* sensorManager = ASensorManager_getInstance(); * */ -#if __ANDROID_API__ >= __ANDROID_API_O__ +#if __ANDROID_API__ >= 26 __attribute__ ((deprecated)) ASensorManager* ASensorManager_getInstance(); #else ASensorManager* ASensorManager_getInstance(); #endif -#if __ANDROID_API__ >= __ANDROID_API_O__ +#if __ANDROID_API__ >= 26 /** * Get a reference to the sensor manager. ASensorManager is a singleton * per package as different packages may have access to different sensors. @@ -488,7 +565,7 @@ * ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz"); * */ -ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName); +ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName) __INTRODUCED_IN(26); #endif /** @@ -507,7 +584,7 @@ * Returns the default sensor with the given type and wakeUp properties or NULL if no sensor * of this type and wakeUp properties exists. */ -ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type, bool wakeUp); +ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type, bool wakeUp) __INTRODUCED_IN(21); #endif /** @@ -525,7 +602,7 @@ */ int ASensorManager_destroyEventQueue(ASensorManager* manager, ASensorEventQueue* queue); -#if __ANDROID_API__ >= __ANDROID_API_O__ +#if __ANDROID_API__ >= 26 /** * Create direct channel based on shared memory * @@ -542,7 +619,7 @@ * {@link ASensorManager_destroyDirectChannel} and * {@link ASensorManager_configureDirectReport}, or value less or equal to 0 for failures. */ -int ASensorManager_createSharedMemoryDirectChannel(ASensorManager* manager, int fd, size_t size); +int ASensorManager_createSharedMemoryDirectChannel(ASensorManager* manager, int fd, size_t size) __INTRODUCED_IN(26); /** * Create direct channel based on AHardwareBuffer @@ -560,7 +637,7 @@ * {@link ASensorManager_configureDirectReport}, or value less or equal to 0 for failures. */ int ASensorManager_createHardwareBufferDirectChannel( - ASensorManager* manager, AHardwareBuffer const * buffer, size_t size); + ASensorManager* manager, AHardwareBuffer const * buffer, size_t size) __INTRODUCED_IN(26); /** * Destroy a direct channel @@ -575,7 +652,7 @@ * {@link ASensorManager_createSharedMemoryDirectChannel} or * {@link ASensorManager_createHardwareBufferDirectChannel}. */ -void ASensorManager_destroyDirectChannel(ASensorManager* manager, int channelId); +void ASensorManager_destroyDirectChannel(ASensorManager* manager, int channelId) __INTRODUCED_IN(26); /** * Configure direct report on channel @@ -612,9 +689,9 @@ * * \return positive token for success or negative error code. */ -int ASensorManager_configureDirectReport( - ASensorManager* manager, ASensor const* sensor, int channelId, int rate); -#endif +int ASensorManager_configureDirectReport(ASensorManager* manager, + ASensor const* sensor, int channelId, int rate) __INTRODUCED_IN(26); +#endif /* __ANDROID_API__ >= 26 */ /*****************************************************************************/ @@ -703,6 +780,29 @@ */ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count); +#if __ANDROID_API__ >= __ANDROID_API_Q__ +/** + * Request that {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to be delivered on + * the given {@link ASensorEventQueue}. + * + * Sensor data events are always delivered to the {@ASensorEventQueue}. + * + * The {@link ASENSOR_TYPE_ADDITIONAL_INFO} events will be returned through + * {@link ASensorEventQueue_getEvents}. The client is responsible for checking + * {@link ASensorEvent#type} to determine the event type prior to handling of + * the event. + * + * The client must be tolerant of any value for + * {@link AAdditionalInfoEvent#type}, as new values may be defined in the future + * and may delivered to the client. + * + * \param queue {@link ASensorEventQueue} to configure + * \param enable true to request {@link ASENSOR_TYPE_ADDITIONAL_INFO} events, + * false to stop receiving events + * \return 0 on success or a negative error code on failure + */ +int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable); +#endif /* __ANDROID_API__ >= __ANDRDOID_API_Q__ */ /*****************************************************************************/ @@ -738,30 +838,30 @@ * Returns the maximum size of batches for this sensor. Batches will often be * smaller, as the hardware fifo might be used for other sensors. */ -int ASensor_getFifoMaxEventCount(ASensor const* sensor); +int ASensor_getFifoMaxEventCount(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns the hardware batch fifo size reserved to this sensor. */ -int ASensor_getFifoReservedEventCount(ASensor const* sensor); +int ASensor_getFifoReservedEventCount(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns this sensor's string type. */ -const char* ASensor_getStringType(ASensor const* sensor); +const char* ASensor_getStringType(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns the reporting mode for this sensor. One of AREPORTING_MODE_* constants. */ -int ASensor_getReportingMode(ASensor const* sensor); +int ASensor_getReportingMode(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns true if this is a wake up sensor, false otherwise. */ -bool ASensor_isWakeUpSensor(ASensor const* sensor); +bool ASensor_isWakeUpSensor(ASensor const* sensor) __INTRODUCED_IN(21); #endif /* __ANDROID_API__ >= 21 */ -#if __ANDROID_API__ >= __ANDROID_API_O__ +#if __ANDROID_API__ >= 26 /** * Test if sensor supports a certain type of direct channel. * @@ -771,7 +871,8 @@ * or {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER}. * \returns true if sensor supports the specified direct channel type. */ -bool ASensor_isDirectChannelTypeSupported(ASensor const* sensor, int channelType); +bool ASensor_isDirectChannelTypeSupported(ASensor const* sensor, int channelType) __INTRODUCED_IN(26); + /** * Get the highest direct rate level that a sensor support. * @@ -781,8 +882,26 @@ * If return value is {@link ASENSOR_DIRECT_RATE_STOP}, it means the sensor * does not support direct report. */ -int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor); -#endif +int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor) __INTRODUCED_IN(26); +#endif /* __ANDROID_API__ >= 26 */ + +#if __ANDROID_API__ >= __ANDROID_API_Q__ +/** + * Returns the sensor's handle. + * + * The handle identifies the sensor within the system and is included in the + * {@link ASensorEvent#sensor} field of sensor events, including those sent with type + * {@link ASENSOR_TYPE_ADDITIONAL_INFO}. + * + * A sensor's handle is able to be used to map {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to the + * sensor that generated the event. + * + * It is important to note that the value returned by {@link ASensor_getHandle} is not the same as + * the value returned by the Java API {@link android.hardware.Sensor#getId} and no mapping exists + * between the values. + */ +int ASensor_getHandle(ASensor const* sensor) __INTRODUCED_IN(__ANDROID_API_Q__); +#endif /* __ANDROID_API__ >= ANDROID_API_Q__ */ #ifdef __cplusplus };
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h index 46d2f4b..7f5177b 100644 --- a/include/android/sharedmem.h +++ b/include/android/sharedmem.h
@@ -21,12 +21,14 @@ /** * @file sharedmem.h + * @brief Shared memory buffers that can be shared across process. */ #ifndef ANDROID_SHARED_MEMORY_H #define ANDROID_SHARED_MEMORY_H #include <stddef.h> +#include <sys/cdefs.h> /****************************************************************** * @@ -44,15 +46,11 @@ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ -/** - * Structures and functions for a shared memory buffer that can be shared across process. - */ - #ifdef __cplusplus extern "C" { #endif -#if __ANDROID_API__ >= __ANDROID_API_O__ +#if __ANDROID_API__ >= 26 /** * Create a shared memory region. @@ -63,19 +61,23 @@ * * Use close() to release the shared memory region. * + * Available since API level 26. + * * \param name an optional name. * \param size size of the shared memory region * \return file descriptor that denotes the shared memory; error code on failure. */ -int ASharedMemory_create(const char *name, size_t size); +int ASharedMemory_create(const char *name, size_t size) __INTRODUCED_IN(26); /** * Get the size of the shared memory region. * + * Available since API level 26. + * * \param fd file descriptor of the shared memory region * \return size in bytes; 0 if fd is not a valid shared memory file descriptor. */ -size_t ASharedMemory_getSize(int fd); +size_t ASharedMemory_getSize(int fd) __INTRODUCED_IN(26); /** * Restrict access of shared memory region. @@ -92,7 +94,8 @@ * int fd = ASharedMemory_create("memory", 128); * * // By default it has PROT_READ | PROT_WRITE | PROT_EXEC. - * char *buffer = (char *) mmap(NULL, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + * size_t memSize = ASharedMemory_getSize(fd); + * char *buffer = (char *) mmap(NULL, memSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); * * strcpy(buffer, "This is an example."); // trivially initialize content * @@ -101,14 +104,16 @@ * * // share fd with another process here and the other process can only map with PROT_READ. * + * Available since API level 26. + * * \param fd file descriptor of the shared memory region. * \param prot any bitwise-or'ed combination of PROT_READ, PROT_WRITE, PROT_EXEC denoting * updated access. Note access can only be removed, but not added back. * \return 0 for success, error code on failure. */ -int ASharedMemory_setProt(int fd, int prot); +int ASharedMemory_setProt(int fd, int prot) __INTRODUCED_IN(26); -#endif +#endif // __ANDROID_API__ >= 26 #ifdef __cplusplus };
diff --git a/include/android/sharedmem_jni.h b/include/android/sharedmem_jni.h index 85ac78f..13e56e6 100644 --- a/include/android/sharedmem_jni.h +++ b/include/android/sharedmem_jni.h
@@ -21,6 +21,7 @@ /** * @file sharedmem_jni.h + * @brief Shared memory buffers that can be shared across process. */ #ifndef ANDROID_SHARED_MEMORY_JNI_H @@ -29,6 +30,7 @@ #include <jni.h> #include <android/sharedmem.h> #include <stddef.h> +#include <sys/cdefs.h> /****************************************************************** * @@ -46,15 +48,11 @@ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ -/** - * Structures and functions for a shared memory buffer that can be shared across process. - */ - #ifdef __cplusplus extern "C" { #endif -#if __ANDROID_API__ >= __ANDROID_API_O_MR1__ +#if __ANDROID_API__ >= 27 /** * Returns a dup'd FD from the given Java android.os.SharedMemory object. The returned file @@ -64,15 +62,17 @@ * * Use close() to release the shared memory region. * + * Available since API level 27. + * * \param env The JNIEnv* pointer * \param sharedMemory The Java android.os.SharedMemory object * \return file descriptor that denotes the shared memory; -1 if the shared memory object is * already closed, if the JNIEnv or jobject is NULL, or if there are too many open file * descriptors (errno=EMFILE) */ -int ASharedMemory_dupFromJava(JNIEnv* env, jobject sharedMemory); +int ASharedMemory_dupFromJava(JNIEnv* env, jobject sharedMemory) __INTRODUCED_IN(27); -#endif +#endif // __ANDROID_API__ >= 27 #ifdef __cplusplus };
diff --git a/include/android/surface_control.h b/include/android/surface_control.h new file mode 100644 index 0000000..ef2ad99 --- /dev/null +++ b/include/android/surface_control.h
@@ -0,0 +1,355 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup NativeActivity Native Activity + * @{ + */ + +/** + * @file surface_control.h + */ + +#ifndef ANDROID_SURFACE_CONTROL_H +#define ANDROID_SURFACE_CONTROL_H + +#include <sys/cdefs.h> + +#include <android/data_space.h> +#include <android/hardware_buffer.h> +#include <android/hdr_metadata.h> +#include <android/native_window.h> + +__BEGIN_DECLS + +#if __ANDROID_API__ >= 29 + +struct ASurfaceControl; + +/** + * The SurfaceControl API can be used to provide a hierarchy of surfaces for + * composition to the system compositor. ASurfaceControl represents a content node in + * this hierarchy. + */ +typedef struct ASurfaceControl ASurfaceControl; + +/* + * Creates an ASurfaceControl with either ANativeWindow or an ASurfaceControl as its parent. + * |debug_name| is a debug name associated with this surface. It can be used to + * identify this surface in the SurfaceFlinger's layer tree. It must not be + * null. + * + * The caller takes ownership of the ASurfaceControl returned and must release it + * using ASurfaceControl_release below. + */ +ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* parent, const char* debug_name) + __INTRODUCED_IN(29); + +ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name) + __INTRODUCED_IN(29); + +/** + * Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer + * has ownership of the AsurfaceControl. The surface and it's children may remain on display as long + * as their parent remains on display. + */ +void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29); + +struct ASurfaceTransaction; + +/** + * ASurfaceTransaction is a collection of updates to the surface tree that must + * be applied atomically. + */ +typedef struct ASurfaceTransaction ASurfaceTransaction; + +/** + * The caller takes ownership of the transaction and must release it using + * ASurfaceControl_delete below. + */ +ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29); + +/** + * Destroys the |transaction| object. + */ +void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29); + +/** + * Applies the updates accumulated in |transaction|. + * + * Note that the transaction is guaranteed to be applied atomically. The + * transactions which are applied on the same thread are also guaranteed to be + * applied in order. + */ +void ASurfaceTransaction_apply(ASurfaceTransaction* transaction) __INTRODUCED_IN(29); + +/** + * An opaque handle returned during a callback that can be used to query general stats and stats for + * surfaces which were either removed or for which buffers were updated after this transaction was + * applied. + */ +typedef struct ASurfaceTransactionStats ASurfaceTransactionStats; + +/** + * Since the transactions are applied asynchronously, the + * ASurfaceTransaction_OnComplete callback can be used to be notified when a frame + * including the updates in a transaction was presented. + * + * |context| is the optional context provided by the client that is passed into + * the callback. + * + * |stats| is an opaque handle that can be passed to ASurfaceTransactionStats functions to query + * information about the transaction. The handle is only valid during the callback. + * + * THREADING + * The transaction completed callback can be invoked on any thread. + */ +typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats) + __INTRODUCED_IN(29); + +/** + * Returns the timestamp of when the frame was latched by the framework. Once a frame is + * latched by the framework, it is presented at the following hardware vsync. + */ +int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_transaction_stats) + __INTRODUCED_IN(29); + +/** + * Returns a sync fence that signals when the transaction has been presented. + * The recipient of the callback takes ownership of the fence and is responsible for closing + * it. + */ +int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats) + __INTRODUCED_IN(29); + +/** + * |outASurfaceControls| returns an array of ASurfaceControl pointers that were updated during the + * transaction. Stats for the surfaces can be queried through ASurfaceTransactionStats functions. + * When the client is done using the array, it must release it by calling + * ASurfaceTransactionStats_releaseASurfaceControls. + * + * |outASurfaceControlsSize| returns the size of the ASurfaceControls array. + */ +void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats, + ASurfaceControl*** outASurfaceControls, + size_t* outASurfaceControlsSize) + __INTRODUCED_IN(29); +/** + * Releases the array of ASurfaceControls that were returned by + * ASurfaceTransactionStats_getASurfaceControls. + */ +void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_controls) + __INTRODUCED_IN(29); + +/** + * Returns the timestamp of when the CURRENT buffer was acquired. A buffer is considered + * acquired when its acquire_fence_fd has signaled. A buffer cannot be latched or presented until + * it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1. + */ +int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats, + ASurfaceControl* surface_control) + __INTRODUCED_IN(29); + +/** + * The returns the fence used to signal the release of the PREVIOUS buffer set on + * this surface. If this fence is valid (>=0), the PREVIOUS buffer has not yet been released and the + * fence will signal when the PREVIOUS buffer has been released. If the fence is -1 , the PREVIOUS + * buffer is already released. The recipient of the callback takes ownership of the + * previousReleaseFenceFd and is responsible for closing it. + * + * Each time a buffer is set through ASurfaceTransaction_setBuffer()/_setCachedBuffer() on a + * transaction which is applied, the framework takes a ref on this buffer. The framework treats the + * addition of a buffer to a particular surface as a unique ref. When a transaction updates or + * removes a buffer from a surface, or removes the surface itself from the tree, this ref is + * guaranteed to be released in the OnComplete callback for this transaction. The + * ASurfaceControlStats provided in the callback for this surface may contain an optional fence + * which must be signaled before the ref is assumed to be released. + * + * The client must ensure that all pending refs on a buffer are released before attempting to reuse + * this buffer, otherwise synchronization errors may occur. + */ +int ASurfaceTransactionStats_getPreviousReleaseFenceFd( + ASurfaceTransactionStats* surface_transaction_stats, + ASurfaceControl* surface_control) + __INTRODUCED_IN(29); + +/** + * Sets the callback that will be invoked when the updates from this transaction + * are presented. For details on the callback semantics and data, see the + * comments on the ASurfaceTransaction_OnComplete declaration above. + */ +void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* context, + ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29); + +/** + * Reparents the |surface_control| from its old parent to the |new_parent| surface control. + * Any children of the* reparented |surface_control| will remain children of the |surface_control|. + * + * The |new_parent| can be null. Surface controls with a null parent do not appear on the display. + */ +void ASurfaceTransaction_reparent(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, ASurfaceControl* new_parent) + __INTRODUCED_IN(29); + +/* Parameter for ASurfaceTransaction_setVisibility */ +enum { + ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0, + ASURFACE_TRANSACTION_VISIBILITY_SHOW = 1, +}; +/** + * Updates the visibility of |surface_control|. If show is set to + * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the |surface_control| and all surfaces in its subtree will + * be hidden. + */ +void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, int8_t visibility) + __INTRODUCED_IN(29); + +/** + * Updates the z order index for |surface_control|. Note that the z order for a surface + * is relative to other surfaces which are siblings of this surface. The behavior of sibilings with + * the same z order is undefined. + * + * Z orders may be from MIN_INT32 to MAX_INT32. A layer's default z order index is 0. + */ +void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, int32_t z_order) + __INTRODUCED_IN(29); + +/** + * Updates the AHardwareBuffer displayed for |surface_control|. If not -1, the + * acquire_fence_fd should be a file descriptor that is signaled when all pending work + * for the buffer is complete and the buffer can be safely read. + * + * The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible + * for closing it. + */ +void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, AHardwareBuffer* buffer, + int acquire_fence_fd = -1) __INTRODUCED_IN(29); + +/** + * Updates the color for |surface_control|. This will make the background color for the + * ASurfaceControl visible in transparent regions of the surface. Colors |r|, |g|, + * and |b| must be within the range that is valid for |dataspace|. |dataspace| and |alpha| + * will be the dataspace and alpha set for the background color layer. + */ +void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, float r, float g, float b, + float alpha, ADataSpace dataspace) + __INTRODUCED_IN(29); + +/** + * |source| the sub-rect within the buffer's content to be rendered inside the surface's area + * The surface's source rect is clipped by the bounds of its current buffer. The source rect's width + * and height must be > 0. + * + * |destination| specifies the rect in the parent's space where this surface will be drawn. The post + * source rect bounds are scaled to fit the destination rect. The surface's destination rect is + * clipped by the bounds of its parent. The destination rect's width and height must be > 0. + * + * |transform| the transform applied after the source rect is applied to the buffer. This parameter + * should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_* + * enum. + */ +void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, const ARect& source, + const ARect& destination, int32_t transform) + __INTRODUCED_IN(29); + + +/* Parameter for ASurfaceTransaction_setBufferTransparency */ +enum { + ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT = 0, + ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT = 1, + ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE = 2, +}; +/** + * Updates whether the content for the buffer associated with this surface is + * completely opaque. If true, every pixel of content inside the buffer must be + * opaque or visual errors can occur. + */ +void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, + int8_t transparency) + __INTRODUCED_IN(29); + +/** + * Updates the region for the content on this surface updated in this + * transaction. If unspecified, the complete surface is assumed to be damaged. + */ +void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, const ARect rects[], + uint32_t count) __INTRODUCED_IN(29); + +/** + * Specifies a desiredPresentTime for the transaction. The framework will try to present + * the transaction at or after the time specified. + * + * Transactions will not be presented until all of their acquire fences have signaled even if the + * app requests an earlier present time. + * + * If an earlier transaction has a desired present time of x, and a later transaction has a desired + * present time that is before x, the later transaction will not preempt the earlier transaction. + */ +void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction, + int64_t desiredPresentTime) __INTRODUCED_IN(29); + +/** + * Sets the alpha for the buffer. It uses a premultiplied blending. + * + * The |alpha| must be between 0.0 and 1.0. + */ +void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, float alpha) + __INTRODUCED_IN(29); + +/** + * Sets the data space of the surface_control's buffers. + * + * If no data space is set, the surface control defaults to ADATASPACE_SRGB. + */ +void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, ADataSpace data_space) + __INTRODUCED_IN(29); + +/* + * SMPTE ST 2086 "Mastering Display Color Volume" static metadata + * + * When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering + * the surface's buffer. + */ +void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, + struct AHdrMetadata_smpte2086* metadata) + __INTRODUCED_IN(29); + +/* + * Sets the CTA 861.3 "HDR Static Metadata Extension" static metadata on a surface. + * + * When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering + * the surface's buffer. + */ +void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transaction, + ASurfaceControl* surface_control, + struct AHdrMetadata_cta861_3* metadata) + __INTRODUCED_IN(29); + +#endif // __ANDROID_API__ >= 29 + +__END_DECLS + +#endif // ANDROID_SURFACE_CONTROL_H
diff --git a/include/android/surface_texture.h b/include/android/surface_texture.h index 56b3342..540d23a 100644 --- a/include/android/surface_texture.h +++ b/include/android/surface_texture.h
@@ -43,6 +43,7 @@ */ #include <stdint.h> +#include <sys/cdefs.h> #include <android/native_window.h> @@ -58,13 +59,15 @@ */ typedef struct ASurfaceTexture ASurfaceTexture; +#if __ANDROID_API__ >= 28 + /** * Release the reference to the native ASurfaceTexture acquired with * ASurfaceTexture_fromSurfaceTexture(). * Failing to do so will result in leaked memory and graphic resources. * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() */ -void ASurfaceTexture_release(ASurfaceTexture* st); +void ASurfaceTexture_release(ASurfaceTexture* st) __INTRODUCED_IN(28); /** * Returns a reference to an ANativeWindow (i.e. the Producer) for this SurfaceTexture. @@ -73,9 +76,9 @@ * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * @return A reference to an ANativeWindow. This reference MUST BE released when no longer needed * using ANativeWindow_release(). Failing to do so will result in leaked resources. nullptr is - * returned if \st is null or if it's not an instance of android.graphics.SurfaceTexture + * returned if \p st is null or if it's not an instance of android.graphics.SurfaceTexture */ -ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st); +ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st) __INTRODUCED_IN(28); /** * Attach the SurfaceTexture to the OpenGL ES context that is current on the calling thread. A @@ -92,7 +95,7 @@ * must be unusued in the OpenGL ES context that is current on the calling thread. * \return 0 on success, negative posix error code otherwise (see <errno.h>) */ -int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t texName); +int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t texName) __INTRODUCED_IN(28); /** * Detach the SurfaceTexture from the OpenGL ES context that owns the OpenGL ES texture object. @@ -108,7 +111,7 @@ * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \return 0 on success, negative posix error code otherwise (see <errno.h>) */ -int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st); +int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st) __INTRODUCED_IN(28); /** * Update the texture image to the most recent frame from the image stream. This may only be @@ -118,7 +121,7 @@ * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \return 0 on success, negative posix error code otherwise (see <errno.h>) */ -int ASurfaceTexture_updateTexImage(ASurfaceTexture* st); +int ASurfaceTexture_updateTexImage(ASurfaceTexture* st) __INTRODUCED_IN(28); /** * Retrieve the 4x4 texture coordinate transform matrix associated with the texture image set by @@ -136,7 +139,7 @@ * \param mtx the array into which the 4x4 matrix will be stored. The array must have exactly * 16 elements. */ -void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]); +void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]) __INTRODUCED_IN(28); /** * Retrieve the timestamp associated with the texture image set by the most recent call to @@ -155,7 +158,9 @@ * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() */ -int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st); +int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) __INTRODUCED_IN(28); + +#endif /* __ANDROID_API__ >= 28 */ __END_DECLS
diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h new file mode 100644 index 0000000..dde9055 --- /dev/null +++ b/include/android/system_fonts.h
@@ -0,0 +1,131 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup Font + * { + */ + +/** + * @file system_fonts.h + * @brief Provides the system font configurations. + * + * These APIs provides the list of system installed font files with additional metadata about the + * font. + * + * The ASystemFontIterator_open method will give you an iterator which can iterate all system + * installed font files as shown in the following example. + * + * \code{.cpp} + * ASystemFontIterator* iterator = ASystemFontIterator_open(); + * ASystemFont* font = NULL; + * + * while ((font = ASystemFontIterator_next(iterator)) != nullptr) { + * // Look if the font is your desired one. + * if (ASystemFont_getWeight(font) == 400 && !ASystemFont_isItalic(font) + * && ASystemFont_getLocale(font) == NULL) { + * break; + * } + * ASystemFont_close(font); + * } + * ASystemFontIterator_close(iterator); + * + * int fd = open(ASystemFont_getFontFilePath(font), O_RDONLY); + * int collectionIndex = ASystemFont_getCollectionINdex(font); + * std::vector<std::pair<uint32_t, float>> variationSettings; + * for (size_t i = 0; i < ASystemFont_getAxisCount(font); ++i) { + * variationSettings.push_back(std::make_pair( + * ASystemFont_getAxisTag(font, i), + * ASystemFont_getAxisValue(font, i))); + * } + * ASystemFont_close(font); + * + * // Use this font for your text rendering engine. + * + * \endcode + * + * Available since API level 29. + */ + +#ifndef ANDROID_SYSTEM_FONTS_H +#define ANDROID_SYSTEM_FONTS_H + +#include <stdbool.h> +#include <stddef.h> +#include <sys/cdefs.h> + +#include <android/font.h> + +/****************************************************************** + * + * IMPORTANT NOTICE: + * + * This file is part of Android's set of stable system headers + * exposed by the Android NDK (Native Development Kit). + * + * Third-party source AND binary code relies on the definitions + * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. + * + * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) + * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS + * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY + * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES + */ + +__BEGIN_DECLS + +#if __ANDROID_API__ >= 29 + +/** + * ASystemFontIterator provides access to the system font configuration. + * + * ASystemFontIterator is an iterator for all available system font settings. + * This iterator is not a thread-safe object. Do not pass this iterator to other threads. + */ +struct ASystemFontIterator; + +/** + * Create a system font iterator. + * + * Use ASystemFont_close() to close the iterator. + * + * \return a pointer for a newly allocated iterator, nullptr on failure. + */ +ASystemFontIterator* _Nullable ASystemFontIterator_open() __INTRODUCED_IN(29); + +/** + * Close an opened system font iterator, freeing any related resources. + * + * \param iterator a pointer of an iterator for the system fonts. Do nothing if NULL is passed. + */ +void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTRODUCED_IN(29); + +/** + * Move to the next system font. + * + * \param iterator an iterator for the system fonts. Passing NULL is not allowed. + * \return a font. If no more font is available, returns nullptr. You need to release the returned + * font by ASystemFont_close when it is no longer needed. + */ +AFont* _Nullable ASystemFontIterator_next(ASystemFontIterator* _Nonnull iterator) __INTRODUCED_IN(29); + +#endif // __ANDROID_API__ >= 29 + +__END_DECLS + +#endif // ANDROID_SYSTEM_FONTS_H + +/** @} */
diff --git a/include/android/trace.h b/include/android/trace.h index d3b1fb6..bb7ff28 100644 --- a/include/android/trace.h +++ b/include/android/trace.h
@@ -15,17 +15,25 @@ */ /** + * @addtogroup Tracing + * @{ + */ + +/** * @file trace.h * @brief Writes trace events to the system trace buffer. * * These trace events can be collected and visualized using the Systrace tool. * For information about using the Systrace tool, read <a href="https://developer.android.com/studio/profile/systrace.html">Analyzing UI Performance with Systrace</a>. + * + * Available since API level 23. */ #ifndef ANDROID_NATIVE_TRACE_H #define ANDROID_NATIVE_TRACE_H #include <stdbool.h> +#include <stdint.h> #include <sys/cdefs.h> #ifdef __cplusplus @@ -35,33 +43,75 @@ #if __ANDROID_API__ >= 23 /** - * Returns true if tracing is enabled. Use this signal to avoid expensive computation only necessary + * Returns true if tracing is enabled. Use this to avoid expensive computation only necessary * when tracing is enabled. + * + * Available since API level 23. */ -bool ATrace_isEnabled(); +bool ATrace_isEnabled() __INTRODUCED_IN(23); /** * Writes a tracing message to indicate that the given section of code has begun. This call must be - * followed by a corresponding call to endSection() on the same thread. + * followed by a corresponding call to {@link ATrace_endSection} on the same thread. * - * Note: At this time the vertical bar character '|' and newline character '\n' are used internally - * by the tracing mechanism. If sectionName contains these characters they will be replaced with a + * Note: At this time the vertical bar character '|' and newline character '\\n' are used internally + * by the tracing mechanism. If \p sectionName contains these characters they will be replaced with a * space character in the trace. + * + * Available since API level 23. */ -void ATrace_beginSection(const char* sectionName); +void ATrace_beginSection(const char* sectionName) __INTRODUCED_IN(23); /** * Writes a tracing message to indicate that a given section of code has ended. This call must be - * preceeded by a corresponding call to beginSection(char*) on the same thread. Calling this method + * preceeded by a corresponding call to {@link ATrace_beginSection} on the same thread. Calling this method * will mark the end of the most recently begun section of code, so care must be taken to ensure - * that beginSection / endSection pairs are properly nested and called from the same thread. + * that {@link ATrace_beginSection}/{@link ATrace_endSection} pairs are properly nested and called from the same thread. + * + * Available since API level 23. */ -void ATrace_endSection(); +void ATrace_endSection() __INTRODUCED_IN(23); #endif /* __ANDROID_API__ >= 23 */ +#if __ANDROID_API__ >= __ANDROID_API_Q__ + +/** + * Writes a trace message to indicate that a given section of code has + * begun. Must be followed by a call to {@link ATrace_endAsyncSection} with the same + * methodName and cookie. Unlike {@link ATrace_beginSection} and {@link ATrace_endSection}, + * asynchronous events do not need to be nested. The name and cookie used to + * begin an event must be used to end it. + * + * \param sectionName The method name to appear in the trace. + * \param cookie Unique identifier for distinguishing simultaneous events + */ +void ATrace_beginAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCED_IN(29); + +/** + * Writes a trace message to indicate that the current method has ended. + * Must be called exactly once for each call to {@link ATrace_beginAsyncSection} + * using the same name and cookie. + * + * \param methodName The method name to appear in the trace. + * \param cookie Unique identifier for distinguishing simultaneous events + */ +void ATrace_endAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCED_IN(29); + +/** + * Writes trace message to indicate the value of a given counter. + * + * \param counterName The counter name to appear in the trace. + * \param counterValue The counter value. + */ +void ATrace_setCounter(const char* counterName, int64_t counterValue) __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ + #ifdef __cplusplus }; #endif #endif // ANDROID_NATIVE_TRACE_H + +/** @} */
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h index 009dc52..639df7a 100644 --- a/include/audiomanager/AudioManager.h +++ b/include/audiomanager/AudioManager.h
@@ -20,7 +20,6 @@ namespace android { // must be kept in sync with definitions in AudioPlaybackConfiguration.java - #define PLAYER_PIID_INVALID -1 typedef enum { @@ -40,6 +39,15 @@ PLAYER_STATE_STOPPED = 4, } player_state_t; +// must be kept in sync with definitions in AudioManager.java +#define RECORD_RIID_INVALID -1 + +typedef enum { + RECORDER_STATE_UNKNOWN = -1, + RECORDER_STATE_STARTED = 0, + RECORDER_STATE_STOPPED = 1, +} recorder_state_t; + }; // namespace android #endif // ANDROID_AUDIOMANAGER_H
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h index d279bbd..2f5ccb8 100644 --- a/include/audiomanager/IAudioManager.h +++ b/include/audiomanager/IAudioManager.h
@@ -36,6 +36,9 @@ PLAYER_ATTRIBUTES = IBinder::FIRST_CALL_TRANSACTION + 1, PLAYER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 2, RELEASE_PLAYER = IBinder::FIRST_CALL_TRANSACTION + 3, + TRACK_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 4, + RECORDER_EVENT = IBinder::FIRST_CALL_TRANSACTION + 5, + RELEASE_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 6, }; DECLARE_META_INTERFACE(AudioManager) @@ -48,6 +51,9 @@ audio_content_type_t content)= 0; /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) = 0; /*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0; + virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0; + /*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0; + /*oneway*/ virtual status_t releaseRecorder(audio_unique_id_t riid) = 0; }; // ----------------------------------------------------------------------------
diff --git a/include/batteryservice b/include/batteryservice deleted file mode 120000 index 2178c32..0000000 --- a/include/batteryservice +++ /dev/null
@@ -1 +0,0 @@ -../services/batteryservice/include/batteryservice/ \ No newline at end of file
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index 86da4d3..fa456bb 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h
@@ -17,11 +17,39 @@ #ifndef _LIBINPUT_DISPLAY_VIEWPORT_H #define _LIBINPUT_DISPLAY_VIEWPORT_H +#include <android-base/stringprintf.h> #include <ui/DisplayInfo.h> #include <input/Input.h> +#include <inttypes.h> +#include <optional> + +using android::base::StringPrintf; namespace android { +/** + * Describes the different type of viewports supported by input flinger. + * Keep in sync with values in InputManagerService.java. + */ +enum class ViewportType : int32_t { + VIEWPORT_INTERNAL = 1, + VIEWPORT_EXTERNAL = 2, + VIEWPORT_VIRTUAL = 3, +}; + +static const char* viewportTypeToString(ViewportType type) { + switch(type) { + case ViewportType::VIEWPORT_INTERNAL: + return "INTERNAL"; + case ViewportType::VIEWPORT_EXTERNAL: + return "EXTERNAL"; + case ViewportType::VIEWPORT_VIRTUAL: + return "VIRTUAL"; + default: + return "UNKNOWN"; + } +} + /* * Describes how coordinates are mapped on a physical display. * See com.android.server.display.DisplayViewport. @@ -39,13 +67,18 @@ int32_t physicalBottom; int32_t deviceWidth; int32_t deviceHeight; - String8 uniqueId; + std::string uniqueId; + // The actual (hardware) port that the associated display is connected to. + // Not all viewports will have this specified. + std::optional<uint8_t> physicalPort; + ViewportType type; DisplayViewport() : displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0), logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0), physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0), - deviceWidth(0), deviceHeight(0) { + deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt), + type(ViewportType::VIEWPORT_INTERNAL) { } bool operator==(const DisplayViewport& other) const { @@ -61,7 +94,9 @@ && physicalBottom == other.physicalBottom && deviceWidth == other.deviceWidth && deviceHeight == other.deviceHeight - && uniqueId == other.uniqueId; + && uniqueId == other.uniqueId + && physicalPort == other.physicalPort + && type == other.type; } bool operator!=(const DisplayViewport& other) const { @@ -86,17 +121,25 @@ deviceWidth = width; deviceHeight = height; uniqueId.clear(); + physicalPort = std::nullopt; + type = ViewportType::VIEWPORT_INTERNAL; } -}; -/** - * Describes the different type of viewports supported by input flinger. - * Keep in sync with values in InputManagerService.java. - */ -enum class ViewportType : int32_t { - VIEWPORT_INTERNAL = 1, - VIEWPORT_EXTERNAL = 2, - VIEWPORT_VIRTUAL = 3, + std::string toString() const { + return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, " + "logicalFrame=[%d, %d, %d, %d], " + "physicalFrame=[%d, %d, %d, %d], " + "deviceSize=[%d, %d]", + viewportTypeToString(type), displayId, + uniqueId.c_str(), + physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>", + orientation, + logicalLeft, logicalTop, + logicalRight, logicalBottom, + physicalLeft, physicalTop, + physicalRight, physicalBottom, + deviceWidth, deviceHeight); + } }; } // namespace android
diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h index 11bb721..4365a3c 100644 --- a/include/input/IInputFlinger.h +++ b/include/input/IInputFlinger.h
@@ -22,6 +22,9 @@ #include <binder/IInterface.h> +#include <input/InputWindow.h> +#include <input/ISetInputWindowsListener.h> + namespace android { /* @@ -31,6 +34,12 @@ class IInputFlinger : public IInterface { public: DECLARE_META_INTERFACE(InputFlinger) + + virtual void setInputWindows(const std::vector<InputWindowInfo>& inputHandles, + const sp<ISetInputWindowsListener>& setInputWindowsListener) = 0; + virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0; + virtual void registerInputChannel(const sp<InputChannel>& channel) = 0; + virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0; }; @@ -40,7 +49,10 @@ class BnInputFlinger : public BnInterface<IInputFlinger> { public: enum { - DO_SOMETHING_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + REGISTER_INPUT_CHANNEL_TRANSACTION, + UNREGISTER_INPUT_CHANNEL_TRANSACTION, + TRANSFER_TOUCH_FOCUS }; virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/include/input/ISetInputWindowsListener.h b/include/input/ISetInputWindowsListener.h new file mode 100644 index 0000000..15d31b2 --- /dev/null +++ b/include/input/ISetInputWindowsListener.h
@@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +class ISetInputWindowsListener : public IInterface { +public: + DECLARE_META_INTERFACE(SetInputWindowsListener) + virtual void onSetInputWindowsFinished() = 0; +}; + +class BnSetInputWindowsListener: public BnInterface<ISetInputWindowsListener> { +public: + enum SetInputWindowsTag : uint32_t { + ON_SET_INPUT_WINDOWS_FINISHED + }; + + virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0) override; +}; + +}; // namespace android
diff --git a/include/input/Input.h b/include/input/Input.h index cfcafab..805957a 100644 --- a/include/input/Input.h +++ b/include/input/Input.h
@@ -17,6 +17,8 @@ #ifndef _LIBINPUT_INPUT_H #define _LIBINPUT_INPUT_H +#pragma GCC system_header + /** * Native input event structures. */ @@ -25,7 +27,6 @@ #include <utils/BitSet.h> #include <utils/KeyedVector.h> #include <utils/RefBase.h> -#include <utils/String8.h> #include <utils/Timers.h> #include <utils/Vector.h> #include <stdint.h> @@ -58,6 +59,12 @@ */ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2, + /** + * This flag indicates that the event has been generated by a gesture generator. It + * provides a hint to the GestureDetector to not apply any touch slop. + */ + AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8, + /* Motion event is inconsistent with previously sent motion events. */ AMOTION_EVENT_FLAG_TAINTED = 0x80000000, }; @@ -213,6 +220,32 @@ POLICY_FLAG_PASS_TO_USER = 0x40000000, }; +/** + * Classifications of the current gesture, if available. + * + * The following values must be kept in sync with MotionEvent.java + */ +enum class MotionClassification : uint8_t { + /** + * No classification is available. + */ + NONE = 0, + /** + * Too early to classify the current gesture. Need more events. Look for changes in the + * upcoming motion events. + */ + AMBIGUOUS_GESTURE = 1, + /** + * The current gesture likely represents a user intentionally exerting force on the touchscreen. + */ + DEEP_PRESS = 2, +}; + +/** + * String representation of MotionClassification + */ +const char* motionClassificationToString(MotionClassification classification); + /* * Pointer coordinate data. */ @@ -237,7 +270,12 @@ float getAxisValue(int32_t axis) const; status_t setAxisValue(int32_t axis, float value); - void scale(float scale); + void scale(float globalScale); + + // Scale the pointer coordinates according to a global scale and a + // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR + // axes, however the window scaling will not. + void scale(float globalScale, float windowXScale, float windowYScale); void applyOffset(float xOffset, float yOffset); inline float getX() const { @@ -302,12 +340,18 @@ inline void setSource(int32_t source) { mSource = source; } + inline int32_t getDisplayId() const { return mDisplayId; } + + inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; } + + protected: - void initialize(int32_t deviceId, int32_t source); + void initialize(int32_t deviceId, int32_t source, int32_t displayId); void initialize(const InputEvent& from); int32_t mDeviceId; int32_t mSource; + int32_t mDisplayId; }; /* @@ -339,10 +383,11 @@ static const char* getLabel(int32_t keyCode); static int32_t getKeyCodeFromLabel(const char* label); - + void initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -400,6 +445,8 @@ inline void setButtonState(int32_t buttonState) { mButtonState = buttonState; } + inline MotionClassification getClassification() const { return mClassification; } + inline int32_t getActionButton() const { return mActionButton; } inline void setActionButton(int32_t button) { mActionButton = button; } @@ -556,12 +603,14 @@ void initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xOffset, float yOffset, float xPrecision, @@ -580,7 +629,7 @@ void offsetLocation(float xOffset, float yOffset); - void scale(float scaleFactor); + void scale(float globalScaleFactor); // Apply 3x3 perspective matrix transformation. // Matrix is in row-major form and compatible with SkMatrix. @@ -615,6 +664,7 @@ int32_t mEdgeFlags; int32_t mMetaState; int32_t mButtonState; + MotionClassification mClassification; float mXOffset; float mYOffset; float mXPrecision; @@ -661,7 +711,7 @@ */ class PooledInputEventFactory : public InputEventFactoryInterface { public: - PooledInputEventFactory(size_t maxPoolSize = 20); + explicit PooledInputEventFactory(size_t maxPoolSize = 20); virtual ~PooledInputEventFactory(); virtual KeyEvent* createKeyEvent();
diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h new file mode 100644 index 0000000..7f04611 --- /dev/null +++ b/include/input/InputApplication.h
@@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011 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 _UI_INPUT_APPLICATION_H +#define _UI_INPUT_APPLICATION_H + +#include <string> + +#include <binder/IBinder.h> +#include <binder/Parcel.h> + +#include <input/Input.h> +#include <utils/RefBase.h> +#include <utils/Timers.h> + +namespace android { + +/* + * Describes the properties of an application that can receive input. + */ +struct InputApplicationInfo { + sp<IBinder> token; + std::string name; + nsecs_t dispatchingTimeout; + + status_t write(Parcel& output) const; + static InputApplicationInfo read(const Parcel& from); +}; + + +/* + * Handle for an application that can receive input. + * + * Used by the native input dispatcher as a handle for the window manager objects + * that describe an application. + */ +class InputApplicationHandle : public RefBase { +public: + inline const InputApplicationInfo* getInfo() const { + return &mInfo; + } + + inline std::string getName() const { + return !mInfo.name.empty() ? mInfo.name : "<invalid>"; + } + + inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const { + return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; + } + + inline sp<IBinder> getApplicationToken() const { + return mInfo.token; + } + + /** + * Requests that the state of this object be updated to reflect + * the most current available information about the application. + * + * This method should only be called from within the input dispatcher's + * critical section. + * + * Returns true on success, or false if the handle is no longer valid. + */ + virtual bool updateInfo() = 0; +protected: + InputApplicationHandle(); + virtual ~InputApplicationHandle(); + + InputApplicationInfo mInfo; +}; + +} // namespace android + +#endif // _UI_INPUT_APPLICATION_H
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 1ea69d3..b6efc82 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h
@@ -19,6 +19,7 @@ #include <input/Input.h> #include <input/KeyCharacterMap.h> +#include <vector> namespace android { @@ -31,9 +32,9 @@ } // Information provided by the kernel. - String8 name; - String8 location; - String8 uniqueId; + std::string name; + std::string location; + std::string uniqueId; uint16_t bus; uint16_t vendor; uint16_t product; @@ -45,12 +46,21 @@ // It is hashed from whatever kernel provided information is available. // Ideally, the way this value is computed should not change between Android releases // because that would invalidate persistent settings that rely on it. - String8 descriptor; + std::string descriptor; // A value added to uniquely identify a device in the absence of a unique id. This // is intended to be a minimum way to distinguish from other active devices and may // reuse values that are not associated with an input anymore. uint16_t nonce; + + /** + * Return InputDeviceIdentifier.name that has been adjusted as follows: + * - all characters besides alphanumerics, dash, + * and underscore have been replaced with underscores. + * This helps in situations where a file that matches the device name is needed, + * while conforming to the filename limitations. + */ + std::string getCanonicalName() const; }; /* @@ -73,16 +83,16 @@ }; void initialize(int32_t id, int32_t generation, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal, + const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal, bool hasMic); inline int32_t getId() const { return mId; } inline int32_t getControllerNumber() const { return mControllerNumber; } inline int32_t getGeneration() const { return mGeneration; } inline const InputDeviceIdentifier& getIdentifier() const { return mIdentifier; } - inline const String8& getAlias() const { return mAlias; } - inline const String8& getDisplayName() const { - return mAlias.isEmpty() ? mIdentifier.name : mAlias; + inline const std::string& getAlias() const { return mAlias; } + inline const std::string& getDisplayName() const { + return mAlias.empty() ? mIdentifier.name : mAlias; } inline bool isExternal() const { return mIsExternal; } inline bool hasMic() const { return mHasMic; } @@ -112,7 +122,7 @@ inline void setButtonUnderPad(bool hasButton) { mHasButtonUnderPad = hasButton; } inline bool hasButtonUnderPad() const { return mHasButtonUnderPad; } - inline const Vector<MotionRange>& getMotionRanges() const { + inline const std::vector<MotionRange>& getMotionRanges() const { return mMotionRanges; } @@ -121,7 +131,7 @@ int32_t mGeneration; int32_t mControllerNumber; InputDeviceIdentifier mIdentifier; - String8 mAlias; + std::string mAlias; bool mIsExternal; bool mHasMic; uint32_t mSources; @@ -130,7 +140,7 @@ bool mHasVibrator; bool mHasButtonUnderPad; - Vector<MotionRange> mMotionRanges; + std::vector<MotionRange> mMotionRanges; }; /* Types of input device configuration files. */ @@ -149,7 +159,7 @@ * * Returns an empty string if not found. */ -extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( +extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type); @@ -162,8 +172,15 @@ * * Returns an empty string if not found. */ -extern String8 getInputDeviceConfigurationFilePathByName( - const String8& name, InputDeviceConfigurationFileType type); +extern std::string getInputDeviceConfigurationFilePathByName( + const std::string& name, InputDeviceConfigurationFileType type); + +enum ReservedInputDeviceId : int32_t { + // Device id of a special "virtual" keyboard that is always present. + VIRTUAL_KEYBOARD_ID = -1, + // Device id of the "built-in" keyboard if there is one. + BUILT_IN_KEYBOARD_ID = 0, +}; } // namespace android
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index 4b33a96..59d16d1 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h
@@ -325,8 +325,11 @@ DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), DEFINE_KEYCODE(ALL_APPS), DEFINE_KEYCODE(REFRESH), + DEFINE_KEYCODE(THUMBS_UP), + DEFINE_KEYCODE(THUMBS_DOWN), + DEFINE_KEYCODE(PROFILE_SWITCH), - { NULL, 0 } + { nullptr, 0 } }; static const InputEventLabel AXES[] = { @@ -375,7 +378,7 @@ // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. - { NULL, 0 } + { nullptr, 0 } }; static const InputEventLabel LEDS[] = { @@ -396,7 +399,7 @@ DEFINE_LED(CONTROLLER_4), // NOTE: If you add new LEDs here, you must also add them to Input.h - { NULL, 0 } + { nullptr, 0 } }; static const InputEventLabel FLAGS[] = { @@ -404,7 +407,7 @@ DEFINE_FLAG(FUNCTION), DEFINE_FLAG(GESTURE), - { NULL, 0 } + { nullptr, 0 } }; static int lookupValueByLabel(const char* literal, const InputEventLabel *list) { @@ -424,7 +427,7 @@ } list++; } - return NULL; + return nullptr; } static inline int32_t getKeyCodeByLabel(const char* label) { @@ -435,7 +438,7 @@ if (keyCode >= 0 && keyCode < static_cast<int32_t>(size(KEYCODES))) { return KEYCODES[keyCode].literal; } - return NULL; + return nullptr; } static inline uint32_t getKeyFlagByLabel(const char* label) {
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index ecdc075..63606e5 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h
@@ -17,6 +17,8 @@ #ifndef _LIBINPUT_INPUT_TRANSPORT_H #define _LIBINPUT_INPUT_TRANSPORT_H +#pragma GCC system_header + /** * Native input transport. * @@ -27,6 +29,9 @@ * The InputConsumer is used by the application to receive events from the input dispatcher. */ +#include <string> + +#include <binder/IBinder.h> #include <input/Input.h> #include <utils/Errors.h> #include <utils/Timers.h> @@ -35,6 +40,7 @@ #include <utils/BitSet.h> namespace android { +class Parcel; /* * Intermediate representation used to send input events and related signals. @@ -99,8 +105,9 @@ int32_t flags; int32_t metaState; int32_t buttonState; + MotionClassification classification; // base type: uint8_t + uint8_t empty2[3]; int32_t edgeFlags; - uint32_t empty2; nsecs_t downTime __attribute__((aligned(8))); float xOffset; float yOffset; @@ -154,6 +161,7 @@ virtual ~InputChannel(); public: + InputChannel() = default; InputChannel(const std::string& name, int fd); /* Creates a pair of input channels. @@ -194,9 +202,19 @@ /* Returns a new object that has a duplicate of this channel's fd. */ sp<InputChannel> dup() const; + status_t write(Parcel& out) const; + status_t read(const Parcel& from); + + sp<IBinder> getToken() const; + void setToken(const sp<IBinder>& token); + private: + void setFd(int fd); + std::string mName; - int mFd; + int mFd = -1; + + sp<IBinder> mToken = nullptr; }; /* @@ -225,6 +243,7 @@ uint32_t seq, int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -253,6 +272,7 @@ int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xOffset, float yOffset, float xPrecision, @@ -318,7 +338,7 @@ * Other errors probably indicate that the channel is broken. */ status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId); + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); /* Sends a finished signal to the publisher to inform it that the message * with the specified sequence number has finished being process and whether @@ -473,10 +493,9 @@ Vector<SeqChain> mSeqChains; status_t consumeBatch(InputEventFactoryInterface* factory, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId); + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); status_t consumeSamples(InputEventFactoryInterface* factory, - Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent, - int32_t* displayId); + Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent); void updateTouchState(InputMessage& msg); void resampleTouchState(nsecs_t frameTime, MotionEvent* event,
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h new file mode 100644 index 0000000..916af69 --- /dev/null +++ b/include/input/InputWindow.h
@@ -0,0 +1,250 @@ +/* + * Copyright (C) 2011 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 _UI_INPUT_WINDOW_H +#define _UI_INPUT_WINDOW_H + +#include <input/Input.h> +#include <input/InputTransport.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <utils/RefBase.h> +#include <utils/Timers.h> + +#include "InputApplication.h" + +namespace android { +class Parcel; + +/* + * Describes the properties of a window that can receive input. + */ +struct InputWindowInfo { + InputWindowInfo() = default; + InputWindowInfo(const Parcel& from); + + // Window flags from WindowManager.LayoutParams + enum { + FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, + FLAG_DIM_BEHIND = 0x00000002, + FLAG_BLUR_BEHIND = 0x00000004, + FLAG_NOT_FOCUSABLE = 0x00000008, + FLAG_NOT_TOUCHABLE = 0x00000010, + FLAG_NOT_TOUCH_MODAL = 0x00000020, + FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040, + FLAG_KEEP_SCREEN_ON = 0x00000080, + FLAG_LAYOUT_IN_SCREEN = 0x00000100, + FLAG_LAYOUT_NO_LIMITS = 0x00000200, + FLAG_FULLSCREEN = 0x00000400, + FLAG_FORCE_NOT_FULLSCREEN = 0x00000800, + FLAG_DITHER = 0x00001000, + FLAG_SECURE = 0x00002000, + FLAG_SCALED = 0x00004000, + FLAG_IGNORE_CHEEK_PRESSES = 0x00008000, + FLAG_LAYOUT_INSET_DECOR = 0x00010000, + FLAG_ALT_FOCUSABLE_IM = 0x00020000, + FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000, + FLAG_SHOW_WHEN_LOCKED = 0x00080000, + FLAG_SHOW_WALLPAPER = 0x00100000, + FLAG_TURN_SCREEN_ON = 0x00200000, + FLAG_DISMISS_KEYGUARD = 0x00400000, + FLAG_SPLIT_TOUCH = 0x00800000, + FLAG_SLIPPERY = 0x20000000, + FLAG_NEEDS_MENU_KEY = 0x40000000, + }; + + // Window types from WindowManager.LayoutParams + enum { + FIRST_APPLICATION_WINDOW = 1, + TYPE_BASE_APPLICATION = 1, + TYPE_APPLICATION = 2, + TYPE_APPLICATION_STARTING = 3, + LAST_APPLICATION_WINDOW = 99, + FIRST_SUB_WINDOW = 1000, + TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW, + TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1, + TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2, + TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3, + TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4, + LAST_SUB_WINDOW = 1999, + FIRST_SYSTEM_WINDOW = 2000, + TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW, + TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1, + TYPE_PHONE = FIRST_SYSTEM_WINDOW+2, + TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3, + TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4, + TYPE_TOAST = FIRST_SYSTEM_WINDOW+5, + TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6, + TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7, + TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8, + TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9, + TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10, + TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11, + TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12, + TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13, + TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14, + TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15, + TYPE_DRAG = FIRST_SYSTEM_WINDOW+16, + TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17, + TYPE_POINTER = FIRST_SYSTEM_WINDOW+18, + TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19, + TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20, + TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21, + TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22, + TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24, + TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27, + TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32, + TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34, + LAST_SYSTEM_WINDOW = 2999, + }; + + enum { + INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001, + INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002, + INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004, + }; + + /* These values are filled in by the WM and passed through SurfaceFlinger + * unless specified otherwise. + */ + sp<IBinder> token; + std::string name; + int32_t layoutParamsFlags; + int32_t layoutParamsType; + nsecs_t dispatchingTimeout; + + /* These values are filled in by SurfaceFlinger. */ + int32_t frameLeft; + int32_t frameTop; + int32_t frameRight; + int32_t frameBottom; + + /* + * SurfaceFlinger consumes this value to shrink the computed frame. This is + * different from shrinking the touchable region in that it DOES shift the coordinate + * space where-as the touchable region does not and is more like "cropping". This + * is used for window shadows. + */ + int32_t surfaceInset = 0; + + // A global scaling factor for all windows. Unlike windowScaleX/Y this results + // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis. + float globalScaleFactor; + + // Scaling factors applied to individual windows. + float windowXScale = 1.0f; + float windowYScale = 1.0f; + + /* + * This is filled in by the WM relative to the frame and then translated + * to absolute coordinates by SurfaceFlinger once the frame is computed. + */ + Region touchableRegion; + bool visible; + bool canReceiveKeys; + bool hasFocus; + bool hasWallpaper; + bool paused; + int32_t layer; + int32_t ownerPid; + int32_t ownerUid; + int32_t inputFeatures; + int32_t displayId; + int32_t portalToDisplayId = ADISPLAY_ID_NONE; + InputApplicationInfo applicationInfo; + bool replaceTouchableRegionWithCrop; + wp<IBinder> touchableRegionCropHandle; + + void addTouchableRegion(const Rect& region); + + bool touchableRegionContainsPoint(int32_t x, int32_t y) const; + bool frameContainsPoint(int32_t x, int32_t y) const; + + /* Returns true if the window is of a trusted type that is allowed to silently + * overlay other windows for the purpose of implementing the secure views feature. + * Trusted overlays, such as IME windows, can partly obscure other windows without causing + * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. + */ + bool isTrustedOverlay() const; + + bool supportsSplitTouch() const; + + bool overlaps(const InputWindowInfo* other) const; + + status_t write(Parcel& output) const; + static InputWindowInfo read(const Parcel& from); +}; + + +/* + * Handle for a window that can receive input. + * + * Used by the native input dispatcher to indirectly refer to the window manager objects + * that describe a window. + */ +class InputWindowHandle : public RefBase { +public: + + inline const InputWindowInfo* getInfo() const { + return &mInfo; + } + + sp<IBinder> getToken() const; + + sp<IBinder> getApplicationToken() { + return mInfo.applicationInfo.token; + } + + inline std::string getName() const { + return mInfo.token ? mInfo.name : "<invalid>"; + } + + inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const { + return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; + } + + /** + * Requests that the state of this object be updated to reflect + * the most current available information about the application. + * + * This method should only be called from within the input dispatcher's + * critical section. + * + * Returns true on success, or false if the handle is no longer valid. + */ + virtual bool updateInfo() = 0; + + /** + * Updates from another input window handle. + */ + void updateFrom(const sp<InputWindowHandle> handle); + + /** + * Releases the channel used by the associated information when it is + * no longer needed. + */ + void releaseChannel(); + +protected: + explicit InputWindowHandle(); + virtual ~InputWindowHandle(); + + InputWindowInfo mInfo; +}; + +} // namespace android + +#endif // _UI_INPUT_WINDOW_H
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index 33d2757..a1a32a6 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h
@@ -27,7 +27,6 @@ #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/Tokenizer.h> -#include <utils/String8.h> #include <utils/Unicode.h> #include <utils/RefBase.h> @@ -75,10 +74,10 @@ }; /* Loads a key character map from a file. */ - static status_t load(const String8& filename, Format format, sp<KeyCharacterMap>* outMap); + static status_t load(const std::string& filename, Format format, sp<KeyCharacterMap>* outMap); /* Loads a key character map from its string contents. */ - static status_t loadContents(const String8& filename, + static status_t loadContents(const std::string& filename, const char* contents, Format format, sp<KeyCharacterMap>* outMap); /* Combines a base key character map and an overlay. */ @@ -196,7 +195,7 @@ }; struct Property { - inline Property(int32_t property = 0, int32_t metaState = 0) : + inline explicit Property(int32_t property = 0, int32_t metaState = 0) : property(property), metaState(metaState) { } int32_t property; @@ -221,7 +220,7 @@ status_t parseKey(); status_t parseKeyProperty(); status_t finishKey(Key* key); - status_t parseModifier(const String8& token, int32_t* outMetaState); + status_t parseModifier(const std::string& token, int32_t* outMetaState); status_t parseCharacterLiteral(char16_t* outCharacter); };
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index 1e8de71..26f3501 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h
@@ -62,11 +62,11 @@ */ class KeyLayoutMap : public RefBase { public: - static status_t load(const String8& filename, sp<KeyLayoutMap>* outMap); + static status_t load(const std::string& filename, sp<KeyLayoutMap>* outMap); status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const; - status_t findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const; + status_t findScanCodesForKey(int32_t keyCode, std::vector<int32_t>* outScanCodes) const; status_t findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const; status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const;
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h index d4903e9..92da10c 100644 --- a/include/input/Keyboard.h +++ b/include/input/Keyboard.h
@@ -21,20 +21,10 @@ #include <input/InputDevice.h> #include <input/InputEventLabels.h> #include <utils/Errors.h> -#include <utils/String8.h> #include <utils/PropertyMap.h> namespace android { -enum { - /* Device id of the built in keyboard. */ - DEVICE_ID_BUILT_IN_KEYBOARD = 0, - - /* Device id of a generic virtual keyboard with a full layout that can be used - * to synthesize key events. */ - DEVICE_ID_VIRTUAL_KEYBOARD = -1, -}; - class KeyLayoutMap; class KeyCharacterMap; @@ -43,10 +33,10 @@ */ class KeyMap { public: - String8 keyLayoutFile; + std::string keyLayoutFile; sp<KeyLayoutMap> keyLayoutMap; - String8 keyCharacterMapFile; + std::string keyCharacterMapFile; sp<KeyCharacterMap> keyCharacterMap; KeyMap(); @@ -56,11 +46,11 @@ const PropertyMap* deviceConfiguration); inline bool haveKeyLayout() const { - return !keyLayoutFile.isEmpty(); + return !keyLayoutFile.empty(); } inline bool haveKeyCharacterMap() const { - return !keyCharacterMapFile.isEmpty(); + return !keyCharacterMapFile.empty(); } inline bool isComplete() const { @@ -68,12 +58,12 @@ } private: - bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const String8& name); - status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const String8& name); + bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); + status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, - const String8& name); - String8 getPath(const InputDeviceIdentifier& deviceIdentifier, - const String8& name, InputDeviceConfigurationFileType type); + const std::string& name); + std::string getPath(const InputDeviceIdentifier& deviceIdentifier, + const std::string& name, InputDeviceConfigurationFileType type); }; /**
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h new file mode 100644 index 0000000..b49c623 --- /dev/null +++ b/include/input/TouchVideoFrame.h
@@ -0,0 +1,80 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBINPUT_TOUCHVIDEOFRAME_H +#define _LIBINPUT_TOUCHVIDEOFRAME_H + +#include <stdint.h> +#include <sys/time.h> +#include <ui/DisplayInfo.h> +#include <vector> + +namespace android { + +/** + * Represents data from a single scan of the touchscreen device. + * Similar in concept to a video frame, but the touch strength is used as + * the values instead. + */ +class TouchVideoFrame { +public: + TouchVideoFrame(uint32_t height, uint32_t width, std::vector<int16_t> data, + const struct timeval& timestamp); + + bool operator==(const TouchVideoFrame& rhs) const; + + /** + * Height of the frame + */ + uint32_t getHeight() const; + /** + * Width of the frame + */ + uint32_t getWidth() const; + /** + * The touch strength data. + * The array is a 2-D row-major matrix, with dimensions (height, width). + * Total size of the array should equal getHeight() * getWidth(). + * Data is allowed to be negative. + */ + const std::vector<int16_t>& getData() const; + /** + * Time at which the heatmap was taken. + */ + const struct timeval& getTimestamp() const; + + /** + * Rotate the video frame. + * The rotation value is an enum from ui/DisplayInfo.h + */ + void rotate(int32_t orientation); + +private: + uint32_t mHeight; + uint32_t mWidth; + std::vector<int16_t> mData; + struct timeval mTimestamp; + + /** + * Common method for 90 degree and 270 degree rotation + */ + void rotateQuarterTurn(bool clockwise); + void rotate180(); +}; + +} // namespace android + +#endif // _LIBINPUT_TOUCHVIDEOFRAME_H
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index ffa1614..727865a 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h
@@ -63,7 +63,7 @@ // Creates a velocity tracker using the specified strategy. // If strategy is NULL, uses the default strategy for the platform. - VelocityTracker(const char* strategy = NULL); + VelocityTracker(const char* strategy = nullptr); ~VelocityTracker();
diff --git a/include/input/VirtualKeyMap.h b/include/input/VirtualKeyMap.h index e245ead..6e8e2c9 100644 --- a/include/input/VirtualKeyMap.h +++ b/include/input/VirtualKeyMap.h
@@ -23,8 +23,8 @@ #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/Tokenizer.h> -#include <utils/String8.h> #include <utils/Unicode.h> +#include <vector> namespace android { @@ -50,9 +50,9 @@ public: ~VirtualKeyMap(); - static status_t load(const String8& filename, VirtualKeyMap** outMap); + static std::unique_ptr<VirtualKeyMap> load(const std::string& filename); - inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const { + inline const std::vector<VirtualKeyDefinition>& getVirtualKeys() const { return mVirtualKeys; } @@ -71,7 +71,7 @@ bool parseNextIntField(int32_t* outValue); }; - Vector<VirtualKeyDefinition> mVirtualKeys; + std::vector<VirtualKeyDefinition> mVirtualKeys; VirtualKeyMap(); };
diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h index 3c81f0f..853f0c9 100644 --- a/include/powermanager/IPowerManager.h +++ b/include/powermanager/IPowerManager.h
@@ -45,7 +45,7 @@ IS_INTERACTIVE = IBinder::FIRST_CALL_TRANSACTION + 11, IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12, GET_POWER_SAVE_STATE = IBinder::FIRST_CALL_TRANSACTION + 13, - SET_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 14, + SET_POWER_SAVE_MODE_ENABLED = IBinder::FIRST_CALL_TRANSACTION + 14, REBOOT = IBinder::FIRST_CALL_TRANSACTION + 17, REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 18, SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 19,
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp new file mode 100644 index 0000000..9284acb --- /dev/null +++ b/libs/android_runtime_lazy/Android.bp
@@ -0,0 +1,66 @@ +/* + * 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. + */ + +// libandroid_runtime_lazy is a shim library. +// This provides very limited small set of APIs from libandroid_runtime. +// +// By depending on this instead of libandroid_runtime, +// a library can be loaded without paying the cost of libandroid_runtime +// which is quite huge. The cost will be paid when libandroid_runtime is actually used. +// +// For Partial-source PDK build, there is a constraint that +// frameworks/native modules should not depend on frameworks/base. +// This library can be used to cut down the dependency between them. +// (e.g. libbinder_ndk) +// +// Some libraries which serve as LL-NDK and NDK as well may depend on this +// instead of libandroid_runtime. When they are used by a vendor process, +// depending on libandroid_runtime is meaningless. In this case, +// they can depend on libandroid_runtime_lazy. +cc_library { + name: "libandroid_runtime_lazy", + vendor_available: true, + double_loadable: true, + + cflags: [ + "-Wall", + "-Werror", + "-Wunused", + "-Wunreachable-code", + ], + + srcs: [ + "android_runtime_lazy.cpp", + ], + + shared_libs: [ + "liblog", + "libutils", + ], + + required: [ + "libandroid_runtime", + ], + + export_include_dirs: [ + "include", + ], + + header_libs: [ + "jni_headers", + "libbinder_headers", + ], +}
diff --git a/libs/android_runtime_lazy/android_runtime_lazy.cpp b/libs/android_runtime_lazy/android_runtime_lazy.cpp new file mode 100644 index 0000000..98d8e8a --- /dev/null +++ b/libs/android_runtime_lazy/android_runtime_lazy.cpp
@@ -0,0 +1,98 @@ +/* + * 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 LOG_TAG "ANDROID_RUNTIME_LAZY" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" + +#include <dlfcn.h> +#include <mutex> + +#include <log/log.h> + +namespace android { +namespace { + +std::once_flag loadFlag; + +typedef JNIEnv* (*getJNIEnv_t)(); +typedef sp<IBinder> (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj); +typedef jobject (*javaObjectForIBinder_t)(JNIEnv* env, const sp<IBinder>& val); + +getJNIEnv_t _getJNIEnv; +ibinderForJavaObject_t _ibinderForJavaObject; +javaObjectForIBinder_t _javaObjectForIBinder; + +void load() { + std::call_once(loadFlag, []() { + void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY); + if (handle == nullptr) { + ALOGE("Could not open libandroid_runtime."); + return; + } + + _getJNIEnv = reinterpret_cast<getJNIEnv_t>( + dlsym(handle, "_ZN7android14AndroidRuntime9getJNIEnvEv")); + if (_getJNIEnv == nullptr) { + ALOGW("Could not find getJNIEnv."); + // no return + } + + _ibinderForJavaObject = reinterpret_cast<ibinderForJavaObject_t>( + dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject")); + if (_ibinderForJavaObject == nullptr) { + ALOGW("Could not find ibinderForJavaObject."); + // no return + } + + _javaObjectForIBinder = reinterpret_cast<javaObjectForIBinder_t>( + dlsym(handle, + "_ZN7android20javaObjectForIBinderEP7_JNIEnvRKNS_2spINS_7IBinderEEE")); + if (_javaObjectForIBinder == nullptr) { + ALOGW("Could not find javaObjectForIBinder."); + // no return + } + }); +} + +} // namespace + +// exports delegate functions + +JNIEnv* AndroidRuntime::getJNIEnv() { + load(); + if (_getJNIEnv == nullptr) { + return nullptr; + } + return _getJNIEnv(); +} + +sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj) { + load(); + if (_ibinderForJavaObject == nullptr) { + return nullptr; + } + return _ibinderForJavaObject(env, obj); +} + +jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) { + load(); + if (_javaObjectForIBinder == nullptr) { + return nullptr; + } + return _javaObjectForIBinder(env, val); +} + +} // namespace android
diff --git a/libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h b/libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h new file mode 100644 index 0000000..85231fa --- /dev/null +++ b/libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h
@@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "jni.h" + +namespace android { + +// Intentionally use the same name with AndroidRuntime in frameworks/base/core/jni/ +// to make the client use this in the same way with the original class. +class AndroidRuntime { +public: + static JNIEnv* getJNIEnv(); +}; + +} // namespace android
diff --git a/libs/android_runtime_lazy/include/android_util_Binder.h b/libs/android_runtime_lazy/include/android_util_Binder.h new file mode 100644 index 0000000..e47390e --- /dev/null +++ b/libs/android_runtime_lazy/include/android_util_Binder.h
@@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IBinder.h> +#include "jni.h" + +namespace android { + +// The name of this file is same with the file in frameworks/base/core/jni/ +// This is intentional to make the client use these exported functions +// in the same way with the original. + +jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val); +sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj); + +} // namespace android
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index a20154f..ad8287c 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp
@@ -25,4 +25,9 @@ host_supported: true, vendor_available: true, export_include_dirs: ["include"], + target: { + windows: { + enabled: true, + }, + }, }
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp index 2728f35..49a9414 100644 --- a/libs/binder/ActivityManager.cpp +++ b/libs/binder/ActivityManager.cpp
@@ -34,16 +34,16 @@ std::lock_guard<Mutex> scoped_lock(mLock); int64_t startTime = 0; sp<IActivityManager> service = mService; - while (service == NULL || !IInterface::asBinder(service)->isBinderAlive()) { + while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { sp<IBinder> binder = defaultServiceManager()->checkService(String16("activity")); - if (binder == NULL) { + if (binder == nullptr) { // Wait for the activity service to come back... if (startTime == 0) { startTime = uptimeMillis(); ALOGI("Waiting for activity service"); } else if ((uptimeMillis() - startTime) > 1000000) { ALOGW("Waiting too long for activity service, giving up"); - service = NULL; + service = nullptr; break; } usleep(25000); @@ -58,7 +58,7 @@ int ActivityManager::openContentUri(const String16& stringUri) { sp<IActivityManager> service = getService(); - return service != NULL ? service->openContentUri(stringUri) : -1; + return service != nullptr ? service->openContentUri(stringUri) : -1; } void ActivityManager::registerUidObserver(const sp<IUidObserver>& observer, @@ -67,7 +67,7 @@ const String16& callingPackage) { sp<IActivityManager> service = getService(); - if (service != NULL) { + if (service != nullptr) { service->registerUidObserver(observer, event, cutpoint, callingPackage); } } @@ -75,7 +75,7 @@ void ActivityManager::unregisterUidObserver(const sp<IUidObserver>& observer) { sp<IActivityManager> service = getService(); - if (service != NULL) { + if (service != nullptr) { service->unregisterUidObserver(observer); } } @@ -83,15 +83,24 @@ bool ActivityManager::isUidActive(const uid_t uid, const String16& callingPackage) { sp<IActivityManager> service = getService(); - if (service != NULL) { + if (service != nullptr) { return service->isUidActive(uid, callingPackage); } return false; } +int32_t ActivityManager::getUidProcessState(const uid_t uid, const String16& callingPackage) +{ + sp<IActivityManager> service = getService(); + if (service != nullptr) { + return service->getUidProcessState(uid, callingPackage); + } + return PROCESS_STATE_UNKNOWN; +} + status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) { sp<IActivityManager> service = getService(); - if (service != NULL) { + if (service != nullptr) { return IInterface::asBinder(service)->linkToDeath(recipient); } return INVALID_OPERATION; @@ -99,7 +108,7 @@ status_t ActivityManager::unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient) { sp<IActivityManager> service = getService(); - if (service != NULL) { + if (service != nullptr) { return IInterface::asBinder(service)->unlinkToDeath(recipient); } return INVALID_OPERATION;
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 7c1eaaf..aedf6b0 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp
@@ -28,7 +28,7 @@ ], } -cc_library { +cc_library_shared { name: "libbinder", // for vndbinder @@ -36,6 +36,7 @@ vndk: { enabled: true, }, + double_loadable: true, srcs: [ "ActivityManager.cpp", @@ -62,6 +63,7 @@ "MemoryDealer.cpp", "MemoryHeapBase.cpp", "Parcel.cpp", + "ParcelFileDescriptor.cpp", "PermissionCache.cpp", "PermissionController.cpp", "PersistableBundle.cpp", @@ -105,6 +107,7 @@ "-Wall", "-Wextra", "-Werror", + "-Wzero-as-null-pointer-constant", ], product_variables: { binder32bit: { @@ -117,7 +120,7 @@ "liblog", "libcutils", "libutils", - "libutilscallstack", + "libbinderthreadstate", ], header_libs: [
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp index 62c8987..525685c 100644 --- a/libs/binder/AppOpsManager.cpp +++ b/libs/binder/AppOpsManager.cpp
@@ -42,7 +42,7 @@ static const sp<IBinder>& getToken(const sp<IAppOpsService>& service) { pthread_mutex_lock(&gTokenMutex); - if (gToken == NULL || gToken->pingBinder() != NO_ERROR) { + if (gToken == nullptr || gToken->pingBinder() != NO_ERROR) { gToken = service->getToken(new BBinder()); } pthread_mutex_unlock(&gTokenMutex); @@ -63,16 +63,16 @@ std::lock_guard<Mutex> scoped_lock(mLock); int64_t startTime = 0; sp<IAppOpsService> service = mService; - while (service == NULL || !IInterface::asBinder(service)->isBinderAlive()) { + while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { sp<IBinder> binder = defaultServiceManager()->checkService(_appops); - if (binder == NULL) { + if (binder == nullptr) { // Wait for the app ops service to come back... if (startTime == 0) { startTime = uptimeMillis(); ALOGI("Waiting for app ops service"); } else if ((uptimeMillis()-startTime) > 10000) { ALOGW("Waiting too long for app ops service, giving up"); - service = NULL; + service = nullptr; break; } sleep(1); @@ -88,14 +88,22 @@ int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage) { sp<IAppOpsService> service = getService(); - return service != NULL + return service != nullptr ? service->checkOperation(op, uid, callingPackage) : APP_OPS_MANAGER_UNAVAILABLE_MODE; } +int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid, + const String16& callingPackage) { + sp<IAppOpsService> service = getService(); + return service != nullptr + ? service->checkAudioOperation(op, usage, uid, callingPackage) + : APP_OPS_MANAGER_UNAVAILABLE_MODE; +} + int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) { sp<IAppOpsService> service = getService(); - return service != NULL + return service != nullptr ? service->noteOperation(op, uid, callingPackage) : APP_OPS_MANAGER_UNAVAILABLE_MODE; } @@ -103,14 +111,14 @@ int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, bool startIfModeDefault) { sp<IAppOpsService> service = getService(); - return service != NULL + return service != nullptr ? service->startOperation(getToken(service), op, uid, callingPackage, startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE; } void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) { sp<IAppOpsService> service = getService(); - if (service != NULL) { + if (service != nullptr) { service->finishOperation(getToken(service), op, uid, callingPackage); } } @@ -118,21 +126,21 @@ void AppOpsManager::startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) { sp<IAppOpsService> service = getService(); - if (service != NULL) { + if (service != nullptr) { service->startWatchingMode(op, packageName, callback); } } void AppOpsManager::stopWatchingMode(const sp<IAppOpsCallback>& callback) { sp<IAppOpsService> service = getService(); - if (service != NULL) { + if (service != nullptr) { service->stopWatchingMode(callback); } } int32_t AppOpsManager::permissionToOpCode(const String16& permission) { sp<IAppOpsService> service = getService(); - if (service != NULL) { + if (service != nullptr) { return service->permissionToOpCode(permission); } return -1;
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index a81f44e..cb0e08d 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp
@@ -43,17 +43,17 @@ sp<IInterface> IBinder::queryLocalInterface(const String16& /*descriptor*/) { - return NULL; + return nullptr; } BBinder* IBinder::localBinder() { - return NULL; + return nullptr; } BpBinder* IBinder::remoteBinder() { - return NULL; + return nullptr; } bool IBinder::checkSubclass(const void* /*subclassID*/) const @@ -76,8 +76,8 @@ for (size_t i = 0; i < numArgs; i++) { send.writeString16(args[i]); } - send.writeStrongBinder(callback != NULL ? IInterface::asBinder(callback) : NULL); - send.writeStrongBinder(resultReceiver != NULL ? IInterface::asBinder(resultReceiver) : NULL); + send.writeStrongBinder(callback != nullptr ? IInterface::asBinder(callback) : nullptr); + send.writeStrongBinder(resultReceiver != nullptr ? IInterface::asBinder(resultReceiver) : nullptr); return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply); } @@ -86,6 +86,10 @@ class BBinder::Extras { public: + // unlocked objects + bool mRequestingSid = false; + + // for below objects Mutex mLock; BpBinder::ObjectManager mObjects; }; @@ -115,6 +119,7 @@ return sEmptyDescriptor; } +// NOLINTNEXTLINE(google-default-arguments) status_t BBinder::transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -130,13 +135,14 @@ break; } - if (reply != NULL) { + if (reply != nullptr) { reply->setDataPosition(0); } return err; } +// NOLINTNEXTLINE(google-default-arguments) status_t BBinder::linkToDeath( const sp<DeathRecipient>& /*recipient*/, void* /*cookie*/, uint32_t /*flags*/) @@ -144,6 +150,7 @@ return INVALID_OPERATION; } +// NOLINTNEXTLINE(google-default-arguments) status_t BBinder::unlinkToDeath( const wp<DeathRecipient>& /*recipient*/, void* /*cookie*/, uint32_t /*flags*/, wp<DeathRecipient>* /*outRecipient*/) @@ -160,19 +167,8 @@ const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) { - Extras* e = mExtras.load(std::memory_order_acquire); - - if (!e) { - e = new Extras; - Extras* expected = nullptr; - if (!mExtras.compare_exchange_strong(expected, e, - std::memory_order_release, - std::memory_order_acquire)) { - delete e; - e = expected; // Filled in by CAS - } - if (e == 0) return; // out of memory - } + Extras* e = getOrCreateExtras(); + if (!e) return; // out of memory AutoMutex _l(e->mLock); e->mObjects.attach(objectID, object, cleanupCookie, func); @@ -181,7 +177,7 @@ void* BBinder::findObject(const void* objectID) const { Extras* e = mExtras.load(std::memory_order_acquire); - if (!e) return NULL; + if (!e) return nullptr; AutoMutex _l(e->mLock); return e->mObjects.find(objectID); @@ -201,6 +197,30 @@ return this; } +bool BBinder::isRequestingSid() +{ + Extras* e = mExtras.load(std::memory_order_acquire); + + return e && e->mRequestingSid; +} + +void BBinder::setRequestingSid(bool requestingSid) +{ + Extras* e = mExtras.load(std::memory_order_acquire); + + if (!e) { + // default is false. Most things don't need sids, so avoiding allocations when possible. + if (!requestingSid) { + return; + } + + e = getOrCreateExtras(); + if (!e) return; // out of memory + } + + e->mRequestingSid = requestingSid; +} + BBinder::~BBinder() { Extras* e = mExtras.load(std::memory_order_relaxed); @@ -208,6 +228,7 @@ } +// NOLINTNEXTLINE(google-default-arguments) status_t BBinder::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t /*flags*/) { @@ -246,7 +267,7 @@ (void)out; (void)err; - if (resultReceiver != NULL) { + if (resultReceiver != nullptr) { resultReceiver->send(INVALID_OPERATION); } @@ -263,6 +284,25 @@ } } +BBinder::Extras* BBinder::getOrCreateExtras() +{ + Extras* e = mExtras.load(std::memory_order_acquire); + + if (!e) { + e = new Extras; + Extras* expected = nullptr; + if (!mExtras.compare_exchange_strong(expected, e, + std::memory_order_release, + std::memory_order_acquire)) { + delete e; + e = expected; // Filled in by CAS + } + if (e == nullptr) return nullptr; // out of memory + } + + return e; +} + // --------------------------------------------------------------------------- enum { @@ -273,7 +313,7 @@ }; BpRefBase::BpRefBase(const sp<IBinder>& o) - : mRemote(o.get()), mRefs(NULL), mState(0) + : mRemote(o.get()), mRefs(nullptr), mState(0) { extendObjectLifetime(OBJECT_LIFETIME_WEAK);
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 449a9e9..ec170f7 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp
@@ -80,7 +80,7 @@ void* BpBinder::ObjectManager::find(const void* objectID) const { const ssize_t i = mObjects.indexOfKey(objectID); - if (i < 0) return NULL; + if (i < 0) return nullptr; return mObjects.valueAt(i).object; } @@ -95,7 +95,7 @@ ALOGV("Killing %zu objects in manager %p", N, this); for (size_t i=0; i<N; i++) { const entry_t& e = mObjects.valueAt(i); - if (e.func != NULL) { + if (e.func != nullptr) { e.func(mObjects.keyAt(i), e.object, e.cleanupCookie); } } @@ -139,7 +139,7 @@ : mHandle(handle) , mAlive(1) , mObitsSent(0) - , mObituaries(NULL) + , mObituaries(nullptr) , mTrackedUid(trackedUid) { ALOGV("Creating BpBinder %p handle %d\n", this, mHandle); @@ -206,6 +206,7 @@ return err; } +// NOLINTNEXTLINE(google-default-arguments) status_t BpBinder::transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -220,6 +221,7 @@ return DEAD_OBJECT; } +// NOLINTNEXTLINE(google-default-arguments) status_t BpBinder::linkToDeath( const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags) { @@ -228,7 +230,7 @@ ob.cookie = cookie; ob.flags = flags; - LOG_ALWAYS_FATAL_IF(recipient == NULL, + LOG_ALWAYS_FATAL_IF(recipient == nullptr, "linkToDeath(): recipient must be non-NULL"); { @@ -254,6 +256,7 @@ return DEAD_OBJECT; } +// NOLINTNEXTLINE(google-default-arguments) status_t BpBinder::unlinkToDeath( const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags, wp<DeathRecipient>* outRecipient) @@ -268,9 +271,9 @@ for (size_t i=0; i<N; i++) { const Obituary& obit = mObituaries->itemAt(i); if ((obit.recipient == recipient - || (recipient == NULL && obit.cookie == cookie)) + || (recipient == nullptr && obit.cookie == cookie)) && obit.flags == flags) { - if (outRecipient != NULL) { + if (outRecipient != nullptr) { *outRecipient = mObituaries->itemAt(i).recipient; } mObituaries->removeAt(i); @@ -280,7 +283,7 @@ self->clearDeathNotification(mHandle, this); self->flushCommands(); delete mObituaries; - mObituaries = NULL; + mObituaries = nullptr; } return NO_ERROR; } @@ -299,12 +302,12 @@ mLock.lock(); Vector<Obituary>* obits = mObituaries; - if(obits != NULL) { + if(obits != nullptr) { ALOGV("Clearing sent death notification: %p handle %d\n", this, mHandle); IPCThreadState* self = IPCThreadState::self(); self->clearDeathNotification(mHandle, this); self->flushCommands(); - mObituaries = NULL; + mObituaries = nullptr; } mObitsSent = 1; mLock.unlock(); @@ -312,7 +315,7 @@ ALOGV("Reporting death of proxy %p for %zu recipients\n", this, obits ? obits->size() : 0U); - if (obits != NULL) { + if (obits != nullptr) { const size_t N = obits->size(); for (size_t i=0; i<N; i++) { reportOneDeath(obits->itemAt(i)); @@ -326,7 +329,7 @@ { sp<DeathRecipient> recipient = obit.recipient.promote(); ALOGV("Reporting death to recipient: %p\n", recipient.get()); - if (recipient == NULL) return; + if (recipient == nullptr) return; recipient->binderDied(this); } @@ -386,13 +389,13 @@ mLock.lock(); Vector<Obituary>* obits = mObituaries; - if(obits != NULL) { + if(obits != nullptr) { if (ipc) ipc->clearDeathNotification(mHandle, this); - mObituaries = NULL; + mObituaries = nullptr; } mLock.unlock(); - if (obits != NULL) { + if (obits != nullptr) { // XXX Should we tell any remaining DeathRecipient // objects that the last strong ref has gone away, so they // are no longer linked?
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp index 30e70b0..857bbf9 100644 --- a/libs/binder/BufferedTextOutput.cpp +++ b/libs/binder/BufferedTextOutput.cpp
@@ -25,6 +25,7 @@ #include <private/binder/Static.h> +#include <pthread.h> #include <stdio.h> #include <stdlib.h> @@ -36,7 +37,7 @@ { explicit BufferState(int32_t _seq) : seq(_seq) - , buffer(NULL) + , buffer(nullptr) , bufferPos(0) , bufferSize(0) , atFront(true) @@ -87,7 +88,7 @@ Vector<sp<BufferedTextOutput::BufferState> > states; }; -static mutex_t gMutex; +static pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER; static thread_store_t tls; @@ -113,7 +114,7 @@ { int32_t res = -1; - mutex_lock(&gMutex); + pthread_mutex_lock(&gMutex); if (gFreeBufferIndex >= 0) { res = gFreeBufferIndex; @@ -125,17 +126,17 @@ gTextBuffers.add(-1); } - mutex_unlock(&gMutex); + pthread_mutex_unlock(&gMutex); return res; } static void freeBufferIndex(int32_t idx) { - mutex_lock(&gMutex); + pthread_mutex_lock(&gMutex); gTextBuffers.editItemAt(idx) = gFreeBufferIndex; gFreeBufferIndex = idx; - mutex_unlock(&gMutex); + pthread_mutex_unlock(&gMutex); } // --------------------------------------------------------------------------- @@ -188,7 +189,7 @@ // them out without going through the buffer. // Slurp up all of the lines. - const char* lastLine = txt+1; + const char* lastLine = txt; while (txt < end) { if (*txt++ == '\n') lastLine = txt; } @@ -266,13 +267,13 @@ if ((mFlags&MULTITHREADED) != 0) { ThreadState* ts = getThreadState(); if (ts) { - while (ts->states.size() <= (size_t)mIndex) ts->states.add(NULL); + while (ts->states.size() <= (size_t)mIndex) ts->states.add(nullptr); BufferState* bs = ts->states[mIndex].get(); - if (bs != NULL && bs->seq == mSeq) return bs; + if (bs != nullptr && bs->seq == mSeq) return bs; ts->states.editItemAt(mIndex) = new BufferState(mIndex); bs = ts->states[mIndex].get(); - if (bs != NULL) return bs; + if (bs != nullptr) return bs; } }
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp index 4ac61a3..a1c2a8b 100644 --- a/libs/binder/Debug.cpp +++ b/libs/binder/Debug.cpp
@@ -165,13 +165,13 @@ else if (bytesPerLine >= 8) alignment = 2; else alignment = 1; } - if (func == NULL) func = defaultPrintFunc; + if (func == nullptr) func = defaultPrintFunc; size_t offset; unsigned char *pos = (unsigned char *)buf; - if (pos == NULL) { + if (pos == nullptr) { if (singleLineBytesCutoff < 0) func(cookie, "\n"); func(cookie, "(NULL)"); return; @@ -221,7 +221,11 @@ for (word = 0; word < bytesPerLine; ) { - const size_t startIndex = word+(alignment-(alignment?1:0)); + size_t align_offset = alignment-(alignment?1:0); + if (remain > 0 && (size_t)remain <= align_offset) { + align_offset = remain - 1; + } + const size_t startIndex = word+align_offset; for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) { @@ -297,7 +301,7 @@ ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf) { sp<ProcessState> proc = ProcessState::selfOrNull(); - if (proc.get() == NULL) { + if (proc.get() == nullptr) { return 0; }
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp index 428db4d..377f604 100644 --- a/libs/binder/IActivityManager.cpp +++ b/libs/binder/IActivityManager.cpp
@@ -17,8 +17,8 @@ #include <unistd.h> #include <fcntl.h> +#include <binder/ActivityManager.h> #include <binder/IActivityManager.h> - #include <binder/Parcel.h> namespace android { @@ -90,6 +90,20 @@ if (reply.readExceptionCode() != 0) return false; return reply.readInt32() == 1; } + + virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) + { + Parcel data, reply; + data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); + data.writeInt32(uid); + data.writeString16(callingPackage); + remote()->transact(GET_UID_PROCESS_STATE_TRANSACTION, data, &reply); + // fail on exception + if (reply.readExceptionCode() != 0) { + return ActivityManager::PROCESS_STATE_UNKNOWN; + } + return reply.readInt32(); + } }; // ------------------------------------------------------------------------------------
diff --git a/libs/binder/IAppOpsCallback.cpp b/libs/binder/IAppOpsCallback.cpp index f9ec593..aba4967 100644 --- a/libs/binder/IAppOpsCallback.cpp +++ b/libs/binder/IAppOpsCallback.cpp
@@ -49,6 +49,7 @@ // ---------------------------------------------------------------------- +// NOLINTNEXTLINE(google-default-arguments) status_t BnAppOpsCallback::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -56,7 +57,8 @@ case OP_CHANGED_TRANSACTION: { CHECK_INTERFACE(IAppOpsCallback, data, reply); int32_t op = data.readInt32(); - String16 packageName = data.readString16(); + String16 packageName; + (void)data.readString16(&packageName); opChanged(op, packageName); reply->writeNoException(); return NO_ERROR;
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp index 9c76350..66d6e31 100644 --- a/libs/binder/IAppOpsService.cpp +++ b/libs/binder/IAppOpsService.cpp
@@ -109,7 +109,7 @@ data.writeStrongBinder(clientToken); remote()->transact(GET_TOKEN_TRANSACTION, data, &reply); // fail on exception - if (reply.readExceptionCode() != 0) return NULL; + if (reply.readExceptionCode() != 0) return nullptr; return reply.readStrongBinder(); } @@ -123,12 +123,29 @@ if (reply.readExceptionCode() != 0) return -1; return reply.readInt32(); } + + virtual int32_t checkAudioOperation(int32_t code, int32_t usage, + int32_t uid, const String16& packageName) { + Parcel data, reply; + data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); + data.writeInt32(code); + data.writeInt32(usage); + data.writeInt32(uid); + data.writeString16(packageName); + remote()->transact(CHECK_AUDIO_OPERATION_TRANSACTION, data, &reply); + // fail on exception + if (reply.readExceptionCode() != 0) { + return MODE_ERRORED; + } + return reply.readInt32(); + } }; IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService"); // ---------------------------------------------------------------------- +// NOLINTNEXTLINE(google-default-arguments) status_t BnAppOpsService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -208,6 +225,17 @@ reply->writeInt32(opCode); return NO_ERROR; } break; + case CHECK_AUDIO_OPERATION_TRANSACTION: { + CHECK_INTERFACE(IAppOpsService, data, reply); + const int32_t code = data.readInt32(); + const int32_t usage = data.readInt32(); + const int32_t uid = data.readInt32(); + const String16 packageName = data.readString16(); + const int32_t res = checkAudioOperation(code, usage, uid, packageName); + reply->writeNoException(); + reply->writeInt32(res); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); }
diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp index ad1e69f..b307e3e 100644 --- a/libs/binder/IBatteryStats.cpp +++ b/libs/binder/IBatteryStats.cpp
@@ -136,6 +136,7 @@ // ---------------------------------------------------------------------- +// NOLINTNEXTLINE(google-default-arguments) status_t BnBatteryStats::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp index 2fcd3d9..59d51ed 100644 --- a/libs/binder/IInterface.cpp +++ b/libs/binder/IInterface.cpp
@@ -32,14 +32,14 @@ // static sp<IBinder> IInterface::asBinder(const IInterface* iface) { - if (iface == NULL) return NULL; + if (iface == nullptr) return nullptr; return const_cast<IInterface*>(iface)->onAsBinder(); } // static sp<IBinder> IInterface::asBinder(const sp<IInterface>& iface) { - if (iface == NULL) return NULL; + if (iface == nullptr) return nullptr; return iface->onAsBinder(); } @@ -47,21 +47,3 @@ // --------------------------------------------------------------------------- }; // namespace android - -extern "C" { - -void _ZN7android10IInterface8asBinderEv(void *retval, void* self) { - ALOGW("deprecated asBinder call, please update your code"); - //ALOGI("self: %p, retval: %p", self, retval); - android::sp<android::IBinder> *ret = new(retval) android::sp<android::IBinder>; - *ret = android::IInterface::asBinder((android::IInterface*)self); -} - -void _ZNK7android10IInterface8asBinderEv(void *retval, void *self) { - ALOGW("deprecated asBinder call, please update your code"); - //ALOGI("self: %p, retval: %p", self, retval); - android::sp<android::IBinder> *ret = new(retval) android::sp<android::IBinder>; - *ret = android::IInterface::asBinder((android::IInterface*)self); -} - -} // extern "C"
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp index 5c1a4f4..caf2318 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp
@@ -31,7 +31,6 @@ #include <binder/Parcel.h> #include <log/log.h> -#include <utils/CallStack.h> #include <utils/KeyedVector.h> #include <utils/threads.h> @@ -87,7 +86,7 @@ virtual void* getBase() const; virtual size_t getSize() const; virtual uint32_t getFlags() const; - virtual uint32_t getOffset() const; + off_t getOffset() const override; private: friend class IMemory; @@ -114,7 +113,7 @@ mutable void* mBase; mutable size_t mSize; mutable uint32_t mFlags; - mutable uint32_t mOffset; + mutable off_t mOffset; mutable bool mRealHeap; mutable Mutex mLock; }; @@ -130,7 +129,8 @@ public: explicit BpMemory(const sp<IBinder>& impl); virtual ~BpMemory(); - virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const; + // NOLINTNEXTLINE(google-default-arguments) + virtual sp<IMemoryHeap> getMemory(ssize_t* offset=nullptr, size_t* size=nullptr) const; private: mutable sp<IMemoryHeap> mHeap; @@ -145,22 +145,22 @@ sp<IMemoryHeap> realHeap = BpMemoryHeap::get_heap(binder); void* const base = realHeap->base(); if (base == MAP_FAILED) - return 0; + return nullptr; return static_cast<char*>(base) + offset; } void* IMemory::pointer() const { ssize_t offset; sp<IMemoryHeap> heap = getMemory(&offset); - void* const base = heap!=0 ? heap->base() : MAP_FAILED; + void* const base = heap!=nullptr ? heap->base() : MAP_FAILED; if (base == MAP_FAILED) - return 0; + return nullptr; return static_cast<char*>(base) + offset; } size_t IMemory::size() const { size_t size; - getMemory(NULL, &size); + getMemory(nullptr, &size); return size; } @@ -181,20 +181,24 @@ { } +// NOLINTNEXTLINE(google-default-arguments) sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const { - if (mHeap == 0) { + if (mHeap == nullptr) { Parcel data, reply; data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { sp<IBinder> heap = reply.readStrongBinder(); - ssize_t o = reply.readInt32(); - size_t s = reply.readInt32(); - if (heap != 0) { + if (heap != nullptr) { mHeap = interface_cast<IMemoryHeap>(heap); - if (mHeap != 0) { + if (mHeap != nullptr) { + const int64_t offset64 = reply.readInt64(); + const uint64_t size64 = reply.readUint64(); + const ssize_t o = (ssize_t)offset64; + const size_t s = (size_t)size64; size_t heapSize = mHeap->getSize(); - if (s <= heapSize + if (s == size64 && o == offset64 // ILP32 bounds check + && s <= heapSize && o >= 0 && (static_cast<size_t>(o) <= heapSize - s)) { mOffset = o; @@ -202,7 +206,7 @@ } else { // Hm. android_errorWriteWithInfoLog(0x534e4554, - "26877992", -1, NULL, 0); + "26877992", -1, nullptr, 0); mOffset = 0; mSize = 0; } @@ -212,7 +216,7 @@ } if (offset) *offset = mOffset; if (size) *size = mSize; - return (mSize > 0) ? mHeap : 0; + return (mSize > 0) ? mHeap : nullptr; } // --------------------------------------------------------------------------- @@ -225,6 +229,7 @@ BnMemory::~BnMemory() { } +// NOLINTNEXTLINE(google-default-arguments) status_t BnMemory::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -234,8 +239,8 @@ ssize_t offset; size_t size; reply->writeStrongBinder( IInterface::asBinder(getMemory(&offset, &size)) ); - reply->writeInt32(offset); - reply->writeInt32(size); + reply->writeInt64(offset); + reply->writeUint64(size); return NO_ERROR; } break; default: @@ -264,7 +269,6 @@ if (VERBOSE) { ALOGD("UNMAPPING binder=%p, heap=%p, size=%zu, fd=%d", binder.get(), this, mSize, heapId); - CallStack stack(LOG_TAG); } munmap(mBase, mSize); @@ -315,18 +319,23 @@ data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); status_t err = remote()->transact(HEAP_ID, data, &reply); int parcel_fd = reply.readFileDescriptor(); - ssize_t size = reply.readInt32(); - uint32_t flags = reply.readInt32(); - uint32_t offset = reply.readInt32(); - - ALOGE_IF(err, "binder=%p transaction failed fd=%d, size=%zd, err=%d (%s)", - IInterface::asBinder(this).get(), - parcel_fd, size, err, strerror(-err)); + const uint64_t size64 = reply.readUint64(); + const int64_t offset64 = reply.readInt64(); + const uint32_t flags = reply.readUint32(); + const size_t size = (size_t)size64; + const off_t offset = (off_t)offset64; + if (err != NO_ERROR || // failed transaction + size != size64 || offset != offset64) { // ILP32 size check + ALOGE("binder=%p transaction failed fd=%d, size=%zu, err=%d (%s)", + IInterface::asBinder(this).get(), + parcel_fd, size, err, strerror(-err)); + return; + } Mutex::Autolock _l(mLock); if (mHeapId.load(memory_order_relaxed) == -1) { int fd = fcntl(parcel_fd, F_DUPFD_CLOEXEC, 0); - ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, err=%d (%s)", + ALOGE_IF(fd == -1, "cannot dup fd=%d, size=%zu, err=%d (%s)", parcel_fd, size, err, strerror(errno)); int access = PROT_READ; @@ -334,9 +343,9 @@ access |= PROT_WRITE; } mRealHeap = true; - mBase = mmap(0, size, access, MAP_SHARED, fd, offset); + mBase = mmap(nullptr, size, access, MAP_SHARED, fd, offset); if (mBase == MAP_FAILED) { - ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zd, fd=%d (%s)", + ALOGE("cannot map BpMemoryHeap (binder=%p), size=%zu, fd=%d (%s)", IInterface::asBinder(this).get(), size, fd, strerror(errno)); close(fd); } else { @@ -370,7 +379,7 @@ return mFlags; } -uint32_t BpMemoryHeap::getOffset() const { +off_t BpMemoryHeap::getOffset() const { assertMapped(); return mOffset; } @@ -385,6 +394,7 @@ BnMemoryHeap::~BnMemoryHeap() { } +// NOLINTNEXTLINE(google-default-arguments) status_t BnMemoryHeap::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -392,9 +402,9 @@ case HEAP_ID: { CHECK_INTERFACE(IMemoryHeap, data, reply); reply->writeFileDescriptor(getHeapID()); - reply->writeInt32(getSize()); - reply->writeInt32(getFlags()); - reply->writeInt32(getOffset()); + reply->writeUint64(getSize()); + reply->writeInt64(getOffset()); + reply->writeUint32(getFlags()); return NO_ERROR; } break; default:
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index fd552b4..9a561cb 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp
@@ -17,12 +17,15 @@ #define LOG_TAG "IPCThreadState" #include <binder/IPCThreadState.h> +#include <binderthreadstate/IPCThreadStateBase.h> #include <binder/Binder.h> #include <binder/BpBinder.h> #include <binder/TextOutput.h> +#include <android-base/macros.h> #include <cutils/sched_policy.h> +#include <utils/CallStack.h> #include <utils/Log.h> #include <utils/SystemClock.h> #include <utils/threads.h> @@ -85,7 +88,8 @@ "BR_FINISHED", "BR_DEAD_BINDER", "BR_CLEAR_DEATH_NOTIFICATION_DONE", - "BR_FAILED_REPLY" + "BR_FAILED_REPLY", + "BR_TRANSACTION_SEC_CTX", }; static const char *kCommandStrings[] = { @@ -108,6 +112,8 @@ "BC_DEAD_BINDER_DONE" }; +static const int64_t kWorkSourcePropagatedBitIndex = 32; + static const char* getReturnString(uint32_t cmd) { size_t idx = cmd & 0xff; @@ -289,7 +295,7 @@ if (gShutdown) { ALOGW("Calling IPCThreadState::self() during shutdown is dangerous, expect a crash.\n"); - return NULL; + return nullptr; } pthread_mutex_lock(&gTLSMutex); @@ -299,7 +305,7 @@ pthread_mutex_unlock(&gTLSMutex); ALOGW("IPCThreadState::self() unable to create TLS key, expect a crash: %s\n", strerror(key_create_value)); - return NULL; + return nullptr; } gHaveTLS = true; } @@ -314,7 +320,7 @@ IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); return st; } - return NULL; + return nullptr; } void IPCThreadState::shutdown() @@ -326,7 +332,7 @@ IPCThreadState* st = (IPCThreadState*)pthread_getspecific(gTLS); if (st) { delete st; - pthread_setspecific(gTLS, NULL); + pthread_setspecific(gTLS, nullptr); } pthread_key_delete(gTLS); gHaveTLS = false; @@ -360,6 +366,11 @@ return mCallingPid; } +const char* IPCThreadState::getCallingSid() const +{ + return mCallingSid; +} + uid_t IPCThreadState::getCallingUid() const { return mCallingUid; @@ -367,6 +378,7 @@ int64_t IPCThreadState::clearCallingIdentity() { + // ignore mCallingSid for legacy reasons int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; clearCaller(); return token; @@ -382,6 +394,48 @@ return mStrictModePolicy; } +int64_t IPCThreadState::setCallingWorkSourceUid(uid_t uid) +{ + int64_t token = setCallingWorkSourceUidWithoutPropagation(uid); + mPropagateWorkSource = true; + return token; +} + +int64_t IPCThreadState::setCallingWorkSourceUidWithoutPropagation(uid_t uid) +{ + const int64_t propagatedBit = ((int64_t)mPropagateWorkSource) << kWorkSourcePropagatedBitIndex; + int64_t token = propagatedBit | mWorkSource; + mWorkSource = uid; + return token; +} + +void IPCThreadState::clearPropagateWorkSource() +{ + mPropagateWorkSource = false; +} + +bool IPCThreadState::shouldPropagateWorkSource() const +{ + return mPropagateWorkSource; +} + +uid_t IPCThreadState::getCallingWorkSourceUid() const +{ + return mWorkSource; +} + +int64_t IPCThreadState::clearCallingWorkSource() +{ + return setCallingWorkSourceUid(kUnsetWorkSource); +} + +void IPCThreadState::restoreCallingWorkSource(int64_t token) +{ + uid_t uid = (int)token; + setCallingWorkSourceUidWithoutPropagation(uid); + mPropagateWorkSource = ((token >> kWorkSourcePropagatedBitIndex) & 1) == 1; +} + void IPCThreadState::setLastTransactionBinderFlags(int32_t flags) { mLastTransactionBinderFlags = flags; @@ -395,12 +449,14 @@ void IPCThreadState::restoreCallingIdentity(int64_t token) { mCallingUid = (int)(token>>32); + mCallingSid = nullptr; // not enough data to restore mCallingPid = (int)token; } void IPCThreadState::clearCaller() { mCallingPid = getpid(); + mCallingSid = nullptr; // expensive to lookup mCallingUid = getuid(); } @@ -608,7 +664,7 @@ LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(), (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY"); - err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); + err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr); if (err != NO_ERROR) { if (reply) reply->setError(err); @@ -616,6 +672,16 @@ } if ((flags & TF_ONE_WAY) == 0) { + if (UNLIKELY(mCallRestriction != ProcessState::CallRestriction::NONE)) { + if (mCallRestriction == ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY) { + ALOGE("Process making non-oneway call but is restricted."); + CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(), + ANDROID_LOG_ERROR); + } else /* FATAL_IF_NOT_ONEWAY */ { + LOG_ALWAYS_FATAL("Process may not make oneway calls."); + } + } + #if 0 if (code == 4) { // relayout ALOGI(">>>>>> CALLING transaction 4"); @@ -645,7 +711,7 @@ else alog << "(none requested)" << endl; } } else { - err = waitForResponse(NULL, NULL); + err = waitForResponse(nullptr, nullptr); } return err; @@ -735,13 +801,17 @@ IPCThreadState::IPCThreadState() : mProcess(ProcessState::self()), + mWorkSource(kUnsetWorkSource), + mPropagateWorkSource(false), mStrictModePolicy(0), - mLastTransactionBinderFlags(0) + mLastTransactionBinderFlags(0), + mCallRestriction(mProcess->mCallRestriction) { pthread_setspecific(gTLS, this); clearCaller(); mIn.setDataCapacity(256); mOut.setDataCapacity(256); + mIPCThreadStateBase = IPCThreadStateBase::self(); } IPCThreadState::~IPCThreadState() @@ -755,7 +825,7 @@ err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer); if (err < NO_ERROR) return err; - return waitForResponse(NULL, NULL); + return waitForResponse(nullptr, nullptr); } status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) @@ -815,14 +885,14 @@ freeBuffer, this); } else { err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer); - freeBuffer(NULL, + freeBuffer(nullptr, reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), tr.data_size, reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), tr.offsets_size/sizeof(binder_size_t), this); } } else { - freeBuffer(NULL, + freeBuffer(nullptr, reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), tr.data_size, reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), @@ -1074,14 +1144,26 @@ } break; + case BR_TRANSACTION_SEC_CTX: case BR_TRANSACTION: { - binder_transaction_data tr; - result = mIn.read(&tr, sizeof(tr)); + binder_transaction_data_secctx tr_secctx; + binder_transaction_data& tr = tr_secctx.transaction_data; + + if (cmd == (int) BR_TRANSACTION_SEC_CTX) { + result = mIn.read(&tr_secctx, sizeof(tr_secctx)); + } else { + result = mIn.read(&tr, sizeof(tr)); + tr_secctx.secctx = 0; + } + ALOG_ASSERT(result == NO_ERROR, "Not enough command data for brTRANSACTION"); if (result != NO_ERROR) break; + //Record the fact that we're in a binder call. + mIPCThreadStateBase->pushCurrentState( + IPCThreadStateBase::CallState::BINDER); Parcel buffer; buffer.ipcSetDataReference( reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), @@ -1090,15 +1172,25 @@ tr.offsets_size/sizeof(binder_size_t), freeBuffer, this); const pid_t origPid = mCallingPid; + const char* origSid = mCallingSid; const uid_t origUid = mCallingUid; const int32_t origStrictModePolicy = mStrictModePolicy; const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags; + const int32_t origWorkSource = mWorkSource; + const bool origPropagateWorkSet = mPropagateWorkSource; + // Calling work source will be set by Parcel#enforceInterface. Parcel#enforceInterface + // is only guaranteed to be called for AIDL-generated stubs so we reset the work source + // here to never propagate it. + clearCallingWorkSource(); + clearPropagateWorkSource(); mCallingPid = tr.sender_pid; + mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx); mCallingUid = tr.sender_euid; mLastTransactionBinderFlags = tr.flags; - //ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid); + // ALOGI(">>>> TRANSACT from pid %d sid %s uid %d\n", mCallingPid, + // (mCallingSid ? mCallingSid : "<N/A>"), mCallingUid); Parcel reply; status_t error; @@ -1129,8 +1221,9 @@ error = the_context_object->transact(tr.code, buffer, &reply, tr.flags); } - //ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n", - // mCallingPid, origPid, origUid); + mIPCThreadStateBase->popCurrentState(); + //ALOGI("<<<< TRANSACT from pid %d restore pid %d sid %s uid %d\n", + // mCallingPid, origPid, (origSid ? origSid : "<N/A>"), origUid); if ((tr.flags & TF_ONE_WAY) == 0) { LOG_ONEWAY("Sending reply to %d!", mCallingPid); @@ -1141,9 +1234,12 @@ } mCallingPid = origPid; + mCallingSid = origSid; mCallingUid = origUid; mStrictModePolicy = origStrictModePolicy; mLastTransactionBinderFlags = origTransactionBinderFlags; + mWorkSource = origWorkSource; + mPropagateWorkSource = origPropagateWorkSet; IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); @@ -1192,6 +1288,10 @@ return result; } +bool IPCThreadState::isServingCall() const { + return mIPCThreadStateBase->getCurrentBinderCallState() == IPCThreadStateBase::CallState::BINDER; +} + void IPCThreadState::threadDestructor(void *st) { IPCThreadState* const self = static_cast<IPCThreadState*>(st); @@ -1217,7 +1317,7 @@ alog << "Writing BC_FREE_BUFFER for " << data << endl; } ALOG_ASSERT(data != NULL, "Called with NULL data"); - if (parcel != NULL) parcel->closeFileDescriptors(); + if (parcel != nullptr) parcel->closeFileDescriptors(); IPCThreadState* state = self(); state->mOut.writeInt32(BC_FREE_BUFFER); state->mOut.writePointer((uintptr_t)data);
diff --git a/libs/binder/IPermissionController.cpp b/libs/binder/IPermissionController.cpp index 89ebc6c..6b99150 100644 --- a/libs/binder/IPermissionController.cpp +++ b/libs/binder/IPermissionController.cpp
@@ -109,6 +109,7 @@ // ---------------------------------------------------------------------- +// NOLINTNEXTLINE(google-default-arguments) status_t BnPermissionController::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp index 646809e..159763d 100644 --- a/libs/binder/IResultReceiver.cpp +++ b/libs/binder/IResultReceiver.cpp
@@ -40,7 +40,7 @@ Parcel data; data.writeInterfaceToken(IResultReceiver::getInterfaceDescriptor()); data.writeInt32(resultCode); - remote()->transact(OP_SEND, data, NULL, IBinder::FLAG_ONEWAY); + remote()->transact(OP_SEND, data, nullptr, IBinder::FLAG_ONEWAY); } }; @@ -48,6 +48,7 @@ // ---------------------------------------------------------------------- +// NOLINTNEXTLINE(google-default-arguments) status_t BnResultReceiver::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -56,7 +57,7 @@ CHECK_INTERFACE(IResultReceiver, data, reply); int32_t resultCode = data.readInt32(); send(resultCode); - if (reply != NULL) { + if (reply != nullptr) { reply->writeNoException(); } return NO_ERROR;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 4896f68..4ba6c2a 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp
@@ -27,7 +27,6 @@ #include <cutils/properties.h> #include <utils/String8.h> #include <utils/SystemClock.h> -#include <utils/CallStack.h> #include <private/binder/Static.h> @@ -37,14 +36,14 @@ sp<IServiceManager> defaultServiceManager() { - if (gDefaultServiceManager != NULL) return gDefaultServiceManager; + if (gDefaultServiceManager != nullptr) return gDefaultServiceManager; { AutoMutex _l(gDefaultServiceManagerLock); - while (gDefaultServiceManager == NULL) { + while (gDefaultServiceManager == nullptr) { gDefaultServiceManager = interface_cast<IServiceManager>( - ProcessState::self()->getContextObject(NULL)); - if (gDefaultServiceManager == NULL) + ProcessState::self()->getContextObject(nullptr)); + if (gDefaultServiceManager == nullptr) sleep(1); } } @@ -57,7 +56,7 @@ bool checkCallingPermission(const String16& permission) { - return checkCallingPermission(permission, NULL, NULL); + return checkCallingPermission(permission, nullptr, nullptr); } static String16 _permission("permission"); @@ -83,7 +82,7 @@ int64_t startTime = 0; while (true) { - if (pc != NULL) { + if (pc != nullptr) { bool res = pc->checkPermission(permission, pid, uid); if (res) { if (startTime != 0) { @@ -104,14 +103,14 @@ // Object is dead! gDefaultServiceManagerLock.lock(); if (gPermissionController == pc) { - gPermissionController = NULL; + gPermissionController = nullptr; } gDefaultServiceManagerLock.unlock(); } // Need to retrieve the permission controller. sp<IBinder> binder = defaultServiceManager()->checkService(_permission); - if (binder == NULL) { + if (binder == nullptr) { // Wait for the permission controller to come back... if (startTime == 0) { startTime = uptimeMillis(); @@ -144,7 +143,7 @@ virtual sp<IBinder> getService(const String16& name) const { sp<IBinder> svc = checkService(name); - if (svc != NULL) return svc; + if (svc != nullptr) return svc; const bool isVendorService = strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0; @@ -161,19 +160,15 @@ int n = 0; while (uptimeMillis() < timeout) { n++; - if (isVendorService) { - ALOGI("Waiting for vendor service %s...", String8(name).string()); - CallStack stack(LOG_TAG); - } else if (n%10 == 0) { - ALOGI("Waiting for service %s...", String8(name).string()); - } + ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(), + ProcessState::self()->getDriverName().c_str()); usleep(1000*sleepTime); sp<IBinder> svc = checkService(name); - if (svc != NULL) return svc; + if (svc != nullptr) return svc; } ALOGW("Service %s didn't start. Returning NULL", String8(name).string()); - return NULL; + return nullptr; } virtual sp<IBinder> checkService( const String16& name) const
diff --git a/libs/binder/IShellCallback.cpp b/libs/binder/IShellCallback.cpp index 23b83a6..6c697de 100644 --- a/libs/binder/IShellCallback.cpp +++ b/libs/binder/IShellCallback.cpp
@@ -58,6 +58,7 @@ // ---------------------------------------------------------------------- +// NOLINTNEXTLINE(google-default-arguments) status_t BnShellCallback::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -68,7 +69,7 @@ String16 seLinuxContext(data.readString16()); String16 mode(data.readString16()); int fd = openFile(path, seLinuxContext, mode); - if (reply != NULL) { + if (reply != nullptr) { reply->writeNoException(); if (fd >= 0) { reply->writeInt32(1);
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp index 697e948..82f9047 100644 --- a/libs/binder/IUidObserver.cpp +++ b/libs/binder/IUidObserver.cpp
@@ -55,6 +55,16 @@ data.writeInt32(disabled ? 1 : 0); remote()->transact(ON_UID_IDLE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); } + + virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq) + { + Parcel data, reply; + data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor()); + data.writeInt32((int32_t) uid); + data.writeInt32(procState); + data.writeInt64(procStateSeq); + remote()->transact(ON_UID_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); + } }; // ---------------------------------------------------------------------- @@ -89,6 +99,14 @@ onUidIdle(uid, disabled); return NO_ERROR; } break; + case ON_UID_STATE_CHANGED_TRANSACTION: { + CHECK_INTERFACE(IUidObserver, data, reply); + uid_t uid = data.readInt32(); + int32_t procState = data.readInt32(); + int64_t procStateSeq = data.readInt64(); + onUidStateChanged(uid, procState, procStateSeq); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); }
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp index 1cfe02a..eacad3b 100644 --- a/libs/binder/MemoryDealer.cpp +++ b/libs/binder/MemoryDealer.cpp
@@ -52,8 +52,8 @@ NODE* mLast; public: - LinkedList() : mFirst(0), mLast(0) { } - bool isEmpty() const { return mFirst == 0; } + LinkedList() : mFirst(nullptr), mLast(nullptr) { } + bool isEmpty() const { return mFirst == nullptr; } NODE const* head() const { return mFirst; } NODE* head() { return mFirst; } NODE const* tail() const { return mLast; } @@ -62,7 +62,7 @@ void insertAfter(NODE* node, NODE* newNode) { newNode->prev = node; newNode->next = node->next; - if (node->next == 0) mLast = newNode; + if (node->next == nullptr) mLast = newNode; else node->next->prev = newNode; node->next = newNode; } @@ -70,17 +70,17 @@ void insertBefore(NODE* node, NODE* newNode) { newNode->prev = node->prev; newNode->next = node; - if (node->prev == 0) mFirst = newNode; + if (node->prev == nullptr) mFirst = newNode; else node->prev->next = newNode; node->prev = newNode; } void insertHead(NODE* newNode) { - if (mFirst == 0) { + if (mFirst == nullptr) { mFirst = mLast = newNode; - newNode->prev = newNode->next = 0; + newNode->prev = newNode->next = nullptr; } else { - newNode->prev = 0; + newNode->prev = nullptr; newNode->next = mFirst; mFirst->prev = newNode; mFirst = newNode; @@ -99,9 +99,9 @@ } NODE* remove(NODE* node) { - if (node->prev == 0) mFirst = node->next; + if (node->prev == nullptr) mFirst = node->next; else node->prev->next = node->next; - if (node->next == 0) mLast = node->prev; + if (node->next == nullptr) mLast = node->prev; else node->next->prev = node->prev; return node; } @@ -141,7 +141,7 @@ struct chunk_t { chunk_t(size_t start, size_t size) - : start(start), size(size), free(1), prev(0), next(0) { + : start(start), size(size), free(1), prev(nullptr), next(nullptr) { } size_t start; size_t size : 28; @@ -329,7 +329,7 @@ return 0; } size = (size + kMemoryAlign-1) / kMemoryAlign; - chunk_t* free_chunk = 0; + chunk_t* free_chunk = nullptr; chunk_t* cur = mList.head(); size_t pagesize = getpagesize(); @@ -418,7 +418,7 @@ } cur = cur->next; } - return 0; + return nullptr; } void SimpleBestFitAllocator::dump(const char* what) const
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 03f00be..4c300b4 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp
@@ -36,17 +36,17 @@ MemoryHeapBase::MemoryHeapBase() : mFD(-1), mSize(0), mBase(MAP_FAILED), - mDevice(NULL), mNeedUnmap(false), mOffset(0) + mDevice(nullptr), mNeedUnmap(false), mOffset(0) { } MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), - mDevice(0), mNeedUnmap(false), mOffset(0) + mDevice(nullptr), mNeedUnmap(false), mOffset(0) { const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1)); - int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size); + 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) { @@ -59,7 +59,7 @@ MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), - mDevice(0), mNeedUnmap(false), mOffset(0) + mDevice(nullptr), mNeedUnmap(false), mOffset(0) { int open_flags = O_RDWR; if (flags & NO_CACHING) @@ -76,16 +76,16 @@ } } -MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset) +MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, off_t offset) : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), - mDevice(0), mNeedUnmap(false), mOffset(0) + mDevice(nullptr), mNeedUnmap(false), mOffset(0) { const size_t pagesize = getpagesize(); size = ((size + pagesize-1) & ~(pagesize-1)); mapfd(fcntl(fd, F_DUPFD_CLOEXEC, 0), size, offset); } -status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device) +status_t MemoryHeapBase::init(int fd, void *base, size_t size, int flags, const char* device) { if (mFD != -1) { return INVALID_OPERATION; @@ -98,30 +98,37 @@ return NO_ERROR; } -status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset) +status_t MemoryHeapBase::mapfd(int fd, size_t size, off_t offset) { if (size == 0) { // try to figure out the size automatically struct stat sb; - if (fstat(fd, &sb) == 0) - size = sb.st_size; + if (fstat(fd, &sb) == 0) { + size = (size_t)sb.st_size; + // sb.st_size is off_t which on ILP32 may be 64 bits while size_t is 32 bits. + if ((off_t)size != sb.st_size) { + ALOGE("%s: size of file %lld cannot fit in memory", + __func__, (long long)sb.st_size); + return INVALID_OPERATION; + } + } // if it didn't work, let mmap() fail. } if ((mFlags & DONT_MAP_LOCALLY) == 0) { - void* base = (uint8_t*)mmap(0, size, + void* base = (uint8_t*)mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); if (base == MAP_FAILED) { - ALOGE("mmap(fd=%d, size=%u) failed (%s)", - fd, uint32_t(size), strerror(errno)); + ALOGE("mmap(fd=%d, size=%zu) failed (%s)", + fd, size, strerror(errno)); close(fd); return -errno; } - //ALOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); + //ALOGD("mmap(fd=%d, base=%p, size=%zu)", fd, base, size); mBase = base; mNeedUnmap = true; } else { - mBase = 0; // not MAP_FAILED + mBase = nullptr; // not MAP_FAILED mNeedUnmap = false; } mFD = fd; @@ -140,10 +147,10 @@ int fd = android_atomic_or(-1, &mFD); if (fd >= 0) { if (mNeedUnmap) { - //ALOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize); + //ALOGD("munmap(fd=%d, base=%p, size=%zu)", fd, mBase, mSize); munmap(mBase, mSize); } - mBase = 0; + mBase = nullptr; mSize = 0; close(fd); } @@ -169,7 +176,7 @@ return mDevice; } -uint32_t MemoryHeapBase::getOffset() const { +off_t MemoryHeapBase::getOffset() const { return mOffset; }
diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS new file mode 100644 index 0000000..350994a --- /dev/null +++ b/libs/binder/OWNERS
@@ -0,0 +1,5 @@ +arve@google.com +ctate@google.com +hackbod@google.com +maco@google.com +smoreland@google.com
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 43e4580..5ad3027 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp
@@ -74,15 +74,7 @@ } // Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER -#define STRICT_MODE_PENALTY_GATHER (0x40 << 16) - -// XXX This can be made public if we want to provide -// support for typed data. -struct small_flat_data -{ - uint32_t type; - uint32_t data; -}; +#define STRICT_MODE_PENALTY_GATHER (1 << 31) namespace android { @@ -117,7 +109,7 @@ return; case BINDER_TYPE_HANDLE: { const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle); - if (b != NULL) { + if (b != nullptr) { LOG_REFS("Parcel %p acquiring reference on remote %p", who, b.get()); b->incStrong(who); } @@ -125,11 +117,11 @@ } case BINDER_TYPE_WEAK_HANDLE: { const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle); - if (b != NULL) b.get_refs()->incWeak(who); + if (b != nullptr) b.get_refs()->incWeak(who); return; } case BINDER_TYPE_FD: { - if ((obj.cookie != 0) && (outAshmemSize != NULL) && ashmem_valid(obj.handle)) { + if ((obj.cookie != 0) && (outAshmemSize != nullptr) && ashmem_valid(obj.handle)) { // If we own an ashmem fd, keep track of how much memory it refers to. int size = ashmem_get_size_region(obj.handle); if (size > 0) { @@ -146,7 +138,7 @@ void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj, const void* who) { - acquire_object(proc, obj, who, NULL); + acquire_object(proc, obj, who, nullptr); } static void release_object(const sp<ProcessState>& proc, @@ -165,7 +157,7 @@ return; case BINDER_TYPE_HANDLE: { const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle); - if (b != NULL) { + if (b != nullptr) { LOG_REFS("Parcel %p releasing reference on remote %p", who, b.get()); b->decStrong(who); } @@ -173,15 +165,18 @@ } case BINDER_TYPE_WEAK_HANDLE: { const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle); - if (b != NULL) b.get_refs()->decWeak(who); + if (b != nullptr) b.get_refs()->decWeak(who); return; } case BINDER_TYPE_FD: { if (obj.cookie != 0) { // owned - if ((outAshmemSize != NULL) && ashmem_valid(obj.handle)) { + if ((outAshmemSize != nullptr) && ashmem_valid(obj.handle)) { int size = ashmem_get_size_region(obj.handle); if (size > 0) { - *outAshmemSize -= size; + // ashmem size might have changed since last time it was accounted for, e.g. + // in acquire_object(). Value of *outAshmemSize is not critical since we are + // releasing the object anyway. Check for integer overflow condition. + *outAshmemSize -= std::min(*outAshmemSize, static_cast<size_t>(size)); } } @@ -197,7 +192,7 @@ void release_object(const sp<ProcessState>& proc, const flat_binder_object& obj, const void* who) { - release_object(proc, obj, who, NULL); + release_object(proc, obj, who, nullptr); } inline static status_t finish_flatten_binder( @@ -219,11 +214,11 @@ obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS; } - if (binder != NULL) { - IBinder *local = binder->localBinder(); + if (binder != nullptr) { + BBinder *local = binder->localBinder(); if (!local) { BpBinder *proxy = binder->remoteBinder(); - if (proxy == NULL) { + if (proxy == nullptr) { ALOGE("null proxy"); } const int32_t handle = proxy ? proxy->handle() : 0; @@ -232,6 +227,9 @@ obj.handle = handle; obj.cookie = 0; } else { + if (local->isRequestingSid()) { + obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX; + } obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs()); obj.cookie = reinterpret_cast<uintptr_t>(local); @@ -251,13 +249,13 @@ flat_binder_object obj; obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - if (binder != NULL) { + if (binder != nullptr) { sp<IBinder> real = binder.promote(); - if (real != NULL) { + if (real != nullptr) { IBinder *local = real->localBinder(); if (!local) { BpBinder *proxy = real->remoteBinder(); - if (proxy == NULL) { + if (proxy == nullptr) { ALOGE("null proxy"); } const int32_t handle = proxy ? proxy->handle() : 0; @@ -284,13 +282,13 @@ obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; - return finish_flatten_binder(NULL, obj, out); + return finish_flatten_binder(nullptr, obj, out); } else { obj.hdr.type = BINDER_TYPE_BINDER; obj.binder = 0; obj.cookie = 0; - return finish_flatten_binder(NULL, obj, out); + return finish_flatten_binder(nullptr, obj, out); } } @@ -310,7 +308,7 @@ switch (flat->hdr.type) { case BINDER_TYPE_BINDER: *out = reinterpret_cast<IBinder*>(flat->cookie); - return finish_unflatten_binder(NULL, *flat, in); + return finish_unflatten_binder(nullptr, *flat, in); case BINDER_TYPE_HANDLE: *out = proc->getStrongProxyForHandle(flat->handle); return finish_unflatten_binder( @@ -329,16 +327,16 @@ switch (flat->hdr.type) { case BINDER_TYPE_BINDER: *out = reinterpret_cast<IBinder*>(flat->cookie); - return finish_unflatten_binder(NULL, *flat, in); + return finish_unflatten_binder(nullptr, *flat, in); case BINDER_TYPE_WEAK_BINDER: if (flat->binder != 0) { out->set_object_and_refs( reinterpret_cast<IBinder*>(flat->cookie), reinterpret_cast<RefBase::weakref_type*>(flat->binder)); } else { - *out = NULL; + *out = nullptr; } - return finish_unflatten_binder(NULL, *flat, in); + return finish_unflatten_binder(nullptr, *flat, in); case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: *out = proc->getWeakProxyForHandle(flat->handle); @@ -467,7 +465,6 @@ status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len) { - const sp<ProcessState> proc(ProcessState::self()); status_t err; const uint8_t *data = parcel->mData; const binder_size_t *objects = parcel->mObjects; @@ -520,13 +517,14 @@ err = NO_ERROR; if (numObjects > 0) { + const sp<ProcessState> proc(ProcessState::self()); // grow objects if (mObjectsCapacity < mObjectsSize + numObjects) { size_t newSize = ((mObjectsSize + numObjects)*3)/2; if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY; // overflow binder_size_t *objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t)); - if (objects == (binder_size_t*)0) { + if (objects == (binder_size_t*)nullptr) { return NO_MEMORY; } mObjects = objects; @@ -596,15 +594,53 @@ return mHasFds; } +void Parcel::updateWorkSourceRequestHeaderPosition() const { + // Only update the request headers once. We only want to point + // to the first headers read/written. + if (!mRequestHeaderPresent) { + mWorkSourceRequestHeaderPosition = dataPosition(); + mRequestHeaderPresent = true; + } +} + // Write RPC headers. (previously just the interface token) status_t Parcel::writeInterfaceToken(const String16& interface) { - writeInt32(IPCThreadState::self()->getStrictModePolicy() | - STRICT_MODE_PENALTY_GATHER); + const IPCThreadState* threadState = IPCThreadState::self(); + writeInt32(threadState->getStrictModePolicy() | STRICT_MODE_PENALTY_GATHER); + updateWorkSourceRequestHeaderPosition(); + writeInt32(threadState->shouldPropagateWorkSource() ? + threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource); // currently the interface identification token is just its name as a string return writeString16(interface); } +bool Parcel::replaceCallingWorkSourceUid(uid_t uid) +{ + if (!mRequestHeaderPresent) { + return false; + } + + const size_t initialPosition = dataPosition(); + setDataPosition(mWorkSourceRequestHeaderPosition); + status_t err = writeInt32(uid); + setDataPosition(initialPosition); + return err == NO_ERROR; +} + +uid_t Parcel::readCallingWorkSourceUid() +{ + if (!mRequestHeaderPresent) { + return IPCThreadState::kUnsetWorkSource; + } + + const size_t initialPosition = dataPosition(); + setDataPosition(mWorkSourceRequestHeaderPosition); + uid_t uid = readInt32(); + setDataPosition(initialPosition); + return uid; +} + bool Parcel::checkInterface(IBinder* binder) const { return enforceInterface(binder->getInterfaceDescriptor()); @@ -613,8 +649,9 @@ bool Parcel::enforceInterface(const String16& interface, IPCThreadState* threadState) const { + // StrictModePolicy. int32_t strictPolicy = readInt32(); - if (threadState == NULL) { + if (threadState == nullptr) { threadState = IPCThreadState::self(); } if ((threadState->getLastTransactionBinderFlags() & @@ -627,6 +664,11 @@ } else { threadState->setStrictModePolicy(strictPolicy); } + // WorkSource. + updateWorkSourceRequestHeaderPosition(); + int32_t workSource = readInt32(); + threadState->setCallingWorkSourceUidWithoutPropagation(workSource); + // Interface descriptor. const String16 str(readString16()); if (str == interface) { return true; @@ -722,14 +764,14 @@ if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. - return NULL; + return nullptr; } const size_t padded = pad_size(len); // sanity check for integer overflow if (mDataPos+padded < mDataPos) { - return NULL; + return nullptr; } if ((mDataPos+padded) <= mDataCapacity) { @@ -760,7 +802,7 @@ status_t err = growData(padded); if (err == NO_ERROR) goto restart_write; - return NULL; + return nullptr; } status_t Parcel::writeUtf8AsUtf16(const std::string& str) { @@ -871,6 +913,16 @@ return writeNullableTypedVector(val, &Parcel::writeInt64); } +status_t Parcel::writeUint64Vector(const std::vector<uint64_t>& val) +{ + return writeTypedVector(val, &Parcel::writeUint64); +} + +status_t Parcel::writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val) +{ + return writeNullableTypedVector(val, &Parcel::writeUint64); +} + status_t Parcel::writeFloatVector(const std::vector<float>& val) { return writeTypedVector(val, &Parcel::writeFloat); @@ -1063,7 +1115,7 @@ status_t Parcel::writeString16(const char16_t* str, size_t len) { - if (str == NULL) return writeInt32(-1); + if (str == nullptr) return writeInt32(-1); status_t err = writeInt32(len); if (err == NO_ERROR) { @@ -1180,6 +1232,19 @@ return writeFileDescriptor(fd, takeOwnership); } +status_t Parcel::writeDupParcelFileDescriptor(int fd) +{ + int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0); + if (dupFd < 0) { + return -errno; + } + status_t err = writeParcelFileDescriptor(dupFd, true /*takeOwnership*/); + if (err != OK) { + close(dupFd); + } + return err; +} + status_t Parcel::writeUniqueFileDescriptor(const base::unique_fd& fd) { return writeDupFileDescriptor(fd.get()); } @@ -1221,7 +1286,7 @@ if (result < 0) { status = result; } else { - void* ptr = ::mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + void* ptr = ::mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) { status = -errno; } else { @@ -1278,10 +1343,10 @@ // payload void* const buf = this->writeInplace(len); - if (buf == NULL) + if (buf == nullptr) return BAD_VALUE; - int* fds = NULL; + int* fds = nullptr; if (fd_count) { fds = new (std::nothrow) int[fd_count]; if (fds == nullptr) { @@ -1337,7 +1402,7 @@ size_t newSize = ((mObjectsSize+2)*3)/2; if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY; // overflow binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t)); - if (objects == NULL) return NO_MEMORY; + if (objects == nullptr) return NO_MEMORY; mObjects = objects; mObjectsCapacity = newSize; } @@ -1383,7 +1448,7 @@ status_t Parcel::writeNullableMap(const std::unique_ptr<binder::Map>& map) { - if (map == NULL) { + if (map == nullptr) { return writeInt32(-1); } @@ -1555,7 +1620,7 @@ if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. - return NULL; + return nullptr; } if ((mDataPos+pad_size(len)) >= mDataPos && (mDataPos+pad_size(len)) <= mDataSize @@ -1566,7 +1631,7 @@ // Still increment the data position by the expected length mDataPos += pad_size(len); ALOGV("readInplace Setting data pos of %p to %zu", this, mDataPos); - return NULL; + return nullptr; } } @@ -1575,7 +1640,7 @@ ALOGV("readInplace Setting data pos of %p to %zu", this, mDataPos); return data; } - return NULL; + return nullptr; } template<class T> @@ -1719,6 +1784,14 @@ return readTypedVector(val, &Parcel::readInt64); } +status_t Parcel::readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const { + return readNullableTypedVector(val, &Parcel::readUint64); +} + +status_t Parcel::readUint64Vector(std::vector<uint64_t>* val) const { + return readTypedVector(val, &Parcel::readUint64); +} + status_t Parcel::readFloatVector(std::unique_ptr<std::vector<float>>* val) const { return readNullableTypedVector(val, &Parcel::readFloat); } @@ -2025,7 +2098,7 @@ return str; } } - return NULL; + return nullptr; } String8 Parcel::readString8() const @@ -2056,7 +2129,7 @@ return OK; } const char* str = (const char*)readInplace(size + 1); - if (str == NULL) { + if (str == nullptr) { return BAD_VALUE; } pArg->setTo(str, size); @@ -2115,12 +2188,12 @@ if (size >= 0 && size < INT32_MAX) { *outLen = size; const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t)); - if (str != NULL) { + if (str != nullptr) { return str; } } *outLen = 0; - return NULL; + return nullptr; } status_t Parcel::readStrongBinder(sp<IBinder>* val) const @@ -2182,13 +2255,13 @@ int numFds, numInts; status_t err; err = readInt32(&numFds); - if (err != NO_ERROR) return 0; + if (err != NO_ERROR) return nullptr; err = readInt32(&numInts); - if (err != NO_ERROR) return 0; + if (err != NO_ERROR) return nullptr; native_handle* h = native_handle_create(numFds, numInts); if (!h) { - return 0; + return nullptr; } for (int i=0 ; err==NO_ERROR && i<numFds ; i++) { @@ -2198,14 +2271,14 @@ close(h->data[j]); } native_handle_delete(h); - return 0; + return nullptr; } } err = read(h->data + numFds, sizeof(int)*numInts); if (err != NO_ERROR) { native_handle_close(h); native_handle_delete(h); - h = 0; + h = nullptr; } return h; } @@ -2226,8 +2299,30 @@ int32_t hasComm = readInt32(); int fd = readFileDescriptor(); if (hasComm != 0) { - // skip - readFileDescriptor(); + // detach (owned by the binder driver) + int comm = readFileDescriptor(); + + // warning: this must be kept in sync with: + // frameworks/base/core/java/android/os/ParcelFileDescriptor.java + enum ParcelFileDescriptorStatus { + DETACHED = 2, + }; + +#if BYTE_ORDER == BIG_ENDIAN + const int32_t message = ParcelFileDescriptorStatus::DETACHED; +#endif +#if BYTE_ORDER == LITTLE_ENDIAN + const int32_t message = __builtin_bswap32(ParcelFileDescriptorStatus::DETACHED); +#endif + + ssize_t written = TEMP_FAILURE_RETRY( + ::write(comm, &message, sizeof(message))); + + if (written == -1 || written != sizeof(message)) { + ALOGW("Failed to detach ParcelFileDescriptor written: %zd err: %s", + written, strerror(errno)); + return BAD_TYPE; + } } return fd; } @@ -2249,6 +2344,22 @@ return OK; } +status_t Parcel::readUniqueParcelFileDescriptor(base::unique_fd* val) const +{ + int got = readParcelFileDescriptor(); + + if (got == BAD_TYPE) { + return BAD_TYPE; + } + + val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0)); + + if (val->get() < 0) { + return BAD_VALUE; + } + + return OK; +} status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const { return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor); @@ -2278,7 +2389,16 @@ int fd = readFileDescriptor(); if (fd == int(BAD_TYPE)) return BAD_VALUE; - void* ptr = ::mmap(NULL, len, isMutable ? PROT_READ | PROT_WRITE : PROT_READ, + if (!ashmem_valid(fd)) { + ALOGE("invalid fd"); + return BAD_VALUE; + } + int size = ashmem_get_size_region(fd); + if (size < 0 || size_t(size) < len) { + ALOGE("request size %zu does not match fd size %d", len, size); + return BAD_VALUE; + } + void* ptr = ::mmap(nullptr, len, isMutable ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, fd, 0); if (ptr == MAP_FAILED) return NO_MEMORY; @@ -2300,10 +2420,10 @@ // payload void const* const buf = this->readInplace(pad_size(len)); - if (buf == NULL) + if (buf == nullptr) return BAD_VALUE; - int* fds = NULL; + int* fds = nullptr; if (fd_count) { fds = new (std::nothrow) int[fd_count]; if (fds == nullptr) { @@ -2394,7 +2514,7 @@ ALOGW("Attempt to read object from Parcel %p at offset %zu that is not in the object list", this, DPOS); } - return NULL; + return nullptr; } void Parcel::closeFileDescriptors() @@ -2492,8 +2612,11 @@ void Parcel::releaseObjects() { - const sp<ProcessState> proc(ProcessState::self()); size_t i = mObjectsSize; + if (i == 0) { + return; + } + sp<ProcessState> proc(ProcessState::self()); uint8_t* const data = mData; binder_size_t* const objects = mObjects; while (i > 0) { @@ -2506,8 +2629,11 @@ void Parcel::acquireObjects() { - const sp<ProcessState> proc(ProcessState::self()); size_t i = mObjectsSize; + if (i == 0) { + return; + } + const sp<ProcessState> proc(ProcessState::self()); uint8_t* const data = mData; binder_size_t* const objects = mObjects; while (i > 0) { @@ -2604,7 +2730,7 @@ ALOGV("restartWrite Setting data pos of %p to %zu", this, mDataPos); free(mObjects); - mObjects = NULL; + mObjects = nullptr; mObjectsSize = mObjectsCapacity = 0; mNextObjectHint = 0; mObjectsSorted = false; @@ -2652,7 +2778,7 @@ mError = NO_MEMORY; return NO_MEMORY; } - binder_size_t* objects = NULL; + binder_size_t* objects = nullptr; if (objectsSize) { objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t)); @@ -2679,7 +2805,7 @@ } //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid()); mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); - mOwner = NULL; + mOwner = nullptr; LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired); pthread_mutex_lock(&gParcelGlobalAllocSizeLock); @@ -2762,7 +2888,7 @@ return NO_MEMORY; } - if(!(mDataCapacity == 0 && mObjects == NULL + if(!(mDataCapacity == 0 && mObjects == nullptr && mObjectsCapacity == 0)) { ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity, mObjects, mObjectsCapacity, desired); } @@ -2787,13 +2913,13 @@ { LOG_ALLOC("Parcel %p: initState", this); mError = NO_ERROR; - mData = 0; + mData = nullptr; mDataSize = 0; mDataCapacity = 0; mDataPos = 0; ALOGV("initState Setting data size of %p to %zu", this, mDataSize); ALOGV("initState Setting data pos of %p to %zu", this, mDataPos); - mObjects = NULL; + mObjects = nullptr; mObjectsSize = 0; mObjectsCapacity = 0; mNextObjectHint = 0; @@ -2801,8 +2927,10 @@ mHasFds = false; mFdsKnown = true; mAllowFds = true; - mOwner = NULL; + mOwner = nullptr; mOpenAshmemSize = 0; + mWorkSourceRequestHeaderPosition = 0; + mRequestHeaderPresent = false; // racing multiple init leads only to multiple identical write if (gMaxFds == 0) { @@ -2848,7 +2976,7 @@ // --- Parcel::Blob --- Parcel::Blob::Blob() : - mFd(-1), mData(NULL), mSize(0), mMutable(false) { + mFd(-1), mData(nullptr), mSize(0), mMutable(false) { } Parcel::Blob::~Blob() { @@ -2871,7 +2999,7 @@ void Parcel::Blob::clear() { mFd = -1; - mData = NULL; + mData = nullptr; mSize = 0; mMutable = false; }
diff --git a/libs/binder/ParcelFileDescriptor.cpp b/libs/binder/ParcelFileDescriptor.cpp new file mode 100644 index 0000000..4f8b76f --- /dev/null +++ b/libs/binder/ParcelFileDescriptor.cpp
@@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/ParcelFileDescriptor.h> + +namespace android { +namespace os { + +ParcelFileDescriptor::ParcelFileDescriptor() = default; + +ParcelFileDescriptor::ParcelFileDescriptor(android::base::unique_fd fd) : mFd(std::move(fd)) {} + +ParcelFileDescriptor::~ParcelFileDescriptor() = default; + +status_t ParcelFileDescriptor::writeToParcel(Parcel* parcel) const { + return parcel->writeDupParcelFileDescriptor(mFd.get()); +} + +status_t ParcelFileDescriptor::readFromParcel(const Parcel* parcel) { + return parcel->readUniqueParcelFileDescriptor(&mFd); +} + +} // namespace os +} // namespace android
diff --git a/libs/binder/PermissionCache.cpp b/libs/binder/PermissionCache.cpp index a503be8..a4c28ad 100644 --- a/libs/binder/PermissionCache.cpp +++ b/libs/binder/PermissionCache.cpp
@@ -75,7 +75,7 @@ } bool PermissionCache::checkCallingPermission(const String16& permission) { - return PermissionCache::checkCallingPermission(permission, NULL, NULL); + return PermissionCache::checkCallingPermission(permission, nullptr, nullptr); } bool PermissionCache::checkCallingPermission(
diff --git a/libs/binder/PermissionController.cpp b/libs/binder/PermissionController.cpp index 96df33c..34b2ca5 100644 --- a/libs/binder/PermissionController.cpp +++ b/libs/binder/PermissionController.cpp
@@ -41,7 +41,7 @@ ALOGI("Waiting for permission service"); } else if ((uptimeMillis() - startTime) > 10000) { ALOGW("Waiting too long for permission service, giving up"); - service = NULL; + service = nullptr; break; } sleep(1); @@ -56,13 +56,13 @@ bool PermissionController::checkPermission(const String16& permission, int32_t pid, int32_t uid) { sp<IPermissionController> service = getService(); - return service != NULL ? service->checkPermission(permission, pid, uid) : false; + return service != nullptr ? service->checkPermission(permission, pid, uid) : false; } int32_t PermissionController::noteOp(const String16& op, int32_t uid, const String16& packageName) { sp<IPermissionController> service = getService(); - return service != NULL ? service->noteOp(op, uid, packageName) : MODE_ERRORED; + return service != nullptr ? service->noteOp(op, uid, packageName) : MODE_ERRORED; } void PermissionController::getPackagesForUid(const uid_t uid, Vector<String16> &packages)
diff --git a/libs/binder/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp index 8939d9c..5cb2033 100644 --- a/libs/binder/ProcessInfoService.cpp +++ b/libs/binder/ProcessInfoService.cpp
@@ -36,7 +36,7 @@ for (int i = 0; i < BINDER_ATTEMPT_LIMIT; i++) { - if (pis != NULL) { + if (pis != nullptr) { err = pis->getProcessStatesFromPids(length, /*in*/ pids, /*out*/ states); if (err == NO_ERROR) return NO_ERROR; // success if (IInterface::asBinder(pis)->isBinderAlive()) return err; @@ -68,7 +68,7 @@ for (int i = 0; i < BINDER_ATTEMPT_LIMIT; i++) { - if (pis != NULL) { + if (pis != nullptr) { err = pis->getProcessStatesAndOomScoresFromPids(length, /*in*/ pids, /*out*/ states, /*out*/ scores); if (err == NO_ERROR) return NO_ERROR; // success @@ -93,7 +93,7 @@ void ProcessInfoService::updateBinderLocked() { const sp<IServiceManager> sm(defaultServiceManager()); - if (sm != NULL) { + if (sm != nullptr) { const String16 name("processinfo"); mProcessInfoService = interface_cast<IProcessInfoService>(sm->checkService(name)); }
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index d1c6f84..63f49dd 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp
@@ -43,6 +43,12 @@ #define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2) #define DEFAULT_MAX_BINDER_THREADS 15 +#ifdef __ANDROID_VNDK__ +const char* kDefaultDriver = "/dev/vndbinder"; +#else +const char* kDefaultDriver = "/dev/binder"; +#endif + // ------------------------------------------------------------------------- namespace android { @@ -68,17 +74,17 @@ sp<ProcessState> ProcessState::self() { Mutex::Autolock _l(gProcessMutex); - if (gProcess != NULL) { + if (gProcess != nullptr) { return gProcess; } - gProcess = new ProcessState("/dev/binder"); + gProcess = new ProcessState(kDefaultDriver); return gProcess; } sp<ProcessState> ProcessState::initWithDriver(const char* driver) { Mutex::Autolock _l(gProcessMutex); - if (gProcess != NULL) { + if (gProcess != nullptr) { // Allow for initWithDriver to be called repeatedly with the same // driver. if (!strcmp(gProcess->getDriverName().c_str(), driver)) { @@ -122,18 +128,18 @@ { mLock.lock(); sp<IBinder> object( - mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : NULL); + mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : nullptr); mLock.unlock(); //printf("Getting context object %s for %p\n", String8(name).string(), caller.get()); - if (object != NULL) return object; + if (object != nullptr) return object; // Don't attempt to retrieve contexts if we manage them if (mManagesContexts) { ALOGE("getContextObject(%s) failed, but we manage the contexts!\n", String8(name).string()); - return NULL; + return nullptr; } IPCThreadState* ipc = IPCThreadState::self(); @@ -150,7 +156,7 @@ ipc->flushCommands(); - if (object != NULL) setContextObject(object, name); + if (object != nullptr) setContextObject(object, name); return object; } @@ -175,13 +181,25 @@ mBinderContextCheckFunc = checkFunc; mBinderContextUserData = userData; - int dummy = 0; - status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); + flat_binder_object obj { + .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX, + }; + + status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj); + + // fallback to original method + if (result != 0) { + android_errorWriteLog(0x534e4554, "121035042"); + + int dummy = 0; + result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); + } + if (result == 0) { mManagesContexts = true; } else if (result == -1) { - mBinderContextCheckFunc = NULL; - mBinderContextUserData = NULL; + mBinderContextCheckFunc = nullptr; + mBinderContextUserData = nullptr; ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); } } @@ -196,18 +214,9 @@ // already be invalid. ssize_t ProcessState::getKernelReferences(size_t buf_count, uintptr_t* buf) { - // TODO: remove these when they are defined by bionic's binder.h - struct binder_node_debug_info { - binder_uintptr_t ptr; - binder_uintptr_t cookie; - __u32 has_strong_ref; - __u32 has_weak_ref; - }; -#define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info) - binder_node_debug_info info = {}; - uintptr_t* end = buf ? buf + buf_count : NULL; + uintptr_t* end = buf ? buf + buf_count : nullptr; size_t count = 0; do { @@ -228,15 +237,21 @@ return count; } +void ProcessState::setCallRestriction(CallRestriction restriction) { + LOG_ALWAYS_FATAL_IF(IPCThreadState::selfOrNull(), "Call restrictions must be set before the threadpool is started."); + + mCallRestriction = restriction; +} + ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle) { const size_t N=mHandleToObject.size(); if (N <= (size_t)handle) { handle_entry e; - e.binder = NULL; - e.refs = NULL; + e.binder = nullptr; + e.refs = nullptr; status_t err = mHandleToObject.insertAt(e, N, handle+1-N); - if (err < NO_ERROR) return NULL; + if (err < NO_ERROR) return nullptr; } return &mHandleToObject.editItemAt(handle); } @@ -249,12 +264,12 @@ handle_entry* e = lookupHandleLocked(handle); - if (e != NULL) { + if (e != nullptr) { // We need to create a new BpBinder if there isn't currently one, OR we // are unable to acquire a weak reference on this current one. See comment // in getWeakProxyForHandle() for more info about this. IBinder* b = e->binder; - if (b == NULL || !e->refs->attemptIncWeak(this)) { + if (b == nullptr || !e->refs->attemptIncWeak(this)) { if (handle == 0) { // Special case for context manager... // The context manager is the only object for which we create @@ -277,9 +292,9 @@ Parcel data; status_t status = IPCThreadState::self()->transact( - 0, IBinder::PING_TRANSACTION, data, NULL, 0); + 0, IBinder::PING_TRANSACTION, data, nullptr, 0); if (status == DEAD_OBJECT) - return NULL; + return nullptr; } b = BpBinder::create(handle); @@ -306,7 +321,7 @@ handle_entry* e = lookupHandleLocked(handle); - if (e != NULL) { + if (e != nullptr) { // We need to create a new BpBinder if there isn't currently one, OR we // are unable to acquire a weak reference on this current one. The // attemptIncWeak() is safe because we know the BpBinder destructor will always @@ -315,7 +330,7 @@ // releasing a reference on this BpBinder, and a new reference on its handle // arriving from the driver. IBinder* b = e->binder; - if (b == NULL || !e->refs->attemptIncWeak(this)) { + if (b == nullptr || !e->refs->attemptIncWeak(this)) { b = BpBinder::create(handle); result = b; e->binder = b; @@ -338,7 +353,7 @@ // This handle may have already been replaced with a new BpBinder // (if someone failed the AttemptIncWeak() above); we don't want // to overwrite it. - if (e && e->binder == binder) e->binder = NULL; + if (e && e->binder == binder) e->binder = nullptr; } String8 ProcessState::makeBinderThreadName() { @@ -416,14 +431,15 @@ , mMaxThreads(DEFAULT_MAX_BINDER_THREADS) , mStarvationStartTimeMs(0) , mManagesContexts(false) - , mBinderContextCheckFunc(NULL) - , mBinderContextUserData(NULL) + , mBinderContextCheckFunc(nullptr) + , mBinderContextUserData(nullptr) , mThreadPoolStarted(false) , mThreadPoolSeq(1) + , mCallRestriction(CallRestriction::NONE) { if (mDriverFD >= 0) { // mmap the binder, providing a chunk of virtual address space to receive transactions. - mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); + mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); if (mVMStart == MAP_FAILED) { // *sigh* ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp index d6d0340..bd0e6f9 100644 --- a/libs/binder/Static.cpp +++ b/libs/binder/Static.cpp
@@ -75,21 +75,6 @@ Mutex& gProcessMutex = *new Mutex; sp<ProcessState> gProcess; -class LibBinderIPCtStatics -{ -public: - LibBinderIPCtStatics() - { - } - - ~LibBinderIPCtStatics() - { - IPCThreadState::shutdown(); - } -}; - -static LibBinderIPCtStatics gIPCStatics; - // ------------ IServiceManager.cpp Mutex gDefaultServiceManagerLock;
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp index fe0e592..0ad99ce 100644 --- a/libs/binder/Status.cpp +++ b/libs/binder/Status.cpp
@@ -24,11 +24,17 @@ } Status Status::fromExceptionCode(int32_t exceptionCode) { + if (exceptionCode == EX_TRANSACTION_FAILED) { + return Status(exceptionCode, FAILED_TRANSACTION); + } return Status(exceptionCode, OK); } Status Status::fromExceptionCode(int32_t exceptionCode, const String8& message) { + if (exceptionCode == EX_TRANSACTION_FAILED) { + return Status(exceptionCode, FAILED_TRANSACTION, message); + } return Status(exceptionCode, OK, message); } @@ -57,6 +63,26 @@ return ret; } +std::string Status::exceptionToString(int32_t exceptionCode) { + switch (exceptionCode) { + #define EXCEPTION_TO_CASE(EXCEPTION) case EXCEPTION: return #EXCEPTION; + EXCEPTION_TO_CASE(EX_NONE) + EXCEPTION_TO_CASE(EX_SECURITY) + EXCEPTION_TO_CASE(EX_BAD_PARCELABLE) + EXCEPTION_TO_CASE(EX_ILLEGAL_ARGUMENT) + EXCEPTION_TO_CASE(EX_NULL_POINTER) + EXCEPTION_TO_CASE(EX_ILLEGAL_STATE) + EXCEPTION_TO_CASE(EX_NETWORK_MAIN_THREAD) + EXCEPTION_TO_CASE(EX_UNSUPPORTED_OPERATION) + EXCEPTION_TO_CASE(EX_SERVICE_SPECIFIC) + EXCEPTION_TO_CASE(EX_PARCELABLE) + EXCEPTION_TO_CASE(EX_HAS_REPLY_HEADER) + EXCEPTION_TO_CASE(EX_TRANSACTION_FAILED) + #undef EXCEPTION_TO_CASE + default: return std::to_string(exceptionCode); + } +} + Status::Status(int32_t exceptionCode, int32_t errorCode) : mException(exceptionCode), mErrorCode(errorCode) {} @@ -185,7 +211,7 @@ void Status::setException(int32_t ex, const String8& message) { mException = ex; - mErrorCode = NO_ERROR; // an exception, not a transaction failure. + mErrorCode = ex == EX_TRANSACTION_FAILED ? FAILED_TRANSACTION : NO_ERROR; mMessage.setTo(message); } @@ -205,7 +231,7 @@ if (mException == EX_NONE) { ret.append("No error"); } else { - ret.appendFormat("Status(%d): '", mException); + ret.appendFormat("Status(%d, %s): '", mException, exceptionToString(mException).c_str()); if (mException == EX_SERVICE_SPECIFIC || mException == EX_TRANSACTION_FAILED) { ret.appendFormat("%d: ", mErrorCode);
diff --git a/libs/binder/Value.cpp b/libs/binder/Value.cpp index 85cd739..19c57ba 100644 --- a/libs/binder/Value.cpp +++ b/libs/binder/Value.cpp
@@ -97,7 +97,7 @@ template<typename T> class Value::Content : public Value::ContentBase { public: Content() = default; - Content(const T & value) : mValue(value) { } + explicit Content(const T & value) : mValue(value) { } virtual ~Content() = default; @@ -143,12 +143,12 @@ // ==================================================================== -Value::Value() : mContent(NULL) +Value::Value() : mContent(nullptr) { } Value::Value(const Value& value) - : mContent(value.mContent ? value.mContent->clone() : NULL) + : mContent(value.mContent ? value.mContent->clone() : nullptr) { } @@ -165,8 +165,8 @@ return true; } - if ( (lhs.mContent == NULL) - || (rhs.mContent == NULL) + if ( (lhs.mContent == nullptr) + || (rhs.mContent == nullptr) ) { return false; } @@ -186,25 +186,25 @@ delete mContent; mContent = rhs.mContent ? rhs.mContent->clone() - : NULL; + : nullptr; } return *this; } bool Value::empty() const { - return mContent == NULL; + return mContent == nullptr; } void Value::clear() { delete mContent; - mContent = NULL; + mContent = nullptr; } int32_t Value::parcelType() const { - const void* t_info(mContent ? mContent->type_ptr() : NULL); + const void* t_info(mContent ? mContent->type_ptr() : nullptr); if (t_info == internal_type_ptr<bool>()) return VAL_BOOLEAN; if (t_info == internal_type_ptr<uint8_t>()) return VAL_BYTE; @@ -229,7 +229,7 @@ #ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO const std::type_info& Value::type() const { - return mContent != NULL + return mContent != nullptr ? mContent->type() : typeid(void); } @@ -306,7 +306,7 @@ #define BEGIN_HANDLE_WRITE() \ do { \ - const void* t_info(mContent?mContent->type_ptr():NULL); \ + const void* t_info(mContent?mContent->type_ptr():nullptr); \ if (false) { } #define HANDLE_WRITE_TYPE(T, TYPEVAL, TYPEMETHOD) \ else if (t_info == internal_type_ptr<T>()) { \ @@ -381,7 +381,7 @@ int32_t value_type = VAL_NULL; delete mContent; - mContent = NULL; + mContent = nullptr; RETURN_IF_FAILED(parcel->readInt32(&value_type));
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl index 5b66b92..a7a7292 100644 --- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl +++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -54,4 +54,37 @@ long getVersionCodeForPackage(in String packageName); + /** + * Return if each app, identified by its package name allows its audio to be recorded. + * Unknown packages are mapped to false. + */ + boolean[] isAudioPlaybackCaptureAllowed(in @utf8InCpp String[] packageNames); + + /* ApplicationInfo.isSystemApp() == true */ + const int LOCATION_SYSTEM = 0x1; + /* ApplicationInfo.isVendor() == true */ + const int LOCATION_VENDOR = 0x2; + /* ApplicationInfo.isProduct() == true */ + const int LOCATION_PRODUCT = 0x4; + + /** + * Returns a set of bitflags about package location. + * LOCATION_SYSTEM: getApplicationInfo(packageName).isSystemApp() + * LOCATION_VENDOR: getApplicationInfo(packageName).isVendor() + * LOCATION_PRODUCT: getApplicationInfo(packageName).isProduct() + */ + int getLocationFlags(in @utf8InCpp String packageName); + + /** + * Returns the target SDK version for the given package. + * Unknown packages will cause the call to fail. The caller must check the + * returned Status before using the result of this function. + */ + int getTargetSdkVersionForPackage(in String packageName); + + /** + * Returns the name of module metadata package, or empty string if device doesn't have such + * package. + */ + @utf8InCpp String getModuleMetadataPackageName(); }
diff --git a/libs/binder/aidl/android/content/pm/OWNERS b/libs/binder/aidl/android/content/pm/OWNERS new file mode 100644 index 0000000..b99ca09 --- /dev/null +++ b/libs/binder/aidl/android/content/pm/OWNERS
@@ -0,0 +1,4 @@ +narayan@google.com +patb@google.com +svetoslavganov@google.com +toddke@google.com \ No newline at end of file
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h index b8db091..5f324c7 100644 --- a/libs/binder/include/binder/ActivityManager.h +++ b/libs/binder/include/binder/ActivityManager.h
@@ -31,6 +31,8 @@ public: enum { + // Flag for registerUidObserver: report uid state changed + UID_OBSERVER_PROCSTATE = 1<<0, // Flag for registerUidObserver: report uid gone UID_OBSERVER_GONE = 1<<1, // Flag for registerUidObserver: report uid has become idle @@ -40,8 +42,29 @@ }; enum { - // Not a real process state - PROCESS_STATE_UNKNOWN = -1 + PROCESS_STATE_UNKNOWN = -1, + PROCESS_STATE_PERSISTENT = 0, + PROCESS_STATE_PERSISTENT_UI = 1, + PROCESS_STATE_TOP = 2, + PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3, + PROCESS_STATE_BOUND_TOP = 4, + PROCESS_STATE_FOREGROUND_SERVICE = 5, + PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6, + PROCESS_STATE_IMPORTANT_FOREGROUND = 7, + PROCESS_STATE_IMPORTANT_BACKGROUND = 8, + PROCESS_STATE_TRANSIENT_BACKGROUND = 9, + PROCESS_STATE_BACKUP = 10, + PROCESS_STATE_SERVICE = 11, + PROCESS_STATE_RECEIVER = 12, + PROCESS_STATE_TOP_SLEEPING = 13, + PROCESS_STATE_HEAVY_WEIGHT = 14, + PROCESS_STATE_HOME = 15, + PROCESS_STATE_LAST_ACTIVITY = 16, + PROCESS_STATE_CACHED_ACTIVITY = 17, + PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18, + PROCESS_STATE_CACHED_RECENT = 19, + PROCESS_STATE_CACHED_EMPTY = 20, + PROCESS_STATE_NONEXISTENT = 21, }; ActivityManager(); @@ -53,8 +76,10 @@ const String16& callingPackage); 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 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/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h index c5b57c7..17493b4 100644 --- a/libs/binder/include/binder/AppOpsManager.h +++ b/libs/binder/include/binder/AppOpsManager.h
@@ -95,11 +95,27 @@ OP_USE_FINGERPRINT = 55, OP_BODY_SENSORS = 56, OP_AUDIO_ACCESSIBILITY_VOLUME = 64, + OP_READ_PHONE_NUMBERS = 65, + OP_REQUEST_INSTALL_PACKAGES = 66, + OP_PICTURE_IN_PICTURE = 67, + OP_INSTANT_APP_START_FOREGROUND = 68, + OP_ANSWER_PHONE_CALLS = 69, + OP_RUN_ANY_IN_BACKGROUND = 70, + OP_CHANGE_WIFI_STATE = 71, + OP_REQUEST_DELETE_PACKAGES = 72, + OP_BIND_ACCESSIBILITY_SERVICE = 73, + OP_ACCEPT_HANDOVER = 74, + OP_MANAGE_IPSEC_TUNNELS = 75, + OP_START_FOREGROUND = 76, + OP_BLUETOOTH_SCAN = 77, + OP_USE_BIOMETRIC = 78, }; AppOpsManager(); int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage); + int32_t checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid, + const String16& callingPackage); int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage); int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, bool startIfModeDefault);
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h index 3404881..cf3ef84 100644 --- a/libs/binder/include/binder/Binder.h +++ b/libs/binder/include/binder/Binder.h
@@ -34,19 +34,22 @@ virtual status_t pingBinder(); virtual status_t dump(int fd, const Vector<String16>& args); + // NOLINTNEXTLINE(google-default-arguments) virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); + // NOLINTNEXTLINE(google-default-arguments) virtual status_t linkToDeath(const sp<DeathRecipient>& recipient, - void* cookie = NULL, + void* cookie = nullptr, uint32_t flags = 0); + // NOLINTNEXTLINE(google-default-arguments) virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient, - void* cookie = NULL, + void* cookie = nullptr, uint32_t flags = 0, - wp<DeathRecipient>* outRecipient = NULL); + wp<DeathRecipient>* outRecipient = nullptr); virtual void attachObject( const void* objectID, void* object, @@ -57,9 +60,14 @@ virtual BBinder* localBinder(); + bool isRequestingSid(); + // This must be called before the object is sent to another process. Not thread safe. + void setRequestingSid(bool requestSid); + protected: virtual ~BBinder(); + // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, @@ -71,6 +79,8 @@ class Extras; + Extras* getOrCreateExtras(); + std::atomic<Extras*> mExtras; void* mReserved0; };
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 8bd297b..1d4f881 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h
@@ -41,18 +41,22 @@ virtual status_t pingBinder(); virtual status_t dump(int fd, const Vector<String16>& args); + // NOLINTNEXTLINE(google-default-arguments) virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); + // NOLINTNEXTLINE(google-default-arguments) virtual status_t linkToDeath(const sp<DeathRecipient>& recipient, - void* cookie = NULL, + void* cookie = nullptr, uint32_t flags = 0); + + // NOLINTNEXTLINE(google-default-arguments) virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient, - void* cookie = NULL, + void* cookie = nullptr, uint32_t flags = 0, - wp<DeathRecipient>* outRecipient = NULL); + wp<DeathRecipient>* outRecipient = nullptr); virtual void attachObject( const void* objectID, void* object,
diff --git a/libs/binder/include/binder/BufferedTextOutput.h b/libs/binder/include/binder/BufferedTextOutput.h index 9a7c43b..feae93d 100644 --- a/libs/binder/include/binder/BufferedTextOutput.h +++ b/libs/binder/include/binder/BufferedTextOutput.h
@@ -32,7 +32,7 @@ MULTITHREADED = 0x0001 }; - BufferedTextOutput(uint32_t flags = 0); + explicit BufferedTextOutput(uint32_t flags = 0); virtual ~BufferedTextOutput(); virtual status_t print(const char* txt, size_t len);
diff --git a/libs/binder/include/binder/Debug.h b/libs/binder/include/binder/Debug.h index be0266c..58e2b32 100644 --- a/libs/binder/include/binder/Debug.h +++ b/libs/binder/include/binder/Debug.h
@@ -31,12 +31,12 @@ typedef void (*debugPrintFunc)(void* cookie, const char* txt); void printTypeCode(uint32_t typeCode, - debugPrintFunc func = 0, void* cookie = 0); + debugPrintFunc func = nullptr, void* cookie = nullptr); void printHexData(int32_t indent, const void *buf, size_t length, size_t bytesPerLine=16, int32_t singleLineBytesCutoff=16, size_t alignment=0, bool cArrayStyle=false, - debugPrintFunc func = 0, void* cookie = 0); + debugPrintFunc func = nullptr, void* cookie = nullptr); ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf);
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h index f34969b..6abc071 100644 --- a/libs/binder/include/binder/IActivityManager.h +++ b/libs/binder/include/binder/IActivityManager.h
@@ -38,12 +38,14 @@ const String16& callingPackage) = 0; 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; enum { OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, REGISTER_UID_OBSERVER_TRANSACTION, UNREGISTER_UID_OBSERVER_TRANSACTION, - IS_UID_ACTIVE_TRANSACTION + IS_UID_ACTIVE_TRANSACTION, + GET_UID_PROCESS_STATE_TRANSACTION }; };
diff --git a/libs/binder/include/binder/IAppOpsCallback.h b/libs/binder/include/binder/IAppOpsCallback.h index e5b12a9..b500219 100644 --- a/libs/binder/include/binder/IAppOpsCallback.h +++ b/libs/binder/include/binder/IAppOpsCallback.h
@@ -43,6 +43,7 @@ class BnAppOpsCallback : public BnInterface<IAppOpsCallback> { public: + // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply,
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h index f0c5e17..3dbd0d9 100644 --- a/libs/binder/include/binder/IAppOpsService.h +++ b/libs/binder/include/binder/IAppOpsService.h
@@ -43,6 +43,8 @@ virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0; virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0; virtual int32_t permissionToOpCode(const String16& permission) = 0; + virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid, + const String16& packageName) = 0; enum { CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, @@ -53,6 +55,7 @@ STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5, GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6, PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7, + CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8, }; enum { @@ -67,6 +70,7 @@ class BnAppOpsService : public BnInterface<IAppOpsService> { public: + // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply,
diff --git a/libs/binder/include/binder/IBatteryStats.h b/libs/binder/include/binder/IBatteryStats.h index 59e806c..48da865 100644 --- a/libs/binder/include/binder/IBatteryStats.h +++ b/libs/binder/include/binder/IBatteryStats.h
@@ -68,6 +68,7 @@ class BnBatteryStats : public BnInterface<IBatteryStats> { public: + // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply,
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h index 2e62957..aa44285 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h
@@ -47,7 +47,7 @@ * (method calls, property get and set) is down through a low-level * protocol implemented on top of the transact() API. */ -class IBinder : public virtual RefBase +class [[clang::lto_visibility_public]] IBinder : public virtual RefBase { public: enum { @@ -86,6 +86,7 @@ Vector<String16>& args, const sp<IShellCallback>& callback, const sp<IResultReceiver>& resultReceiver); + // NOLINTNEXTLINE(google-default-arguments) virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, @@ -131,8 +132,9 @@ * (Nor should you need to, as there is nothing useful you can * directly do with it now that it has passed on.) */ + // NOLINTNEXTLINE(google-default-arguments) virtual status_t linkToDeath(const sp<DeathRecipient>& recipient, - void* cookie = NULL, + void* cookie = nullptr, uint32_t flags = 0) = 0; /** @@ -141,21 +143,45 @@ * dies. The @a cookie is optional. If non-NULL, you can * supply a NULL @a recipient, and the recipient previously * added with that cookie will be unlinked. + * + * If the binder is dead, this will return DEAD_OBJECT. Deleting + * the object will also unlink all death recipients. */ + // NOLINTNEXTLINE(google-default-arguments) virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient, - void* cookie = NULL, + void* cookie = nullptr, uint32_t flags = 0, - wp<DeathRecipient>* outRecipient = NULL) = 0; + wp<DeathRecipient>* outRecipient = nullptr) = 0; virtual bool checkSubclass(const void* subclassID) const; typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie); + /** + * This object is attached for the lifetime of this binder object. When + * this binder object is destructed, the cleanup function of all attached + * objects are invoked with their respective objectID, object, and + * cleanupCookie. Access to these APIs can be made from multiple threads, + * but calls from different threads are allowed to be interleaved. + */ virtual void attachObject( const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) = 0; + /** + * Returns object attached with attachObject. + */ virtual void* findObject(const void* objectID) const = 0; + /** + * WARNING: this API does not call the cleanup function for legacy reasons. + * It also does not return void* for legacy reasons. If you need to detach + * an object and destroy it, there are two options: + * - if you can, don't call detachObject and instead wait for the destructor + * to clean it up. + * - manually retrieve and destruct the object (if multiple of your threads + * are accessing these APIs, you must guarantee that attachObject isn't + * called after findObject and before detachObject is called). + */ virtual void detachObject(const void* objectID) = 0; virtual BBinder* localBinder();
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index 0f1fe5b..0d30560 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h
@@ -54,6 +54,7 @@ virtual const String16& getInterfaceDescriptor() const; protected: + typedef INTERFACE BaseInterface; virtual IBinder* onAsBinder(); }; @@ -66,18 +67,25 @@ explicit BpInterface(const sp<IBinder>& remote); protected: + typedef INTERFACE BaseInterface; virtual IBinder* onAsBinder(); }; // ---------------------------------------------------------------------- #define DECLARE_META_INTERFACE(INTERFACE) \ +public: \ static const ::android::String16 descriptor; \ static ::android::sp<I##INTERFACE> asInterface( \ const ::android::sp<::android::IBinder>& obj); \ virtual const ::android::String16& getInterfaceDescriptor() const; \ I##INTERFACE(); \ virtual ~I##INTERFACE(); \ + static bool setDefaultImpl(std::unique_ptr<I##INTERFACE> impl); \ + static const std::unique_ptr<I##INTERFACE>& getDefaultImpl(); \ +private: \ + static std::unique_ptr<I##INTERFACE> default_impl; \ +public: \ #define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ @@ -90,22 +98,37 @@ const ::android::sp<::android::IBinder>& obj) \ { \ ::android::sp<I##INTERFACE> intr; \ - if (obj != NULL) { \ + if (obj != nullptr) { \ intr = static_cast<I##INTERFACE*>( \ obj->queryLocalInterface( \ I##INTERFACE::descriptor).get()); \ - if (intr == NULL) { \ + if (intr == nullptr) { \ intr = new Bp##INTERFACE(obj); \ } \ } \ return intr; \ } \ + std::unique_ptr<I##INTERFACE> I##INTERFACE::default_impl; \ + bool I##INTERFACE::setDefaultImpl(std::unique_ptr<I##INTERFACE> impl)\ + { \ + if (!I##INTERFACE::default_impl && impl) { \ + I##INTERFACE::default_impl = std::move(impl); \ + return true; \ + } \ + return false; \ + } \ + const std::unique_ptr<I##INTERFACE>& I##INTERFACE::getDefaultImpl() \ + { \ + return I##INTERFACE::default_impl; \ + } \ I##INTERFACE::I##INTERFACE() { } \ I##INTERFACE::~I##INTERFACE() { } \ #define CHECK_INTERFACE(interface, data, reply) \ - if (!(data).checkInterface(this)) { return PERMISSION_DENIED; } \ + do { \ + if (!(data).checkInterface(this)) { return PERMISSION_DENIED; } \ + } while (false) \ // ---------------------------------------------------------------------- @@ -116,7 +139,7 @@ const String16& _descriptor) { if (_descriptor == INTERFACE::descriptor) return this; - return NULL; + return nullptr; } template<typename INTERFACE> @@ -142,7 +165,7 @@ { return remote(); } - + // ---------------------------------------------------------------------- }; // namespace android
diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h index 15a104f..071946f 100644 --- a/libs/binder/include/binder/IMemory.h +++ b/libs/binder/include/binder/IMemory.h
@@ -43,7 +43,7 @@ virtual void* getBase() const = 0; virtual size_t getSize() const = 0; virtual uint32_t getFlags() const = 0; - virtual uint32_t getOffset() const = 0; + virtual off_t getOffset() const = 0; // these are there just for backward source compatibility int32_t heapID() const { return getHeapID(); } @@ -54,7 +54,8 @@ class BnMemoryHeap : public BnInterface<IMemoryHeap> { public: - virtual status_t onTransact( + // NOLINTNEXTLINE(google-default-arguments) + virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, @@ -72,7 +73,8 @@ public: DECLARE_META_INTERFACE(Memory) - virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0; + // NOLINTNEXTLINE(google-default-arguments) + virtual sp<IMemoryHeap> getMemory(ssize_t* offset=nullptr, size_t* size=nullptr) const = 0; // helpers void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const; @@ -84,6 +86,7 @@ class BnMemory : public BnInterface<IMemory> { public: + // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data,
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index c1d9a9a..614b0b3 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h
@@ -29,6 +29,8 @@ // --------------------------------------------------------------------------- namespace android { +class IPCThreadStateBase; + class IPCThreadState { public: @@ -40,15 +42,34 @@ status_t clearLastError(); pid_t getCallingPid() const; + // nullptr if unavailable + // + // this can't be restored once it's cleared, and it does not return the + // context of the current process when not in a binder call. + const char* getCallingSid() const; uid_t getCallingUid() const; void setStrictModePolicy(int32_t policy); int32_t getStrictModePolicy() const; + // See Binder#setCallingWorkSourceUid in Binder.java. + int64_t setCallingWorkSourceUid(uid_t uid); + // Internal only. Use setCallingWorkSourceUid(uid) instead. + int64_t setCallingWorkSourceUidWithoutPropagation(uid_t uid); + // See Binder#getCallingWorkSourceUid in Binder.java. + uid_t getCallingWorkSourceUid() const; + // See Binder#clearCallingWorkSource in Binder.java. + int64_t clearCallingWorkSource(); + // See Binder#restoreCallingWorkSource in Binder.java. + void restoreCallingWorkSource(int64_t token); + void clearPropagateWorkSource(); + bool shouldPropagateWorkSource() const; + void setLastTransactionBinderFlags(int32_t flags); int32_t getLastTransactionBinderFlags() const; int64_t clearCallingIdentity(); + // Restores PID/UID (not SID) void restoreCallingIdentity(int64_t token); int setupPolling(int* fd); @@ -89,13 +110,47 @@ // the maximum number of binder threads threads allowed for this process. void blockUntilThreadAvailable(); + + // Is this thread currently serving a binder call. This method + // returns true if while traversing backwards from the function call + // stack for this thread, we encounter a function serving a binder + // call before encountering a hwbinder call / hitting the end of the + // call stack. + // Eg: If thread T1 went through the following call pattern + // 1) T1 receives and executes hwbinder call H1. + // 2) While handling H1, T1 makes binder call B1. + // 3) The handler of B1, calls into T1 with a callback B2. + // If isServingCall() is called during H1 before 3), this method + // will return false, else true. + // + // ---- + // | B2 | ---> While callback B2 is being handled, during 3). + // ---- + // | H1 | ---> While H1 is being handled. + // ---- + // Fig: Thread Call stack while handling B2 + // + // This is since after 3), while traversing the thread call stack, + // we hit a binder call before a hwbinder call / end of stack. This + // method may be typically used to determine whether to use + // hardware::IPCThreadState methods or IPCThreadState methods to + // infer information about thread state. + bool isServingCall() const; + + // The work source represents the UID of the process we should attribute the transaction + // to. We use -1 to specify that the work source was not set using #setWorkSource. + // + // This constant needs to be kept in sync with Binder.UNSET_WORKSOURCE from the Java + // side. + static const int32_t kUnsetWorkSource = -1; + private: IPCThreadState(); ~IPCThreadState(); status_t sendReply(const Parcel& reply, uint32_t flags); status_t waitForResponse(Parcel *reply, - status_t *acquireResult=NULL); + status_t *acquireResult=nullptr); status_t talkWithDriver(bool doReceive=true); status_t writeTransactionData(int32_t cmd, uint32_t binderFlags, @@ -125,9 +180,18 @@ Parcel mOut; status_t mLastError; pid_t mCallingPid; + const char* mCallingSid; uid_t mCallingUid; + // The UID of the process who is responsible for this transaction. + // This is used for resource attribution. + int32_t mWorkSource; + // Whether the work source should be propagated. + bool mPropagateWorkSource; int32_t mStrictModePolicy; int32_t mLastTransactionBinderFlags; + IPCThreadStateBase *mIPCThreadStateBase; + + ProcessState::CallRestriction mCallRestriction; }; }; // namespace android
diff --git a/libs/binder/include/binder/IPermissionController.h b/libs/binder/include/binder/IPermissionController.h index 3ec459f..26a1b23 100644 --- a/libs/binder/include/binder/IPermissionController.h +++ b/libs/binder/include/binder/IPermissionController.h
@@ -56,6 +56,7 @@ class BnPermissionController : public BnInterface<IPermissionController> { public: + // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply,
diff --git a/libs/binder/include/binder/IResultReceiver.h b/libs/binder/include/binder/IResultReceiver.h index e494fba..00b3d89 100644 --- a/libs/binder/include/binder/IResultReceiver.h +++ b/libs/binder/include/binder/IResultReceiver.h
@@ -41,6 +41,7 @@ class BnResultReceiver : public BnInterface<IResultReceiver> { public: + // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply,
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index 197026d..aeea1a2 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h
@@ -61,6 +61,7 @@ /** * Register a service. */ + // NOLINTNEXTLINE(google-default-arguments) virtual status_t addService(const String16& name, const sp<IBinder>& service, bool allowIsolated = false, int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) = 0; @@ -68,6 +69,7 @@ /** * Return list of all existing services. */ + // NOLINTNEXTLINE(google-default-arguments) virtual Vector<String16> listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0; enum { @@ -84,9 +86,9 @@ status_t getService(const String16& name, sp<INTERFACE>* outService) { const sp<IServiceManager> sm = defaultServiceManager(); - if (sm != NULL) { + if (sm != nullptr) { *outService = interface_cast<INTERFACE>(sm->getService(name)); - if ((*outService) != NULL) return NO_ERROR; + if ((*outService) != nullptr) return NO_ERROR; } return NAME_NOT_FOUND; }
diff --git a/libs/binder/include/binder/IShellCallback.h b/libs/binder/include/binder/IShellCallback.h index b47e995..6715678 100644 --- a/libs/binder/include/binder/IShellCallback.h +++ b/libs/binder/include/binder/IShellCallback.h
@@ -42,6 +42,7 @@ class BnShellCallback : public BnInterface<IShellCallback> { public: + // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply,
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h index d81789e..a1f530d 100644 --- a/libs/binder/include/binder/IUidObserver.h +++ b/libs/binder/include/binder/IUidObserver.h
@@ -34,11 +34,13 @@ virtual void onUidGone(uid_t uid, bool disabled) = 0; virtual void onUidActive(uid_t uid) = 0; virtual void onUidIdle(uid_t uid, bool disabled) = 0; + virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq) = 0; enum { ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, ON_UID_ACTIVE_TRANSACTION, - ON_UID_IDLE_TRANSACTION + ON_UID_IDLE_TRANSACTION, + ON_UID_STATE_CHANGED_TRANSACTION }; }; @@ -47,6 +49,7 @@ class BnUidObserver : public BnInterface<IUidObserver> { public: + // NOLINTNEXTLINE(google-default-arguments) virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
diff --git a/libs/binder/include/binder/IpPrefix.h b/libs/binder/include/binder/IpPrefix.h index dd5bc3a..c7e7a50 100644 --- a/libs/binder/include/binder/IpPrefix.h +++ b/libs/binder/include/binder/IpPrefix.h
@@ -74,8 +74,8 @@ private: union InternalUnion { InternalUnion() = default; - InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { }; - InternalUnion(const struct in_addr &addr):mInAddr(addr) { }; + explicit InternalUnion(const struct in6_addr &addr):mIn6Addr(addr) { }; + explicit InternalUnion(const struct in_addr &addr):mInAddr(addr) { }; struct in6_addr mIn6Addr; struct in_addr mInAddr; } mUnion;
diff --git a/libs/binder/include/binder/MemoryDealer.h b/libs/binder/include/binder/MemoryDealer.h index 60a624c..b483be0 100644 --- a/libs/binder/include/binder/MemoryDealer.h +++ b/libs/binder/include/binder/MemoryDealer.h
@@ -34,7 +34,7 @@ class MemoryDealer : public RefBase { public: - MemoryDealer(size_t size, const char* name = 0, + explicit MemoryDealer(size_t size, const char* name = nullptr, uint32_t flags = 0 /* or bits such as MemoryHeapBase::READ_ONLY */ ); virtual sp<IMemory> allocate(size_t size);
diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h index ea9b66c..100d784 100644 --- a/libs/binder/include/binder/MemoryHeapBase.h +++ b/libs/binder/include/binder/MemoryHeapBase.h
@@ -42,17 +42,17 @@ * maps the memory referenced by fd. but DOESN'T take ownership * of the filedescriptor (it makes a copy with dup() */ - MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, uint32_t offset = 0); + MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, off_t offset = 0); /* * maps memory from the given device */ - MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0); + explicit MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0); /* * maps memory from ashmem, with the given name for debugging */ - MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL); + explicit MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = nullptr); virtual ~MemoryHeapBase(); @@ -64,7 +64,7 @@ virtual size_t getSize() const; virtual uint32_t getFlags() const; - virtual uint32_t getOffset() const; + off_t getOffset() const override; const char* getDevice() const; @@ -74,7 +74,7 @@ /* this is only needed as a workaround, use only if you know * what you are doing */ status_t setDevice(const char* device) { - if (mDevice == 0) + if (mDevice == nullptr) mDevice = device; return mDevice ? NO_ERROR : ALREADY_EXISTS; } @@ -82,11 +82,11 @@ protected: MemoryHeapBase(); // init() takes ownership of fd - status_t init(int fd, void *base, int size, - int flags = 0, const char* device = NULL); + status_t init(int fd, void *base, size_t size, + int flags = 0, const char* device = nullptr); private: - status_t mapfd(int fd, size_t size, uint32_t offset = 0); + status_t mapfd(int fd, size_t size, off_t offset = 0); int mFD; size_t mSize; @@ -94,7 +94,7 @@ uint32_t mFlags; const char* mDevice; bool mNeedUnmap; - uint32_t mOffset; + off_t mOffset; }; // ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index dede78f..e5219a5 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h
@@ -20,6 +20,8 @@ #include <string> #include <vector> +#include <linux/android/binder.h> + #include <android-base/unique_fd.h> #include <cutils/native_handle.h> #include <utils/Errors.h> @@ -27,7 +29,6 @@ #include <utils/String16.h> #include <utils/Vector.h> #include <utils/Flattenable.h> -#include <linux/android/binder.h> #include <binder/IInterface.h> #include <binder/Parcelable.h> @@ -91,7 +92,7 @@ // IPCThreadState, which as an optimization may optionally be // passed in. bool enforceInterface(const String16& interface, - IPCThreadState* threadState = NULL) const; + IPCThreadState* threadState = nullptr) const; bool checkInterface(IBinder*) const; void freeData(); @@ -139,6 +140,8 @@ status_t writeInt32Vector(const std::vector<int32_t>& val); status_t writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val); status_t writeInt64Vector(const std::vector<int64_t>& val); + status_t writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val); + status_t writeUint64Vector(const std::vector<uint64_t>& val); status_t writeFloatVector(const std::unique_ptr<std::vector<float>>& val); status_t writeFloatVector(const std::vector<float>& val); status_t writeDoubleVector(const std::unique_ptr<std::vector<double>>& val); @@ -205,6 +208,10 @@ // The Parcel does not take ownership of the given fd unless you ask it to. status_t writeParcelFileDescriptor(int fd, bool takeOwnership = false); + // Place a Java "parcel file descriptor" into the parcel. A dup of the fd is made, which will + // be closed once the parcel is destroyed. + status_t writeDupParcelFileDescriptor(int fd); + // Place a file descriptor into the parcel. This will not affect the // semantics of the smart file descriptor. A new descriptor will be // created, and will be closed when the parcel is destroyed. @@ -309,6 +316,8 @@ status_t readInt32Vector(std::vector<int32_t>* val) const; status_t readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const; status_t readInt64Vector(std::vector<int64_t>* val) const; + status_t readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const; + status_t readUint64Vector(std::vector<uint64_t>* val) const; status_t readFloatVector(std::unique_ptr<std::vector<float>>* val) const; status_t readFloatVector(std::vector<float>* val) const; status_t readDoubleVector(std::unique_ptr<std::vector<double>>* val) const; @@ -364,6 +373,9 @@ status_t readUniqueFileDescriptor( base::unique_fd* val) const; + // Retrieve a Java "parcel file descriptor" from the parcel. + status_t readUniqueParcelFileDescriptor(base::unique_fd* val) const; + // Retrieve a vector of smart file descriptors from the parcel. status_t readUniqueFileDescriptorVector( @@ -384,6 +396,12 @@ static size_t getGlobalAllocSize(); static size_t getGlobalAllocCount(); + bool replaceCallingWorkSourceUid(uid_t uid); + // Returns the work source provided by the caller. This can only be trusted for trusted calling + // uid. + uid_t readCallingWorkSourceUid(); + void readRequestHeaders() const; + private: typedef void (*release_func)(Parcel* parcel, const uint8_t* data, size_t dataSize, @@ -418,6 +436,7 @@ void initState(); void scanForFds() const; status_t validateReadData(size_t len) const; + void updateWorkSourceRequestHeaderPosition() const; template<class T> status_t readAligned(T *pArg) const; @@ -466,6 +485,9 @@ mutable size_t mNextObjectHint; mutable bool mObjectsSorted; + mutable bool mRequestHeaderPresent; + mutable size_t mWorkSourceRequestHeaderPosition; + mutable bool mFdsKnown; mutable bool mHasFds; bool mAllowFds; @@ -547,7 +569,7 @@ friend class Parcel; public: inline const void* data() const { return mData; } - inline void* mutableData() { return isMutable() ? mData : NULL; } + inline void* mutableData() { return isMutable() ? mData : nullptr; } }; class WritableBlob : public Blob { @@ -587,7 +609,7 @@ } if (size) { void* buffer = writeInplace(size); - if (buffer == NULL) + if (buffer == nullptr) return NO_MEMORY; return val.flatten(buffer, size); } @@ -615,7 +637,7 @@ } if (size) { void const* buffer = readInplace(size); - return buffer == NULL ? NO_MEMORY : + return buffer == nullptr ? NO_MEMORY : val.unflatten(buffer, size); } return NO_ERROR;
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h new file mode 100644 index 0000000..662e56e --- /dev/null +++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -0,0 +1,52 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_PARCEL_FILE_DESCRIPTOR_H_ +#define ANDROID_PARCEL_FILE_DESCRIPTOR_H_ + +#include <android-base/unique_fd.h> +#include <binder/Parcel.h> +#include <binder/Parcelable.h> + +namespace android { +namespace os { + +/* + * C++ implementation of the Java class android.os.ParcelFileDescriptor + */ +class ParcelFileDescriptor : public android::Parcelable { +public: + ParcelFileDescriptor(); + explicit ParcelFileDescriptor(android::base::unique_fd fd); + ParcelFileDescriptor(ParcelFileDescriptor&& other) : mFd(std::move(other.mFd)) { } + ~ParcelFileDescriptor() override; + + int get() const { return mFd.get(); } + android::base::unique_fd release() { return std::move(mFd); } + void reset(android::base::unique_fd fd = android::base::unique_fd()) { mFd = std::move(fd); } + + // android::Parcelable override: + android::status_t writeToParcel(android::Parcel* parcel) const override; + android::status_t readFromParcel(const android::Parcel* parcel) override; + +private: + android::base::unique_fd mFd; +}; + +} // namespace os +} // namespace android + +#endif // ANDROID_OS_PARCEL_FILE_DESCRIPTOR_H_
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index f85c261..224cb36 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h
@@ -36,9 +36,12 @@ public: static sp<ProcessState> self(); static sp<ProcessState> selfOrNull(); + /* initWithDriver() can be used to configure libbinder to use * a different binder driver dev node. It must be called *before* - * any call to ProcessState::self(). /dev/binder remains the default. + * any call to ProcessState::self(). The default is /dev/vndbinder + * for processes built with the VNDK and /dev/binder for those + * which are not. */ static sp<ProcessState> initWithDriver(const char *driver); @@ -74,10 +77,22 @@ ssize_t getKernelReferences(size_t count, uintptr_t* buf); + enum class CallRestriction { + // all calls okay + NONE, + // log when calls are blocking + ERROR_IF_NOT_ONEWAY, + // abort process on blocking calls + FATAL_IF_NOT_ONEWAY, + }; + // Sets calling restrictions for all transactions in this process. This must be called + // before any threads are spawned. + void setCallRestriction(CallRestriction restriction); + private: friend class IPCThreadState; - ProcessState(const char* driver); + explicit ProcessState(const char* driver); ~ProcessState(); ProcessState(const ProcessState& o); @@ -120,6 +135,8 @@ String8 mRootDir; bool mThreadPoolStarted; volatile int32_t mThreadPoolSeq; + + CallRestriction mCallRestriction; }; }; // namespace android
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h index 3bfd462..5fa2ff6 100644 --- a/libs/binder/include/binder/SafeInterface.h +++ b/libs/binder/include/binder/SafeInterface.h
@@ -152,6 +152,12 @@ return callParcel("writeParcelableVector", [&]() { return parcel->writeParcelableVector(v); }); } + status_t read(const Parcel& parcel, float* f) const { + return callParcel("readFloat", [&]() { return parcel.readFloat(f); }); + } + status_t write(Parcel* parcel, float f) const { + return callParcel("writeFloat", [&]() { return parcel->writeFloat(f); }); + } // Templates to handle integral types. We use a struct template to require that the called // function exactly matches the signedness and size of the argument (e.g., the argument isn't
diff --git a/libs/binder/include/binder/Status.h b/libs/binder/include/binder/Status.h index c3738f8..7d889b6 100644 --- a/libs/binder/include/binder/Status.h +++ b/libs/binder/include/binder/Status.h
@@ -22,6 +22,7 @@ #include <binder/Parcel.h> #include <utils/String8.h> +#include <string> namespace android { namespace binder { @@ -97,6 +98,8 @@ static Status fromStatusT(status_t status); + static std::string exceptionToString(status_t exceptionCode); + Status() = default; ~Status() = default;
diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h index 851e01f..5b5f766 100644 --- a/libs/binder/include/binder/TextOutput.h +++ b/libs/binder/include/binder/TextOutput.h
@@ -38,7 +38,7 @@ class Bundle { public: - inline Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); } + inline explicit Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); } inline ~Bundle() { mTO.popBundle(); } private: TextOutput& mTO; @@ -80,7 +80,7 @@ class TypeCode { public: - inline TypeCode(uint32_t code); + inline explicit TypeCode(uint32_t code); inline ~TypeCode(); inline uint32_t typeCode() const;
diff --git a/libs/binder/include/binder/Value.h b/libs/binder/include/binder/Value.h index 4dee3d8..735f40e 100644 --- a/libs/binder/include/binder/Value.h +++ b/libs/binder/include/binder/Value.h
@@ -74,20 +74,20 @@ bool operator!=(const Value& rhs) const { return !this->operator==(rhs); } Value(const Value& value); - Value(const bool& value); - Value(const int8_t& value); - Value(const int32_t& value); - Value(const int64_t& value); - Value(const double& value); - Value(const String16& value); - Value(const std::vector<bool>& value); - Value(const std::vector<uint8_t>& value); - Value(const std::vector<int32_t>& value); - Value(const std::vector<int64_t>& value); - Value(const std::vector<double>& value); - Value(const std::vector<String16>& value); - Value(const os::PersistableBundle& value); - Value(const binder::Map& value); + Value(const bool& value); // NOLINT(google-explicit-constructor) + Value(const int8_t& value); // NOLINT(google-explicit-constructor) + Value(const int32_t& value); // NOLINT(google-explicit-constructor) + Value(const int64_t& value); // NOLINT(google-explicit-constructor) + Value(const double& value); // NOLINT(google-explicit-constructor) + Value(const String16& value); // NOLINT(google-explicit-constructor) + Value(const std::vector<bool>& value); // NOLINT(google-explicit-constructor) + Value(const std::vector<uint8_t>& value); // NOLINT(google-explicit-constructor) + Value(const std::vector<int32_t>& value); // NOLINT(google-explicit-constructor) + Value(const std::vector<int64_t>& value); // NOLINT(google-explicit-constructor) + Value(const std::vector<double>& value); // NOLINT(google-explicit-constructor) + Value(const std::vector<String16>& value); // NOLINT(google-explicit-constructor) + Value(const os::PersistableBundle& value); // NOLINT(google-explicit-constructor) + Value(const binder::Map& value); // NOLINT(google-explicit-constructor) Value& operator=(const Value& rhs); Value& operator=(const int8_t& rhs); @@ -153,8 +153,8 @@ // String Convenience Adapters // --------------------------- - Value(const String8& value): Value(String16(value)) { } - Value(const ::std::string& value): Value(String8(value.c_str())) { } + explicit Value(const String8& value): Value(String16(value)) { } + explicit Value(const ::std::string& value): Value(String8(value.c_str())) { } void putString(const String8& value) { return putString(String16(value)); } void putString(const ::std::string& value) { return putString(String8(value.c_str())); } Value& operator=(const String8& rhs) { return *this = String16(rhs); }
diff --git a/libs/binder/ndk/.clang-format b/libs/binder/ndk/.clang-format new file mode 100644 index 0000000..9a9d936 --- /dev/null +++ b/libs/binder/ndk/.clang-format
@@ -0,0 +1,10 @@ +BasedOnStyle: Google +ColumnLimit: 100 +IndentWidth: 4 +ContinuationIndentWidth: 8 +PointerAlignment: Left +TabWidth: 4 +AllowShortFunctionsOnASingleLine: Inline +PointerAlignment: Left +TabWidth: 4 +UseTab: Never
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp new file mode 100644 index 0000000..21bef2e --- /dev/null +++ b/libs/binder/ndk/Android.bp
@@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +cc_library { + name: "libbinder_ndk", + vendor_available: true, + + export_include_dirs: [ + "include_ndk", + "include_apex", + ], + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + + srcs: [ + "ibinder.cpp", + "ibinder_jni.cpp", + "parcel.cpp", + "process.cpp", + "status.cpp", + "service_manager.cpp", + ], + + shared_libs: [ + "libandroid_runtime_lazy", + "libbase", + "libbinder", + "libutils", + ], + + header_libs: [ + "jni_headers", + ], + export_header_lib_headers: [ + "jni_headers", + ], + + version_script: "libbinder_ndk.map.txt", + stubs: { + symbol_file: "libbinder_ndk.map.txt", + versions: ["29"], + }, +} + +ndk_headers { + name: "libbinder_ndk_headers", + from: "include_ndk/android", + to: "android", + srcs: [ + "include_ndk/android/*.h", + ], + license: "NOTICE", +} + +ndk_library { + name: "libbinder_ndk", + symbol_file: "libbinder_ndk.map.txt", + first_version: "29", +}
diff --git a/libs/binder/ndk/NOTICE b/libs/binder/ndk/NOTICE new file mode 100644 index 0000000..d1ab54c --- /dev/null +++ b/libs/binder/ndk/NOTICE
@@ -0,0 +1,189 @@ + + Copyright (c) 2018, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp new file mode 100644 index 0000000..bd6886d --- /dev/null +++ b/libs/binder/ndk/ibinder.cpp
@@ -0,0 +1,591 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_ibinder.h> +#include "ibinder_internal.h" + +#include <android/binder_status.h> +#include "parcel_internal.h" +#include "status_internal.h" + +#include <android-base/logging.h> +#include <binder/IPCThreadState.h> + +using DeathRecipient = ::android::IBinder::DeathRecipient; + +using ::android::IBinder; +using ::android::Parcel; +using ::android::sp; +using ::android::status_t; +using ::android::String16; +using ::android::String8; +using ::android::wp; + +namespace ABBinderTag { + +static const void* kId = "ABBinder"; +static void* kValue = static_cast<void*>(new bool{true}); +void clean(const void* /*id*/, void* /*obj*/, void* /*cookie*/){/* do nothing */}; + +static void attach(const sp<IBinder>& binder) { + binder->attachObject(kId, kValue, nullptr /*cookie*/, clean); +} +static bool has(const sp<IBinder>& binder) { + return binder != nullptr && binder->findObject(kId) == kValue; +} + +} // namespace ABBinderTag + +namespace ABpBinderTag { + +static std::mutex gLock; +static const void* kId = "ABpBinder"; +struct Value { + wp<ABpBinder> binder; +}; +void clean(const void* id, void* obj, void* cookie) { + CHECK(id == kId) << id << " " << obj << " " << cookie; + + delete static_cast<Value*>(obj); +}; + +} // namespace ABpBinderTag + +AIBinder::AIBinder(const AIBinder_Class* clazz) : mClazz(clazz) {} +AIBinder::~AIBinder() {} + +bool AIBinder::associateClass(const AIBinder_Class* clazz) { + if (clazz == nullptr) return false; + if (mClazz == clazz) return true; + + String8 newDescriptor(clazz->getInterfaceDescriptor()); + + if (mClazz != nullptr) { + String8 currentDescriptor(mClazz->getInterfaceDescriptor()); + if (newDescriptor == currentDescriptor) { + LOG(ERROR) << __func__ << ": Class descriptors '" << currentDescriptor + << "' match during associateClass, but they are different class objects. " + "Class descriptor collision?"; + } else { + LOG(ERROR) << __func__ + << ": Class cannot be associated on object which already has a class. " + "Trying to associate to '" + << newDescriptor.c_str() << "' but already set to '" + << currentDescriptor.c_str() << "'."; + } + + // always a failure because we know mClazz != clazz + return false; + } + + CHECK(asABpBinder() != nullptr); // ABBinder always has a descriptor + + String8 descriptor(getBinder()->getInterfaceDescriptor()); + if (descriptor != newDescriptor) { + LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor.c_str() + << "' but descriptor is actually '" << descriptor.c_str() << "'."; + return false; + } + + // if this is a local object, it's not one known to libbinder_ndk + mClazz = clazz; + + return true; +} + +ABBinder::ABBinder(const AIBinder_Class* clazz, void* userData) + : AIBinder(clazz), BBinder(), mUserData(userData) { + CHECK(clazz != nullptr); +} +ABBinder::~ABBinder() { + getClass()->onDestroy(mUserData); +} + +const String16& ABBinder::getInterfaceDescriptor() const { + return getClass()->getInterfaceDescriptor(); +} + +status_t ABBinder::dump(int fd, const ::android::Vector<String16>& args) { + AIBinder_onDump onDump = getClass()->onDump; + + if (onDump == nullptr) { + return STATUS_OK; + } + + // technically UINT32_MAX would be okay here, but INT32_MAX is expected since this may be + // null in Java + if (args.size() > INT32_MAX) { + LOG(ERROR) << "ABBinder::dump received too many arguments: " << args.size(); + return STATUS_BAD_VALUE; + } + + std::vector<String8> utf8Args; // owns memory of utf8s + utf8Args.reserve(args.size()); + std::vector<const char*> utf8Pointers; // what can be passed over NDK API + utf8Pointers.reserve(args.size()); + + for (size_t i = 0; i < args.size(); i++) { + utf8Args.push_back(String8(args[i])); + utf8Pointers.push_back(utf8Args[i].c_str()); + } + + return onDump(this, fd, utf8Pointers.data(), utf8Pointers.size()); +} + +status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parcel* reply, + binder_flags_t flags) { + if (isUserCommand(code)) { + if (!data.checkInterface(this)) { + return STATUS_BAD_TYPE; + } + + const AParcel in = AParcel::readOnly(this, &data); + AParcel out = AParcel(this, reply, false /*owns*/); + + binder_status_t status = getClass()->onTransact(this, code, &in, &out); + return PruneStatusT(status); + } else { + return BBinder::onTransact(code, data, reply, flags); + } +} + +ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder) + : AIBinder(nullptr /*clazz*/), BpRefBase(binder) { + CHECK(binder != nullptr); +} +ABpBinder::~ABpBinder() {} + +void ABpBinder::onLastStrongRef(const void* id) { + { + std::lock_guard<std::mutex> lock(ABpBinderTag::gLock); + // Since ABpBinder is OBJECT_LIFETIME_WEAK, we must remove this weak reference in order for + // the ABpBinder to be deleted. Since a strong reference to this ABpBinder object should no + // longer be able to exist at the time of this method call, there is no longer a need to + // recover it. + + ABpBinderTag::Value* value = + static_cast<ABpBinderTag::Value*>(remote()->findObject(ABpBinderTag::kId)); + if (value != nullptr) { + value->binder = nullptr; + } + } + + BpRefBase::onLastStrongRef(id); +} + +sp<AIBinder> ABpBinder::lookupOrCreateFromBinder(const ::android::sp<::android::IBinder>& binder) { + if (binder == nullptr) { + return nullptr; + } + if (ABBinderTag::has(binder)) { + return static_cast<ABBinder*>(binder.get()); + } + + // The following code ensures that for a given binder object (remote or local), if it is not an + // ABBinder then at most one ABpBinder object exists in a given process representing it. + std::lock_guard<std::mutex> lock(ABpBinderTag::gLock); + + ABpBinderTag::Value* value = + static_cast<ABpBinderTag::Value*>(binder->findObject(ABpBinderTag::kId)); + if (value == nullptr) { + value = new ABpBinderTag::Value; + binder->attachObject(ABpBinderTag::kId, static_cast<void*>(value), nullptr /*cookie*/, + ABpBinderTag::clean); + } + + sp<ABpBinder> ret = value->binder.promote(); + if (ret == nullptr) { + ret = new ABpBinder(binder); + value->binder = ret; + } + + return ret; +} + +struct AIBinder_Weak { + wp<AIBinder> binder; +}; +AIBinder_Weak* AIBinder_Weak_new(AIBinder* binder) { + if (binder == nullptr) { + return nullptr; + } + + return new AIBinder_Weak{wp<AIBinder>(binder)}; +} +void AIBinder_Weak_delete(AIBinder_Weak* weakBinder) { + delete weakBinder; +} +AIBinder* AIBinder_Weak_promote(AIBinder_Weak* weakBinder) { + if (weakBinder == nullptr) { + return nullptr; + } + + sp<AIBinder> binder = weakBinder->binder.promote(); + AIBinder_incStrong(binder.get()); + return binder.get(); +} + +AIBinder_Class::AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate, + AIBinder_Class_onDestroy onDestroy, + AIBinder_Class_onTransact onTransact) + : onCreate(onCreate), + onDestroy(onDestroy), + onTransact(onTransact), + mInterfaceDescriptor(interfaceDescriptor) {} + +AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor, + AIBinder_Class_onCreate onCreate, + AIBinder_Class_onDestroy onDestroy, + AIBinder_Class_onTransact onTransact) { + if (interfaceDescriptor == nullptr || onCreate == nullptr || onDestroy == nullptr || + onTransact == nullptr) { + return nullptr; + } + + return new AIBinder_Class(interfaceDescriptor, onCreate, onDestroy, onTransact); +} + +void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) { + CHECK(clazz != nullptr) << "setOnDump requires non-null clazz"; + + // this is required to be called before instances are instantiated + clazz->onDump = onDump; +} + +void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) { + CHECK(who == mWho); + + mOnDied(mCookie); + + sp<AIBinder_DeathRecipient> recipient = mParentRecipient.promote(); + sp<IBinder> strongWho = who.promote(); + + // otherwise this will be cleaned up later with pruneDeadTransferEntriesLocked + if (recipient != nullptr && strongWho != nullptr) { + status_t result = recipient->unlinkToDeath(strongWho, mCookie); + if (result != ::android::DEAD_OBJECT) { + LOG(WARNING) << "Unlinking to dead binder resulted in: " << result; + } + } + + mWho = nullptr; +} + +AIBinder_DeathRecipient::AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied) + : mOnDied(onDied) { + CHECK(onDied != nullptr); +} + +void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() { + mDeathRecipients.erase(std::remove_if(mDeathRecipients.begin(), mDeathRecipients.end(), + [](const sp<TransferDeathRecipient>& tdr) { + return tdr->getWho() == nullptr; + }), + mDeathRecipients.end()); +} + +binder_status_t AIBinder_DeathRecipient::linkToDeath(sp<IBinder> binder, void* cookie) { + CHECK(binder != nullptr); + + std::lock_guard<std::mutex> l(mDeathRecipientsMutex); + + sp<TransferDeathRecipient> recipient = + new TransferDeathRecipient(binder, cookie, this, mOnDied); + + status_t status = binder->linkToDeath(recipient, cookie, 0 /*flags*/); + if (status != STATUS_OK) { + return PruneStatusT(status); + } + + mDeathRecipients.push_back(recipient); + + pruneDeadTransferEntriesLocked(); + return STATUS_OK; +} + +binder_status_t AIBinder_DeathRecipient::unlinkToDeath(sp<IBinder> binder, void* cookie) { + CHECK(binder != nullptr); + + std::lock_guard<std::mutex> l(mDeathRecipientsMutex); + + for (auto it = mDeathRecipients.rbegin(); it != mDeathRecipients.rend(); ++it) { + sp<TransferDeathRecipient> recipient = *it; + + if (recipient->getCookie() == cookie && recipient->getWho() == binder) { + mDeathRecipients.erase(it.base() - 1); + + status_t status = binder->unlinkToDeath(recipient, cookie, 0 /*flags*/); + if (status != ::android::OK) { + LOG(ERROR) << __func__ + << ": removed reference to death recipient but unlink failed."; + } + return PruneStatusT(status); + } + } + + return STATUS_NAME_NOT_FOUND; +} + +// start of C-API methods + +AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) { + if (clazz == nullptr) { + LOG(ERROR) << __func__ << ": Must provide class to construct local binder."; + return nullptr; + } + + void* userData = clazz->onCreate(args); + + sp<AIBinder> ret = new ABBinder(clazz, userData); + ABBinderTag::attach(ret->getBinder()); + + AIBinder_incStrong(ret.get()); + return ret.get(); +} + +bool AIBinder_isRemote(const AIBinder* binder) { + if (binder == nullptr) { + return false; + } + + return binder->isRemote(); +} + +bool AIBinder_isAlive(const AIBinder* binder) { + if (binder == nullptr) { + return false; + } + + return const_cast<AIBinder*>(binder)->getBinder()->isBinderAlive(); +} + +binder_status_t AIBinder_ping(AIBinder* binder) { + if (binder == nullptr) { + return STATUS_UNEXPECTED_NULL; + } + + return PruneStatusT(binder->getBinder()->pingBinder()); +} + +binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint32_t numArgs) { + if (binder == nullptr) { + return STATUS_UNEXPECTED_NULL; + } + + ABBinder* bBinder = binder->asABBinder(); + if (bBinder != nullptr) { + AIBinder_onDump onDump = binder->getClass()->onDump; + if (onDump == nullptr) { + return STATUS_OK; + } + return PruneStatusT(onDump(bBinder, fd, args, numArgs)); + } + + ::android::Vector<String16> utf16Args; + utf16Args.setCapacity(numArgs); + for (uint32_t i = 0; i < numArgs; i++) { + utf16Args.push(String16(String8(args[i]))); + } + + status_t status = binder->getBinder()->dump(fd, utf16Args); + return PruneStatusT(status); +} + +binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, + void* cookie) { + if (binder == nullptr || recipient == nullptr) { + LOG(ERROR) << __func__ << ": Must provide binder and recipient."; + return STATUS_UNEXPECTED_NULL; + } + + // returns binder_status_t + return recipient->linkToDeath(binder->getBinder(), cookie); +} + +binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, + void* cookie) { + if (binder == nullptr || recipient == nullptr) { + LOG(ERROR) << __func__ << ": Must provide binder and recipient."; + return STATUS_UNEXPECTED_NULL; + } + + // returns binder_status_t + return recipient->unlinkToDeath(binder->getBinder(), cookie); +} + +uid_t AIBinder_getCallingUid() { + return ::android::IPCThreadState::self()->getCallingUid(); +} + +pid_t AIBinder_getCallingPid() { + return ::android::IPCThreadState::self()->getCallingPid(); +} + +void AIBinder_incStrong(AIBinder* binder) { + if (binder == nullptr) { + LOG(ERROR) << __func__ << ": on null binder"; + return; + } + + binder->incStrong(nullptr); +} +void AIBinder_decStrong(AIBinder* binder) { + if (binder == nullptr) { + LOG(ERROR) << __func__ << ": on null binder"; + return; + } + + binder->decStrong(nullptr); +} +int32_t AIBinder_debugGetRefCount(AIBinder* binder) { + if (binder == nullptr) { + LOG(ERROR) << __func__ << ": on null binder"; + return -1; + } + + return binder->getStrongCount(); +} + +bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz) { + if (binder == nullptr) { + return false; + } + + return binder->associateClass(clazz); +} + +const AIBinder_Class* AIBinder_getClass(AIBinder* binder) { + if (binder == nullptr) { + return nullptr; + } + + return binder->getClass(); +} + +void* AIBinder_getUserData(AIBinder* binder) { + if (binder == nullptr) { + return nullptr; + } + + ABBinder* bBinder = binder->asABBinder(); + if (bBinder == nullptr) { + return nullptr; + } + + return bBinder->getUserData(); +} + +binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) { + if (binder == nullptr || in == nullptr) { + LOG(ERROR) << __func__ << ": requires non-null parameters."; + return STATUS_UNEXPECTED_NULL; + } + const AIBinder_Class* clazz = binder->getClass(); + if (clazz == nullptr) { + LOG(ERROR) << __func__ + << ": Class must be defined for a remote binder transaction. See " + "AIBinder_associateClass."; + return STATUS_INVALID_OPERATION; + } + + if (!binder->isRemote()) { + LOG(WARNING) << "A binder object at " << binder + << " is being transacted on, however, this object is in the same process as " + "its proxy. Transacting with this binder is expensive compared to just " + "calling the corresponding functionality in the same process."; + } + + *in = new AParcel(binder); + status_t status = (*in)->get()->writeInterfaceToken(clazz->getInterfaceDescriptor()); + binder_status_t ret = PruneStatusT(status); + + if (ret != STATUS_OK) { + delete *in; + *in = nullptr; + } + + return ret; +} + +static void DestroyParcel(AParcel** parcel) { + delete *parcel; + *parcel = nullptr; +} + +binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, AParcel** in, + AParcel** out, binder_flags_t flags) { + if (in == nullptr) { + LOG(ERROR) << __func__ << ": requires non-null in parameter"; + return STATUS_UNEXPECTED_NULL; + } + + using AutoParcelDestroyer = std::unique_ptr<AParcel*, void (*)(AParcel**)>; + // This object is the input to the transaction. This function takes ownership of it and deletes + // it. + AutoParcelDestroyer forIn(in, DestroyParcel); + + if (!isUserCommand(code)) { + LOG(ERROR) << __func__ << ": Only user-defined transactions can be made from the NDK."; + return STATUS_UNKNOWN_TRANSACTION; + } + + if ((flags & ~FLAG_ONEWAY) != 0) { + LOG(ERROR) << __func__ << ": Unrecognized flags sent: " << flags; + return STATUS_BAD_VALUE; + } + + if (binder == nullptr || *in == nullptr || out == nullptr) { + LOG(ERROR) << __func__ << ": requires non-null parameters."; + return STATUS_UNEXPECTED_NULL; + } + + if ((*in)->getBinder() != binder) { + LOG(ERROR) << __func__ << ": parcel is associated with binder object " << binder + << " but called with " << (*in)->getBinder(); + return STATUS_BAD_VALUE; + } + + *out = new AParcel(binder); + + status_t status = binder->getBinder()->transact(code, *(*in)->get(), (*out)->get(), flags); + binder_status_t ret = PruneStatusT(status); + + if (ret != STATUS_OK) { + delete *out; + *out = nullptr; + } + + return ret; +} + +AIBinder_DeathRecipient* AIBinder_DeathRecipient_new( + AIBinder_DeathRecipient_onBinderDied onBinderDied) { + if (onBinderDied == nullptr) { + LOG(ERROR) << __func__ << ": requires non-null onBinderDied parameter."; + return nullptr; + } + auto ret = new AIBinder_DeathRecipient(onBinderDied); + ret->incStrong(nullptr); + return ret; +} + +void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) { + if (recipient == nullptr) { + return; + } + + recipient->decStrong(nullptr); +}
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h new file mode 100644 index 0000000..5cb68c2 --- /dev/null +++ b/libs/binder/ndk/ibinder_internal.h
@@ -0,0 +1,169 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/binder_ibinder.h> +#include "ibinder_internal.h" + +#include <atomic> +#include <mutex> +#include <vector> + +#include <binder/Binder.h> +#include <binder/IBinder.h> +#include <utils/Vector.h> + +inline bool isUserCommand(transaction_code_t code) { + return code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION; +} + +struct ABBinder; +struct ABpBinder; + +struct AIBinder : public virtual ::android::RefBase { + explicit AIBinder(const AIBinder_Class* clazz); + virtual ~AIBinder(); + + bool associateClass(const AIBinder_Class* clazz); + const AIBinder_Class* getClass() const { return mClazz; } + + virtual ::android::sp<::android::IBinder> getBinder() = 0; + virtual ABBinder* asABBinder() { return nullptr; } + virtual ABpBinder* asABpBinder() { return nullptr; } + + bool isRemote() const { + ::android::sp<::android::IBinder> binder = const_cast<AIBinder*>(this)->getBinder(); + return binder->remoteBinder() != nullptr; + } + + private: + // AIBinder instance is instance of this class for a local object. In order to transact on a + // remote object, this also must be set for simplicity (although right now, only the + // interfaceDescriptor from it is used). + const AIBinder_Class* mClazz; +}; + +// This is a local AIBinder object with a known class. +struct ABBinder : public AIBinder, public ::android::BBinder { + virtual ~ABBinder(); + + void* getUserData() { return mUserData; } + + ::android::sp<::android::IBinder> getBinder() override { return this; } + ABBinder* asABBinder() override { return this; } + + const ::android::String16& getInterfaceDescriptor() const override; + ::android::status_t dump(int fd, const ::android::Vector<::android::String16>& args) override; + ::android::status_t onTransact(uint32_t code, const ::android::Parcel& data, + ::android::Parcel* reply, binder_flags_t flags) override; + + private: + ABBinder(const AIBinder_Class* clazz, void* userData); + + // only thing that should create an ABBinder + friend AIBinder* AIBinder_new(const AIBinder_Class*, void*); + + // Can contain implementation if this is a local binder. This can still be nullptr for a local + // binder. If it is nullptr, the implication is the implementation state is entirely external to + // this object and the functionality provided in the AIBinder_Class is sufficient. + void* mUserData; +}; + +// This binder object may be remote or local (even though it is 'Bp'). The implication if it is +// local is that it is an IBinder object created outside of the domain of libbinder_ndk. +struct ABpBinder : public AIBinder, public ::android::BpRefBase { + // Looks up to see if this object has or is an existing ABBinder or ABpBinder object, otherwise + // it creates an ABpBinder object. + static ::android::sp<AIBinder> lookupOrCreateFromBinder( + const ::android::sp<::android::IBinder>& binder); + + virtual ~ABpBinder(); + + void onLastStrongRef(const void* id) override; + + ::android::sp<::android::IBinder> getBinder() override { return remote(); } + ABpBinder* asABpBinder() override { return this; } + + private: + explicit ABpBinder(const ::android::sp<::android::IBinder>& binder); +}; + +struct AIBinder_Class { + AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate, + AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact); + + const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; } + + // required to be non-null, implemented for every class + const AIBinder_Class_onCreate onCreate; + const AIBinder_Class_onDestroy onDestroy; + const AIBinder_Class_onTransact onTransact; + + // optional methods for a class + AIBinder_onDump onDump; + + private: + // This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to + // one. + const ::android::String16 mInterfaceDescriptor; +}; + +// Ownership is like this (when linked to death): +// +// AIBinder_DeathRecipient -sp-> TransferDeathRecipient <-wp-> IBinder +// +// When the AIBinder_DeathRecipient is dropped, so are the actual underlying death recipients. When +// the IBinder dies, only a wp to it is kept. +struct AIBinder_DeathRecipient : ::android::RefBase { + // One of these is created for every linkToDeath. This is to be able to recover data when a + // binderDied receipt only gives us information about the IBinder. + struct TransferDeathRecipient : ::android::IBinder::DeathRecipient { + TransferDeathRecipient(const ::android::wp<::android::IBinder>& who, void* cookie, + const ::android::wp<AIBinder_DeathRecipient>& parentRecipient, + const AIBinder_DeathRecipient_onBinderDied onDied) + : mWho(who), mCookie(cookie), mParentRecipient(parentRecipient), mOnDied(onDied) {} + + void binderDied(const ::android::wp<::android::IBinder>& who) override; + + const ::android::wp<::android::IBinder>& getWho() { return mWho; } + void* getCookie() { return mCookie; } + + private: + ::android::wp<::android::IBinder> mWho; + void* mCookie; + + ::android::wp<AIBinder_DeathRecipient> mParentRecipient; + + // This is kept separately from AIBinder_DeathRecipient in case the death recipient is + // deleted while the death notification is fired + const AIBinder_DeathRecipient_onBinderDied mOnDied; + }; + + explicit AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied); + binder_status_t linkToDeath(::android::sp<::android::IBinder>, void* cookie); + binder_status_t unlinkToDeath(::android::sp<::android::IBinder> binder, void* cookie); + + private: + // When the user of this API deletes a Bp object but not the death recipient, the + // TransferDeathRecipient object can't be cleaned up. This is called whenever a new + // TransferDeathRecipient is linked, and it ensures that mDeathRecipients can't grow unbounded. + void pruneDeadTransferEntriesLocked(); + + std::mutex mDeathRecipientsMutex; + std::vector<::android::sp<TransferDeathRecipient>> mDeathRecipients; + AIBinder_DeathRecipient_onBinderDied mOnDied; +};
diff --git a/libs/binder/ndk/ibinder_jni.cpp b/libs/binder/ndk/ibinder_jni.cpp new file mode 100644 index 0000000..d931785 --- /dev/null +++ b/libs/binder/ndk/ibinder_jni.cpp
@@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_ibinder_jni.h> +#include "ibinder_internal.h" + +#include <android_util_Binder.h> + +using ::android::IBinder; +using ::android::ibinderForJavaObject; +using ::android::javaObjectForIBinder; +using ::android::sp; + +AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder) { + if (binder == nullptr) { + return nullptr; + } + + sp<IBinder> ibinder = ibinderForJavaObject(env, binder); + sp<AIBinder> cbinder = ABpBinder::lookupOrCreateFromBinder(ibinder); + AIBinder_incStrong(cbinder.get()); + + return cbinder.get(); +} + +jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder) { + if (binder == nullptr) { + return nullptr; + } + + return javaObjectForIBinder(env, binder->getBinder()); +}
diff --git a/libs/binder/ndk/include_apex/android/binder_manager.h b/libs/binder/ndk/include_apex/android/binder_manager.h new file mode 100644 index 0000000..055c79b --- /dev/null +++ b/libs/binder/ndk/include_apex/android/binder_manager.h
@@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/binder_ibinder.h> +#include <android/binder_status.h> + +__BEGIN_DECLS + +/** + * This registers the service with the default service manager under this instance name. This does + * not take ownership of binder. + * + * \param binder object to register globally with the service manager. + * \param instance identifier of the service. This will be used to lookup the service. + * + * \return STATUS_OK on success. + */ +binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance); + +/** + * Gets a binder object with this specific instance name. Will return nullptr immediately if the + * service is not available This also implicitly calls AIBinder_incStrong (so the caller of this + * function is responsible for calling AIBinder_decStrong). + * + * \param instance identifier of the service used to lookup the service. + */ +__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance); + +/** + * Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on + * it. This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible + * for calling AIBinder_decStrong). + * + * \param instance identifier of the service used to lookup the service. + */ +__attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance); + +__END_DECLS
diff --git a/libs/binder/ndk/include_apex/android/binder_process.h b/libs/binder/ndk/include_apex/android/binder_process.h new file mode 100644 index 0000000..69e6387 --- /dev/null +++ b/libs/binder/ndk/include_apex/android/binder_process.h
@@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <stdint.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * This creates a threadpool for incoming binder transactions if it has not already been created. + */ +void ABinderProcess_startThreadPool(); +/** + * This sets the maximum number of threads that can be started in the threadpool. By default, after + * startThreadPool is called, this is one. If it is called additional times, it will only prevent + * the kernel from starting new threads and will not delete already existing threads. + */ +bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads); +/** + * This adds the current thread to the threadpool. This may cause the threadpool to exceed the + * maximum size. + */ +void ABinderProcess_joinThreadPool(); + +__END_DECLS
diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h new file mode 100644 index 0000000..c6868b0 --- /dev/null +++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
@@ -0,0 +1,262 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup NdkBinder + * @{ + */ + +/** + * @file binder_auto_utils.h + * @brief These objects provide a more C++-like thin interface to the . + */ + +#pragma once + +#include <android/binder_ibinder.h> +#include <android/binder_parcel.h> +#include <android/binder_status.h> + +#include <assert.h> + +#include <unistd.h> +#include <cstddef> + +namespace ndk { + +/** + * Represents one strong pointer to an AIBinder object. + */ +class SpAIBinder { + public: + /** + * Takes ownership of one strong refcount of binder. + */ + explicit SpAIBinder(AIBinder* binder = nullptr) : mBinder(binder) {} + + /** + * Convenience operator for implicitly constructing an SpAIBinder from nullptr. This is not + * explicit because it is not taking ownership of anything. + */ + SpAIBinder(std::nullptr_t) : SpAIBinder() {} // NOLINT(google-explicit-constructor) + + /** + * This will delete the underlying object if it exists. See operator=. + */ + SpAIBinder(const SpAIBinder& other) { *this = other; } + + /** + * This deletes the underlying object if it exists. See set. + */ + ~SpAIBinder() { set(nullptr); } + + /** + * This takes ownership of a binder from another AIBinder object but it does not affect the + * ownership of that other object. + */ + SpAIBinder& operator=(const SpAIBinder& other) { + AIBinder_incStrong(other.mBinder); + set(other.mBinder); + return *this; + } + + /** + * Takes ownership of one strong refcount of binder + */ + void set(AIBinder* binder) { + AIBinder* old = *const_cast<AIBinder* volatile*>(&mBinder); + if (old != nullptr) AIBinder_decStrong(old); + if (old != *const_cast<AIBinder* volatile*>(&mBinder)) { + __assert(__FILE__, __LINE__, "Race detected."); + } + mBinder = binder; + } + + /** + * This returns the underlying binder object for transactions. If it is used to create another + * SpAIBinder object, it should first be incremented. + */ + AIBinder* get() const { return mBinder; } + + /** + * This allows the value in this class to be set from beneath it. If you call this method and + * then change the value of T*, you must take ownership of the value you are replacing and add + * ownership to the object that is put in here. + * + * Recommended use is like this: + * SpAIBinder a; // will be nullptr + * SomeInitFunction(a.getR()); // value is initialized with refcount + * + * Other usecases are discouraged. + * + */ + AIBinder** getR() { return &mBinder; } + + private: + AIBinder* mBinder = nullptr; +}; + +namespace impl { + +/** + * This baseclass owns a single object, used to make various classes RAII. + */ +template <typename T, typename R, R (*Destroy)(T), T DEFAULT> +class ScopedAResource { + public: + /** + * Takes ownership of t. + */ + explicit ScopedAResource(T t = DEFAULT) : mT(t) {} + + /** + * This deletes the underlying object if it exists. See set. + */ + ~ScopedAResource() { set(DEFAULT); } + + /** + * Takes ownership of t. + */ + void set(T t) { + Destroy(mT); + mT = t; + } + + /** + * This returns the underlying object to be modified but does not affect ownership. + */ + T get() { return mT; } + + /** + * This returns the const underlying object but does not affect ownership. + */ + const T get() const { return mT; } + + /** + * This allows the value in this class to be set from beneath it. If you call this method and + * then change the value of T*, you must take ownership of the value you are replacing and add + * ownership to the object that is put in here. + * + * Recommended use is like this: + * ScopedAResource<T> a; // will be nullptr + * SomeInitFunction(a.getR()); // value is initialized with refcount + * + * Other usecases are discouraged. + * + */ + T* getR() { return &mT; } + + // copy-constructing, or move/copy assignment is disallowed + ScopedAResource(const ScopedAResource&) = delete; + ScopedAResource& operator=(const ScopedAResource&) = delete; + ScopedAResource& operator=(ScopedAResource&&) = delete; + + // move-constructing is okay + ScopedAResource(ScopedAResource&& other) : mT(std::move(other.mT)) { other.mT = DEFAULT; } + + private: + T mT; +}; + +} // namespace impl + +/** + * Convenience wrapper. See AParcel. + */ +class ScopedAParcel : public impl::ScopedAResource<AParcel*, void, AParcel_delete, nullptr> { + public: + /** + * Takes ownership of a. + */ + explicit ScopedAParcel(AParcel* a = nullptr) : ScopedAResource(a) {} + ~ScopedAParcel() {} + ScopedAParcel(ScopedAParcel&&) = default; +}; + +/** + * Convenience wrapper. See AStatus. + */ +class ScopedAStatus : public impl::ScopedAResource<AStatus*, void, AStatus_delete, nullptr> { + public: + /** + * Takes ownership of a. + */ + explicit ScopedAStatus(AStatus* a = nullptr) : ScopedAResource(a) {} + ~ScopedAStatus() {} + ScopedAStatus(ScopedAStatus&&) = default; + + /** + * See AStatus_isOk. + */ + bool isOk() { return get() != nullptr && AStatus_isOk(get()); } + + /** + * Convenience method for okay status. + */ + static ScopedAStatus ok() { return ScopedAStatus(AStatus_newOk()); } +}; + +/** + * Convenience wrapper. See AIBinder_DeathRecipient. + */ +class ScopedAIBinder_DeathRecipient + : public impl::ScopedAResource<AIBinder_DeathRecipient*, void, AIBinder_DeathRecipient_delete, + nullptr> { + public: + /** + * Takes ownership of a. + */ + explicit ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient* a = nullptr) + : ScopedAResource(a) {} + ~ScopedAIBinder_DeathRecipient() {} + ScopedAIBinder_DeathRecipient(ScopedAIBinder_DeathRecipient&&) = default; +}; + +/** + * Convenience wrapper. See AIBinder_Weak. + */ +class ScopedAIBinder_Weak + : public impl::ScopedAResource<AIBinder_Weak*, void, AIBinder_Weak_delete, nullptr> { + public: + /** + * Takes ownership of a. + */ + explicit ScopedAIBinder_Weak(AIBinder_Weak* a = nullptr) : ScopedAResource(a) {} + ~ScopedAIBinder_Weak() {} + ScopedAIBinder_Weak(ScopedAIBinder_Weak&&) = default; + + /** + * See AIBinder_Weak_promote. + */ + SpAIBinder promote() { return SpAIBinder(AIBinder_Weak_promote(get())); } +}; + +/** + * Convenience wrapper for a file descriptor. + */ +class ScopedFileDescriptor : public impl::ScopedAResource<int, int, close, -1> { + public: + /** + * Takes ownership of a. + */ + explicit ScopedFileDescriptor(int a = -1) : ScopedAResource(a) {} + ~ScopedFileDescriptor() {} + ScopedFileDescriptor(ScopedFileDescriptor&&) = default; +}; + +} // namespace ndk + +/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h new file mode 100644 index 0000000..80d1254 --- /dev/null +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -0,0 +1,515 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup NdkBinder + * @{ + */ + +/** + * @file binder_ibinder.h + * @brief Object which can receive transactions and be sent across processes. + */ + +#pragma once + +#include <stdint.h> +#include <sys/cdefs.h> +#include <sys/types.h> + +#include <android/binder_parcel.h> +#include <android/binder_status.h> + +__BEGIN_DECLS +#if __ANDROID_API__ >= __ANDROID_API_Q__ + +// Also see TF_* in kernel's binder.h +typedef uint32_t binder_flags_t; +enum { + /** + * The transaction will be dispatched and then returned to the caller. The outgoing process + * cannot block a call made by this, and execution of the call will not be waited on. An error + * can still be returned if the call is unable to be processed by the binder driver. All oneway + * calls are guaranteed to be ordered if they are sent on the same AIBinder object. + */ + FLAG_ONEWAY = 0x01, +}; + +// Also see IBinder.h in libbinder +typedef uint32_t transaction_code_t; +enum { + /** + * The first transaction code available for user commands (inclusive). + */ + FIRST_CALL_TRANSACTION = 0x00000001, + /** + * The last transaction code available for user commands (inclusive). + */ + LAST_CALL_TRANSACTION = 0x00ffffff, +}; + +/** + * Represents a type of AIBinder object which can be sent out. + */ +struct AIBinder_Class; +typedef struct AIBinder_Class AIBinder_Class; + +/** + * Represents a local or remote object which can be used for IPC or which can itself be sent. + * + * This object has a refcount associated with it and will be deleted when its refcount reaches zero. + * How methods interactive with this refcount is described below. When using this API, it is + * intended for a client of a service to hold a strong reference to that service. This also means + * that user data typically should hold a strong reference to a local AIBinder object. A remote + * AIBinder object automatically holds a strong reference to the AIBinder object in the server's + * process. A typically memory layout looks like this: + * + * Key: + * ---> Ownership/a strong reference + * ...> A weak reference + * + * (process boundary) + * | + * MyInterface ---> AIBinder_Weak | ProxyForMyInterface + * ^ . | | + * | . | | + * | v | v + * UserData <--- AIBinder <-|- AIBinder + * | + * + * In this way, you'll notice that a proxy for the interface holds a strong reference to the + * implementation and that in the server process, the AIBinder object which was sent can be resent + * so that the same AIBinder object always represents the same object. This allows, for instance, an + * implementation (usually a callback) to transfer all ownership to a remote process and + * automatically be deleted when the remote process is done with it or dies. Other memory models are + * possible, but this is the standard one. + * + * If the process containing an AIBinder dies, it is possible to be holding a strong reference to + * an object which does not exist. In this case, transactions to this binder will return + * STATUS_DEAD_OBJECT. See also AIBinder_linkToDeath, AIBinder_unlinkToDeath, and AIBinder_isAlive. + * + * Once an AIBinder is created, anywhere it is passed (remotely or locally), there is a 1-1 + * correspondence between the address of an AIBinder and the object it represents. This means that + * when two AIBinder pointers point to the same address, they represent the same object (whether + * that object is local or remote). This correspondance can be broken accidentally if AIBinder_new + * is erronesouly called to create the same object multiple times. + */ +struct AIBinder; +typedef struct AIBinder AIBinder; + +/** + * The AIBinder object associated with this can be retrieved if it is still alive so that it can be + * re-used. The intention of this is to enable the same AIBinder object to always represent the same + * object. + */ +struct AIBinder_Weak; +typedef struct AIBinder_Weak AIBinder_Weak; + +/** + * Represents a handle on a death notification. See AIBinder_linkToDeath/AIBinder_unlinkToDeath. + */ +struct AIBinder_DeathRecipient; +typedef struct AIBinder_DeathRecipient AIBinder_DeathRecipient; + +/** + * This is called whenever a new AIBinder object is needed of a specific class. + * + * \param args these can be used to construct a new class. These are passed from AIBinder_new. + * \return this is the userdata representing the class. It can be retrieved using + * AIBinder_getUserData. + */ +typedef void* (*AIBinder_Class_onCreate)(void* args); + +/** + * This is called whenever an AIBinder object is no longer referenced and needs destroyed. + * + * Typically, this just deletes whatever the implementation is. + * + * \param userData this is the same object returned by AIBinder_Class_onCreate + */ +typedef void (*AIBinder_Class_onDestroy)(void* userData); + +/** + * This is called whenever a transaction needs to be processed by a local implementation. + * + * \param binder the object being transacted on. + * \param code implementation-specific code representing which transaction should be taken. + * \param in the implementation-specific input data to this transaction. + * \param out the implementation-specific output data to this transaction. + * + * \return the implementation-specific output code. This may be forwarded from another service, the + * result of a parcel read or write, or another error as is applicable to the specific + * implementation. Usually, implementation-specific error codes are written to the output parcel, + * and the transaction code is reserved for kernel errors or error codes that have been repeated + * from subsequent transactions. + */ +typedef binder_status_t (*AIBinder_Class_onTransact)(AIBinder* binder, transaction_code_t code, + const AParcel* in, AParcel* out); + +/** + * This creates a new instance of a class of binders which can be instantiated. This is called one + * time during library initialization and cleaned up when the process exits or execs. + * + * None of these parameters can be null. + * + * \param interfaceDescriptor this is a unique identifier for the class. This is used internally for + * sanity checks on transactions. + * \param onCreate see AIBinder_Class_onCreate. + * \param onDestroy see AIBinder_Class_onDestroy. + * \param onTransact see AIBinder_Class_onTransact. + * + * \return the class object representing these parameters or null on error. + */ +__attribute__((warn_unused_result)) AIBinder_Class* AIBinder_Class_define( + const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate, + AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact) + __INTRODUCED_IN(29); + +/** + * Dump information about an AIBinder (usually for debugging). + * + * When no arguments are provided, a brief overview of the interview should be given. + * + * \param binder interface being dumped + * \param fd file descriptor to be dumped to, should be flushed, ownership is not passed. + * \param args array of null-terminated strings for dump (may be null if numArgs is 0) + * \param numArgs number of args to be sent + * + * \return binder_status_t result of transaction (if remote, for instance) + */ +typedef binder_status_t (*AIBinder_onDump)(AIBinder* binder, int fd, const char** args, + uint32_t numArgs); + +/** + * This sets the implementation of the dump method for a class. + * + * If this isn't set, nothing will be dumped when dump is called (for instance with + * android.os.Binder#dump). Must be called before any instance of the class is created. + * + * \param dump function to call when an instance of this binder class is being dumped. + */ +void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29); + +/** + * Creates a new binder object of the appropriate class. + * + * Ownership of args is passed to this object. The lifecycle is implemented with AIBinder_incStrong + * and AIBinder_decStrong. When the reference count reaches zero, onDestroy is called. + * + * When this is called, the refcount is implicitly 1. So, calling decStrong exactly one time is + * required to delete this object. + * + * Once an AIBinder object is created using this API, re-creating that AIBinder for the same + * instance of the same class will break pointer equality for that specific AIBinder object. For + * instance, if someone erroneously created two AIBinder instances representing the same callback + * object and passed one to a hypothetical addCallback function and then later another one to a + * hypothetical removeCallback function, the remote process would have no way to determine that + * these two objects are actually equal using the AIBinder pointer alone (which they should be able + * to do). Also see the suggested memory ownership model suggested above. + * + * \param clazz the type of the object to be created. + * \param args the args to pass to AIBinder_onCreate for that class. + * + * \return a binder object representing the newly instantiated object. + */ +__attribute__((warn_unused_result)) AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) + __INTRODUCED_IN(29); + +/** + * If this is hosted in a process other than the current one. + * + * \param binder the binder being queried. + * + * \return true if the AIBinder represents an object in another process. + */ +bool AIBinder_isRemote(const AIBinder* binder) __INTRODUCED_IN(29); + +/** + * If this binder is known to be alive. This will not send a transaction to a remote process and + * returns a result based on the last known information. That is, whenever a transaction is made, + * this is automatically updated to reflect the current alive status of this binder. This will be + * updated as the result of a transaction made using AIBinder_transact, but it will also be updated + * based on the results of bookkeeping or other transactions made internally. + * + * \param binder the binder being queried. + * + * \return true if the binder is alive. + */ +bool AIBinder_isAlive(const AIBinder* binder) __INTRODUCED_IN(29); + +/** + * Built-in transaction for all binder objects. This sends a transaction that will immediately + * return. Usually this is used to make sure that a binder is alive, as a placeholder call, or as a + * sanity check. + * + * \param binder the binder being queried. + * + * \return STATUS_OK if the ping succeeds. + */ +binder_status_t AIBinder_ping(AIBinder* binder) __INTRODUCED_IN(29); + +/** + * Built-in transaction for all binder objects. This dumps information about a given binder. + * + * See also AIBinder_Class_setOnDump, AIBinder_onDump + * + * \param binder the binder to dump information about + * \param fd where information should be dumped to + * \param args null-terminated arguments to pass (may be null if numArgs is 0) + * \param numArgs number of args to send + * + * \return STATUS_OK if dump succeeds (or if there is nothing to dump) + */ +binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint32_t numArgs) + __INTRODUCED_IN(29); + +/** + * Registers for notifications that the associated binder is dead. The same death recipient may be + * associated with multiple different binders. If the binder is local, then no death recipient will + * be given (since if the local process dies, then no recipient will exist to recieve a + * transaction). The cookie is passed to recipient in the case that this binder dies and can be + * null. The exact cookie must also be used to unlink this transaction (see AIBinder_linkToDeath). + * This function may return a binder transaction failure. The cookie can be used both for + * identification and holding user data. + * + * If binder is local, this will return STATUS_INVALID_OPERATION. + * + * \param binder the binder object you want to receive death notifications from. + * \param recipient the callback that will receive notifications when/if the binder dies. + * \param cookie the value that will be passed to the death recipient on death. + * + * \return STATUS_OK on success. + */ +binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, + void* cookie) __INTRODUCED_IN(29); + +/** + * Stops registration for the associated binder dying. Does not delete the recipient. This function + * may return a binder transaction failure and in case the death recipient cannot be found, it + * returns STATUS_NAME_NOT_FOUND. + * + * This only ever needs to be called when the AIBinder_DeathRecipient remains for use with other + * AIBinder objects. If the death recipient is deleted, all binders will automatically be unlinked. + * If the binder dies, it will automatically unlink. If the binder is deleted, it will be + * automatically unlinked. + * + * \param binder the binder object to remove a previously linked death recipient from. + * \param recipient the callback to remove. + * \param cookie the cookie used to link to death. + * + * \return STATUS_OK on success. STATUS_NAME_NOT_FOUND if the binder cannot be found to be unlinked. + */ +binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, + void* cookie) __INTRODUCED_IN(29); + +/** + * This returns the calling UID assuming that this thread is called from a thread that is processing + * a binder transaction (for instance, in the implementation of AIBinder_Class_onTransact). + * + * This can be used with higher-level system services to determine the caller's identity and check + * permissions. + * + * \return calling uid or the current process's UID if this thread isn't processing a transaction. + */ +uid_t AIBinder_getCallingUid(); + +/** + * This returns the calling PID assuming that this thread is called from a thread that is processing + * a binder transaction (for instance, in the implementation of AIBinder_Class_onTransact). + * + * This can be used with higher-level system services to determine the caller's identity and check + * permissions. However, when doing this, one should be aware of possible TOCTOU problems when the + * calling process dies and is replaced with another process with elevated permissions and the same + * PID. + * + * \return calling pid or the current process's PID if this thread isn't processing a transaction. + * If the transaction being processed is a oneway transaction, then this method will return 0. + */ +pid_t AIBinder_getCallingPid(); + +/** + * This can only be called if a strong reference to this object already exists in process. + * + * \param binder the binder object to add a refcount to. + */ +void AIBinder_incStrong(AIBinder* binder) __INTRODUCED_IN(29); + +/** + * This will delete the object and call onDestroy once the refcount reaches zero. + * + * \param binder the binder object to remove a refcount from. + */ +void AIBinder_decStrong(AIBinder* binder) __INTRODUCED_IN(29); + +/** + * For debugging only! + * + * \param binder the binder object to retrieve the refcount of. + * + * \return the number of strong-refs on this binder in this process. If binder is null, this will be + * -1. + */ +int32_t AIBinder_debugGetRefCount(AIBinder* binder) __INTRODUCED_IN(29); + +/** + * This sets the class of an AIBinder object. This checks to make sure the remote object is of + * the expected class. A class must be set in order to use transactions on an AIBinder object. + * However, if an object is just intended to be passed through to another process or used as a + * handle this need not be called. + * + * This returns true if the class association succeeds. If it fails, no change is made to the + * binder object. + * + * \param binder the object to attach the class to. + * \param clazz the clazz to attach to binder. + * + * \return true if the binder has the class clazz and if the association was successful. + */ +bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz) __INTRODUCED_IN(29); + +/** + * Returns the class that this binder was constructed with or associated with. + * + * \param binder the object that is being queried. + * + * \return the class that this binder is associated with. If this binder wasn't created with + * AIBinder_new, and AIBinder_associateClass hasn't been called, then this will return null. + */ +const AIBinder_Class* AIBinder_getClass(AIBinder* binder) __INTRODUCED_IN(29); + +/** + * Value returned by onCreate for a local binder. For stateless classes (if onCreate returns + * null), this also returns null. For a remote binder, this will always return null. + * + * \param binder the object that is being queried. + * + * \return the userdata returned from AIBinder_onCreate when this object was created. This may be + * null for stateless objects. For remote objects, this is always null. + */ +void* AIBinder_getUserData(AIBinder* binder) __INTRODUCED_IN(29); + +/** + * A transaction is a series of calls to these functions which looks this + * - call AIBinder_prepareTransaction + * - fill out the in parcel with parameters (lifetime of the 'in' variable) + * - call AIBinder_transact + * - read results from the out parcel (lifetime of the 'out' variable) + */ + +/** + * Creates a parcel to start filling out for a transaction. This may add data to the parcel for + * security, debugging, or other purposes. This parcel is to be sent via AIBinder_transact and it + * represents the input data to the transaction. It is recommended to check if the object is local + * and call directly into its user data before calling this as the parceling and unparceling cost + * can be avoided. This AIBinder must be either built with a class or associated with a class before + * using this API. + * + * This does not affect the ownership of binder. When this function succeeds, the in parcel's + * ownership is passed to the caller. At this point, the parcel can be filled out and passed to + * AIBinder_transact. Alternatively, if there is an error while filling out the parcel, it can be + * deleted with AParcel_delete. + * + * \param binder the binder object to start a transaction on. + * \param in out parameter for input data to the transaction. + * + * \return STATUS_OK on success. This will return STATUS_INVALID_OPERATION if the binder has not yet + * been associated with a class (see AIBinder_new and AIBinder_associateClass). + */ +binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) __INTRODUCED_IN(29); + +/** + * Transact using a parcel created from AIBinder_prepareTransaction. This actually communicates with + * the object representing this binder object. This also passes out a parcel to be used for the + * return transaction. This takes ownership of the in parcel and automatically deletes it after it + * is sent to the remote process. The output parcel is the result of the transaction. If the + * transaction has FLAG_ONEWAY, the out parcel will be empty. Otherwise, this will block until the + * remote process has processed the transaction, and the out parcel will contain the output data + * from transaction. + * + * This does not affect the ownership of binder. The out parcel's ownership is passed to the caller + * and must be released with AParcel_delete when finished reading. + * + * \param binder the binder object to transact on. + * \param code the implementation-specific code representing which transaction should be taken. + * \param in the implementation-specific input data to this transaction. + * \param out the implementation-specific output data to this transaction. + * \param flags possible flags to alter the way in which the transaction is conducted or 0. + * + * \return the result from the kernel or from the remote process. Usually, implementation-specific + * error codes are written to the output parcel, and the transaction code is reserved for kernel + * errors or error codes that have been repeated from subsequent transactions. + */ +binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, AParcel** in, + AParcel** out, binder_flags_t flags) __INTRODUCED_IN(29); + +/** + * This does not take any ownership of the input binder, but it can be used to retrieve it if + * something else in some process still holds a reference to it. + * + * \param binder object to create a weak pointer to. + * + * \return object representing a weak pointer to binder (or null if binder is null). + */ +__attribute__((warn_unused_result)) AIBinder_Weak* AIBinder_Weak_new(AIBinder* binder) + __INTRODUCED_IN(29); + +/** + * Deletes the weak reference. This will have no impact on the lifetime of the binder. + * + * \param weakBinder object created with AIBinder_Weak_new. + */ +void AIBinder_Weak_delete(AIBinder_Weak* weakBinder) __INTRODUCED_IN(29); + +/** + * If promotion succeeds, result will have one strong refcount added to it. Otherwise, this returns + * null. + * + * \param weakBinder weak pointer to attempt retrieving the original object from. + * + * \return an AIBinder object with one refcount given to the caller or null. + */ +__attribute__((warn_unused_result)) AIBinder* AIBinder_Weak_promote(AIBinder_Weak* weakBinder) + __INTRODUCED_IN(29); + +/** + * This function is executed on death receipt. See AIBinder_linkToDeath/AIBinder_unlinkToDeath. + * + * \param cookie the cookie passed to AIBinder_linkToDeath. + */ +typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie) __INTRODUCED_IN(29); + +/** + * Creates a new binder death recipient. This can be attached to multiple different binder objects. + * + * \param onBinderDied the callback to call when this death recipient is invoked. + * + * \return the newly constructed object (or null if onBinderDied is null). + */ +__attribute__((warn_unused_result)) AIBinder_DeathRecipient* AIBinder_DeathRecipient_new( + AIBinder_DeathRecipient_onBinderDied onBinderDied) __INTRODUCED_IN(29); + +/** + * Deletes a binder death recipient. It is not necessary to call AIBinder_unlinkToDeath before + * calling this as these will all be automatically unlinked. + * + * \param recipient the binder to delete (previously created with AIBinder_DeathRecipient_new). + */ +void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) __INTRODUCED_IN(29); + +#endif //__ANDROID_API__ >= __ANDROID_API_Q__ +__END_DECLS + +/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h new file mode 100644 index 0000000..124f36c --- /dev/null +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
@@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup NdkBinder + * @{ + */ + +/** + * @file binder_ibinder_jni.h + * @brief Conversions between AIBinder and android.os.IBinder + */ + +#pragma once + +#include <android/binder_ibinder.h> + +#include <jni.h> + +__BEGIN_DECLS +#if __ANDROID_API__ >= __ANDROID_API_Q__ + +/** + * Converts an android.os.IBinder object into an AIBinder* object. + * + * If either env or the binder is null, null is returned. If this binder object was originally an + * AIBinder object, the original object is returned. The returned object has one refcount + * associated with it, and so this should be accompanied with an AIBinder_decStrong call. + * + * \param env Java environment. + * \param binder android.os.IBinder java object. + * + * \return an AIBinder object representing the Java binder object. If either parameter is null, or + * the Java object is of the wrong type, this will return null. + */ +__attribute__((warn_unused_result)) AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder) + __INTRODUCED_IN(29); + +/** + * Converts an AIBinder* object into an android.os.IBinder object. + * + * If either env or the binder is null, null is returned. If this binder object was originally an + * IBinder object, the original java object will be returned. + * + * \param env Java environment. + * \param binder the object to convert. + * + * \return an android.os.IBinder object or null if the parameters were null. + */ +__attribute__((warn_unused_result)) jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder) + __INTRODUCED_IN(29); + +#endif //__ANDROID_API__ >= __ANDROID_API_Q__ +__END_DECLS + +/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h new file mode 100644 index 0000000..83a1048 --- /dev/null +++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
@@ -0,0 +1,260 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup NdkBinder + * @{ + */ + +/** + * @file binder_interface_utils.h + * @brief This provides common C++ classes for common operations and as base classes for C++ + * interfaces. + */ + +#pragma once + +#include <android/binder_auto_utils.h> +#include <android/binder_ibinder.h> + +#include <assert.h> + +#include <memory> +#include <mutex> + +namespace ndk { + +/** + * analog using std::shared_ptr for internally held refcount + * + * ref must be called at least one time during the lifetime of this object. The recommended way to + * construct this object is with SharedRefBase::make. + */ +class SharedRefBase { + public: + SharedRefBase() {} + virtual ~SharedRefBase() { + std::call_once(mFlagThis, [&]() { + __assert(__FILE__, __LINE__, "SharedRefBase: no ref created during lifetime"); + }); + } + + /** + * A shared_ptr must be held to this object when this is called. This must be called once during + * the lifetime of this object. + */ + std::shared_ptr<SharedRefBase> ref() { + std::shared_ptr<SharedRefBase> thiz = mThis.lock(); + + std::call_once(mFlagThis, [&]() { mThis = thiz = std::shared_ptr<SharedRefBase>(this); }); + + return thiz; + } + + /** + * Convenience method for a ref (see above) which automatically casts to the desired child type. + */ + template <typename CHILD> + std::shared_ptr<CHILD> ref() { + return std::static_pointer_cast<CHILD>(ref()); + } + + /** + * Convenience method for making an object directly with a reference. + */ + template <class T, class... Args> + static std::shared_ptr<T> make(Args&&... args) { + T* t = new T(std::forward<Args>(args)...); + return t->template ref<T>(); + } + + private: + std::once_flag mFlagThis; + std::weak_ptr<SharedRefBase> mThis; +}; + +/** + * wrapper analog to IInterface + */ +class ICInterface : public SharedRefBase { + public: + ICInterface() {} + virtual ~ICInterface() {} + + /** + * This either returns the single existing implementation or creates a new implementation. + */ + virtual SpAIBinder asBinder() = 0; + + /** + * Returns whether this interface is in a remote process. If it cannot be determined locally, + * this will be checked using AIBinder_isRemote. + */ + virtual bool isRemote() = 0; + + /** + * Dumps information about the interface. By default, dumps nothing. + */ + virtual inline binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/); + + /** + * Interprets this binder as this underlying interface if this has stored an ICInterface in the + * binder's user data. + * + * This does not do type checking and should only be used when the binder is known to originate + * from ICInterface. Most likely, you want to use I*::fromBinder. + */ + static inline std::shared_ptr<ICInterface> asInterface(AIBinder* binder); + + /** + * Helper method to create a class + */ + static inline AIBinder_Class* defineClass(const char* interfaceDescriptor, + AIBinder_Class_onTransact onTransact); + + private: + class ICInterfaceData { + public: + std::shared_ptr<ICInterface> interface; + + static inline std::shared_ptr<ICInterface> getInterface(AIBinder* binder); + + static inline void* onCreate(void* args); + static inline void onDestroy(void* userData); + static inline binder_status_t onDump(AIBinder* binder, int fd, const char** args, + uint32_t numArgs); + }; +}; + +/** + * implementation of IInterface for server (n = native) + */ +template <typename INTERFACE> +class BnCInterface : public INTERFACE { + public: + BnCInterface() {} + virtual ~BnCInterface() {} + + SpAIBinder asBinder() override; + + bool isRemote() override { return false; } + + protected: + /** + * This function should only be called by asBinder. Otherwise, there is a possibility of + * multiple AIBinder* objects being created for the same instance of an object. + */ + virtual SpAIBinder createBinder() = 0; + + private: + std::mutex mMutex; // for asBinder + ScopedAIBinder_Weak mWeakBinder; +}; + +/** + * implementation of IInterface for client (p = proxy) + */ +template <typename INTERFACE> +class BpCInterface : public INTERFACE { + public: + explicit BpCInterface(const SpAIBinder& binder) : mBinder(binder) {} + virtual ~BpCInterface() {} + + SpAIBinder asBinder() override; + + bool isRemote() override { return AIBinder_isRemote(mBinder.get()); } + + binder_status_t dump(int fd, const char** args, uint32_t numArgs) override { + return AIBinder_dump(asBinder().get(), fd, args, numArgs); + } + + private: + SpAIBinder mBinder; +}; + +// END OF CLASS DECLARATIONS + +binder_status_t ICInterface::dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/) { + return STATUS_OK; +} + +std::shared_ptr<ICInterface> ICInterface::asInterface(AIBinder* binder) { + return ICInterfaceData::getInterface(binder); +} + +AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor, + AIBinder_Class_onTransact onTransact) { + AIBinder_Class* clazz = AIBinder_Class_define(interfaceDescriptor, ICInterfaceData::onCreate, + ICInterfaceData::onDestroy, onTransact); + if (clazz == nullptr) { + return nullptr; + } + + // We can't know if this method is overriden by a subclass interface, so we must register + // ourselves. The default (nothing to dump) is harmless. + AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump); + return clazz; +} + +std::shared_ptr<ICInterface> ICInterface::ICInterfaceData::getInterface(AIBinder* binder) { + if (binder == nullptr) return nullptr; + + void* userData = AIBinder_getUserData(binder); + if (userData == nullptr) return nullptr; + + return static_cast<ICInterfaceData*>(userData)->interface; +} + +void* ICInterface::ICInterfaceData::onCreate(void* args) { + std::shared_ptr<ICInterface> interface = static_cast<ICInterface*>(args)->ref<ICInterface>(); + ICInterfaceData* data = new ICInterfaceData{interface}; + return static_cast<void*>(data); +} + +void ICInterface::ICInterfaceData::onDestroy(void* userData) { + delete static_cast<ICInterfaceData*>(userData); +} + +binder_status_t ICInterface::ICInterfaceData::onDump(AIBinder* binder, int fd, const char** args, + uint32_t numArgs) { + std::shared_ptr<ICInterface> interface = getInterface(binder); + return interface->dump(fd, args, numArgs); +} + +template <typename INTERFACE> +SpAIBinder BnCInterface<INTERFACE>::asBinder() { + std::lock_guard<std::mutex> l(mMutex); + + SpAIBinder binder; + if (mWeakBinder.get() != nullptr) { + binder.set(AIBinder_Weak_promote(mWeakBinder.get())); + } + if (binder.get() == nullptr) { + binder = createBinder(); + mWeakBinder.set(AIBinder_Weak_new(binder.get())); + } + + return binder; +} + +template <typename INTERFACE> +SpAIBinder BpCInterface<INTERFACE>::asBinder() { + return mBinder; +} + +} // namespace ndk + +/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h new file mode 100644 index 0000000..2258210 --- /dev/null +++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h
@@ -0,0 +1,1020 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup NdkBinder + * @{ + */ + +/** + * @file binder_parcel.h + * @brief A collection of data that can be sent as a single packet. + */ + +#pragma once + +#include <sys/cdefs.h> + +#include <android/binder_status.h> + +struct AIBinder; +typedef struct AIBinder AIBinder; + +__BEGIN_DECLS +#if __ANDROID_API__ >= __ANDROID_API_Q__ + +/** + * This object represents a package of data that can be sent between processes. When transacting, an + * instance of it is automatically created to be used for the transaction. When two processes use + * binder to communicate, they must agree on a format of this parcel to be used in order to transfer + * data. This is usually done in an IDL (see AIDL, specificially). + */ +struct AParcel; +typedef struct AParcel AParcel; + +/** + * Cleans up a parcel. + * + * \param parcel A parcel returned by AIBinder_prepareTransaction or AIBinder_transact when a + * transaction is being aborted. + */ +void AParcel_delete(AParcel* parcel) __INTRODUCED_IN(29); + +/** + * Sets the position within the parcel. + * + * \param parcel The parcel of which to set the position. + * \param position Position of the parcel to set. This must be a value returned by + * AParcel_getDataPosition. Positions are constant for a given parcel between processes. + * + * \return STATUS_OK on success. If position is negative, then STATUS_BAD_VALUE will be returned. + */ +binder_status_t AParcel_setDataPosition(const AParcel* parcel, int32_t position) + __INTRODUCED_IN(29); + +/** + * Gets the current position within the parcel. + * + * \param parcel The parcel of which to get the position. + * + * \return The size of the parcel. This will always be greater than 0. The values returned by this + * function before and after calling various reads and writes are not defined. Only the delta + * between two positions between a specific sequence of calls is defined. For instance, if position + * is X, writeBool is called, and then position is Y, readBool can be called from position X will + * return the same value, and then position will be Y. + */ +int32_t AParcel_getDataPosition(const AParcel* parcel) __INTRODUCED_IN(29); + +/** + * This is called to allocate a buffer for a C-style string (null-terminated). The returned buffer + * should be at least length bytes. This includes space for a null terminator. For a string, length + * will always be strictly less than or equal to the maximum size that can be held in a size_t and + * will always be greater than 0. However, if a 'null' string is being read, length will be -1. + * + * See also AParcel_readString. + * + * If allocation fails, null should be returned. + * + * \param stringData some external representation of a string + * \param length the length of the buffer needed to fill (including the null-terminator) + * \param buffer a buffer of size 'length' or null if allocation failed. + * + * \return true if the allocation succeeded, false otherwise. If length is -1, a true return here + * means that a 'null' value (or equivalent) was successfully stored. + */ +typedef bool (*AParcel_stringAllocator)(void* stringData, int32_t length, char** buffer); + +/** + * This is called to allocate an array of size 'length'. If length is -1, then a 'null' array (or + * equivalent) should be created. + * + * See also AParcel_readStringArray + * + * \param arrayData some external representation of an array + * \param length the length to allocate this array to + * + * \return true if allocation succeeded. If length is -1, a true return here means that a 'null' + * value (or equivalent) was successfully stored. + */ +typedef bool (*AParcel_stringArrayAllocator)(void* arrayData, int32_t length); + +/** + * This is called to allocate a string inside of an array that was allocated by an + * AParcel_stringArrayAllocator. + * + * The index returned will always be within the range [0, length of arrayData). The returned buffer + * should be at least length bytes. This includes space for a null-terminator. For a string, length + * will always be strictly less than or equal to the maximum size that can be held in a size_t and + * will always be greater than 0. However, if a 'null' string is being read, length will be -1. + * + * See also AParcel_readStringArray + * + * \param arrayData some external representation of an array. + * \param index the index at which a string should be allocated. + * \param length the length of the string to be allocated at this index. See also + * AParcel_stringAllocator. This includes the length required for a null-terminator. + * \param buffer a buffer of size 'length' or null if allocation failed. + * + * \return true if the allocation succeeded, false otherwise. If length is -1, a true return here + * means that a 'null' value (or equivalent) was successfully stored. + */ +typedef bool (*AParcel_stringArrayElementAllocator)(void* arrayData, size_t index, int32_t length, + char** buffer); + +/** + * This returns the length and buffer of an array at a specific index in an arrayData object. + * + * See also AParcel_writeStringArray + * + * \param arrayData some external representation of an array. + * \param index the index at which a string should be allocated. + * \param outLength an out parameter for the length of the string at the specified index. This + * should not include the length for a null-terminator if there is one. If the object at this index + * is 'null', then this should be set to -1. + * + * \param a buffer of size outLength or more representing the string at the provided index. This is + * not required to be null-terminated. If the object at index is null, then this should be null. + */ +typedef const char* (*AParcel_stringArrayElementGetter)(const void* arrayData, size_t index, + int32_t* outLength); + +/** + * This is called to allocate an array of size 'length'. If length is -1, then a 'null' array (or + * equivalent) should be created. + * + * See also AParcel_readParcelableArray + * + * \param arrayData some external representation of an array + * \param length the length to allocate this array to + * + * \return true if allocation succeeded. If length is -1, a true return here means that a 'null' + * value (or equivalent) was successfully stored. + */ +typedef bool (*AParcel_parcelableArrayAllocator)(void* arrayData, int32_t length); + +/** + * This is called to parcel the underlying data from an arrayData object at index. + * + * See also AParcel_writeParcelableArray + * + * \param parcel parcel to write the parcelable to + * \param arrayData some external representation of an array of parcelables (a user-defined type). + * \param index the index of the value to be retrieved. + * + * \return status (usually returned from other parceling functions). STATUS_OK for success. + */ +typedef binder_status_t (*AParcel_writeParcelableElement)(AParcel* parcel, const void* arrayData, + size_t index); + +/** + * This is called to set an underlying value in an arrayData object at index. + * + * See also AParcel_readParcelableArray + * + * \param parcel parcel to read the parcelable from + * \param arrayData some external representation of an array of parcelables (a user-defined type). + * \param index the index of the value to be set. + * + * \return status (usually returned from other parceling functions). STATUS_OK for success. + */ +typedef binder_status_t (*AParcel_readParcelableElement)(const AParcel* parcel, void* arrayData, + size_t index); + +// @START-PRIMITIVE-VECTOR-GETTERS +/** + * This is called to get the underlying data from an arrayData object. + * + * The implementation of this function should allocate a contiguous array of size 'length' and + * return that underlying buffer to be filled out. If there is an error or length is 0, null may be + * returned. If length is -1, this should allocate some representation of a null array. + * + * See also AParcel_readInt32Array + * + * \param arrayData some external representation of an array of int32_t. + * \param length the length to allocate arrayData to. + * \param outBuffer a buffer of int32_t of size 'length' (if length is >= 0, if length is 0, this + * may be nullptr). + * + * \return whether or not the allocation was successful (or whether a null array is represented when + * length is -1). + */ +typedef bool (*AParcel_int32ArrayAllocator)(void* arrayData, int32_t length, int32_t** outBuffer); + +/** + * This is called to get the underlying data from an arrayData object. + * + * The implementation of this function should allocate a contiguous array of size 'length' and + * return that underlying buffer to be filled out. If there is an error or length is 0, null may be + * returned. If length is -1, this should allocate some representation of a null array. + * + * See also AParcel_readUint32Array + * + * \param arrayData some external representation of an array of uint32_t. + * \param length the length to allocate arrayData to. + * \param outBuffer a buffer of uint32_t of size 'length' (if length is >= 0, if length is 0, this + * may be nullptr). + * + * \return whether or not the allocation was successful (or whether a null array is represented when + * length is -1). + */ +typedef bool (*AParcel_uint32ArrayAllocator)(void* arrayData, int32_t length, uint32_t** outBuffer); + +/** + * This is called to get the underlying data from an arrayData object. + * + * The implementation of this function should allocate a contiguous array of size 'length' and + * return that underlying buffer to be filled out. If there is an error or length is 0, null may be + * returned. If length is -1, this should allocate some representation of a null array. + * + * See also AParcel_readInt64Array + * + * \param arrayData some external representation of an array of int64_t. + * \param length the length to allocate arrayData to. + * \param outBuffer a buffer of int64_t of size 'length' (if length is >= 0, if length is 0, this + * may be nullptr). + * + * \return whether or not the allocation was successful (or whether a null array is represented when + * length is -1). + */ +typedef bool (*AParcel_int64ArrayAllocator)(void* arrayData, int32_t length, int64_t** outBuffer); + +/** + * This is called to get the underlying data from an arrayData object. + * + * The implementation of this function should allocate a contiguous array of size 'length' and + * return that underlying buffer to be filled out. If there is an error or length is 0, null may be + * returned. If length is -1, this should allocate some representation of a null array. + * + * See also AParcel_readUint64Array + * + * \param arrayData some external representation of an array of uint64_t. + * \param length the length to allocate arrayData to. + * \param outBuffer a buffer of uint64_t of size 'length' (if length is >= 0, if length is 0, this + * may be nullptr). + * + * \return whether or not the allocation was successful (or whether a null array is represented when + * length is -1). + */ +typedef bool (*AParcel_uint64ArrayAllocator)(void* arrayData, int32_t length, uint64_t** outBuffer); + +/** + * This is called to get the underlying data from an arrayData object. + * + * The implementation of this function should allocate a contiguous array of size 'length' and + * return that underlying buffer to be filled out. If there is an error or length is 0, null may be + * returned. If length is -1, this should allocate some representation of a null array. + * + * See also AParcel_readFloatArray + * + * \param arrayData some external representation of an array of float. + * \param length the length to allocate arrayData to. + * \param outBuffer a buffer of float of size 'length' (if length is >= 0, if length is 0, this may + * be nullptr). + * + * \return whether or not the allocation was successful (or whether a null array is represented when + * length is -1). + */ +typedef bool (*AParcel_floatArrayAllocator)(void* arrayData, int32_t length, float** outBuffer); + +/** + * This is called to get the underlying data from an arrayData object. + * + * The implementation of this function should allocate a contiguous array of size 'length' and + * return that underlying buffer to be filled out. If there is an error or length is 0, null may be + * returned. If length is -1, this should allocate some representation of a null array. + * + * See also AParcel_readDoubleArray + * + * \param arrayData some external representation of an array of double. + * \param length the length to allocate arrayData to. + * \param outBuffer a buffer of double of size 'length' (if length is >= 0, if length is 0, this may + * be nullptr). + * + * \return whether or not the allocation was successful (or whether a null array is represented when + * length is -1). + */ +typedef bool (*AParcel_doubleArrayAllocator)(void* arrayData, int32_t length, double** outBuffer); + +/** + * This allocates an array of size 'length' inside of arrayData and returns whether or not there was + * a success. If length is -1, then this should allocate some representation of a null array. + * + * See also AParcel_readBoolArray + * + * \param arrayData some external representation of an array of bool. + * \param length the length to allocate arrayData to (or -1 if this represents a null array). + * + * \return whether the allocation succeeded. + */ +typedef bool (*AParcel_boolArrayAllocator)(void* arrayData, int32_t length); + +/** + * This is called to get the underlying data from an arrayData object at index. + * + * See also AParcel_writeBoolArray + * + * \param arrayData some external representation of an array of bool. + * \param index the index of the value to be retrieved. + * + * \return the value of the array at index index. + */ +typedef bool (*AParcel_boolArrayGetter)(const void* arrayData, size_t index); + +/** + * This is called to set an underlying value in an arrayData object at index. + * + * See also AParcel_readBoolArray + * + * \param arrayData some external representation of an array of bool. + * \param index the index of the value to be set. + * \param value the value to set at index index. + */ +typedef void (*AParcel_boolArraySetter)(void* arrayData, size_t index, bool value); + +/** + * This is called to get the underlying data from an arrayData object. + * + * The implementation of this function should allocate a contiguous array of size 'length' and + * return that underlying buffer to be filled out. If there is an error or length is 0, null may be + * returned. If length is -1, this should allocate some representation of a null array. + * + * See also AParcel_readCharArray + * + * \param arrayData some external representation of an array of char16_t. + * \param length the length to allocate arrayData to. + * \param outBuffer a buffer of char16_t of size 'length' (if length is >= 0, if length is 0, this + * may be nullptr). + * + * \return whether or not the allocation was successful (or whether a null array is represented when + * length is -1). + */ +typedef bool (*AParcel_charArrayAllocator)(void* arrayData, int32_t length, char16_t** outBuffer); + +/** + * This is called to get the underlying data from an arrayData object. + * + * The implementation of this function should allocate a contiguous array of size 'length' and + * return that underlying buffer to be filled out. If there is an error or length is 0, null may be + * returned. If length is -1, this should allocate some representation of a null array. + * + * See also AParcel_readByteArray + * + * \param arrayData some external representation of an array of int8_t. + * \param length the length to allocate arrayData to. + * \param outBuffer a buffer of int8_t of size 'length' (if length is >= 0, if length is 0, this may + * be nullptr). + * + * \return whether or not the allocation was successful (or whether a null array is represented when + * length is -1). + */ +typedef bool (*AParcel_byteArrayAllocator)(void* arrayData, int32_t length, int8_t** outBuffer); + +// @END-PRIMITIVE-VECTOR-GETTERS + +/** + * Writes an AIBinder to the next location in a non-null parcel. Can be null. This does not take any + * refcounts of ownership of the binder from the client. + * + * \param parcel the parcel to write to. + * \param binder the value to write to the parcel. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) __INTRODUCED_IN(29); + +/** + * Reads an AIBinder from the next location in a non-null parcel. One strong ref-count of ownership + * is passed to the caller of this function. + * + * \param parcel the parcel to read from. + * \param binder the out parameter for what is read from the parcel. This may be null. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_readStrongBinder(const AParcel* parcel, AIBinder** binder) + __INTRODUCED_IN(29); + +/** + * Writes a file descriptor to the next location in a non-null parcel. This does not take ownership + * of fd. + * + * This corresponds to the SDK's android.os.ParcelFileDescriptor. + * + * \param parcel the parcel to write to. + * \param fd the value to write to the parcel (-1 to represent a null ParcelFileDescriptor). + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd); + +/** + * Reads an int from the next location in a non-null parcel. + * + * The returned fd must be closed. + * + * This corresponds to the SDK's android.os.ParcelFileDescriptor. + * + * \param parcel the parcel to read from. + * \param fd the out parameter for what is read from the parcel (or -1 to represent a null + * ParcelFileDescriptor) + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd); + +/** + * Writes an AStatus object to the next location in a non-null parcel. + * + * If the status is considered to be a low-level status and has no additional information other + * than a binder_status_t (for instance, if it is created with AStatus_fromStatus), then that + * status will be returned from this method and nothing will be written to the parcel. If either + * this happens or if writing the status object itself fails, the return value from this function + * should be propagated to the client, and AParcel_readStatusHeader shouldn't be called. + * + * \param parcel the parcel to write to. + * \param status the value to write to the parcel. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeStatusHeader(AParcel* parcel, const AStatus* status) + __INTRODUCED_IN(29); + +/** + * Reads an AStatus from the next location in a non-null parcel. Ownership is passed to the caller + * of this function. + * + * \param parcel the parcel to read from. + * \param status the out parameter for what is read from the parcel. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status) + __INTRODUCED_IN(29); + +/** + * Writes utf-8 string value to the next location in a non-null parcel. + * + * If length is -1, and string is nullptr, this will write a 'null' string to the parcel. + * + * \param parcel the parcel to write to. + * \param string the null-terminated string to write to the parcel, at least of size 'length'. + * \param length the length of the string to be written. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t length) + __INTRODUCED_IN(29); + +/** + * Reads and allocates utf-8 string value from the next location in a non-null parcel. + * + * Data is passed to the string allocator once the string size is known. This size includes the + * space for the null-terminator of this string. This allocator returns a buffer which is used as + * the output buffer from this read. If there is a 'null' string on the binder buffer, the allocator + * will be called with length -1. + * + * \param parcel the parcel to read from. + * \param stringData some external representation of a string. + * \param allocator allocator that will be called once the size of the string is known. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, + AParcel_stringAllocator allocator) __INTRODUCED_IN(29); + +/** + * Writes utf-8 string array data to the next location in a non-null parcel. + * + * length is the length of the array. AParcel_stringArrayElementGetter will be called for all + * indices in range [0, length) with the arrayData provided here. The string length and buffer + * returned from this function will be used to fill out the data from the parcel. If length is -1, + * this will write a 'null' string array to the binder buffer. + * + * \param parcel the parcel to write to. + * \param arrayData some external representation of an array. + * \param length the length of the array to be written. + * \param getter the callback that will be called for every index of the array to retrieve the + * corresponding string buffer. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeStringArray(AParcel* parcel, const void* arrayData, int32_t length, + AParcel_stringArrayElementGetter getter) + __INTRODUCED_IN(29); + +/** + * Reads and allocates utf-8 string array value from the next location in a non-null parcel. + * + * First, AParcel_stringArrayAllocator will be called with the size of the array to be read where + * length is the length of the array to be read from the parcel. Then, for each index i in [0, + * length), AParcel_stringArrayElementAllocator will be called with the length of the string to be + * read from the parcel. The resultant buffer from each of these calls will be filled according to + * the contents of the string that is read. If the string array being read is 'null', this will + * instead just pass -1 to AParcel_stringArrayAllocator. + * + * \param parcel the parcel to read from. + * \param arrayData some external representation of an array. + * \param allocator the callback that will be called with arrayData once the size of the output + * array is known. + * \param elementAllocator the callback that will be called on every index of arrayData to allocate + * the string at that location. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData, + AParcel_stringArrayAllocator allocator, + AParcel_stringArrayElementAllocator elementAllocator) + __INTRODUCED_IN(29); + +/** + * Writes an array of parcelables (user-defined types) to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). + * \param length the length of arrayData or -1 if this represents a null array. + * \param elementWriter function to be called for every array index to write the user-defined type + * at that location. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeParcelableArray(AParcel* parcel, const void* arrayData, int32_t length, + AParcel_writeParcelableElement elementWriter) + __INTRODUCED_IN(29); + +/** + * Reads an array of parcelables (user-defined types) from the next location in a non-null parcel. + * + * First, allocator will be called with the length of the array. If the allocation succeeds and the + * length is greater than zero, elementReader will be called for every index to read the + * corresponding parcelable. + * + * \param parcel the parcel to read from. + * \param arrayData some external representation of an array. + * \param allocator the callback that will be called to allocate the array. + * \param elementReader the callback that will be called to fill out individual elements. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readParcelableArray(const AParcel* parcel, void* arrayData, + AParcel_parcelableArrayAllocator allocator, + AParcel_readParcelableElement elementReader) + __INTRODUCED_IN(29); + +// @START-PRIMITIVE-READ-WRITE +/** + * Writes int32_t value to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param value the value to write to the parcel. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value) __INTRODUCED_IN(29); + +/** + * Writes uint32_t value to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param value the value to write to the parcel. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeUint32(AParcel* parcel, uint32_t value) __INTRODUCED_IN(29); + +/** + * Writes int64_t value to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param value the value to write to the parcel. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value) __INTRODUCED_IN(29); + +/** + * Writes uint64_t value to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param value the value to write to the parcel. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeUint64(AParcel* parcel, uint64_t value) __INTRODUCED_IN(29); + +/** + * Writes float value to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param value the value to write to the parcel. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeFloat(AParcel* parcel, float value) __INTRODUCED_IN(29); + +/** + * Writes double value to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param value the value to write to the parcel. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeDouble(AParcel* parcel, double value) __INTRODUCED_IN(29); + +/** + * Writes bool value to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param value the value to write to the parcel. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeBool(AParcel* parcel, bool value) __INTRODUCED_IN(29); + +/** + * Writes char16_t value to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param value the value to write to the parcel. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeChar(AParcel* parcel, char16_t value) __INTRODUCED_IN(29); + +/** + * Writes int8_t value to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param value the value to write to the parcel. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeByte(AParcel* parcel, int8_t value) __INTRODUCED_IN(29); + +/** + * Reads into int32_t value from the next location in a non-null parcel. + * + * \param parcel the parcel to read from. + * \param value the value to read from the parcel. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value) __INTRODUCED_IN(29); + +/** + * Reads into uint32_t value from the next location in a non-null parcel. + * + * \param parcel the parcel to read from. + * \param value the value to read from the parcel. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readUint32(const AParcel* parcel, uint32_t* value) __INTRODUCED_IN(29); + +/** + * Reads into int64_t value from the next location in a non-null parcel. + * + * \param parcel the parcel to read from. + * \param value the value to read from the parcel. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value) __INTRODUCED_IN(29); + +/** + * Reads into uint64_t value from the next location in a non-null parcel. + * + * \param parcel the parcel to read from. + * \param value the value to read from the parcel. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readUint64(const AParcel* parcel, uint64_t* value) __INTRODUCED_IN(29); + +/** + * Reads into float value from the next location in a non-null parcel. + * + * \param parcel the parcel to read from. + * \param value the value to read from the parcel. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readFloat(const AParcel* parcel, float* value) __INTRODUCED_IN(29); + +/** + * Reads into double value from the next location in a non-null parcel. + * + * \param parcel the parcel to read from. + * \param value the value to read from the parcel. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readDouble(const AParcel* parcel, double* value) __INTRODUCED_IN(29); + +/** + * Reads into bool value from the next location in a non-null parcel. + * + * \param parcel the parcel to read from. + * \param value the value to read from the parcel. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readBool(const AParcel* parcel, bool* value) __INTRODUCED_IN(29); + +/** + * Reads into char16_t value from the next location in a non-null parcel. + * + * \param parcel the parcel to read from. + * \param value the value to read from the parcel. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readChar(const AParcel* parcel, char16_t* value) __INTRODUCED_IN(29); + +/** + * Reads into int8_t value from the next location in a non-null parcel. + * + * \param parcel the parcel to read from. + * \param value the value to read from the parcel. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readByte(const AParcel* parcel, int8_t* value) __INTRODUCED_IN(29); + +/** + * Writes an array of int32_t to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). + * \param length the length of arrayData or -1 if this represents a null array. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* arrayData, int32_t length) + __INTRODUCED_IN(29); + +/** + * Writes an array of uint32_t to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). + * \param length the length of arrayData or -1 if this represents a null array. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* arrayData, int32_t length) + __INTRODUCED_IN(29); + +/** + * Writes an array of int64_t to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). + * \param length the length of arrayData or -1 if this represents a null array. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* arrayData, int32_t length) + __INTRODUCED_IN(29); + +/** + * Writes an array of uint64_t to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). + * \param length the length of arrayData or -1 if this represents a null array. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* arrayData, int32_t length) + __INTRODUCED_IN(29); + +/** + * Writes an array of float to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). + * \param length the length of arrayData or -1 if this represents a null array. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* arrayData, int32_t length) + __INTRODUCED_IN(29); + +/** + * Writes an array of double to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). + * \param length the length of arrayData or -1 if this represents a null array. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* arrayData, int32_t length) + __INTRODUCED_IN(29); + +/** + * Writes an array of bool to the next location in a non-null parcel. + * + * getter(arrayData, i) will be called for each i in [0, length) in order to get the underlying + * values to write to the parcel. + * + * \param parcel the parcel to write to. + * \param arrayData some external representation of an array. + * \param length the length of arrayData (or -1 if this represents a null array). + * \param getter the callback to retrieve data at specific locations in the array. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData, int32_t length, + AParcel_boolArrayGetter getter) __INTRODUCED_IN(29); + +/** + * Writes an array of char16_t to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). + * \param length the length of arrayData or -1 if this represents a null array. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* arrayData, int32_t length) + __INTRODUCED_IN(29); + +/** + * Writes an array of int8_t to the next location in a non-null parcel. + * + * \param parcel the parcel to write to. + * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). + * \param length the length of arrayData or -1 if this represents a null array. + * + * \return STATUS_OK on successful write. + */ +binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, int32_t length) + __INTRODUCED_IN(29); + +/** + * Reads an array of int32_t from the next location in a non-null parcel. + * + * First, allocator will be called with the length of the array. If the allocation succeeds and the + * length is greater than zero, the buffer returned by the allocator will be filled with the + * corresponding data + * + * \param parcel the parcel to read from. + * \param arrayData some external representation of an array. + * \param allocator the callback that will be called to allocate the array. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readInt32Array(const AParcel* parcel, void* arrayData, + AParcel_int32ArrayAllocator allocator) __INTRODUCED_IN(29); + +/** + * Reads an array of uint32_t from the next location in a non-null parcel. + * + * First, allocator will be called with the length of the array. If the allocation succeeds and the + * length is greater than zero, the buffer returned by the allocator will be filled with the + * corresponding data + * + * \param parcel the parcel to read from. + * \param arrayData some external representation of an array. + * \param allocator the callback that will be called to allocate the array. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readUint32Array(const AParcel* parcel, void* arrayData, + AParcel_uint32ArrayAllocator allocator) __INTRODUCED_IN(29); + +/** + * Reads an array of int64_t from the next location in a non-null parcel. + * + * First, allocator will be called with the length of the array. If the allocation succeeds and the + * length is greater than zero, the buffer returned by the allocator will be filled with the + * corresponding data + * + * \param parcel the parcel to read from. + * \param arrayData some external representation of an array. + * \param allocator the callback that will be called to allocate the array. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readInt64Array(const AParcel* parcel, void* arrayData, + AParcel_int64ArrayAllocator allocator) __INTRODUCED_IN(29); + +/** + * Reads an array of uint64_t from the next location in a non-null parcel. + * + * First, allocator will be called with the length of the array. If the allocation succeeds and the + * length is greater than zero, the buffer returned by the allocator will be filled with the + * corresponding data + * + * \param parcel the parcel to read from. + * \param arrayData some external representation of an array. + * \param allocator the callback that will be called to allocate the array. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readUint64Array(const AParcel* parcel, void* arrayData, + AParcel_uint64ArrayAllocator allocator) __INTRODUCED_IN(29); + +/** + * Reads an array of float from the next location in a non-null parcel. + * + * First, allocator will be called with the length of the array. If the allocation succeeds and the + * length is greater than zero, the buffer returned by the allocator will be filled with the + * corresponding data + * + * \param parcel the parcel to read from. + * \param arrayData some external representation of an array. + * \param allocator the callback that will be called to allocate the array. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readFloatArray(const AParcel* parcel, void* arrayData, + AParcel_floatArrayAllocator allocator) __INTRODUCED_IN(29); + +/** + * Reads an array of double from the next location in a non-null parcel. + * + * First, allocator will be called with the length of the array. If the allocation succeeds and the + * length is greater than zero, the buffer returned by the allocator will be filled with the + * corresponding data + * + * \param parcel the parcel to read from. + * \param arrayData some external representation of an array. + * \param allocator the callback that will be called to allocate the array. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void* arrayData, + AParcel_doubleArrayAllocator allocator) __INTRODUCED_IN(29); + +/** + * Reads an array of bool from the next location in a non-null parcel. + * + * First, allocator will be called with the length of the array. Then, for every i in [0, length), + * setter(arrayData, i, x) will be called where x is the value at the associated index. + * + * \param parcel the parcel to read from. + * \param arrayData some external representation of an array. + * \param allocator the callback that will be called to allocate the array. + * \param setter the callback that will be called to set a value at a specific location in the + * array. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readBoolArray(const AParcel* parcel, void* arrayData, + AParcel_boolArrayAllocator allocator, + AParcel_boolArraySetter setter) __INTRODUCED_IN(29); + +/** + * Reads an array of char16_t from the next location in a non-null parcel. + * + * First, allocator will be called with the length of the array. If the allocation succeeds and the + * length is greater than zero, the buffer returned by the allocator will be filled with the + * corresponding data + * + * \param parcel the parcel to read from. + * \param arrayData some external representation of an array. + * \param allocator the callback that will be called to allocate the array. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readCharArray(const AParcel* parcel, void* arrayData, + AParcel_charArrayAllocator allocator) __INTRODUCED_IN(29); + +/** + * Reads an array of int8_t from the next location in a non-null parcel. + * + * First, allocator will be called with the length of the array. If the allocation succeeds and the + * length is greater than zero, the buffer returned by the allocator will be filled with the + * corresponding data + * + * \param parcel the parcel to read from. + * \param arrayData some external representation of an array. + * \param allocator the callback that will be called to allocate the array. + * + * \return STATUS_OK on successful read. + */ +binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, + AParcel_byteArrayAllocator allocator) __INTRODUCED_IN(29); + +// @END-PRIMITIVE-READ-WRITE + +#endif //__ANDROID_API__ >= __ANDROID_API_Q__ +__END_DECLS + +/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h new file mode 100644 index 0000000..f3bc31b --- /dev/null +++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
@@ -0,0 +1,837 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup NdkBinder + * @{ + */ + +/** + * @file binder_parcel_utils.h + * @brief A collection of helper wrappers for AParcel. + */ + +#pragma once + +#include <android/binder_auto_utils.h> +#include <android/binder_parcel.h> + +#include <optional> +#include <string> +#include <vector> + +namespace ndk { + +/** + * This retrieves and allocates a vector to size 'length' and returns the underlying buffer. + */ +template <typename T> +static inline bool AParcel_stdVectorAllocator(void* vectorData, int32_t length, T** outBuffer) { + if (length < 0) return false; + + std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData); + if (static_cast<size_t>(length) > vec->max_size()) return false; + + vec->resize(length); + *outBuffer = vec->data(); + return true; +} + +/** + * This retrieves and allocates a vector to size 'length' and returns the underlying buffer. + */ +template <typename T> +static inline bool AParcel_nullableStdVectorAllocator(void* vectorData, int32_t length, + T** outBuffer) { + std::optional<std::vector<T>>* vec = static_cast<std::optional<std::vector<T>>*>(vectorData); + + if (length < 0) { + *vec = std::nullopt; + return true; + } + + *vec = std::optional<std::vector<T>>(std::vector<T>{}); + + if (static_cast<size_t>(length) > (*vec)->max_size()) return false; + (*vec)->resize(length); + + *outBuffer = (*vec)->data(); + return true; +} + +/** + * This allocates a vector to size 'length' and returns whether the allocation is successful. + * + * See also AParcel_stdVectorAllocator. Types used with this allocator have their sizes defined + * externally with respect to the NDK, and that size information is not passed into the NDK. + * Instead, it is used in cases where callbacks are used. Note that when this allocator is used, + * null arrays are not supported. + * + * See AParcel_readVector(const AParcel* parcel, std::vector<bool>) + * See AParcel_readVector(const AParcel* parcel, std::vector<std::string>) + */ +template <typename T> +static inline bool AParcel_stdVectorExternalAllocator(void* vectorData, int32_t length) { + if (length < 0) return false; + + std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData); + if (static_cast<size_t>(length) > vec->max_size()) return false; + + vec->resize(length); + return true; +} + +/** + * This allocates a vector to size 'length' and returns whether the allocation is successful. + * + * See also AParcel_stdVectorAllocator. Types used with this allocator have their sizes defined + * externally with respect to the NDK, and that size information is not passed into the NDK. + * Instead, it is used in cases where callbacks are used. Note, when this allocator is used, + * the vector itself can be nullable. + * + * See AParcel_readVector(const AParcel* parcel, + * std::optional<std::vector<std::optional<std::string>>>) + */ +template <typename T> +static inline bool AParcel_nullableStdVectorExternalAllocator(void* vectorData, int32_t length) { + std::optional<std::vector<T>>* vec = static_cast<std::optional<std::vector<T>>*>(vectorData); + + if (length < 0) { + *vec = std::nullopt; + return true; + } + + *vec = std::optional<std::vector<T>>(std::vector<T>{}); + + if (static_cast<size_t>(length) > (*vec)->max_size()) return false; + (*vec)->resize(length); + + return true; +} + +/** + * This retrieves the underlying value in a vector which may not be contiguous at index from a + * corresponding vectorData. + */ +template <typename T> +static inline T AParcel_stdVectorGetter(const void* vectorData, size_t index) { + const std::vector<T>* vec = static_cast<const std::vector<T>*>(vectorData); + return (*vec)[index]; +} + +/** + * This sets the underlying value in a corresponding vectorData which may not be contiguous at + * index. + */ +template <typename T> +static inline void AParcel_stdVectorSetter(void* vectorData, size_t index, T value) { + std::vector<T>* vec = static_cast<std::vector<T>*>(vectorData); + (*vec)[index] = value; +} + +/** + * This sets the underlying value in a corresponding vectorData which may not be contiguous at + * index. + */ +template <typename T> +static inline void AParcel_nullableStdVectorSetter(void* vectorData, size_t index, T value) { + std::optional<std::vector<T>>* vec = static_cast<std::optional<std::vector<T>>*>(vectorData); + vec->value()[index] = value; +} + +/** + * Convenience method to write a nullable strong binder. + */ +static inline binder_status_t AParcel_writeNullableStrongBinder(AParcel* parcel, + const SpAIBinder& binder) { + return AParcel_writeStrongBinder(parcel, binder.get()); +} + +/** + * Convenience method to read a nullable strong binder. + */ +static inline binder_status_t AParcel_readNullableStrongBinder(const AParcel* parcel, + SpAIBinder* binder) { + AIBinder* readBinder; + binder_status_t status = AParcel_readStrongBinder(parcel, &readBinder); + if (status == STATUS_OK) { + binder->set(readBinder); + } + return status; +} + +/** + * Convenience method to write a strong binder but return an error if it is null. + */ +static inline binder_status_t AParcel_writeRequiredStrongBinder(AParcel* parcel, + const SpAIBinder& binder) { + if (binder.get() == nullptr) { + return STATUS_UNEXPECTED_NULL; + } + return AParcel_writeStrongBinder(parcel, binder.get()); +} + +/** + * Convenience method to read a strong binder but return an error if it is null. + */ +static inline binder_status_t AParcel_readRequiredStrongBinder(const AParcel* parcel, + SpAIBinder* binder) { + AIBinder* readBinder; + binder_status_t ret = AParcel_readStrongBinder(parcel, &readBinder); + if (ret == STATUS_OK) { + if (readBinder == nullptr) { + return STATUS_UNEXPECTED_NULL; + } + + binder->set(readBinder); + } + return ret; +} + +/** + * Convenience method to write a ParcelFileDescriptor where -1 represents a null value. + */ +static inline binder_status_t AParcel_writeNullableParcelFileDescriptor( + AParcel* parcel, const ScopedFileDescriptor& fd) { + return AParcel_writeParcelFileDescriptor(parcel, fd.get()); +} + +/** + * Convenience method to read a ParcelFileDescriptor where -1 represents a null value. + */ +static inline binder_status_t AParcel_readNullableParcelFileDescriptor(const AParcel* parcel, + ScopedFileDescriptor* fd) { + int readFd; + binder_status_t status = AParcel_readParcelFileDescriptor(parcel, &readFd); + if (status == STATUS_OK) { + fd->set(readFd); + } + return status; +} + +/** + * Convenience method to write a valid ParcelFileDescriptor. + */ +static inline binder_status_t AParcel_writeRequiredParcelFileDescriptor( + AParcel* parcel, const ScopedFileDescriptor& fd) { + if (fd.get() < 0) { + return STATUS_UNEXPECTED_NULL; + } + return AParcel_writeParcelFileDescriptor(parcel, fd.get()); +} + +/** + * Convenience method to read a valid ParcelFileDescriptor. + */ +static inline binder_status_t AParcel_readRequiredParcelFileDescriptor(const AParcel* parcel, + ScopedFileDescriptor* fd) { + int readFd; + binder_status_t status = AParcel_readParcelFileDescriptor(parcel, &readFd); + if (status == STATUS_OK) { + if (readFd < 0) { + return STATUS_UNEXPECTED_NULL; + } + fd->set(readFd); + } + return status; +} + +/** + * Allocates a std::string to length and returns the underlying buffer. For use with + * AParcel_readString. See use below in AParcel_readString(const AParcel*, std::string*). + */ +static inline bool AParcel_stdStringAllocator(void* stringData, int32_t length, char** buffer) { + if (length <= 0) return false; + + std::string* str = static_cast<std::string*>(stringData); + str->resize(length - 1); + *buffer = &(*str)[0]; + return true; +} + +/** + * Allocates a string in a std::optional<std::string> to size 'length' (or to std::nullopt when + * length is -1) and returns the underlying buffer. For use with AParcel_readString. See use below + * in AParcel_readString(const AParcel*, std::optional<std::string>*). + */ +static inline bool AParcel_nullableStdStringAllocator(void* stringData, int32_t length, + char** buffer) { + if (length == 0) return false; + + std::optional<std::string>* str = static_cast<std::optional<std::string>*>(stringData); + + if (length < 0) { + *str = std::nullopt; + return true; + } + + *str = std::optional<std::string>(std::string{}); + (*str)->resize(length - 1); + *buffer = &(**str)[0]; + return true; +} + +/** + * Allocates a std::string inside of a std::vector<std::string> at index 'index' to size 'length'. + */ +static inline bool AParcel_stdVectorStringElementAllocator(void* vectorData, size_t index, + int32_t length, char** buffer) { + std::vector<std::string>* vec = static_cast<std::vector<std::string>*>(vectorData); + std::string& element = vec->at(index); + return AParcel_stdStringAllocator(static_cast<void*>(&element), length, buffer); +} + +/** + * This gets the length and buffer of a std::string inside of a std::vector<std::string> at index + * index. + */ +static inline const char* AParcel_stdVectorStringElementGetter(const void* vectorData, size_t index, + int32_t* outLength) { + const std::vector<std::string>* vec = static_cast<const std::vector<std::string>*>(vectorData); + const std::string& element = vec->at(index); + + *outLength = element.size(); + return element.c_str(); +} + +/** + * Allocates a string in a std::optional<std::string> inside of a + * std::optional<std::vector<std::optional<std::string>>> at index 'index' to size 'length' (or to + * std::nullopt when length is -1). + */ +static inline bool AParcel_nullableStdVectorStringElementAllocator(void* vectorData, size_t index, + int32_t length, char** buffer) { + std::optional<std::vector<std::optional<std::string>>>* vec = + static_cast<std::optional<std::vector<std::optional<std::string>>>*>(vectorData); + std::optional<std::string>& element = vec->value().at(index); + return AParcel_nullableStdStringAllocator(static_cast<void*>(&element), length, buffer); +} + +/** + * This gets the length and buffer of a std::optional<std::string> inside of a + * std::vector<std::string> at index index. If the string is null, then it returns null and a length + * of -1. + */ +static inline const char* AParcel_nullableStdVectorStringElementGetter(const void* vectorData, + size_t index, + int32_t* outLength) { + const std::optional<std::vector<std::optional<std::string>>>* vec = + static_cast<const std::optional<std::vector<std::optional<std::string>>>*>(vectorData); + const std::optional<std::string>& element = vec->value().at(index); + + if (!element) { + *outLength = -1; + return nullptr; + } + + *outLength = element->size(); + return element->c_str(); +} + +/** + * 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()); +} + +/** + * Convenience API for reading a std::string. + */ +static inline binder_status_t AParcel_readString(const AParcel* parcel, std::string* str) { + void* stringData = static_cast<void*>(str); + return AParcel_readString(parcel, stringData, AParcel_stdStringAllocator); +} + +/** + * Convenience API for writing a std::optional<std::string>. + */ +static inline binder_status_t AParcel_writeString(AParcel* parcel, + const std::optional<std::string>& str) { + if (!str) { + return AParcel_writeString(parcel, nullptr, -1); + } + + return AParcel_writeString(parcel, str->c_str(), str->size()); +} + +/** + * Convenience API for reading a std::optional<std::string>. + */ +static inline binder_status_t AParcel_readString(const AParcel* parcel, + std::optional<std::string>* str) { + void* stringData = static_cast<void*>(str); + return AParcel_readString(parcel, stringData, AParcel_nullableStdStringAllocator); +} + +/** + * Convenience API for writing a std::vector<std::string> + */ +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(), + AParcel_stdVectorStringElementGetter); +} + +/** + * Convenience API for reading a std::vector<std::string> + */ +static inline binder_status_t AParcel_readVector(const AParcel* parcel, + std::vector<std::string>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readStringArray(parcel, vectorData, + AParcel_stdVectorExternalAllocator<std::string>, + AParcel_stdVectorStringElementAllocator); +} + +/** + * Convenience API for writing a std::optional<std::vector<std::optional<std::string>>> + */ +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), + AParcel_nullableStdVectorStringElementGetter); +} + +/** + * Convenience API for reading a std::optional<std::vector<std::optional<std::string>>> + */ +static inline binder_status_t AParcel_readVector( + const AParcel* parcel, std::optional<std::vector<std::optional<std::string>>>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readStringArray( + parcel, vectorData, + AParcel_nullableStdVectorExternalAllocator<std::optional<std::string>>, + AParcel_nullableStdVectorStringElementAllocator); +} + +/** + * Writes a parcelable object of type P inside a std::vector<P> at index 'index' to 'parcel'. + */ +template <typename P> +binder_status_t AParcel_writeStdVectorParcelableElement(AParcel* parcel, const void* vectorData, + size_t index) { + const std::vector<P>* vector = static_cast<const std::vector<P>*>(vectorData); + return vector->at(index).writeToParcel(parcel); +} + +/** + * Reads a parcelable object of type P inside a std::vector<P> at index 'index' from 'parcel'. + */ +template <typename P> +binder_status_t AParcel_readStdVectorParcelableElement(const AParcel* parcel, void* vectorData, + size_t index) { + std::vector<P>* vector = static_cast<std::vector<P>*>(vectorData); + return vector->at(index).readFromParcel(parcel); +} + +/** + * Convenience API for writing a std::vector<P> + */ +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(), + AParcel_writeStdVectorParcelableElement<P>); +} + +/** + * Convenience API for reading a std::vector<P> + */ +template <typename P> +static inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<P>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readParcelableArray(parcel, vectorData, AParcel_stdVectorExternalAllocator<P>, + AParcel_readStdVectorParcelableElement<P>); +} + +// @START +/** + * 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()); +} + +/** + * Writes an optional vector of int32_t to the next location in a non-null parcel. + */ +inline binder_status_t AParcel_writeVector(AParcel* parcel, + const std::optional<std::vector<int32_t>>& vec) { + if (!vec) return AParcel_writeInt32Array(parcel, nullptr, -1); + return AParcel_writeVector(parcel, *vec); +} + +/** + * Reads a vector of int32_t from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<int32_t>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readInt32Array(parcel, vectorData, AParcel_stdVectorAllocator<int32_t>); +} + +/** + * Reads an optional vector of int32_t from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, + std::optional<std::vector<int32_t>>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readInt32Array(parcel, vectorData, AParcel_nullableStdVectorAllocator<int32_t>); +} + +/** + * 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()); +} + +/** + * Writes an optional vector of uint32_t to the next location in a non-null parcel. + */ +inline binder_status_t AParcel_writeVector(AParcel* parcel, + const std::optional<std::vector<uint32_t>>& vec) { + if (!vec) return AParcel_writeUint32Array(parcel, nullptr, -1); + return AParcel_writeVector(parcel, *vec); +} + +/** + * Reads a vector of uint32_t from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<uint32_t>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readUint32Array(parcel, vectorData, AParcel_stdVectorAllocator<uint32_t>); +} + +/** + * Reads an optional vector of uint32_t from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, + std::optional<std::vector<uint32_t>>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readUint32Array(parcel, vectorData, + AParcel_nullableStdVectorAllocator<uint32_t>); +} + +/** + * 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()); +} + +/** + * Writes an optional vector of int64_t to the next location in a non-null parcel. + */ +inline binder_status_t AParcel_writeVector(AParcel* parcel, + const std::optional<std::vector<int64_t>>& vec) { + if (!vec) return AParcel_writeInt64Array(parcel, nullptr, -1); + return AParcel_writeVector(parcel, *vec); +} + +/** + * Reads a vector of int64_t from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<int64_t>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readInt64Array(parcel, vectorData, AParcel_stdVectorAllocator<int64_t>); +} + +/** + * Reads an optional vector of int64_t from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, + std::optional<std::vector<int64_t>>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readInt64Array(parcel, vectorData, AParcel_nullableStdVectorAllocator<int64_t>); +} + +/** + * 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()); +} + +/** + * Writes an optional vector of uint64_t to the next location in a non-null parcel. + */ +inline binder_status_t AParcel_writeVector(AParcel* parcel, + const std::optional<std::vector<uint64_t>>& vec) { + if (!vec) return AParcel_writeUint64Array(parcel, nullptr, -1); + return AParcel_writeVector(parcel, *vec); +} + +/** + * Reads a vector of uint64_t from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<uint64_t>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readUint64Array(parcel, vectorData, AParcel_stdVectorAllocator<uint64_t>); +} + +/** + * Reads an optional vector of uint64_t from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, + std::optional<std::vector<uint64_t>>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readUint64Array(parcel, vectorData, + AParcel_nullableStdVectorAllocator<uint64_t>); +} + +/** + * 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()); +} + +/** + * Writes an optional vector of float to the next location in a non-null parcel. + */ +inline binder_status_t AParcel_writeVector(AParcel* parcel, + const std::optional<std::vector<float>>& vec) { + if (!vec) return AParcel_writeFloatArray(parcel, nullptr, -1); + return AParcel_writeVector(parcel, *vec); +} + +/** + * Reads a vector of float from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<float>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readFloatArray(parcel, vectorData, AParcel_stdVectorAllocator<float>); +} + +/** + * Reads an optional vector of float from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, + std::optional<std::vector<float>>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readFloatArray(parcel, vectorData, AParcel_nullableStdVectorAllocator<float>); +} + +/** + * 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()); +} + +/** + * Writes an optional vector of double to the next location in a non-null parcel. + */ +inline binder_status_t AParcel_writeVector(AParcel* parcel, + const std::optional<std::vector<double>>& vec) { + if (!vec) return AParcel_writeDoubleArray(parcel, nullptr, -1); + return AParcel_writeVector(parcel, *vec); +} + +/** + * Reads a vector of double from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<double>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readDoubleArray(parcel, vectorData, AParcel_stdVectorAllocator<double>); +} + +/** + * Reads an optional vector of double from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, + std::optional<std::vector<double>>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readDoubleArray(parcel, vectorData, AParcel_nullableStdVectorAllocator<double>); +} + +/** + * 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>); +} + +/** + * Writes an optional vector of bool to the next location in a non-null parcel. + */ +inline binder_status_t AParcel_writeVector(AParcel* parcel, + const std::optional<std::vector<bool>>& vec) { + if (!vec) return AParcel_writeBoolArray(parcel, nullptr, -1, AParcel_stdVectorGetter<bool>); + return AParcel_writeVector(parcel, *vec); +} + +/** + * Reads a vector of bool from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<bool>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readBoolArray(parcel, vectorData, AParcel_stdVectorExternalAllocator<bool>, + AParcel_stdVectorSetter<bool>); +} + +/** + * Reads an optional vector of bool from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, + std::optional<std::vector<bool>>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readBoolArray(parcel, vectorData, + AParcel_nullableStdVectorExternalAllocator<bool>, + AParcel_nullableStdVectorSetter<bool>); +} + +/** + * 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()); +} + +/** + * Writes an optional vector of char16_t to the next location in a non-null parcel. + */ +inline binder_status_t AParcel_writeVector(AParcel* parcel, + const std::optional<std::vector<char16_t>>& vec) { + if (!vec) return AParcel_writeCharArray(parcel, nullptr, -1); + return AParcel_writeVector(parcel, *vec); +} + +/** + * Reads a vector of char16_t from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<char16_t>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readCharArray(parcel, vectorData, AParcel_stdVectorAllocator<char16_t>); +} + +/** + * Reads an optional vector of char16_t from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, + std::optional<std::vector<char16_t>>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readCharArray(parcel, vectorData, AParcel_nullableStdVectorAllocator<char16_t>); +} + +/** + * Writes a vector of int8_t to the next location in a non-null parcel. + */ +inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int8_t>& vec) { + return AParcel_writeByteArray(parcel, vec.data(), vec.size()); +} + +/** + * Writes an optional vector of int8_t to the next location in a non-null parcel. + */ +inline binder_status_t AParcel_writeVector(AParcel* parcel, + const std::optional<std::vector<int8_t>>& vec) { + if (!vec) return AParcel_writeByteArray(parcel, nullptr, -1); + return AParcel_writeVector(parcel, *vec); +} + +/** + * Reads a vector of int8_t from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<int8_t>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readByteArray(parcel, vectorData, AParcel_stdVectorAllocator<int8_t>); +} + +/** + * Reads an optional vector of int8_t from the next location in a non-null parcel. + */ +inline binder_status_t AParcel_readVector(const AParcel* parcel, + std::optional<std::vector<int8_t>>* vec) { + void* vectorData = static_cast<void*>(vec); + return AParcel_readByteArray(parcel, vectorData, AParcel_nullableStdVectorAllocator<int8_t>); +} + +// @END + +/** + * Convenience API for writing the size of a vector. + */ +template <typename T> +static inline binder_status_t AParcel_writeVectorSize(AParcel* parcel, const std::vector<T>& vec) { + if (vec.size() > INT32_MAX) { + return STATUS_BAD_VALUE; + } + + return AParcel_writeInt32(parcel, static_cast<int32_t>(vec.size())); +} + +/** + * Convenience API for writing the size of a vector. + */ +template <typename T> +static inline binder_status_t AParcel_writeVectorSize(AParcel* parcel, + const std::optional<std::vector<T>>& vec) { + if (!vec) { + return AParcel_writeInt32(parcel, -1); + } + + if (vec->size() > INT32_MAX) { + return STATUS_BAD_VALUE; + } + + return AParcel_writeInt32(parcel, static_cast<int32_t>(vec->size())); +} + +/** + * Convenience API for resizing a vector. + */ +template <typename T> +static inline binder_status_t AParcel_resizeVector(const AParcel* parcel, std::vector<T>* vec) { + int32_t size; + binder_status_t err = AParcel_readInt32(parcel, &size); + + if (err != STATUS_OK) return err; + if (size < 0) return STATUS_UNEXPECTED_NULL; + + vec->resize(static_cast<size_t>(size)); + return STATUS_OK; +} + +/** + * Convenience API for resizing a vector. + */ +template <typename T> +static inline binder_status_t AParcel_resizeVector(const AParcel* parcel, + std::optional<std::vector<T>>* vec) { + int32_t size; + binder_status_t err = AParcel_readInt32(parcel, &size); + + if (err != STATUS_OK) return err; + if (size < -1) return STATUS_UNEXPECTED_NULL; + + if (size == -1) { + *vec = std::nullopt; + return STATUS_OK; + } + + *vec = std::optional<std::vector<T>>(std::vector<T>{}); + (*vec)->resize(static_cast<size_t>(size)); + return STATUS_OK; +} + +} // namespace ndk + +/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h new file mode 100644 index 0000000..2671b9b --- /dev/null +++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -0,0 +1,237 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup NdkBinder + * @{ + */ + +/** + * @file binder_status.h + */ + +#pragma once + +#include <errno.h> +#include <stdint.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS +#if __ANDROID_API__ >= __ANDROID_API_Q__ + +enum { + STATUS_OK = 0, + + STATUS_UNKNOWN_ERROR = (-2147483647 - 1), // INT32_MIN value + STATUS_NO_MEMORY = -ENOMEM, + STATUS_INVALID_OPERATION = -ENOSYS, + STATUS_BAD_VALUE = -EINVAL, + STATUS_BAD_TYPE = (STATUS_UNKNOWN_ERROR + 1), + STATUS_NAME_NOT_FOUND = -ENOENT, + STATUS_PERMISSION_DENIED = -EPERM, + STATUS_NO_INIT = -ENODEV, + STATUS_ALREADY_EXISTS = -EEXIST, + STATUS_DEAD_OBJECT = -EPIPE, + STATUS_FAILED_TRANSACTION = (STATUS_UNKNOWN_ERROR + 2), + STATUS_BAD_INDEX = -EOVERFLOW, + STATUS_NOT_ENOUGH_DATA = -ENODATA, + STATUS_WOULD_BLOCK = -EWOULDBLOCK, + STATUS_TIMED_OUT = -ETIMEDOUT, + STATUS_UNKNOWN_TRANSACTION = -EBADMSG, + STATUS_FDS_NOT_ALLOWED = (STATUS_UNKNOWN_ERROR + 7), + STATUS_UNEXPECTED_NULL = (STATUS_UNKNOWN_ERROR + 8), +}; + +/** + * One of the STATUS_* values. + * + * All unrecognized values are coerced into STATUS_UNKNOWN_ERROR. + */ +typedef int32_t binder_status_t; + +enum { + EX_NONE = 0, + EX_SECURITY = -1, + EX_BAD_PARCELABLE = -2, + EX_ILLEGAL_ARGUMENT = -3, + EX_NULL_POINTER = -4, + EX_ILLEGAL_STATE = -5, + EX_NETWORK_MAIN_THREAD = -6, + EX_UNSUPPORTED_OPERATION = -7, + EX_SERVICE_SPECIFIC = -8, + EX_PARCELABLE = -9, + + /** + * This is special, and indicates to native binder proxies that the + * transaction has failed at a low level. + */ + EX_TRANSACTION_FAILED = -129, +}; + +/** + * One of the EXCEPTION_* types. + * + * All unrecognized values are coerced into EXCEPTION_TRANSACTION_FAILED. + * + * These exceptions values are used by the SDK for parcelables. Also see Parcel.java. + */ +typedef int32_t binder_exception_t; + +/** + * This is a helper class that encapsulates a standard way to keep track of and chain binder errors + * along with service specific errors. + * + * It is not required to be used in order to parcel/receive transactions, but it is required in + * order to be compatible with standard AIDL transactions since it is written as the header to the + * out parcel for transactions which get executed (don't fail during unparceling of input arguments + * or sooner). + */ +struct AStatus; +typedef struct AStatus AStatus; + +/** + * New status which is considered a success. + * + * \return a newly constructed status object that the caller owns. + */ +__attribute__((warn_unused_result)) AStatus* AStatus_newOk() __INTRODUCED_IN(29); + +/** + * New status with exception code. + * + * \param exception the code that this status should represent. If this is EX_NONE, then this + * constructs an non-error status object. + * + * \return a newly constructed status object that the caller owns. + */ +__attribute__((warn_unused_result)) AStatus* AStatus_fromExceptionCode(binder_exception_t exception) + __INTRODUCED_IN(29); + +/** + * New status with exception code and message. + * + * \param exception the code that this status should represent. If this is EX_NONE, then this + * constructs an non-error status object. + * \param message the error message to associate with this status object. + * + * \return a newly constructed status object that the caller owns. + */ +__attribute__((warn_unused_result)) AStatus* AStatus_fromExceptionCodeWithMessage( + binder_exception_t exception, const char* message) __INTRODUCED_IN(29); + +/** + * New status with a service speciic error. + * + * This is considered to be EX_TRANSACTION_FAILED with extra information. + * + * \param serviceSpecific an implementation defined error code. + * + * \return a newly constructed status object that the caller owns. + */ +__attribute__((warn_unused_result)) AStatus* AStatus_fromServiceSpecificError( + int32_t serviceSpecific) __INTRODUCED_IN(29); + +/** + * New status with a service specific error and message. + * + * This is considered to be EX_TRANSACTION_FAILED with extra information. + * + * \param serviceSpecific an implementation defined error code. + * \param message the error message to associate with this status object. + * + * \return a newly constructed status object that the caller owns. + */ +__attribute__((warn_unused_result)) AStatus* AStatus_fromServiceSpecificErrorWithMessage( + int32_t serviceSpecific, const char* message) __INTRODUCED_IN(29); + +/** + * New status with binder_status_t. This is typically for low level failures when a binder_status_t + * is returned by an API on AIBinder or AParcel, and that is to be returned from a method returning + * an AStatus instance. + * + * \param a low-level error to associate with this status object. + * + * \return a newly constructed status object that the caller owns. + */ +__attribute__((warn_unused_result)) AStatus* AStatus_fromStatus(binder_status_t status) + __INTRODUCED_IN(29); + +/** + * Whether this object represents a successful transaction. If this function returns true, then + * AStatus_getExceptionCode will return EX_NONE. + * + * \param status the status being queried. + * + * \return whether the status represents a successful transaction. For more details, see below. + */ +bool AStatus_isOk(const AStatus* status) __INTRODUCED_IN(29); + +/** + * The exception that this status object represents. + * + * \param status the status being queried. + * + * \return the exception code that this object represents. + */ +binder_exception_t AStatus_getExceptionCode(const AStatus* status) __INTRODUCED_IN(29); + +/** + * The service specific error if this object represents one. This function will only ever return a + * non-zero result if AStatus_getExceptionCode returns EX_SERVICE_SPECIFIC. If this function returns + * 0, the status object may still represent a different exception or status. To find out if this + * transaction as a whole is okay, use AStatus_isOk instead. + * + * \param status the status being queried. + * + * \return the service-specific error code if the exception code is EX_SERVICE_SPECIFIC or 0. + */ +int32_t AStatus_getServiceSpecificError(const AStatus* status) __INTRODUCED_IN(29); + +/** + * The status if this object represents one. This function will only ever return a non-zero result + * if AStatus_getExceptionCode returns EX_TRANSACTION_FAILED. If this function return 0, the status + * object may represent a different exception or a service specific error. To find out if this + * transaction as a whole is okay, use AStatus_isOk instead. + * + * \param status the status being queried. + * + * \return the status code if the exception code is EX_TRANSACTION_FAILED or 0. + */ +binder_status_t AStatus_getStatus(const AStatus* status) __INTRODUCED_IN(29); + +/** + * If there is a message associated with this status, this will return that message. If there is no + * message, this will return an empty string. + * + * The returned string has the lifetime of the status object passed into this function. + * + * \param status the status being queried. + * + * \return the message associated with this error. + */ +const char* AStatus_getMessage(const AStatus* status) __INTRODUCED_IN(29); + +/** + * Deletes memory associated with the status instance. + * + * \param status the status to delete, returned from AStatus_newOk or one of the AStatus_from* APIs. + */ +void AStatus_delete(AStatus* status) __INTRODUCED_IN(29); + +#endif //__ANDROID_API__ >= __ANDROID_API_Q__ +__END_DECLS + +/** @} */
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt new file mode 100644 index 0000000..7e65817 --- /dev/null +++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -0,0 +1,100 @@ +LIBBINDER_NDK { # introduced=29 + global: + AIBinder_associateClass; + AIBinder_Class_define; + AIBinder_Class_setOnDump; + AIBinder_DeathRecipient_delete; + AIBinder_DeathRecipient_new; + AIBinder_debugGetRefCount; + AIBinder_decStrong; + AIBinder_dump; + AIBinder_fromJavaBinder; + AIBinder_getCallingPid; + AIBinder_getCallingUid; + AIBinder_getClass; + AIBinder_getUserData; + AIBinder_incStrong; + AIBinder_isAlive; + AIBinder_isRemote; + AIBinder_linkToDeath; + AIBinder_new; + AIBinder_ping; + AIBinder_prepareTransaction; + AIBinder_toJavaBinder; + AIBinder_transact; + AIBinder_unlinkToDeath; + AIBinder_Weak_delete; + AIBinder_Weak_new; + AIBinder_Weak_promote; + AParcel_delete; + AParcel_getDataPosition; + AParcel_readBool; + AParcel_readBoolArray; + AParcel_readByte; + AParcel_readByteArray; + AParcel_readChar; + AParcel_readCharArray; + AParcel_readDouble; + AParcel_readDoubleArray; + AParcel_readFloat; + AParcel_readFloatArray; + AParcel_readInt32; + AParcel_readInt32Array; + AParcel_readInt64; + AParcel_readInt64Array; + AParcel_readParcelableArray; + AParcel_readParcelFileDescriptor; + AParcel_readStatusHeader; + AParcel_readString; + AParcel_readStringArray; + AParcel_readStrongBinder; + AParcel_readUint32; + AParcel_readUint32Array; + AParcel_readUint64; + AParcel_readUint64Array; + AParcel_setDataPosition; + AParcel_writeBool; + AParcel_writeBoolArray; + AParcel_writeByte; + AParcel_writeByteArray; + AParcel_writeChar; + AParcel_writeCharArray; + AParcel_writeDouble; + AParcel_writeDoubleArray; + AParcel_writeFloat; + AParcel_writeFloatArray; + AParcel_writeInt32; + AParcel_writeInt32Array; + AParcel_writeInt64; + AParcel_writeInt64Array; + AParcel_writeParcelableArray; + AParcel_writeParcelFileDescriptor; + AParcel_writeStatusHeader; + AParcel_writeString; + AParcel_writeStringArray; + AParcel_writeStrongBinder; + AParcel_writeUint32; + AParcel_writeUint32Array; + AParcel_writeUint64; + AParcel_writeUint64Array; + AStatus_delete; + AStatus_fromExceptionCode; + AStatus_fromExceptionCodeWithMessage; + AStatus_fromServiceSpecificError; + AStatus_fromServiceSpecificErrorWithMessage; + AStatus_fromStatus; + AStatus_getExceptionCode; + AStatus_getMessage; + AStatus_getServiceSpecificError; + AStatus_getStatus; + AStatus_isOk; + AStatus_newOk; + ABinderProcess_joinThreadPool; # apex + ABinderProcess_setThreadPoolMaxThreadCount; # apex + ABinderProcess_startThreadPool; # apex + AServiceManager_addService; # apex + AServiceManager_checkService; # apex + AServiceManager_getService; # apex + local: + *; +};
diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp new file mode 100644 index 0000000..ae2276e --- /dev/null +++ b/libs/binder/ndk/parcel.cpp
@@ -0,0 +1,653 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_parcel.h> +#include "parcel_internal.h" + +#include "ibinder_internal.h" +#include "status_internal.h" + +#include <limits> + +#include <android-base/logging.h> +#include <android-base/unique_fd.h> +#include <binder/Parcel.h> +#include <binder/ParcelFileDescriptor.h> +#include <utils/Unicode.h> + +using ::android::IBinder; +using ::android::Parcel; +using ::android::sp; +using ::android::status_t; +using ::android::base::unique_fd; +using ::android::os::ParcelFileDescriptor; + +template <typename T> +using ContiguousArrayAllocator = bool (*)(void* arrayData, int32_t length, T** outBuffer); + +template <typename T> +using ArrayAllocator = bool (*)(void* arrayData, int32_t length); +template <typename T> +using ArrayGetter = T (*)(const void* arrayData, size_t index); +template <typename T> +using ArraySetter = void (*)(void* arrayData, size_t index, T value); + +binder_status_t WriteAndValidateArraySize(AParcel* parcel, bool isNullArray, int32_t length) { + // only -1 can be used to represent a null array + if (length < -1) return STATUS_BAD_VALUE; + + if (!isNullArray && length < 0) { + LOG(ERROR) << __func__ << ": null array must be used with length == -1."; + return STATUS_BAD_VALUE; + } + if (isNullArray && length > 0) { + LOG(ERROR) << __func__ << ": null buffer cannot be for size " << length << " array."; + return STATUS_BAD_VALUE; + } + + Parcel* rawParcel = parcel->get(); + + status_t status = rawParcel->writeInt32(static_cast<int32_t>(length)); + if (status != STATUS_OK) return PruneStatusT(status); + + return STATUS_OK; +} + +template <typename T> +binder_status_t WriteArray(AParcel* parcel, const T* array, int32_t length) { + binder_status_t status = WriteAndValidateArraySize(parcel, array == nullptr, length); + if (status != STATUS_OK) return status; + if (length <= 0) return STATUS_OK; + + int32_t size = 0; + if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY; + + void* const data = parcel->get()->writeInplace(size); + if (data == nullptr) return STATUS_NO_MEMORY; + + memcpy(data, array, size); + + return STATUS_OK; +} + +// Each element in a char16_t array is converted to an int32_t (not packed). +template <> +binder_status_t WriteArray<char16_t>(AParcel* parcel, const char16_t* array, int32_t length) { + binder_status_t status = WriteAndValidateArraySize(parcel, array == nullptr, length); + if (status != STATUS_OK) return status; + if (length <= 0) return STATUS_OK; + + int32_t size = 0; + if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY; + + Parcel* rawParcel = parcel->get(); + + for (int32_t i = 0; i < length; i++) { + status = rawParcel->writeChar(array[i]); + + if (status != STATUS_OK) return PruneStatusT(status); + } + + return STATUS_OK; +} + +template <typename T> +binder_status_t ReadArray(const AParcel* parcel, void* arrayData, + ContiguousArrayAllocator<T> allocator) { + const Parcel* rawParcel = parcel->get(); + + int32_t length; + status_t status = rawParcel->readInt32(&length); + + if (status != STATUS_OK) return PruneStatusT(status); + if (length < -1) return STATUS_BAD_VALUE; + + T* array; + if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY; + + if (length <= 0) return STATUS_OK; + if (array == nullptr) return STATUS_NO_MEMORY; + + int32_t size = 0; + if (__builtin_smul_overflow(sizeof(T), length, &size)) return STATUS_NO_MEMORY; + + const void* data = rawParcel->readInplace(size); + if (data == nullptr) return STATUS_NO_MEMORY; + + memcpy(array, data, size); + + return STATUS_OK; +} + +// Each element in a char16_t array is converted to an int32_t (not packed) +template <> +binder_status_t ReadArray<char16_t>(const AParcel* parcel, void* arrayData, + ContiguousArrayAllocator<char16_t> allocator) { + const Parcel* rawParcel = parcel->get(); + + int32_t length; + status_t status = rawParcel->readInt32(&length); + + if (status != STATUS_OK) return PruneStatusT(status); + if (length < -1) return STATUS_BAD_VALUE; + + char16_t* array; + if (!allocator(arrayData, length, &array)) return STATUS_NO_MEMORY; + + if (length <= 0) return STATUS_OK; + if (array == nullptr) return STATUS_NO_MEMORY; + + int32_t size = 0; + if (__builtin_smul_overflow(sizeof(char16_t), length, &size)) return STATUS_NO_MEMORY; + + for (int32_t i = 0; i < length; i++) { + status = rawParcel->readChar(array + i); + + if (status != STATUS_OK) return PruneStatusT(status); + } + + return STATUS_OK; +} + +template <typename T> +binder_status_t WriteArray(AParcel* parcel, const void* arrayData, int32_t length, + ArrayGetter<T> getter, status_t (Parcel::*write)(T)) { + // we have no clue if arrayData represents a null object or not, we can only infer from length + bool arrayIsNull = length < 0; + binder_status_t status = WriteAndValidateArraySize(parcel, arrayIsNull, length); + if (status != STATUS_OK) return status; + if (length <= 0) return STATUS_OK; + + Parcel* rawParcel = parcel->get(); + + for (int32_t i = 0; i < length; i++) { + status = (rawParcel->*write)(getter(arrayData, i)); + + if (status != STATUS_OK) return PruneStatusT(status); + } + + return STATUS_OK; +} + +template <typename T> +binder_status_t ReadArray(const AParcel* parcel, void* arrayData, ArrayAllocator<T> allocator, + ArraySetter<T> setter, status_t (Parcel::*read)(T*) const) { + const Parcel* rawParcel = parcel->get(); + + int32_t length; + status_t status = rawParcel->readInt32(&length); + + if (status != STATUS_OK) return PruneStatusT(status); + if (length < -1) return STATUS_BAD_VALUE; + + if (!allocator(arrayData, length)) return STATUS_NO_MEMORY; + + if (length <= 0) return STATUS_OK; + + for (int32_t i = 0; i < length; i++) { + T readTarget; + status = (rawParcel->*read)(&readTarget); + if (status != STATUS_OK) return PruneStatusT(status); + + setter(arrayData, i, readTarget); + } + + return STATUS_OK; +} + +void AParcel_delete(AParcel* parcel) { + delete parcel; +} + +binder_status_t AParcel_setDataPosition(const AParcel* parcel, int32_t position) { + if (position < 0) { + return STATUS_BAD_VALUE; + } + + parcel->get()->setDataPosition(position); + return STATUS_OK; +} + +int32_t AParcel_getDataPosition(const AParcel* parcel) { + return parcel->get()->dataPosition(); +} + +binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) { + sp<IBinder> writeBinder = binder != nullptr ? binder->getBinder() : nullptr; + return parcel->get()->writeStrongBinder(writeBinder); +} +binder_status_t AParcel_readStrongBinder(const AParcel* parcel, AIBinder** binder) { + sp<IBinder> readBinder = nullptr; + status_t status = parcel->get()->readNullableStrongBinder(&readBinder); + if (status != STATUS_OK) { + return PruneStatusT(status); + } + sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(readBinder); + AIBinder_incStrong(ret.get()); + *binder = ret.get(); + return PruneStatusT(status); +} + +binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd) { + std::unique_ptr<ParcelFileDescriptor> parcelFd; + + if (fd < 0) { + if (fd != -1) { + return STATUS_UNKNOWN_ERROR; + } + // parcelFd = nullptr + } else { // fd >= 0 + parcelFd = std::make_unique<ParcelFileDescriptor>(unique_fd(fd)); + } + + status_t status = parcel->get()->writeNullableParcelable(parcelFd); + + // ownership is retained by caller + if (parcelFd != nullptr) { + (void)parcelFd->release().release(); + } + + return PruneStatusT(status); +} + +binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd) { + std::unique_ptr<ParcelFileDescriptor> parcelFd; + + status_t status = parcel->get()->readParcelable(&parcelFd); + if (status != STATUS_OK) return PruneStatusT(status); + + if (parcelFd) { + *fd = parcelFd->release().release(); + } else { + *fd = -1; + } + + return STATUS_OK; +} + +binder_status_t AParcel_writeStatusHeader(AParcel* parcel, const AStatus* status) { + return PruneStatusT(status->get()->writeToParcel(parcel->get())); +} +binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status) { + ::android::binder::Status bstatus; + binder_status_t ret = PruneStatusT(bstatus.readFromParcel(*parcel->get())); + if (ret == STATUS_OK) { + *status = new AStatus(std::move(bstatus)); + } + return PruneStatusT(ret); +} + +binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t length) { + if (string == nullptr) { + if (length != -1) { + LOG(WARNING) << __func__ << ": null string must be used with length == -1."; + return STATUS_BAD_VALUE; + } + + status_t err = parcel->get()->writeInt32(-1); + return PruneStatusT(err); + } + + if (length < 0) { + LOG(WARNING) << __func__ << ": Negative string length: " << length; + return STATUS_BAD_VALUE; + } + + const uint8_t* str8 = (uint8_t*)string; + const ssize_t len16 = utf8_to_utf16_length(str8, length); + + if (len16 < 0 || len16 >= std::numeric_limits<int32_t>::max()) { + LOG(WARNING) << __func__ << ": Invalid string length: " << len16; + return STATUS_BAD_VALUE; + } + + status_t err = parcel->get()->writeInt32(len16); + if (err) { + return PruneStatusT(err); + } + + void* str16 = parcel->get()->writeInplace((len16 + 1) * sizeof(char16_t)); + if (str16 == nullptr) { + return STATUS_NO_MEMORY; + } + + utf8_to_utf16(str8, length, (char16_t*)str16, (size_t)len16 + 1); + + return STATUS_OK; +} + +binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, + AParcel_stringAllocator allocator) { + size_t len16; + const char16_t* str16 = parcel->get()->readString16Inplace(&len16); + + if (str16 == nullptr) { + if (allocator(stringData, -1, nullptr)) { + return STATUS_OK; + } + + return STATUS_UNEXPECTED_NULL; + } + + ssize_t len8; + + if (len16 == 0) { + len8 = 1; + } else { + len8 = utf16_to_utf8_length(str16, len16) + 1; + } + + if (len8 <= 0 || len8 > std::numeric_limits<int32_t>::max()) { + LOG(WARNING) << __func__ << ": Invalid string length: " << len8; + return STATUS_BAD_VALUE; + } + + char* str8; + bool success = allocator(stringData, len8, &str8); + + if (!success || str8 == nullptr) { + LOG(WARNING) << __func__ << ": AParcel_stringAllocator failed to allocate."; + return STATUS_NO_MEMORY; + } + + utf16_to_utf8(str16, len16, str8, len8); + + return STATUS_OK; +} + +binder_status_t AParcel_writeStringArray(AParcel* parcel, const void* arrayData, int32_t length, + AParcel_stringArrayElementGetter getter) { + // we have no clue if arrayData represents a null object or not, we can only infer from length + bool arrayIsNull = length < 0; + binder_status_t status = WriteAndValidateArraySize(parcel, arrayIsNull, length); + if (status != STATUS_OK) return status; + if (length <= 0) return STATUS_OK; + + for (int32_t i = 0; i < length; i++) { + int32_t elementLength = 0; + const char* str = getter(arrayData, i, &elementLength); + if (str == nullptr && elementLength != -1) return STATUS_BAD_VALUE; + + binder_status_t status = AParcel_writeString(parcel, str, elementLength); + if (status != STATUS_OK) return status; + } + + return STATUS_OK; +} + +// This implements AParcel_stringAllocator for a string using an array, index, and element +// allocator. +struct StringArrayElementAllocationAdapter { + void* arrayData; // stringData from the NDK + int32_t index; // index into the string array + AParcel_stringArrayElementAllocator elementAllocator; + + static bool Allocator(void* stringData, int32_t length, char** buffer) { + StringArrayElementAllocationAdapter* adapter = + static_cast<StringArrayElementAllocationAdapter*>(stringData); + return adapter->elementAllocator(adapter->arrayData, adapter->index, length, buffer); + } +}; + +binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData, + AParcel_stringArrayAllocator allocator, + AParcel_stringArrayElementAllocator elementAllocator) { + const Parcel* rawParcel = parcel->get(); + + int32_t length; + status_t status = rawParcel->readInt32(&length); + + if (status != STATUS_OK) return PruneStatusT(status); + if (length < -1) return STATUS_BAD_VALUE; + + if (!allocator(arrayData, length)) return STATUS_NO_MEMORY; + + if (length == -1) return STATUS_OK; // null string array + + StringArrayElementAllocationAdapter adapter{ + .arrayData = arrayData, + .index = 0, + .elementAllocator = elementAllocator, + }; + + for (; adapter.index < length; adapter.index++) { + binder_status_t status = AParcel_readString(parcel, static_cast<void*>(&adapter), + StringArrayElementAllocationAdapter::Allocator); + + if (status != STATUS_OK) return status; + } + + return STATUS_OK; +} + +binder_status_t AParcel_writeParcelableArray(AParcel* parcel, const void* arrayData, int32_t length, + AParcel_writeParcelableElement elementWriter) { + // we have no clue if arrayData represents a null object or not, we can only infer from length + bool arrayIsNull = length < 0; + binder_status_t status = WriteAndValidateArraySize(parcel, arrayIsNull, length); + if (status != STATUS_OK) return status; + if (length <= 0) return STATUS_OK; + + for (int32_t i = 0; i < length; i++) { + binder_status_t status = elementWriter(parcel, arrayData, i); + if (status != STATUS_OK) return status; + } + + return STATUS_OK; +} + +binder_status_t AParcel_readParcelableArray(const AParcel* parcel, void* arrayData, + AParcel_parcelableArrayAllocator allocator, + AParcel_readParcelableElement elementReader) { + const Parcel* rawParcel = parcel->get(); + + int32_t length; + status_t status = rawParcel->readInt32(&length); + + if (status != STATUS_OK) return PruneStatusT(status); + if (length < -1) return STATUS_BAD_VALUE; + + if (!allocator(arrayData, length)) return STATUS_NO_MEMORY; + + if (length == -1) return STATUS_OK; // null array + + for (int32_t i = 0; i < length; i++) { + binder_status_t status = elementReader(parcel, arrayData, i); + if (status != STATUS_OK) return status; + } + + return STATUS_OK; +} + +// See gen_parcel_helper.py. These auto-generated read/write methods use the same types for +// libbinder and this library. +// @START +binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value) { + status_t status = parcel->get()->writeInt32(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_writeUint32(AParcel* parcel, uint32_t value) { + status_t status = parcel->get()->writeUint32(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value) { + status_t status = parcel->get()->writeInt64(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_writeUint64(AParcel* parcel, uint64_t value) { + status_t status = parcel->get()->writeUint64(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_writeFloat(AParcel* parcel, float value) { + status_t status = parcel->get()->writeFloat(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_writeDouble(AParcel* parcel, double value) { + status_t status = parcel->get()->writeDouble(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_writeBool(AParcel* parcel, bool value) { + status_t status = parcel->get()->writeBool(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_writeChar(AParcel* parcel, char16_t value) { + status_t status = parcel->get()->writeChar(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_writeByte(AParcel* parcel, int8_t value) { + status_t status = parcel->get()->writeByte(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value) { + status_t status = parcel->get()->readInt32(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_readUint32(const AParcel* parcel, uint32_t* value) { + status_t status = parcel->get()->readUint32(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value) { + status_t status = parcel->get()->readInt64(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_readUint64(const AParcel* parcel, uint64_t* value) { + status_t status = parcel->get()->readUint64(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_readFloat(const AParcel* parcel, float* value) { + status_t status = parcel->get()->readFloat(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_readDouble(const AParcel* parcel, double* value) { + status_t status = parcel->get()->readDouble(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_readBool(const AParcel* parcel, bool* value) { + status_t status = parcel->get()->readBool(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_readChar(const AParcel* parcel, char16_t* value) { + status_t status = parcel->get()->readChar(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_readByte(const AParcel* parcel, int8_t* value) { + status_t status = parcel->get()->readByte(value); + return PruneStatusT(status); +} + +binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* arrayData, int32_t length) { + return WriteArray<int32_t>(parcel, arrayData, length); +} + +binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* arrayData, + int32_t length) { + return WriteArray<uint32_t>(parcel, arrayData, length); +} + +binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* arrayData, int32_t length) { + return WriteArray<int64_t>(parcel, arrayData, length); +} + +binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* arrayData, + int32_t length) { + return WriteArray<uint64_t>(parcel, arrayData, length); +} + +binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* arrayData, int32_t length) { + return WriteArray<float>(parcel, arrayData, length); +} + +binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* arrayData, int32_t length) { + return WriteArray<double>(parcel, arrayData, length); +} + +binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData, int32_t length, + AParcel_boolArrayGetter getter) { + return WriteArray<bool>(parcel, arrayData, length, getter, &Parcel::writeBool); +} + +binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* arrayData, int32_t length) { + return WriteArray<char16_t>(parcel, arrayData, length); +} + +binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, int32_t length) { + return WriteArray<int8_t>(parcel, arrayData, length); +} + +binder_status_t AParcel_readInt32Array(const AParcel* parcel, void* arrayData, + AParcel_int32ArrayAllocator allocator) { + return ReadArray<int32_t>(parcel, arrayData, allocator); +} + +binder_status_t AParcel_readUint32Array(const AParcel* parcel, void* arrayData, + AParcel_uint32ArrayAllocator allocator) { + return ReadArray<uint32_t>(parcel, arrayData, allocator); +} + +binder_status_t AParcel_readInt64Array(const AParcel* parcel, void* arrayData, + AParcel_int64ArrayAllocator allocator) { + return ReadArray<int64_t>(parcel, arrayData, allocator); +} + +binder_status_t AParcel_readUint64Array(const AParcel* parcel, void* arrayData, + AParcel_uint64ArrayAllocator allocator) { + return ReadArray<uint64_t>(parcel, arrayData, allocator); +} + +binder_status_t AParcel_readFloatArray(const AParcel* parcel, void* arrayData, + AParcel_floatArrayAllocator allocator) { + return ReadArray<float>(parcel, arrayData, allocator); +} + +binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void* arrayData, + AParcel_doubleArrayAllocator allocator) { + return ReadArray<double>(parcel, arrayData, allocator); +} + +binder_status_t AParcel_readBoolArray(const AParcel* parcel, void* arrayData, + AParcel_boolArrayAllocator allocator, + AParcel_boolArraySetter setter) { + return ReadArray<bool>(parcel, arrayData, allocator, setter, &Parcel::readBool); +} + +binder_status_t AParcel_readCharArray(const AParcel* parcel, void* arrayData, + AParcel_charArrayAllocator allocator) { + return ReadArray<char16_t>(parcel, arrayData, allocator); +} + +binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, + AParcel_byteArrayAllocator allocator) { + return ReadArray<int8_t>(parcel, arrayData, allocator); +} + +// @END
diff --git a/libs/binder/ndk/parcel_internal.h b/libs/binder/ndk/parcel_internal.h new file mode 100644 index 0000000..6b7295e --- /dev/null +++ b/libs/binder/ndk/parcel_internal.h
@@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/binder_parcel.h> + +#include <sys/cdefs.h> + +#include <binder/Parcel.h> +#include "ibinder_internal.h" + +struct AParcel { + const ::android::Parcel* get() const { return mParcel; } + ::android::Parcel* get() { return mParcel; } + + explicit AParcel(const AIBinder* binder) + : AParcel(binder, new ::android::Parcel, true /*owns*/) {} + AParcel(const AIBinder* binder, ::android::Parcel* parcel, bool owns) + : mBinder(binder), mParcel(parcel), mOwns(owns) {} + + ~AParcel() { + if (mOwns) { + delete mParcel; + } + } + + static const AParcel readOnly(const AIBinder* binder, const ::android::Parcel* parcel) { + return AParcel(binder, const_cast<::android::Parcel*>(parcel), false); + } + + const AIBinder* getBinder() { return mBinder; } + + private: + // This object is associated with a calls to a specific AIBinder object. This is used for sanity + // checking to make sure that a parcel is one that is expected. + const AIBinder* mBinder; + + ::android::Parcel* mParcel; + bool mOwns; +};
diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp new file mode 100644 index 0000000..c89caaf --- /dev/null +++ b/libs/binder/ndk/process.cpp
@@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_process.h> + +#include <mutex> + +#include <android-base/logging.h> +#include <binder/IPCThreadState.h> + +using ::android::IPCThreadState; +using ::android::ProcessState; + +void ABinderProcess_startThreadPool() { + ProcessState::self()->startThreadPool(); + ProcessState::self()->giveThreadPoolName(); +} +bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads) { + return ProcessState::self()->setThreadPoolMaxThreadCount(numThreads) == 0; +} +void ABinderProcess_joinThreadPool() { + IPCThreadState::self()->joinThreadPool(); +}
diff --git a/libs/binder/ndk/runtests.sh b/libs/binder/ndk/runtests.sh new file mode 100755 index 0000000..a0c49fb --- /dev/null +++ b/libs/binder/ndk/runtests.sh
@@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if [ -z $ANDROID_BUILD_TOP ]; then + echo "You need to source and lunch before you can use this script" + exit 1 +fi + +set -ex + +function run_libbinder_ndk_test() { + adb shell /data/nativetest64/libbinder_ndk_test_server/libbinder_ndk_test_server & + + # avoid getService 1s delay for most runs, non-critical + sleep 0.1 + + adb shell /data/nativetest64/libbinder_ndk_test_client/libbinder_ndk_test_client; \ + adb shell killall libbinder_ndk_test_server +} + +[ "$1" != "--skip-build" ] && $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \ + MODULES-IN-frameworks-native-libs-binder-ndk + +adb root +adb wait-for-device +adb sync data + +# very simple unit tests, tests things outside of the NDK as well +run_libbinder_ndk_test + +# CTS tests (much more comprehensive, new tests should ideally go here) +atest android.binder.cts
diff --git a/libs/binder/ndk/scripts/format.sh b/libs/binder/ndk/scripts/format.sh new file mode 100755 index 0000000..698d291 --- /dev/null +++ b/libs/binder/ndk/scripts/format.sh
@@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +echo "Formatting code" + +bpfmt -w $(find $ANDROID_BUILD_TOP/frameworks/native/libs/binder/ndk/ -name "Android.bp") +clang-format -i $(find $ANDROID_BUILD_TOP/frameworks/native/libs/binder/ndk/ -\( -name "*.cpp" -o -name "*.h" -\))
diff --git a/libs/binder/ndk/scripts/gen_parcel_helper.py b/libs/binder/ndk/scripts/gen_parcel_helper.py new file mode 100755 index 0000000..8f587d2 --- /dev/null +++ b/libs/binder/ndk/scripts/gen_parcel_helper.py
@@ -0,0 +1,286 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys + +# list (pretty, cpp) +data_types = [ + ("Int32", "int32_t"), + ("Uint32", "uint32_t"), + ("Int64", "int64_t"), + ("Uint64", "uint64_t"), + ("Float", "float"), + ("Double", "double"), + ("Bool", "bool"), + ("Char", "char16_t"), + ("Byte", "int8_t"), +] + +non_contiguously_addressable = {"Bool"} + +def replaceFileTags(path, content, start_tag, end_tag): + print("Updating", path) + with open(path, "r+") as f: + lines = f.readlines() + + start = lines.index("// @" + start_tag + "\n") + end = lines.index("// @" + end_tag + "\n") + + if end <= start or start < 0 or end < 0: + print("Failed to find tags in", path) + exit(1) + + f.seek(0) + f.write("".join(lines[:start+1]) + content + "".join(lines[end:])) + f.truncate() + +def main(): + if len(sys.argv) != 1: + print("No arguments.") + exit(1) + + ABT = os.environ.get('ANDROID_BUILD_TOP', None) + if ABT is None: + print("Can't get ANDROID_BUILD_TOP. Lunch?") + exit(1) + ROOT = ABT + "/frameworks/native/libs/binder/ndk/" + + print("Updating auto-generated code") + + pre_header = "" + header = "" + source = "" + cpp_helper = "" + + for pretty, cpp in data_types: + header += "/**\n" + header += " * Writes " + cpp + " value to the next location in a non-null parcel.\n" + header += " *\n" + header += " * \\param parcel the parcel to write to.\n" + header += " * \\param value the value to write to the parcel.\n" + header += " *\n" + header += " * \\return STATUS_OK on successful write.\n" + header += " */\n" + header += "binder_status_t AParcel_write" + pretty + "(AParcel* parcel, " + cpp + " value) __INTRODUCED_IN(29);\n\n" + source += "binder_status_t AParcel_write" + pretty + "(AParcel* parcel, " + cpp + " value) {\n" + source += " status_t status = parcel->get()->write" + pretty + "(value);\n" + source += " return PruneStatusT(status);\n" + source += "}\n\n" + + for pretty, cpp in data_types: + header += "/**\n" + header += " * Reads into " + cpp + " value from the next location in a non-null parcel.\n" + header += " *\n" + header += " * \\param parcel the parcel to read from.\n" + header += " * \\param value the value to read from the parcel.\n" + header += " *\n" + header += " * \\return STATUS_OK on successful read.\n" + header += " */\n" + header += "binder_status_t AParcel_read" + pretty + "(const AParcel* parcel, " + cpp + "* value) __INTRODUCED_IN(29);\n\n" + source += "binder_status_t AParcel_read" + pretty + "(const AParcel* parcel, " + cpp + "* value) {\n" + source += " status_t status = parcel->get()->read" + pretty + "(value);\n" + source += " return PruneStatusT(status);\n" + source += "}\n\n" + + for pretty, cpp in data_types: + nca = pretty in non_contiguously_addressable + + arg_types = "const " + cpp + "* arrayData, int32_t length" + if nca: arg_types = "const void* arrayData, int32_t length, AParcel_" + pretty.lower() + "ArrayGetter getter" + args = "arrayData, length" + if nca: args = "arrayData, length, getter, &Parcel::write" + pretty + + header += "/**\n" + header += " * Writes an array of " + cpp + " to the next location in a non-null parcel.\n" + if nca: + header += " *\n" + header += " * getter(arrayData, i) will be called for each i in [0, length) in order to get the underlying values to write " + header += "to the parcel.\n" + header += " *\n" + header += " * \\param parcel the parcel to write to.\n" + if nca: + header += " * \\param arrayData some external representation of an array.\n" + header += " * \\param length the length of arrayData (or -1 if this represents a null array).\n" + header += " * \\param getter the callback to retrieve data at specific locations in the array.\n" + else: + header += " * \\param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0).\n" + header += " * \\param length the length of arrayData or -1 if this represents a null array.\n" + header += " *\n" + header += " * \\return STATUS_OK on successful write.\n" + header += " */\n" + header += "binder_status_t AParcel_write" + pretty + "Array(AParcel* parcel, " + arg_types + ") __INTRODUCED_IN(29);\n\n" + source += "binder_status_t AParcel_write" + pretty + "Array(AParcel* parcel, " + arg_types + ") {\n" + source += " return WriteArray<" + cpp + ">(parcel, " + args + ");\n"; + source += "}\n\n" + + for pretty, cpp in data_types: + nca = pretty in non_contiguously_addressable + + read_func = "AParcel_read" + pretty + "Array" + write_func = "AParcel_write" + pretty + "Array" + allocator_type = "AParcel_" + pretty.lower() + "ArrayAllocator" + getter_type = "AParcel_" + pretty.lower() + "ArrayGetter" + setter_type = "AParcel_" + pretty.lower() + "ArraySetter" + + if nca: + pre_header += "/**\n" + pre_header += " * This allocates an array of size 'length' inside of arrayData and returns whether or not there was " + pre_header += "a success. If length is -1, then this should allocate some representation of a null array.\n" + pre_header += " *\n" + pre_header += " * See also " + read_func + "\n" + pre_header += " *\n" + pre_header += " * \\param arrayData some external representation of an array of " + cpp + ".\n" + pre_header += " * \\param length the length to allocate arrayData to (or -1 if this represents a null array).\n" + pre_header += " *\n" + pre_header += " * \\return whether the allocation succeeded.\n" + pre_header += " */\n" + pre_header += "typedef bool (*" + allocator_type + ")(void* arrayData, int32_t length);\n\n" + + pre_header += "/**\n" + pre_header += " * This is called to get the underlying data from an arrayData object at index.\n" + pre_header += " *\n" + pre_header += " * See also " + write_func + "\n" + pre_header += " *\n" + pre_header += " * \\param arrayData some external representation of an array of " + cpp + ".\n" + pre_header += " * \\param index the index of the value to be retrieved.\n" + pre_header += " *\n" + pre_header += " * \\return the value of the array at index index.\n" + pre_header += " */\n" + pre_header += "typedef " + cpp + " (*" + getter_type + ")(const void* arrayData, size_t index);\n\n" + + pre_header += "/**\n" + pre_header += " * This is called to set an underlying value in an arrayData object at index.\n" + pre_header += " *\n" + pre_header += " * See also " + read_func + "\n" + pre_header += " *\n" + pre_header += " * \\param arrayData some external representation of an array of " + cpp + ".\n" + pre_header += " * \\param index the index of the value to be set.\n" + pre_header += " * \\param value the value to set at index index.\n" + pre_header += " */\n" + pre_header += "typedef void (*" + setter_type + ")(void* arrayData, size_t index, " + cpp + " value);\n\n" + else: + pre_header += "/**\n" + pre_header += " * This is called to get the underlying data from an arrayData object.\n" + pre_header += " *\n" + pre_header += " * The implementation of this function should allocate a contiguous array of size 'length' and " + pre_header += "return that underlying buffer to be filled out. If there is an error or length is 0, null may be " + pre_header += "returned. If length is -1, this should allocate some representation of a null array.\n" + pre_header += " *\n" + pre_header += " * See also " + read_func + "\n" + pre_header += " *\n" + pre_header += " * \\param arrayData some external representation of an array of " + cpp + ".\n" + pre_header += " * \\param length the length to allocate arrayData to.\n" + pre_header += " * \\param outBuffer a buffer of " + cpp + " of size 'length' (if length is >= 0, if length is 0, " + pre_header += "this may be nullptr).\n" + pre_header += " *\n" + pre_header += " * \\return whether or not the allocation was successful (or whether a null array is represented when length is -1).\n" + pre_header += " */\n" + pre_header += "typedef bool (*" + allocator_type + ")(void* arrayData, int32_t length, " + cpp + "** outBuffer);\n\n" + + read_array_args = [("const AParcel*", "parcel")] + read_array_args += [("void*", "arrayData")] + read_array_args += [(allocator_type, "allocator")] + if nca: read_array_args += [(setter_type, "setter")] + + read_type_args = ", ".join((varType + " " + name for varType, name in read_array_args)) + read_call_args = ", ".join((name for varType, name in read_array_args)) + + header += "/**\n" + header += " * Reads an array of " + cpp + " from the next location in a non-null parcel.\n" + header += " *\n" + if nca: + header += " * First, allocator will be called with the length of the array. Then, for every i in [0, length), " + header += "setter(arrayData, i, x) will be called where x is the value at the associated index.\n" + else: + header += " * First, allocator will be called with the length of the array. If the allocation succeeds and the " + header += "length is greater than zero, the buffer returned by the allocator will be filled with the corresponding data\n" + header += " *\n" + header += " * \\param parcel the parcel to read from.\n" + header += " * \\param arrayData some external representation of an array.\n" + header += " * \\param allocator the callback that will be called to allocate the array.\n" + if nca: + header += " * \\param setter the callback that will be called to set a value at a specific location in the array.\n" + header += " *\n" + header += " * \\return STATUS_OK on successful read.\n" + header += " */\n" + header += "binder_status_t " + read_func + "(" + read_type_args + ") __INTRODUCED_IN(29);\n\n" + source += "binder_status_t " + read_func + "(" + read_type_args + ") {\n" + additional_args = "" + if nca: additional_args = ", &Parcel::read" + pretty + source += " return ReadArray<" + cpp + ">(" + read_call_args + additional_args + ");\n"; + source += "}\n\n" + + cpp_helper += "/**\n" + cpp_helper += " * Writes a vector of " + cpp + " to the next location in a non-null parcel.\n" + cpp_helper += " */\n" + cpp_helper += "inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<" + cpp + ">& vec) {\n" + write_args = "vec.data(), vec.size()" + if nca: write_args = "static_cast<const void*>(&vec), vec.size(), AParcel_stdVectorGetter<" + cpp + ">" + cpp_helper += " return AParcel_write" + pretty + "Array(parcel, " + write_args + ");\n" + cpp_helper += "}\n\n" + + cpp_helper += "/**\n" + cpp_helper += " * Writes an optional vector of " + cpp + " to the next location in a non-null parcel.\n" + cpp_helper += " */\n" + cpp_helper += "inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::optional<std::vector<" + cpp + ">>& vec) {\n" + extra_args = "" + if nca: extra_args = ", AParcel_stdVectorGetter<" + cpp + ">" + cpp_helper += " if (!vec) return AParcel_write" + pretty + "Array(parcel, nullptr, -1" + extra_args + ");\n" + cpp_helper += " return AParcel_writeVector(parcel, *vec);\n" + cpp_helper += "}\n\n" + + cpp_helper += "/**\n" + cpp_helper += " * Reads a vector of " + cpp + " from the next location in a non-null parcel.\n" + cpp_helper += " */\n" + cpp_helper += "inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<" + cpp + ">* vec) {\n" + cpp_helper += " void* vectorData = static_cast<void*>(vec);\n" + read_args = [] + read_args += ["parcel"] + read_args += ["vectorData"] + if nca: + read_args += ["AParcel_stdVectorExternalAllocator<bool>"] + read_args += ["AParcel_stdVectorSetter<" + cpp + ">"] + else: + read_args += ["AParcel_stdVectorAllocator<" + cpp + ">"] + cpp_helper += " return AParcel_read" + pretty + "Array(" + ", ".join(read_args) + ");\n" + cpp_helper += "}\n\n" + + cpp_helper += "/**\n" + cpp_helper += " * Reads an optional vector of " + cpp + " from the next location in a non-null parcel.\n" + cpp_helper += " */\n" + cpp_helper += "inline binder_status_t AParcel_readVector(const AParcel* parcel, std::optional<std::vector<" + cpp + ">>* vec) {\n" + cpp_helper += " void* vectorData = static_cast<void*>(vec);\n" + read_args = [] + read_args += ["parcel"] + read_args += ["vectorData"] + if nca: + read_args += ["AParcel_nullableStdVectorExternalAllocator<bool>"] + read_args += ["AParcel_nullableStdVectorSetter<" + cpp + ">"] + else: + read_args += ["AParcel_nullableStdVectorAllocator<" + cpp + ">"] + cpp_helper += " return AParcel_read" + pretty + "Array(" + ", ".join(read_args) + ");\n" + cpp_helper += "}\n\n" + + replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", pre_header, "START-PRIMITIVE-VECTOR-GETTERS", "END-PRIMITIVE-VECTOR-GETTERS") + replaceFileTags(ROOT + "include_ndk/android/binder_parcel.h", header, "START-PRIMITIVE-READ-WRITE", "END-PRIMITIVE-READ-WRITE") + replaceFileTags(ROOT + "parcel.cpp", source, "START", "END") + replaceFileTags(ROOT + "include_ndk/android/binder_parcel_utils.h", cpp_helper, "START", "END") + + print("Updating DONE.") + +if __name__ == "__main__": + main()
diff --git a/libs/binder/ndk/scripts/init_map.sh b/libs/binder/ndk/scripts/init_map.sh new file mode 100755 index 0000000..3529b72 --- /dev/null +++ b/libs/binder/ndk/scripts/init_map.sh
@@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +# Simple helper for ease of development until this API is frozen. + +echo "LIBBINDER_NDK { # introduced=29" +echo " global:" +{ + grep -oP "AIBinder_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_ibinder.h; + grep -oP "AIBinder_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_ibinder_jni.h; + grep -oP "AParcel_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_parcel.h; + grep -oP "AStatus_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_status.h; +} | sort | uniq | awk '{ print " " $0 ";"; }' +{ + grep -oP "AServiceManager_[a-zA-Z0-9_]+(?=\()" include_apex/android/binder_manager.h; + grep -oP "ABinderProcess_[a-zA-Z0-9_]+(?=\()" include_apex/android/binder_process.h; +} | sort | uniq | awk '{ print " " $0 "; # apex"; }' +echo " local:" +echo " *;" +echo "};"
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp new file mode 100644 index 0000000..d0b166d --- /dev/null +++ b/libs/binder/ndk/service_manager.cpp
@@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_manager.h> + +#include "ibinder_internal.h" +#include "status_internal.h" + +#include <binder/IServiceManager.h> + +using ::android::defaultServiceManager; +using ::android::IBinder; +using ::android::IServiceManager; +using ::android::sp; +using ::android::status_t; +using ::android::String16; + +binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance) { + if (binder == nullptr || instance == nullptr) { + return STATUS_UNEXPECTED_NULL; + } + + sp<IServiceManager> sm = defaultServiceManager(); + status_t status = sm->addService(String16(instance), binder->getBinder()); + return PruneStatusT(status); +} +AIBinder* AServiceManager_checkService(const char* instance) { + if (instance == nullptr) { + return nullptr; + } + + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->checkService(String16(instance)); + + sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder); + AIBinder_incStrong(ret.get()); + return ret.get(); +} +AIBinder* AServiceManager_getService(const char* instance) { + if (instance == nullptr) { + return nullptr; + } + + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->getService(String16(instance)); + + sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder); + AIBinder_incStrong(ret.get()); + return ret.get(); +}
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp new file mode 100644 index 0000000..1f75b0b --- /dev/null +++ b/libs/binder/ndk/status.cpp
@@ -0,0 +1,151 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_status.h> +#include "status_internal.h" + +#include <android-base/logging.h> + +using ::android::status_t; +using ::android::binder::Status; + +AStatus* AStatus_newOk() { + return new AStatus(); +} + +AStatus* AStatus_fromExceptionCode(binder_exception_t exception) { + return new AStatus(Status::fromExceptionCode(PruneException(exception))); +} + +AStatus* AStatus_fromExceptionCodeWithMessage(binder_exception_t exception, const char* message) { + return new AStatus(Status::fromExceptionCode(PruneException(exception), message)); +} + +AStatus* AStatus_fromServiceSpecificError(int32_t serviceSpecific) { + return new AStatus(Status::fromServiceSpecificError(serviceSpecific)); +} + +AStatus* AStatus_fromServiceSpecificErrorWithMessage(int32_t serviceSpecific, const char* message) { + return new AStatus(Status::fromServiceSpecificError(serviceSpecific, message)); +} + +AStatus* AStatus_fromStatus(binder_status_t status) { + return new AStatus(Status::fromStatusT(PruneStatusT(status))); +} + +bool AStatus_isOk(const AStatus* status) { + return status->get()->isOk(); +} + +binder_exception_t AStatus_getExceptionCode(const AStatus* status) { + return PruneException(status->get()->exceptionCode()); +} + +int32_t AStatus_getServiceSpecificError(const AStatus* status) { + return status->get()->serviceSpecificErrorCode(); +} + +binder_status_t AStatus_getStatus(const AStatus* status) { + return PruneStatusT(status->get()->transactionError()); +} + +const char* AStatus_getMessage(const AStatus* status) { + return status->get()->exceptionMessage().c_str(); +} + +void AStatus_delete(AStatus* status) { + delete status; +} + +binder_status_t PruneStatusT(status_t status) { + switch (status) { + case ::android::OK: + return STATUS_OK; + case ::android::NO_MEMORY: + return STATUS_NO_MEMORY; + case ::android::INVALID_OPERATION: + return STATUS_INVALID_OPERATION; + case ::android::BAD_VALUE: + return STATUS_BAD_VALUE; + case ::android::BAD_TYPE: + return STATUS_BAD_TYPE; + case ::android::NAME_NOT_FOUND: + return STATUS_NAME_NOT_FOUND; + case ::android::PERMISSION_DENIED: + return STATUS_PERMISSION_DENIED; + case ::android::NO_INIT: + return STATUS_NO_INIT; + case ::android::ALREADY_EXISTS: + return STATUS_ALREADY_EXISTS; + case ::android::DEAD_OBJECT: + return STATUS_DEAD_OBJECT; + case ::android::FAILED_TRANSACTION: + return STATUS_FAILED_TRANSACTION; + case ::android::BAD_INDEX: + return STATUS_BAD_INDEX; + case ::android::NOT_ENOUGH_DATA: + return STATUS_NOT_ENOUGH_DATA; + case ::android::WOULD_BLOCK: + return STATUS_WOULD_BLOCK; + case ::android::TIMED_OUT: + return STATUS_TIMED_OUT; + case ::android::UNKNOWN_TRANSACTION: + return STATUS_UNKNOWN_TRANSACTION; + case ::android::FDS_NOT_ALLOWED: + return STATUS_FDS_NOT_ALLOWED; + case ::android::UNEXPECTED_NULL: + return STATUS_UNEXPECTED_NULL; + case ::android::UNKNOWN_ERROR: + return STATUS_UNKNOWN_ERROR; + + default: + LOG(WARNING) << __func__ + << ": Unknown status_t pruned into STATUS_UNKNOWN_ERROR: " << status; + return STATUS_UNKNOWN_ERROR; + } +} + +binder_exception_t PruneException(int32_t exception) { + switch (exception) { + case Status::EX_NONE: + return EX_NONE; + case Status::EX_SECURITY: + return EX_SECURITY; + case Status::EX_BAD_PARCELABLE: + return EX_BAD_PARCELABLE; + case Status::EX_ILLEGAL_ARGUMENT: + return EX_ILLEGAL_ARGUMENT; + case Status::EX_NULL_POINTER: + return EX_NULL_POINTER; + case Status::EX_ILLEGAL_STATE: + return EX_ILLEGAL_STATE; + case Status::EX_NETWORK_MAIN_THREAD: + return EX_NETWORK_MAIN_THREAD; + case Status::EX_UNSUPPORTED_OPERATION: + return EX_UNSUPPORTED_OPERATION; + case Status::EX_SERVICE_SPECIFIC: + return EX_SERVICE_SPECIFIC; + case Status::EX_PARCELABLE: + return EX_PARCELABLE; + case Status::EX_TRANSACTION_FAILED: + return EX_TRANSACTION_FAILED; + + default: + LOG(WARNING) << __func__ + << ": Unknown status_t pruned into EX_TRANSACTION_FAILED: " << exception; + return EX_TRANSACTION_FAILED; + } +}
diff --git a/libs/binder/ndk/status_internal.h b/libs/binder/ndk/status_internal.h new file mode 100644 index 0000000..f6227f7 --- /dev/null +++ b/libs/binder/ndk/status_internal.h
@@ -0,0 +1,39 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/binder_status.h> + +#include <binder/Status.h> +#include <utils/Errors.h> + +struct AStatus { + AStatus() {} // ok + explicit AStatus(::android::binder::Status&& status) : mStatus(std::move(status)) {} + + ::android::binder::Status* get() { return &mStatus; } + const ::android::binder::Status* get() const { return &mStatus; } + + private: + ::android::binder::Status mStatus; +}; + +// This collapses the statuses into the declared range. +binder_status_t PruneStatusT(android::status_t status); + +// This collapses the exception into the declared range. +binder_exception_t PruneException(int32_t exception);
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp new file mode 100644 index 0000000..8cd4e03 --- /dev/null +++ b/libs/binder/ndk/test/Android.bp
@@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +cc_defaults { + name: "test_libbinder_ndk_defaults", + shared_libs: [ + "libbase", + ], + strip: { + none: true, + }, + cflags: [ + "-O0", + "-g", + ], +} + +cc_library_static { + name: "test_libbinder_ndk_library", + defaults: ["test_libbinder_ndk_defaults"], + export_include_dirs: ["include"], + shared_libs: ["libbinder_ndk"], + export_shared_lib_headers: ["libbinder_ndk"], + srcs: ["iface.cpp"], +} + +cc_defaults { + name: "test_libbinder_ndk_test_defaults", + defaults: ["test_libbinder_ndk_defaults"], + shared_libs: [ + "libandroid_runtime_lazy", + "libbase", + "libbinder", + "libutils", + ], + static_libs: [ + "libbinder_ndk", + "test_libbinder_ndk_library", + ], +} + +// This test is a unit test of the low-level API that is presented here, +// specifically the parts which are outside of the NDK. Actual users should +// also instead use AIDL to generate these stubs. See android.binder.cts. +cc_test { + name: "libbinder_ndk_test_client", + defaults: ["test_libbinder_ndk_test_defaults"], + srcs: ["main_client.cpp"], +} + +cc_test { + name: "libbinder_ndk_test_server", + defaults: ["test_libbinder_ndk_test_defaults"], + srcs: ["main_server.cpp"], + gtest: false, +}
diff --git a/libs/binder/ndk/test/iface.cpp b/libs/binder/ndk/test/iface.cpp new file mode 100644 index 0000000..64832f3 --- /dev/null +++ b/libs/binder/ndk/test/iface.cpp
@@ -0,0 +1,176 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/logging.h> +#include <android/binder_manager.h> +#include <iface/iface.h> + +#include <android/binder_auto_utils.h> + +using ::android::sp; +using ::android::wp; + +const char* IFoo::kSomeInstanceName = "libbinder_ndk-test-IFoo"; +const char* IFoo::kInstanceNameToDieFor = "libbinder_ndk-test-IFoo-to-die"; +const char* kIFooDescriptor = "my-special-IFoo-class"; + +struct IFoo_Class_Data { + sp<IFoo> foo; +}; + +void* IFoo_Class_onCreate(void* args) { + IFoo_Class_Data* foo = static_cast<IFoo_Class_Data*>(args); + // This is a foo, but we're currently not verifying that. So, the method newLocalBinder is + // coupled with this. + return static_cast<void*>(foo); +} + +void IFoo_Class_onDestroy(void* userData) { + delete static_cast<IFoo_Class_Data*>(userData); +} + +binder_status_t IFoo_Class_onTransact(AIBinder* binder, transaction_code_t code, const AParcel* in, + AParcel* out) { + binder_status_t stat = STATUS_FAILED_TRANSACTION; + + sp<IFoo> foo = static_cast<IFoo_Class_Data*>(AIBinder_getUserData(binder))->foo; + CHECK(foo != nullptr) << "Transaction made on already deleted object"; + + switch (code) { + case IFoo::DOFOO: { + int32_t valueIn; + int32_t valueOut; + stat = AParcel_readInt32(in, &valueIn); + if (stat != STATUS_OK) break; + stat = foo->doubleNumber(valueIn, &valueOut); + if (stat != STATUS_OK) break; + stat = AParcel_writeInt32(out, valueOut); + break; + } + case IFoo::DIE: { + stat = foo->die(); + break; + } + } + + return stat; +} + +AIBinder_Class* IFoo::kClass = AIBinder_Class_define(kIFooDescriptor, IFoo_Class_onCreate, + IFoo_Class_onDestroy, IFoo_Class_onTransact); + +class BpFoo : public IFoo { + public: + explicit BpFoo(AIBinder* binder) : mBinder(binder) {} + virtual ~BpFoo() { AIBinder_decStrong(mBinder); } + + virtual binder_status_t doubleNumber(int32_t in, int32_t* out) { + binder_status_t stat = STATUS_OK; + + AParcel* parcelIn; + stat = AIBinder_prepareTransaction(mBinder, &parcelIn); + if (stat != STATUS_OK) return stat; + + stat = AParcel_writeInt32(parcelIn, in); + if (stat != STATUS_OK) return stat; + + ::ndk::ScopedAParcel parcelOut; + stat = AIBinder_transact(mBinder, IFoo::DOFOO, &parcelIn, parcelOut.getR(), 0 /*flags*/); + if (stat != STATUS_OK) return stat; + + stat = AParcel_readInt32(parcelOut.get(), out); + if (stat != STATUS_OK) return stat; + + return stat; + } + + virtual binder_status_t die() { + binder_status_t stat = STATUS_OK; + + AParcel* parcelIn; + stat = AIBinder_prepareTransaction(mBinder, &parcelIn); + + ::ndk::ScopedAParcel parcelOut; + stat = AIBinder_transact(mBinder, IFoo::DIE, &parcelIn, parcelOut.getR(), 0 /*flags*/); + + return stat; + } + + private: + // Always assumes one refcount + AIBinder* mBinder; +}; + +IFoo::~IFoo() { + AIBinder_Weak_delete(mWeakBinder); +} + +binder_status_t IFoo::addService(const char* instance) { + AIBinder* binder = nullptr; + + if (mWeakBinder != nullptr) { + // one strong ref count of binder + binder = AIBinder_Weak_promote(mWeakBinder); + } + if (binder == nullptr) { + // or one strong refcount here + binder = AIBinder_new(IFoo::kClass, static_cast<void*>(new IFoo_Class_Data{this})); + if (mWeakBinder != nullptr) { + AIBinder_Weak_delete(mWeakBinder); + } + mWeakBinder = AIBinder_Weak_new(binder); + } + + binder_status_t status = AServiceManager_addService(binder, instance); + // Strong references we care about kept by remote process + AIBinder_decStrong(binder); + return status; +} + +sp<IFoo> IFoo::getService(const char* instance, AIBinder** outBinder) { + AIBinder* binder = AServiceManager_getService(instance); // maybe nullptr + if (binder == nullptr) { + return nullptr; + } + + if (!AIBinder_associateClass(binder, IFoo::kClass)) { + AIBinder_decStrong(binder); + return nullptr; + } + + if (outBinder != nullptr) { + AIBinder_incStrong(binder); + *outBinder = binder; + } + + if (AIBinder_isRemote(binder)) { + sp<IFoo> ret = new BpFoo(binder); // takes ownership of binder + return ret; + } + + IFoo_Class_Data* data = static_cast<IFoo_Class_Data*>(AIBinder_getUserData(binder)); + + CHECK(data != nullptr); // always created with non-null data + + sp<IFoo> ret = data->foo; + + AIBinder* held = AIBinder_Weak_promote(ret->mWeakBinder); + CHECK(held == binder); + AIBinder_decStrong(held); + + AIBinder_decStrong(binder); + return ret; +}
diff --git a/libs/binder/ndk/test/include/iface/iface.h b/libs/binder/ndk/test/include/iface/iface.h new file mode 100644 index 0000000..cdf5493 --- /dev/null +++ b/libs/binder/ndk/test/include/iface/iface.h
@@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/binder_ibinder.h> +#include <utils/RefBase.h> + +// warning: it is recommended to use AIDL output instead of this. binder_ibinder_utils.h and some of +// the other niceties make sure that, for instance, binder proxies are always the same. They also +// don't use internal Android APIs like refbase which are used here only for convenience. + +class IFoo : public virtual ::android::RefBase { + public: + static const char* kSomeInstanceName; + static const char* kInstanceNameToDieFor; + + static AIBinder_Class* kClass; + + // Takes ownership of IFoo + binder_status_t addService(const char* instance); + static ::android::sp<IFoo> getService(const char* instance, AIBinder** outBinder = nullptr); + + enum Call { + DOFOO = FIRST_CALL_TRANSACTION + 0, + DIE = FIRST_CALL_TRANSACTION + 1, + }; + + virtual ~IFoo(); + + virtual binder_status_t doubleNumber(int32_t in, int32_t* out) = 0; + virtual binder_status_t die() = 0; + + private: + // this variable is only when IFoo is local (since this test combines 'IFoo' and 'BnFoo'), not + // for BpFoo. + AIBinder_Weak* mWeakBinder = nullptr; +};
diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/main_client.cpp new file mode 100644 index 0000000..8467734 --- /dev/null +++ b/libs/binder/ndk/test/main_client.cpp
@@ -0,0 +1,210 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/logging.h> +#include <android/binder_ibinder_jni.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> +#include <gtest/gtest.h> +#include <iface/iface.h> + +#include <chrono> +#include <condition_variable> +#include <mutex> + +using ::android::sp; + +constexpr char kExistingNonNdkService[] = "SurfaceFlinger"; + +// This is too slow +// TEST(NdkBinder, GetServiceThatDoesntExist) { +// sp<IFoo> foo = IFoo::getService("asdfghkl;"); +// EXPECT_EQ(nullptr, foo.get()); +// } + +TEST(NdkBinder, CheckServiceThatDoesntExist) { + AIBinder* binder = AServiceManager_checkService("asdfghkl;"); + ASSERT_EQ(nullptr, binder); +} + +TEST(NdkBinder, CheckServiceThatDoesExist) { + AIBinder* binder = AServiceManager_checkService(kExistingNonNdkService); + EXPECT_NE(nullptr, binder); + EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)); + + AIBinder_decStrong(binder); +} + +TEST(NdkBinder, DoubleNumber) { + sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName); + ASSERT_NE(foo, nullptr); + + int32_t out; + EXPECT_EQ(STATUS_OK, foo->doubleNumber(1, &out)); + EXPECT_EQ(2, out); +} + +void LambdaOnDeath(void* cookie) { + auto onDeath = static_cast<std::function<void(void)>*>(cookie); + (*onDeath)(); +}; +TEST(NdkBinder, DeathRecipient) { + using namespace std::chrono_literals; + + AIBinder* binder; + sp<IFoo> foo = IFoo::getService(IFoo::kInstanceNameToDieFor, &binder); + ASSERT_NE(nullptr, foo.get()); + ASSERT_NE(nullptr, binder); + + std::mutex deathMutex; + std::condition_variable deathCv; + bool deathRecieved = false; + + std::function<void(void)> onDeath = [&] { + std::cerr << "Binder died (as requested)." << std::endl; + deathRecieved = true; + deathCv.notify_one(); + }; + + AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath); + + EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&onDeath))); + + // the binder driver should return this if the service dies during the transaction + EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die()); + + foo = nullptr; + AIBinder_decStrong(binder); + binder = nullptr; + + std::unique_lock<std::mutex> lock(deathMutex); + EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; })); + EXPECT_TRUE(deathRecieved); + + AIBinder_DeathRecipient_delete(recipient); +} + +TEST(NdkBinder, RetrieveNonNdkService) { + AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); + ASSERT_NE(nullptr, binder); + EXPECT_TRUE(AIBinder_isRemote(binder)); + EXPECT_TRUE(AIBinder_isAlive(binder)); + EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)); + + AIBinder_decStrong(binder); +} + +void OnBinderDeath(void* cookie) { + LOG(ERROR) << "BINDER DIED. COOKIE: " << cookie; +} + +TEST(NdkBinder, LinkToDeath) { + AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); + ASSERT_NE(nullptr, binder); + + AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(OnBinderDeath); + ASSERT_NE(nullptr, recipient); + + EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr)); + EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr)); + EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr)); + EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr)); + EXPECT_EQ(STATUS_NAME_NOT_FOUND, AIBinder_unlinkToDeath(binder, recipient, nullptr)); + + AIBinder_DeathRecipient_delete(recipient); + AIBinder_decStrong(binder); +} + +class MyTestFoo : public IFoo { + binder_status_t doubleNumber(int32_t in, int32_t* out) override { + *out = 2 * in; + LOG(INFO) << "doubleNumber (" << in << ") => " << *out; + return STATUS_OK; + } + binder_status_t die() override { + ADD_FAILURE() << "die called on local instance"; + return STATUS_OK; + } +}; + +TEST(NdkBinder, GetServiceInProcess) { + static const char* kInstanceName = "test-get-service-in-process"; + + sp<IFoo> foo = new MyTestFoo; + EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName)); + + sp<IFoo> getFoo = IFoo::getService(kInstanceName); + EXPECT_EQ(foo.get(), getFoo.get()); + + int32_t out; + EXPECT_EQ(STATUS_OK, getFoo->doubleNumber(1, &out)); + EXPECT_EQ(2, out); +} + +TEST(NdkBinder, EqualityOfRemoteBinderPointer) { + AIBinder* binderA = AServiceManager_getService(kExistingNonNdkService); + ASSERT_NE(nullptr, binderA); + + AIBinder* binderB = AServiceManager_getService(kExistingNonNdkService); + ASSERT_NE(nullptr, binderB); + + EXPECT_EQ(binderA, binderB); + + AIBinder_decStrong(binderA); + AIBinder_decStrong(binderB); +} + +TEST(NdkBinder, ToFromJavaNullptr) { + EXPECT_EQ(nullptr, AIBinder_toJavaBinder(nullptr, nullptr)); + EXPECT_EQ(nullptr, AIBinder_fromJavaBinder(nullptr, nullptr)); +} + +TEST(NdkBinder, ABpBinderRefCount) { + AIBinder* binder = AServiceManager_getService(kExistingNonNdkService); + AIBinder_Weak* wBinder = AIBinder_Weak_new(binder); + + ASSERT_NE(nullptr, binder); + EXPECT_EQ(1, AIBinder_debugGetRefCount(binder)); + + AIBinder_decStrong(binder); + + // assert because would need to decStrong if non-null and we shouldn't need to add a no-op here + ASSERT_NE(nullptr, AIBinder_Weak_promote(wBinder)); + + AIBinder_Weak_delete(wBinder); +} + +TEST(NdkBinder, AddServiceMultipleTimes) { + static const char* kInstanceName1 = "test-multi-1"; + static const char* kInstanceName2 = "test-multi-2"; + sp<IFoo> foo = new MyTestFoo; + EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName1)); + EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName2)); + EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2)); +} + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + + ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications/callbacks + ABinderProcess_startThreadPool(); + + return RUN_ALL_TESTS(); +} + +#include <android/binder_auto_utils.h> +#include <android/binder_interface_utils.h> +#include <android/binder_parcel_utils.h>
diff --git a/libs/binder/ndk/test/main_server.cpp b/libs/binder/ndk/test/main_server.cpp new file mode 100644 index 0000000..a6e17e8 --- /dev/null +++ b/libs/binder/ndk/test/main_server.cpp
@@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/logging.h> +#include <android/binder_process.h> +#include <iface/iface.h> + +using ::android::sp; + +class MyFoo : public IFoo { + binder_status_t doubleNumber(int32_t in, int32_t* out) override { + *out = 2 * in; + LOG(INFO) << "doubleNumber (" << in << ") => " << *out; + return STATUS_OK; + } + + binder_status_t die() override { + LOG(FATAL) << "IFoo::die called!"; + return STATUS_UNKNOWN_ERROR; + } +}; + +int service(const char* instance) { + ABinderProcess_setThreadPoolMaxThreadCount(0); + + // Strong reference to MyFoo kept by service manager. + binder_status_t status = (new MyFoo)->addService(instance); + + if (status != STATUS_OK) { + LOG(FATAL) << "Could not register: " << status << " " << instance; + } + + ABinderProcess_joinThreadPool(); + + return 1; // should not return +} + +int main() { + if (fork() == 0) { + return service(IFoo::kInstanceNameToDieFor); + } + + return service(IFoo::kSomeInstanceName); +}
diff --git a/libs/binder/ndk/update.sh b/libs/binder/ndk/update.sh new file mode 100755 index 0000000..9a4577f --- /dev/null +++ b/libs/binder/ndk/update.sh
@@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +set -ex + +# This script makes sure that the source code is in sync with the various scripts +./scripts/gen_parcel_helper.py +./scripts/format.sh +./scripts/init_map.sh > libbinder_ndk.map.txt
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp index 4f00bc1..77ebac8 100644 --- a/libs/binder/tests/binderDriverInterfaceTest.cpp +++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -36,8 +36,8 @@ m_binderFd = open(BINDER_DEV_NAME, O_RDWR | O_NONBLOCK | O_CLOEXEC); ASSERT_GE(m_binderFd, 0); - m_buffer = mmap(NULL, 64*1024, PROT_READ, MAP_SHARED, m_binderFd, 0); - ASSERT_NE(m_buffer, (void *)NULL); + m_buffer = mmap(nullptr, 64*1024, PROT_READ, MAP_SHARED, m_binderFd, 0); + ASSERT_NE(m_buffer, (void *)nullptr); ret = ioctl(m_binderFd, BINDER_SET_MAX_THREADS, &max_threads); EXPECT_EQ(0, ret); EnterLooper(); @@ -156,23 +156,23 @@ } TEST_F(BinderDriverInterfaceTest, WriteReadNull) { - binderTestIoctlErr1(BINDER_WRITE_READ, NULL, EFAULT); + binderTestIoctlErr1(BINDER_WRITE_READ, nullptr, EFAULT); } TEST_F(BinderDriverInterfaceTest, SetIdleTimeoutNull) { - binderTestIoctlErr2(BINDER_SET_IDLE_TIMEOUT, NULL, EFAULT, EINVAL); + binderTestIoctlErr2(BINDER_SET_IDLE_TIMEOUT, nullptr, EFAULT, EINVAL); } TEST_F(BinderDriverInterfaceTest, SetMaxThreadsNull) { - binderTestIoctlErr2(BINDER_SET_MAX_THREADS, NULL, EFAULT, EINVAL); /* TODO: don't accept EINVAL */ + binderTestIoctlErr2(BINDER_SET_MAX_THREADS, nullptr, EFAULT, EINVAL); /* TODO: don't accept EINVAL */ } TEST_F(BinderDriverInterfaceTest, SetIdlePriorityNull) { - binderTestIoctlErr2(BINDER_SET_IDLE_PRIORITY, NULL, EFAULT, EINVAL); + binderTestIoctlErr2(BINDER_SET_IDLE_PRIORITY, nullptr, EFAULT, EINVAL); } TEST_F(BinderDriverInterfaceTest, VersionNull) { - binderTestIoctlErr2(BINDER_VERSION, NULL, EFAULT, EINVAL); /* TODO: don't accept EINVAL */ + binderTestIoctlErr2(BINDER_VERSION, nullptr, EFAULT, EINVAL); /* TODO: don't accept EINVAL */ } TEST_F(BinderDriverInterfaceTest, SetIdleTimeoutNoTest) {
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 1611e11..78f1159 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp
@@ -65,11 +65,14 @@ BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, + BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION, BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION, BINDER_LIB_TEST_EXIT_TRANSACTION, BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION, BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, + BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, + BINDER_LIB_TEST_ECHO_VECTOR, }; pid_t start_server_process(int arg2, bool usePoll = false) @@ -88,7 +91,7 @@ strpipefd1, usepoll, binderserversuffix, - NULL + nullptr }; ret = pipe(pipefd); @@ -123,7 +126,7 @@ } } if (ret < 0) { - wait(NULL); + wait(nullptr); return ret; } return pid; @@ -145,7 +148,7 @@ sp<IServiceManager> sm = defaultServiceManager(); //printf("%s: pid %d, get service\n", __func__, m_pid); m_server = sm->getService(binderLibTestServiceName); - ASSERT_TRUE(m_server != NULL); + ASSERT_TRUE(m_server != nullptr); //printf("%s: pid %d, get service done\n", __func__, m_pid); } virtual void TearDown() { @@ -155,7 +158,7 @@ pid_t pid; //printf("%s: pid %d\n", __func__, m_pid); - if (m_server != NULL) { + if (m_server != nullptr) { ret = m_server->transact(BINDER_LIB_TEST_GET_STATUS_TRANSACTION, data, &reply); EXPECT_EQ(0, ret); ret = m_server->transact(BINDER_LIB_TEST_EXIT_TRANSACTION, data, &reply, TF_ONE_WAY); @@ -178,6 +181,7 @@ public: virtual void SetUp() { m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer(); + IPCThreadState::self()->restoreCallingWorkSource(0); } virtual void TearDown() { } @@ -192,9 +196,9 @@ ret = m_server->transact(code, data, &reply); EXPECT_EQ(NO_ERROR, ret); - EXPECT_FALSE(binder != NULL); + EXPECT_FALSE(binder != nullptr); binder = reply.readStrongBinder(); - EXPECT_TRUE(binder != NULL); + EXPECT_TRUE(binder != nullptr); ret = reply.readInt32(&id); EXPECT_EQ(NO_ERROR, ret); if (idPtr) @@ -202,12 +206,12 @@ return binder; } - sp<IBinder> addServer(int32_t *idPtr = NULL) + sp<IBinder> addServer(int32_t *idPtr = nullptr) { return addServerEtc(idPtr, BINDER_LIB_TEST_ADD_SERVER); } - sp<IBinder> addPollServer(int32_t *idPtr = NULL) + sp<IBinder> addPollServer(int32_t *idPtr = nullptr) { return addServerEtc(idPtr, BINDER_LIB_TEST_ADD_POLL_SERVER); } @@ -229,7 +233,7 @@ { public: BinderLibTestBundle(void) {} - BinderLibTestBundle(const Parcel *source) : m_isValid(false) { + explicit BinderLibTestBundle(const Parcel *source) : m_isValid(false) { int32_t mark; int32_t bundleLen; size_t pos; @@ -274,8 +278,8 @@ BinderLibTestEvent(void) : m_eventTriggered(false) { - pthread_mutex_init(&m_waitMutex, NULL); - pthread_cond_init(&m_waitCond, NULL); + pthread_mutex_init(&m_waitMutex, nullptr); + pthread_cond_init(&m_waitCond, nullptr); } int waitEvent(int timeout_s) { @@ -315,7 +319,7 @@ public: BinderLibTestCallBack() : m_result(NOT_ENOUGH_DATA) - , m_prev_end(NULL) + , m_prev_end(nullptr) { } status_t getResult(void) @@ -413,7 +417,7 @@ int32_t ptrsize; Parcel data, reply; sp<IBinder> server = addServer(); - ASSERT_TRUE(server != NULL); + ASSERT_TRUE(server != nullptr); ret = server->transact(BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, data, &reply); EXPECT_EQ(NO_ERROR, ret); ret = reply.readInt32(&ptrsize); @@ -436,7 +440,7 @@ BinderLibTestBundle datai; server = addServer(&serverId[i]); - ASSERT_TRUE(server != NULL); + ASSERT_TRUE(server != nullptr); data.writeStrongBinder(server); data.writeInt32(BINDER_LIB_TEST_GET_ID_TRANSACTION); datai.appendTo(&data); @@ -480,7 +484,7 @@ BinderLibTestBundle datai2; server = addServer(&serverId[i]); - ASSERT_TRUE(server != NULL); + ASSERT_TRUE(server != nullptr); data.writeStrongBinder(server); data.writeInt32(BINDER_LIB_TEST_INDIRECT_TRANSACTION); @@ -546,7 +550,7 @@ TEST_F(BinderLibTest, AddServer) { sp<IBinder> server = addServer(); - ASSERT_TRUE(server != NULL); + ASSERT_TRUE(server != nullptr); } TEST_F(BinderLibTest, DeathNotificationNoRefs) @@ -557,7 +561,7 @@ { sp<IBinder> binder = addServer(); - ASSERT_TRUE(binder != NULL); + ASSERT_TRUE(binder != nullptr); ret = binder->linkToDeath(testDeathRecipient); EXPECT_EQ(NO_ERROR, ret); } @@ -579,7 +583,7 @@ { sp<IBinder> binder = addServer(); - ASSERT_TRUE(binder != NULL); + ASSERT_TRUE(binder != nullptr); ret = binder->linkToDeath(testDeathRecipient); EXPECT_EQ(NO_ERROR, ret); wbinder = binder; @@ -602,7 +606,7 @@ { sp<IBinder> binder = addServer(); - ASSERT_TRUE(binder != NULL); + ASSERT_TRUE(binder != nullptr); ret = binder->linkToDeath(testDeathRecipient); EXPECT_EQ(NO_ERROR, ret); sbinder = binder; @@ -629,13 +633,13 @@ sp<IBinder> passiveclient[clientcount]; target = addServer(); - ASSERT_TRUE(target != NULL); + ASSERT_TRUE(target != nullptr); for (int i = 0; i < clientcount; i++) { { Parcel data, reply; linkedclient[i] = addServer(); - ASSERT_TRUE(linkedclient[i] != NULL); + ASSERT_TRUE(linkedclient[i] != nullptr); callBack[i] = new BinderLibTestCallBack(); data.writeStrongBinder(target); data.writeStrongBinder(callBack[i]); @@ -646,7 +650,7 @@ Parcel data, reply; passiveclient[i] = addServer(); - ASSERT_TRUE(passiveclient[i] != NULL); + ASSERT_TRUE(passiveclient[i] != nullptr); data.writeStrongBinder(target); ret = passiveclient[i]->transact(BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION, data, &reply, TF_ONE_WAY); EXPECT_EQ(NO_ERROR, ret); @@ -671,9 +675,9 @@ status_t ret; sp<BinderLibTestCallBack> callback; sp<IBinder> target = addServer(); - ASSERT_TRUE(target != NULL); + ASSERT_TRUE(target != nullptr); sp<IBinder> client = addServer(); - ASSERT_TRUE(client != NULL); + ASSERT_TRUE(client != nullptr); sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient(); @@ -763,16 +767,51 @@ close(pipefd[0]); } +TEST_F(BinderLibTest, PassParcelFileDescriptor) { + const int datasize = 123; + std::vector<uint8_t> writebuf(datasize); + for (size_t i = 0; i < writebuf.size(); ++i) { + writebuf[i] = i; + } + + android::base::unique_fd read_end, write_end; + { + int pipefd[2]; + ASSERT_EQ(0, pipe2(pipefd, O_NONBLOCK)); + read_end.reset(pipefd[0]); + write_end.reset(pipefd[1]); + } + { + Parcel data; + EXPECT_EQ(NO_ERROR, data.writeDupParcelFileDescriptor(write_end.get())); + write_end.reset(); + EXPECT_EQ(NO_ERROR, data.writeInt32(datasize)); + EXPECT_EQ(NO_ERROR, data.write(writebuf.data(), datasize)); + + Parcel reply; + EXPECT_EQ(NO_ERROR, + m_server->transact(BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION, data, + &reply)); + } + std::vector<uint8_t> readbuf(datasize); + EXPECT_EQ(datasize, read(read_end.get(), readbuf.data(), datasize)); + EXPECT_EQ(writebuf, readbuf); + + waitForReadData(read_end.get(), 5000); /* wait for other proccess to close pipe */ + + EXPECT_EQ(0, read(read_end.get(), readbuf.data(), datasize)); +} + TEST_F(BinderLibTest, PromoteLocal) { sp<IBinder> strong = new BBinder(); wp<IBinder> weak = strong; sp<IBinder> strong_from_weak = weak.promote(); - EXPECT_TRUE(strong != NULL); + EXPECT_TRUE(strong != nullptr); EXPECT_EQ(strong, strong_from_weak); - strong = NULL; - strong_from_weak = NULL; + strong = nullptr; + strong_from_weak = nullptr; strong_from_weak = weak.promote(); - EXPECT_TRUE(strong_from_weak == NULL); + EXPECT_TRUE(strong_from_weak == nullptr); } TEST_F(BinderLibTest, PromoteRemote) { @@ -781,8 +820,8 @@ sp<IBinder> strong = new BBinder(); sp<IBinder> server = addServer(); - ASSERT_TRUE(server != NULL); - ASSERT_TRUE(strong != NULL); + ASSERT_TRUE(server != nullptr); + ASSERT_TRUE(strong != nullptr); ret = data.writeWeakBinder(strong); EXPECT_EQ(NO_ERROR, ret); @@ -799,7 +838,7 @@ EXPECT_EQ(NO_ERROR, ret); const flat_binder_object *fb = reply.readObject(false); - ASSERT_TRUE(fb != NULL); + ASSERT_TRUE(fb != nullptr); EXPECT_EQ(BINDER_TYPE_HANDLE, fb->hdr.type); EXPECT_EQ(m_server, ProcessState::self()->getStrongProxyForHandle(fb->handle)); EXPECT_EQ((binder_uintptr_t)0, fb->cookie); @@ -810,7 +849,7 @@ status_t ret; sp<IBinder> server = addServer(); - ASSERT_TRUE(server != NULL); + ASSERT_TRUE(server != nullptr); __u32 freedHandle; wp<IBinder> keepFreedBinder; @@ -881,12 +920,12 @@ data2.writeStrongBinder(callBack2); data2.writeInt32(0); // delay in us - ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, NULL, TF_ONE_WAY); + ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data, nullptr, TF_ONE_WAY); EXPECT_EQ(NO_ERROR, ret); // The delay ensures that this second transaction will end up on the async_todo list // (for a single-threaded server) - ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, NULL, TF_ONE_WAY); + ret = pollServer->transact(BINDER_LIB_TEST_DELAYED_CALL_BACK, data2, nullptr, TF_ONE_WAY); EXPECT_EQ(NO_ERROR, ret); // The server will ensure that the two transactions are handled in the expected order; @@ -902,17 +941,152 @@ EXPECT_EQ(NO_ERROR, ret); } +TEST_F(BinderLibTest, WorkSourceUnsetByDefault) +{ + status_t ret; + Parcel data, reply; + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + EXPECT_EQ(-1, reply.readInt32()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceSet) +{ + status_t ret; + Parcel data, reply; + IPCThreadState::self()->clearCallingWorkSource(); + int64_t previousWorkSource = IPCThreadState::self()->setCallingWorkSourceUid(100); + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + EXPECT_EQ(100, reply.readInt32()); + EXPECT_EQ(-1, previousWorkSource); + EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceSetWithoutPropagation) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setCallingWorkSourceUidWithoutPropagation(100); + EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); + + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + EXPECT_EQ(-1, reply.readInt32()); + EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceCleared) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setCallingWorkSourceUid(100); + int64_t token = IPCThreadState::self()->clearCallingWorkSource(); + int32_t previousWorkSource = (int32_t)token; + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + + EXPECT_EQ(-1, reply.readInt32()); + EXPECT_EQ(100, previousWorkSource); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceRestored) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setCallingWorkSourceUid(100); + int64_t token = IPCThreadState::self()->clearCallingWorkSource(); + IPCThreadState::self()->restoreCallingWorkSource(token); + + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + + EXPECT_EQ(100, reply.readInt32()); + EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, PropagateFlagSet) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->clearPropagateWorkSource(); + IPCThreadState::self()->setCallingWorkSourceUid(100); + EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); +} + +TEST_F(BinderLibTest, PropagateFlagCleared) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setCallingWorkSourceUid(100); + IPCThreadState::self()->clearPropagateWorkSource(); + EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); +} + +TEST_F(BinderLibTest, PropagateFlagRestored) +{ + status_t ret; + Parcel data, reply; + + int token = IPCThreadState::self()->setCallingWorkSourceUid(100); + IPCThreadState::self()->restoreCallingWorkSource(token); + + EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); +} + +TEST_F(BinderLibTest, WorkSourcePropagatedForAllFollowingBinderCalls) +{ + IPCThreadState::self()->setCallingWorkSourceUid(100); + + Parcel data, reply; + status_t ret; + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + + Parcel data2, reply2; + status_t ret2; + data2.writeInterfaceToken(binderLibTestServiceName); + ret2 = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data2, &reply2); + EXPECT_EQ(100, reply2.readInt32()); + EXPECT_EQ(NO_ERROR, ret2); +} + +TEST_F(BinderLibTest, VectorSent) { + Parcel data, reply; + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + + std::vector<uint64_t> const testValue = { std::numeric_limits<uint64_t>::max(), 0, 200 }; + data.writeUint64Vector(testValue); + + status_t ret = server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply); + EXPECT_EQ(NO_ERROR, ret); + std::vector<uint64_t> readValue; + ret = reply.readUint64Vector(&readValue); + EXPECT_EQ(readValue, testValue); +} + class BinderLibTestService : public BBinder { public: - BinderLibTestService(int32_t id) + explicit BinderLibTestService(int32_t id) : m_id(id) , m_nextServerId(id + 1) , m_serverStartRequested(false) - , m_callback(NULL) + , m_callback(nullptr) { - pthread_mutex_init(&m_serverWaitMutex, NULL); - pthread_cond_init(&m_serverWaitCond, NULL); + pthread_mutex_init(&m_serverWaitMutex, nullptr); + pthread_cond_init(&m_serverWaitCond, nullptr); } ~BinderLibTestService() { @@ -920,11 +1094,11 @@ } void processPendingCall() { - if (m_callback != NULL) { + if (m_callback != nullptr) { Parcel data; data.writeInt32(NO_ERROR); m_callback->transact(BINDER_LIB_TEST_CALL_BACK, data, nullptr, TF_ONE_WAY); - m_callback = NULL; + m_callback = nullptr; } } @@ -943,7 +1117,7 @@ sp<IBinder> binder; id = data.readInt32(); binder = data.readStrongBinder(); - if (binder == NULL) { + if (binder == nullptr) { return BAD_VALUE; } @@ -993,7 +1167,7 @@ } else { reply->writeStrongBinder(m_serverStarted); reply->writeInt32(serverid); - m_serverStarted = NULL; + m_serverStarted = nullptr; ret = NO_ERROR; } } else if (ret >= 0) { @@ -1008,7 +1182,7 @@ case BINDER_LIB_TEST_DELAYED_CALL_BACK: { // Note: this transaction is only designed for use with a // poll() server. See comments around epoll_wait(). - if (m_callback != NULL) { + if (m_callback != nullptr) { // A callback was already pending; this means that // we received a second call while still processing // the first one. Fail the test. @@ -1016,7 +1190,7 @@ Parcel data2; data2.writeInt32(UNKNOWN_ERROR); - callback->transact(BINDER_LIB_TEST_CALL_BACK, data2, NULL, TF_ONE_WAY); + callback->transact(BINDER_LIB_TEST_CALL_BACK, data2, nullptr, TF_ONE_WAY); } else { m_callback = data.readStrongBinder(); int32_t delayUs = data.readInt32(); @@ -1045,7 +1219,7 @@ Parcel data2, reply2; sp<IBinder> binder; binder = data.readStrongBinder(); - if (binder == NULL) { + if (binder == nullptr) { return BAD_VALUE; } data2.writeInt32(NO_ERROR); @@ -1068,7 +1242,7 @@ reply->writeInt32(count); for (int i = 0; i < count; i++) { binder = data.readStrongBinder(); - if (binder == NULL) { + if (binder == nullptr) { return BAD_VALUE; } indirect_code = data.readInt32(); @@ -1101,11 +1275,11 @@ sp<IBinder> callback; target = data.readStrongBinder(); - if (target == NULL) { + if (target == nullptr) { return BAD_VALUE; } callback = data.readStrongBinder(); - if (callback == NULL) { + if (callback == nullptr) { return BAD_VALUE; } ret = target->linkToDeath(testDeathRecipient); @@ -1130,7 +1304,7 @@ return ret; } buf = data.readInplace(size); - if (buf == NULL) { + if (buf == nullptr) { return BAD_VALUE; } ret = write(fd, buf, size); @@ -1138,6 +1312,28 @@ return UNKNOWN_ERROR; return NO_ERROR; } + case BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION: { + int ret; + int32_t size; + const void *buf; + android::base::unique_fd fd; + + ret = data.readUniqueParcelFileDescriptor(&fd); + if (ret != NO_ERROR) { + return ret; + } + ret = data.readInt32(&size); + if (ret != NO_ERROR) { + return ret; + } + buf = data.readInplace(size); + if (buf == nullptr) { + return BAD_VALUE; + } + ret = write(fd.get(), buf, size); + if (ret != size) return UNKNOWN_ERROR; + return NO_ERROR; + } case BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION: { int ret; wp<IBinder> weak; @@ -1147,7 +1343,7 @@ sp<IBinder> server = sm->getService(binderLibTestServiceName); weak = data.readWeakBinder(); - if (weak == NULL) { + if (weak == nullptr) { return BAD_VALUE; } strong = weak.promote(); @@ -1156,7 +1352,7 @@ if (ret != NO_ERROR) exit(EXIT_FAILURE); - if (strong == NULL) { + if (strong == nullptr) { reply->setError(1); } return NO_ERROR; @@ -1165,7 +1361,7 @@ alarm(10); return NO_ERROR; case BINDER_LIB_TEST_EXIT_TRANSACTION: - while (wait(NULL) != -1 || errno != ECHILD) + while (wait(nullptr) != -1 || errno != ECHILD) ; exit(EXIT_SUCCESS); case BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION: { @@ -1178,6 +1374,19 @@ } return NO_ERROR; } + case BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION: { + data.enforceInterface(binderLibTestServiceName); + reply->writeInt32(IPCThreadState::self()->getCallingWorkSourceUid()); + return NO_ERROR; + } + case BINDER_LIB_TEST_ECHO_VECTOR: { + std::vector<uint64_t> vector; + auto err = data.readUint64Vector(&vector); + if (err != NO_ERROR) + return err; + reply->writeUint64Vector(vector); + return NO_ERROR; + } default: return UNKNOWN_TRANSACTION; }; @@ -1237,7 +1446,7 @@ } IPCThreadState::self()->flushCommands(); // flush BC_ENTER_LOOPER - epoll_fd = epoll_create1(0); + epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (epoll_fd == -1) { return 1; } @@ -1300,4 +1509,3 @@ ProcessState::self()->startThreadPool(); return RUN_ALL_TESTS(); } -
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp index 6a16e24..3b1db27 100644 --- a/libs/binder/tests/binderSafeInterfaceTest.cpp +++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -229,6 +229,7 @@ IncrementUint32, IncrementInt64, IncrementUint64, + IncrementFloat, IncrementTwo, Last, }; @@ -259,6 +260,7 @@ virtual status_t increment(uint32_t a, uint32_t* aPlusOne) const = 0; virtual status_t increment(int64_t a, int64_t* aPlusOne) const = 0; virtual status_t increment(uint64_t a, uint64_t* aPlusOne) const = 0; + virtual status_t increment(float a, float* aPlusOne) const = 0; // This tests that input/output parameter interleaving works correctly virtual status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, @@ -353,6 +355,11 @@ using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const; return callRemote<Signature>(Tag::IncrementUint64, a, aPlusOne); } + status_t increment(float a, float* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + using Signature = status_t (ISafeInterfaceTest::*)(float, float*) const; + return callRemote<Signature>(Tag::IncrementFloat, a, aPlusOne); + } status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); using Signature = @@ -474,6 +481,11 @@ *aPlusOne = a + 1; return NO_ERROR; } + status_t increment(float a, float* aPlusOne) const override { + ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); + *aPlusOne = a + 1.0f; + return NO_ERROR; + } status_t increment(int32_t a, int32_t* aPlusOne, int32_t b, int32_t* bPlusOne) const override { ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__); *aPlusOne = a + 1; @@ -555,6 +567,10 @@ using Signature = status_t (ISafeInterfaceTest::*)(uint64_t, uint64_t*) const; return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); } + case ISafeInterfaceTest::Tag::IncrementFloat: { + using Signature = status_t (ISafeInterfaceTest::*)(float, float*) const; + return callLocal<Signature>(data, reply, &ISafeInterfaceTest::increment); + } case ISafeInterfaceTest::Tag::IncrementTwo: { using Signature = status_t (ISafeInterfaceTest::*)(int32_t, int32_t*, int32_t, int32_t*) const; @@ -804,6 +820,14 @@ ASSERT_EQ(a + 1, aPlusOne); } +TEST_F(SafeInterfaceTest, TestIncrementFloat) { + const float a = 1.0f; + float aPlusOne = 0.0f; + status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(NO_ERROR, result); + ASSERT_EQ(a + 1.0f, aPlusOne); +} + TEST_F(SafeInterfaceTest, TestIncrementTwo) { const int32_t a = 1; int32_t aPlusOne = 0;
diff --git a/libs/binder/tests/binderTextOutputTest.cpp b/libs/binder/tests/binderTextOutputTest.cpp index f6dd22d..ce99f59 100644 --- a/libs/binder/tests/binderTextOutputTest.cpp +++ b/libs/binder/tests/binderTextOutputTest.cpp
@@ -28,15 +28,14 @@ #include <binder/TextOutput.h> #include <binder/Debug.h> -static void CheckMessage(const CapturedStderr& cap, +static void CheckMessage(CapturedStderr& cap, const char* expected, bool singleline) { - std::string output; - ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET)); - android::base::ReadFdToString(cap.fd(), &output); + cap.Stop(); + std::string output = cap.str(); if (singleline) output.erase(std::remove(output.begin(), output.end(), '\n')); - ASSERT_STREQ(output.c_str(), expected); + ASSERT_EQ(output, expected); } #define CHECK_LOG_(input, expect, singleline) \ @@ -60,28 +59,22 @@ TEST(TextOutput, HandlesStdEndl) { CapturedStderr cap; android::aerr << "foobar" << std::endl; - std::string output; - ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET)); - android::base::ReadFdToString(cap.fd(), &output); - ASSERT_STREQ(output.c_str(), "foobar\n"); + cap.Stop(); + ASSERT_EQ(cap.str(), "foobar\n"); } TEST(TextOutput, HandlesCEndl) { CapturedStderr cap; android::aerr << "foobar" << "\n"; - std::string output; - ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET)); - android::base::ReadFdToString(cap.fd(), &output); - ASSERT_STREQ(output.c_str(), "foobar\n"); + cap.Stop(); + ASSERT_EQ(cap.str(), "foobar\n"); } TEST(TextOutput, HandlesAndroidEndl) { CapturedStderr cap; android::aerr << "foobar" << android::endl; - std::string output; - ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET)); - android::base::ReadFdToString(cap.fd(), &output); - ASSERT_STREQ(output.c_str(), "foobar\n"); + cap.Stop(); + ASSERT_EQ(cap.str(), "foobar\n"); } TEST(TextOutput, HandleEmptyString) {
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp index bf41e0b..b790997 100644 --- a/libs/binder/tests/binderThroughputTest.cpp +++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -380,7 +380,7 @@ // Caller specified the max latency in microseconds. // No need to run training round in this case. if (atoi(argv[i+1]) > 0) { - max_time_bucket = strtoull(argv[i+1], (char **)NULL, 10) * 1000; + max_time_bucket = strtoull(argv[i+1], (char **)nullptr, 10) * 1000; time_per_bucket = max_time_bucket / num_buckets; i++; } else {
diff --git a/libs/binder/tests/binderValueTypeTest.cpp b/libs/binder/tests/binderValueTypeTest.cpp index c8f4697..f8922b0 100644 --- a/libs/binder/tests/binderValueTypeTest.cpp +++ b/libs/binder/tests/binderValueTypeTest.cpp
@@ -22,7 +22,6 @@ #include <vector> #include "android-base/file.h" -#include "android-base/test_utils.h" #include <gtest/gtest.h> #include <binder/Parcel.h> @@ -76,13 +75,13 @@ VALUE_TYPE_TEST(bool, Boolean, true) VALUE_TYPE_TEST(int32_t, Int, 31337) -VALUE_TYPE_TEST(int64_t, Long, 13370133701337l) +VALUE_TYPE_TEST(int64_t, Long, 13370133701337L) VALUE_TYPE_TEST(double, Double, 3.14159265358979323846) VALUE_TYPE_TEST(String16, String, String16("Lovely")) VALUE_TYPE_VECTOR_TEST(bool, Boolean, true) VALUE_TYPE_VECTOR_TEST(int32_t, Int, 31337) -VALUE_TYPE_VECTOR_TEST(int64_t, Long, 13370133701337l) +VALUE_TYPE_VECTOR_TEST(int64_t, Long, 13370133701337L) VALUE_TYPE_VECTOR_TEST(double, Double, 3.14159265358979323846) VALUE_TYPE_VECTOR_TEST(String16, String, String16("Lovely"))
diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp index 13f03b1..ec9534a 100644 --- a/libs/binder/tests/schd-dbg.cpp +++ b/libs/binder/tests/schd-dbg.cpp
@@ -218,7 +218,7 @@ uint64_t m_total_time = 0; uint64_t m_miss = 0; bool tracing; - Results(bool _tracing) : tracing(_tracing) { + explicit Results(bool _tracing) : tracing(_tracing) { } inline bool miss_deadline(uint64_t nano) { return nano > deadline_us * 1000; @@ -295,7 +295,7 @@ no_inherent += reply.readInt32(); no_sync += reply.readInt32(); - return 0; + return nullptr; } // create a fifo thread to transact and wait it to finished
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp new file mode 100644 index 0000000..512b069 --- /dev/null +++ b/libs/binderthreadstate/Android.bp
@@ -0,0 +1,46 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library { + name: "libbinderthreadstate", + recovery_available: true, + vendor_available: false, + vndk: { + enabled: true, + support_system_process: true, + }, + srcs: [ + "IPCThreadStateBase.cpp", + ], + + header_libs: [ + "libbase_headers", + "libutils_headers", + ], + + shared_libs: [ + "liblog", + ], + + export_include_dirs: ["include"], + + sanitize: { + misc_undefined: ["integer"], + }, + + cflags: [ + "-Wall", + "-Werror", + ], +}
diff --git a/libs/binderthreadstate/IPCThreadStateBase.cpp b/libs/binderthreadstate/IPCThreadStateBase.cpp new file mode 100644 index 0000000..fede151 --- /dev/null +++ b/libs/binderthreadstate/IPCThreadStateBase.cpp
@@ -0,0 +1,89 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "IPCThreadStateBase" + +#include <binderthreadstate/IPCThreadStateBase.h> +#include <android-base/macros.h> + +#include <utils/Log.h> + +#include <errno.h> +#include <inttypes.h> +#include <pthread.h> + +namespace android { + +static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; +static bool gHaveTLS = false; +static pthread_key_t gTLS = 0; + +IPCThreadStateBase::IPCThreadStateBase() { + pthread_setspecific(gTLS, this); +} + +IPCThreadStateBase* IPCThreadStateBase::self() +{ + if (gHaveTLS) { +restart: + const pthread_key_t k = gTLS; + IPCThreadStateBase* st = (IPCThreadStateBase*)pthread_getspecific(k); + if (st) return st; + return new IPCThreadStateBase; + } + + pthread_mutex_lock(&gTLSMutex); + if (!gHaveTLS) { + int key_create_value = pthread_key_create(&gTLS, threadDestructor); + if (key_create_value != 0) { + pthread_mutex_unlock(&gTLSMutex); + ALOGW("IPCThreadStateBase::self() unable to create TLS key, expect a crash: %s\n", + strerror(key_create_value)); + return nullptr; + } + gHaveTLS = true; + } + pthread_mutex_unlock(&gTLSMutex); + goto restart; +} + +void IPCThreadStateBase::pushCurrentState(CallState callState) { + mCallStateStack.emplace(callState); +} + +IPCThreadStateBase::CallState IPCThreadStateBase::popCurrentState() { + ALOG_ASSERT(mCallStateStack.size > 0); + CallState val = mCallStateStack.top(); + mCallStateStack.pop(); + return val; +} + +IPCThreadStateBase::CallState IPCThreadStateBase::getCurrentBinderCallState() { + if (mCallStateStack.size() > 0) { + return mCallStateStack.top(); + } + return CallState::NONE; +} + +void IPCThreadStateBase::threadDestructor(void *st) +{ + IPCThreadStateBase* const self = static_cast<IPCThreadStateBase*>(st); + if (self) { + delete self; + } +} + +}; // namespace android
diff --git a/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h b/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h new file mode 100644 index 0000000..6fdcc84 --- /dev/null +++ b/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h
@@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H +#define BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H + +#include <stack> +namespace android { + +class IPCThreadStateBase { +public: + enum CallState { + HWBINDER, + BINDER, + NONE, + }; + static IPCThreadStateBase* self(); + void pushCurrentState(CallState callState); + CallState popCurrentState(); + CallState getCurrentBinderCallState(); + +private: + IPCThreadStateBase(); + static void threadDestructor(void *st); + + std::stack<CallState> mCallStateStack; +}; + +}; // namespace android + +#endif // BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp new file mode 100644 index 0000000..28cb138 --- /dev/null +++ b/libs/cputimeinstate/Android.bp
@@ -0,0 +1,30 @@ +cc_library { + name: "libtimeinstate", + srcs: ["cputimeinstate.cpp"], + shared_libs: [ + "libbase", + "libbpf", + "libbpf_android", + "liblog", + "libnetdutils" + ], + cflags: [ + "-Werror", + "-Wall", + "-Wextra", + ], +} + +cc_test { + name: "libtimeinstate_test", + srcs: ["testtimeinstate.cpp"], + shared_libs: [ + "libtimeinstate", + ], + cflags: [ + "-Werror", + "-Wall", + "-Wextra", + ], +} +
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp new file mode 100644 index 0000000..5fd4a95 --- /dev/null +++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -0,0 +1,244 @@ +/* + * 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 LOG_TAG "libtimeinstate" + +#include "cputimeinstate.h" + +#include <dirent.h> +#include <errno.h> +#include <inttypes.h> + +#include <mutex> +#include <set> +#include <string> +#include <unordered_map> +#include <vector> + +#include <android-base/file.h> +#include <android-base/parseint.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <android-base/unique_fd.h> +#include <bpf/BpfMap.h> +#include <libbpf.h> +#include <log/log.h> + +#define BPF_FS_PATH "/sys/fs/bpf/" + +using android::base::StringPrintf; +using android::base::unique_fd; + +namespace android { +namespace bpf { + +struct time_key_t { + uint32_t uid; + uint32_t freq; +}; + +struct val_t { + uint64_t ar[100]; +}; + +static std::mutex gInitializedMutex; +static bool gInitialized = false; +static uint32_t gNPolicies = 0; +static std::vector<std::vector<uint32_t>> gPolicyFreqs; +static std::vector<std::vector<uint32_t>> gPolicyCpus; +static std::set<uint32_t> gAllFreqs; +static unique_fd gMapFd; + +static bool readNumbersFromFile(const std::string &path, std::vector<uint32_t> *out) { + std::string data; + + if (!android::base::ReadFileToString(path, &data)) return false; + + auto strings = android::base::Split(data, " \n"); + for (const auto &s : strings) { + if (s.empty()) continue; + uint32_t n; + if (!android::base::ParseUint(s, &n)) return false; + out->emplace_back(n); + } + return true; +} + +static int isPolicyFile(const struct dirent *d) { + return android::base::StartsWith(d->d_name, "policy"); +} + +static int comparePolicyFiles(const struct dirent **d1, const struct dirent **d2) { + uint32_t policyN1, policyN2; + if (sscanf((*d1)->d_name, "policy%" SCNu32 "", &policyN1) != 1 || + sscanf((*d2)->d_name, "policy%" SCNu32 "", &policyN2) != 1) + return 0; + return policyN1 - policyN2; +} + +static bool initGlobals() { + std::lock_guard<std::mutex> guard(gInitializedMutex); + if (gInitialized) return true; + + struct dirent **dirlist; + const char basepath[] = "/sys/devices/system/cpu/cpufreq"; + int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles); + if (ret == -1) return false; + gNPolicies = ret; + + std::vector<std::string> policyFileNames; + for (uint32_t i = 0; i < gNPolicies; ++i) { + policyFileNames.emplace_back(dirlist[i]->d_name); + free(dirlist[i]); + } + free(dirlist); + + for (const auto &policy : policyFileNames) { + std::vector<uint32_t> freqs; + for (const auto &name : {"available", "boost"}) { + std::string path = + StringPrintf("%s/%s/scaling_%s_frequencies", basepath, policy.c_str(), name); + if (!readNumbersFromFile(path, &freqs)) return false; + } + std::sort(freqs.begin(), freqs.end()); + gPolicyFreqs.emplace_back(freqs); + + for (auto freq : freqs) gAllFreqs.insert(freq); + + std::vector<uint32_t> cpus; + std::string path = StringPrintf("%s/%s/%s", basepath, policy.c_str(), "related_cpus"); + if (!readNumbersFromFile(path, &cpus)) return false; + gPolicyCpus.emplace_back(cpus); + } + + gMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times")}; + if (gMapFd < 0) return false; + + gInitialized = true; + return true; +} + +static bool attachTracepointProgram(const std::string &eventType, const std::string &eventName) { + std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s", + eventType.c_str(), eventName.c_str()); + int prog_fd = bpf_obj_get(path.c_str()); + if (prog_fd < 0) return false; + return bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) >= 0; +} + +// Start tracking and aggregating data to be reported by getUidCpuFreqTimes and getUidsCpuFreqTimes. +// Returns true on success, false otherwise. +// Tracking is active only once a live process has successfully called this function; if the calling +// process dies then it must be called again to resume tracking. +// This function should *not* be called while tracking is already active; doing so is unnecessary +// and can lead to accounting errors. +bool startTrackingUidCpuFreqTimes() { + return attachTracepointProgram("sched", "sched_switch") && + attachTracepointProgram("power", "cpu_frequency"); +} + +// Retrieve the times in ns that uid spent running at each CPU frequency and store in freqTimes. +// Returns false on error. Otherwise, returns true and populates freqTimes with a vector of vectors +// using the format: +// [[t0_0, t0_1, ...], +// [t1_0, t1_1, ...], ...] +// where ti_j is the ns that uid spent running on the ith cluster at that cluster's jth lowest freq. +bool getUidCpuFreqTimes(uint32_t uid, std::vector<std::vector<uint64_t>> *freqTimes) { + if (!gInitialized && !initGlobals()) return false; + time_key_t key = {.uid = uid, .freq = 0}; + + freqTimes->clear(); + freqTimes->resize(gNPolicies); + std::vector<uint32_t> idxs(gNPolicies, 0); + + val_t value; + for (uint32_t freq : gAllFreqs) { + key.freq = freq; + int ret = findMapEntry(gMapFd, &key, &value); + if (ret) { + if (errno == ENOENT) + memset(&value.ar, 0, sizeof(value.ar)); + else + return false; + } + for (uint32_t i = 0; i < gNPolicies; ++i) { + if (idxs[i] == gPolicyFreqs[i].size() || freq != gPolicyFreqs[i][idxs[i]]) continue; + uint64_t time = 0; + for (uint32_t cpu : gPolicyCpus[i]) time += value.ar[cpu]; + idxs[i] += 1; + (*freqTimes)[i].emplace_back(time); + } + } + + return true; +} + +// Retrieve the times in ns that each uid spent running at each CPU freq and store in freqTimeMap. +// Returns false on error. Otherwise, returns true and populates freqTimeMap with a map from uids to +// vectors of vectors using the format: +// { uid0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...], +// uid1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... } +// where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq. +bool getUidsCpuFreqTimes( + std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *freqTimeMap) { + if (!gInitialized && !initGlobals()) return false; + + int fd = bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times"); + if (fd < 0) return false; + BpfMap<time_key_t, val_t> m(fd); + + std::vector<std::unordered_map<uint32_t, uint32_t>> policyFreqIdxs; + for (uint32_t i = 0; i < gNPolicies; ++i) { + std::unordered_map<uint32_t, uint32_t> freqIdxs; + for (size_t j = 0; j < gPolicyFreqs[i].size(); ++j) freqIdxs[gPolicyFreqs[i][j]] = j; + policyFreqIdxs.emplace_back(freqIdxs); + } + + auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val, + const BpfMap<time_key_t, val_t> &) { + if (freqTimeMap->find(key.uid) == freqTimeMap->end()) { + (*freqTimeMap)[key.uid].resize(gNPolicies); + for (uint32_t i = 0; i < gNPolicies; ++i) { + (*freqTimeMap)[key.uid][i].resize(gPolicyFreqs[i].size(), 0); + } + } + + for (size_t policy = 0; policy < gNPolicies; ++policy) { + for (const auto &cpu : gPolicyCpus[policy]) { + auto freqIdx = policyFreqIdxs[policy][key.freq]; + (*freqTimeMap)[key.uid][policy][freqIdx] += val.ar[cpu]; + } + } + return android::netdutils::status::ok; + }; + return isOk(m.iterateWithValue(fn)); +} + +// Clear all time in state data for a given uid. Returns false on error, true otherwise. +bool clearUidCpuFreqTimes(uint32_t uid) { + if (!gInitialized && !initGlobals()) return false; + time_key_t key = {.uid = uid, .freq = 0}; + + std::vector<uint32_t> idxs(gNPolicies, 0); + for (auto freq : gAllFreqs) { + key.freq = freq; + if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false; + } + return true; +} + +} // namespace bpf +} // namespace android
diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h new file mode 100644 index 0000000..9f6103e --- /dev/null +++ b/libs/cputimeinstate/cputimeinstate.h
@@ -0,0 +1,31 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <unordered_map> +#include <vector> + +namespace android { +namespace bpf { + +bool startTrackingUidCpuFreqTimes(); +bool getUidCpuFreqTimes(unsigned int uid, std::vector<std::vector<uint64_t>> *freqTimes); +bool getUidsCpuFreqTimes(std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *tisMap); +bool clearUidCpuFreqTimes(unsigned int uid); + +} // namespace bpf +} // namespace android
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp new file mode 100644 index 0000000..9837865 --- /dev/null +++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -0,0 +1,58 @@ + +#include <unordered_map> +#include <vector> + +#include <gtest/gtest.h> + +#include <cputimeinstate.h> + +namespace android { +namespace bpf { + +using std::vector; + +TEST(TimeInStateTest, SingleUid) { + vector<vector<uint64_t>> times; + ASSERT_TRUE(getUidCpuFreqTimes(0, ×)); + EXPECT_FALSE(times.empty()); +} + +TEST(TimeInStateTest, AllUid) { + vector<size_t> sizes; + std::unordered_map<uint32_t, vector<vector<uint64_t>>> map; + ASSERT_TRUE(getUidsCpuFreqTimes(&map)); + + ASSERT_FALSE(map.empty()); + + auto firstEntry = map.begin()->second; + for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size()); + + for (const auto &vec : map) { + ASSERT_EQ(vec.second.size(), sizes.size()); + for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]); + } +} + +TEST(TimeInStateTest, RemoveUid) { + vector<vector<uint64_t>> times, times2; + ASSERT_TRUE(getUidCpuFreqTimes(0, ×)); + ASSERT_FALSE(times.empty()); + + uint64_t sum = 0; + for (size_t i = 0; i < times.size(); ++i) { + for (auto x : times[i]) sum += x; + } + ASSERT_GT(sum, (uint64_t)0); + + ASSERT_TRUE(clearUidCpuFreqTimes(0)); + + ASSERT_TRUE(getUidCpuFreqTimes(0, ×2)); + ASSERT_EQ(times2.size(), times.size()); + for (size_t i = 0; i < times.size(); ++i) { + ASSERT_EQ(times2[i].size(), times[i].size()); + for (size_t j = 0; j < times[i].size(); ++j) ASSERT_LE(times2[i][j], times[i][j]); + } +} + +} // namespace bpf +} // namespace android
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 8b2f842..40f6b43 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp
@@ -31,11 +31,14 @@ "/system/bin/mediaextractor", // media.extractor "/system/bin/mediametrics", // media.metrics "/system/bin/mediaserver", + "/system/bin/netd", + "/system/bin/vold", "/system/bin/sdcard", "/system/bin/statsd", "/system/bin/surfaceflinger", "/system/bin/vehicle_network_service", "/vendor/bin/hw/android.hardware.media.omx@1.0-service", // media.codec + "/apex/com.android.media.swcodec/bin/mediaswcodec", // media.swcodec NULL, }; @@ -46,10 +49,16 @@ "android.hardware.bluetooth@1.0::IBluetoothHci", "android.hardware.camera.provider@2.4::ICameraProvider", "android.hardware.drm@1.0::IDrmFactory", + "android.hardware.graphics.allocator@2.0::IAllocator", "android.hardware.graphics.composer@2.1::IComposer", + "android.hardware.health@2.0::IHealth", + "android.hardware.media.c2@1.0::IComponentStore", "android.hardware.media.omx@1.0::IOmx", "android.hardware.media.omx@1.0::IOmxStore", + "android.hardware.power@1.3::IPower", + "android.hardware.power.stats@1.0::IPowerStats", "android.hardware.sensors@1.0::ISensors", + "android.hardware.thermal@2.0::IThermal", "android.hardware.vr@1.0::IVr", NULL, }; @@ -102,13 +111,15 @@ } bool IsZygote(int pid) { - static const std::string kZygotePrefix = "zygote"; - std::string cmdline; if (!android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &cmdline)) { return true; } - return (cmdline.find(kZygotePrefix) == 0); + // cmdline has embedded nulls; only consider argv[0]. + cmdline = std::string(cmdline.c_str()); + + return cmdline == "zygote" || cmdline == "zygote64" || cmdline == "usap32" || + cmdline == "usap64"; }
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp index 4da30e9..56521bf 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp
@@ -16,13 +16,20 @@ name: "libgraphicsenv", srcs: [ + "GpuStatsInfo.cpp", "GraphicsEnv.cpp", + "IGpuService.cpp" ], cflags: ["-Wall", "-Werror"], shared_libs: [ + "libbase", + "libbinder", + "libcutils", + "libdl_android", "liblog", + "libutils", ], export_include_dirs: ["include"],
diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp new file mode 100644 index 0000000..4a801be --- /dev/null +++ b/libs/graphicsenv/GpuStatsInfo.cpp
@@ -0,0 +1,126 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <inttypes.h> + +#include <android-base/stringprintf.h> +#include <binder/Parcel.h> +#include <graphicsenv/GpuStatsInfo.h> + +namespace android { + +using base::StringAppendF; + +status_t GpuStatsGlobalInfo::writeToParcel(Parcel* parcel) const { + status_t status; + if ((status = parcel->writeUtf8AsUtf16(driverPackageName)) != OK) return status; + if ((status = parcel->writeUtf8AsUtf16(driverVersionName)) != OK) return status; + if ((status = parcel->writeUint64(driverVersionCode)) != OK) return status; + if ((status = parcel->writeInt64(driverBuildTime)) != OK) return status; + if ((status = parcel->writeInt32(glLoadingCount)) != OK) return status; + if ((status = parcel->writeInt32(glLoadingFailureCount)) != OK) return status; + if ((status = parcel->writeInt32(vkLoadingCount)) != OK) return status; + if ((status = parcel->writeInt32(vkLoadingFailureCount)) != OK) return status; + if ((status = parcel->writeInt32(vulkanVersion)) != OK) return status; + if ((status = parcel->writeInt32(cpuVulkanVersion)) != OK) return status; + if ((status = parcel->writeInt32(glesVersion)) != OK) return status; + if ((status = parcel->writeInt32(angleLoadingCount)) != OK) return status; + if ((status = parcel->writeInt32(angleLoadingFailureCount)) != OK) return status; + return OK; +} + +status_t GpuStatsGlobalInfo::readFromParcel(const Parcel* parcel) { + status_t status; + if ((status = parcel->readUtf8FromUtf16(&driverPackageName)) != OK) return status; + if ((status = parcel->readUtf8FromUtf16(&driverVersionName)) != OK) return status; + if ((status = parcel->readUint64(&driverVersionCode)) != OK) return status; + if ((status = parcel->readInt64(&driverBuildTime)) != OK) return status; + if ((status = parcel->readInt32(&glLoadingCount)) != OK) return status; + if ((status = parcel->readInt32(&glLoadingFailureCount)) != OK) return status; + if ((status = parcel->readInt32(&vkLoadingCount)) != OK) return status; + if ((status = parcel->readInt32(&vkLoadingFailureCount)) != OK) return status; + if ((status = parcel->readInt32(&vulkanVersion)) != OK) return status; + if ((status = parcel->readInt32(&cpuVulkanVersion)) != OK) return status; + if ((status = parcel->readInt32(&glesVersion)) != OK) return status; + if ((status = parcel->readInt32(&angleLoadingCount)) != OK) return status; + if ((status = parcel->readInt32(&angleLoadingFailureCount)) != OK) return status; + return OK; +} + +std::string GpuStatsGlobalInfo::toString() const { + std::string result; + StringAppendF(&result, "driverPackageName = %s\n", driverPackageName.c_str()); + StringAppendF(&result, "driverVersionName = %s\n", driverVersionName.c_str()); + StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode); + StringAppendF(&result, "driverBuildTime = %" PRId64 "\n", driverBuildTime); + StringAppendF(&result, "glLoadingCount = %d\n", glLoadingCount); + StringAppendF(&result, "glLoadingFailureCount = %d\n", glLoadingFailureCount); + StringAppendF(&result, "angleLoadingCount = %d\n", angleLoadingCount); + StringAppendF(&result, "angleLoadingFailureCount = %d\n", angleLoadingFailureCount); + StringAppendF(&result, "vkLoadingCount = %d\n", vkLoadingCount); + StringAppendF(&result, "vkLoadingFailureCount = %d\n", vkLoadingFailureCount); + StringAppendF(&result, "vulkanVersion = %d\n", vulkanVersion); + StringAppendF(&result, "cpuVulkanVersion = %d\n", cpuVulkanVersion); + StringAppendF(&result, "glesVersion = %d\n", glesVersion); + return result; +} + +status_t GpuStatsAppInfo::writeToParcel(Parcel* parcel) const { + status_t status; + if ((status = parcel->writeUtf8AsUtf16(appPackageName)) != OK) return status; + if ((status = parcel->writeUint64(driverVersionCode)) != OK) return status; + if ((status = parcel->writeInt64Vector(glDriverLoadingTime)) != OK) return status; + if ((status = parcel->writeInt64Vector(vkDriverLoadingTime)) != OK) return status; + if ((status = parcel->writeInt64Vector(angleDriverLoadingTime)) != OK) return status; + if ((status = parcel->writeBool(cpuVulkanInUse)) != OK) return status; + return OK; +} + +status_t GpuStatsAppInfo::readFromParcel(const Parcel* parcel) { + status_t status; + if ((status = parcel->readUtf8FromUtf16(&appPackageName)) != OK) return status; + if ((status = parcel->readUint64(&driverVersionCode)) != OK) return status; + if ((status = parcel->readInt64Vector(&glDriverLoadingTime)) != OK) return status; + if ((status = parcel->readInt64Vector(&vkDriverLoadingTime)) != OK) return status; + if ((status = parcel->readInt64Vector(&angleDriverLoadingTime)) != OK) return status; + if ((status = parcel->readBool(&cpuVulkanInUse)) != OK) return status; + return OK; +} + +std::string GpuStatsAppInfo::toString() const { + std::string result; + StringAppendF(&result, "appPackageName = %s\n", appPackageName.c_str()); + StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode); + StringAppendF(&result, "cpuVulkanInUse = %d\n", cpuVulkanInUse); + result.append("glDriverLoadingTime:"); + for (int32_t loadingTime : glDriverLoadingTime) { + StringAppendF(&result, " %d", loadingTime); + } + result.append("\n"); + result.append("angleDriverLoadingTime:"); + for (int32_t loadingTime : angleDriverLoadingTime) { + StringAppendF(&result, " %d", loadingTime); + } + result.append("\n"); + result.append("vkDriverLoadingTime:"); + for (int32_t loadingTime : vkDriverLoadingTime) { + StringAppendF(&result, " %d", loadingTime); + } + result.append("\n"); + return result; +} + +} // namespace android
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index 961f101..bb9e263 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -14,96 +14,626 @@ * limitations under the License. */ +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + //#define LOG_NDEBUG 1 #define LOG_TAG "GraphicsEnv" + #include <graphicsenv/GraphicsEnv.h> -#include <mutex> +#include <dlfcn.h> +#include <unistd.h> +#include <android-base/file.h> +#include <android-base/properties.h> +#include <android-base/strings.h> #include <android/dlext.h> +#include <binder/IServiceManager.h> +#include <cutils/properties.h> +#include <graphicsenv/IGpuService.h> #include <log/log.h> +#include <sys/prctl.h> +#include <utils/Trace.h> + +#include <memory> +#include <string> +#include <thread> // TODO(b/37049319) Get this from a header once one exists extern "C" { - android_namespace_t* android_get_exported_namespace(const char*); - android_namespace_t* android_create_namespace(const char* name, - const char* ld_library_path, - const char* default_library_path, - uint64_t type, - const char* permitted_when_isolated_path, - android_namespace_t* parent); +android_namespace_t* android_get_exported_namespace(const char*); +android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path, + const char* default_library_path, uint64_t type, + const char* permitted_when_isolated_path, + android_namespace_t* parent); +bool android_link_namespaces(android_namespace_t* from, android_namespace_t* to, + const char* shared_libs_sonames); - enum { - ANDROID_NAMESPACE_TYPE_ISOLATED = 1, - ANDROID_NAMESPACE_TYPE_SHARED = 2, - }; +enum { + ANDROID_NAMESPACE_TYPE_ISOLATED = 1, + ANDROID_NAMESPACE_TYPE_SHARED = 2, +}; } +// TODO(ianelliott@): Get the following from an ANGLE header: +#define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting +// Version-2 API: +typedef bool (*fpANGLEGetFeatureSupportUtilAPIVersion)(unsigned int* versionToUse); +typedef bool (*fpANGLEAndroidParseRulesString)(const char* rulesString, void** rulesHandle, + int* rulesVersion); +typedef bool (*fpANGLEGetSystemInfo)(void** handle); +typedef bool (*fpANGLEAddDeviceInfoToSystemInfo)(const char* deviceMfr, const char* deviceModel, + void* handle); +typedef bool (*fpANGLEShouldBeUsedForApplication)(void* rulesHandle, int rulesVersion, + void* systemInfoHandle, const char* appName); +typedef bool (*fpANGLEFreeRulesHandle)(void* handle); +typedef bool (*fpANGLEFreeSystemInfoHandle)(void* handle); + namespace android { +enum NativeLibrary { + LLNDK = 0, + VNDKSP = 1, +}; + +static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/etc/llndk.libraries.txt", + "/etc/vndksp.libraries.txt"}; + +static std::string vndkVersionStr() { +#ifdef __BIONIC__ + std::string version = android::base::GetProperty("ro.vndk.version", ""); + if (version != "" && version != "current") { + return "." + version; + } +#endif + return ""; +} + +static void insertVndkVersionStr(std::string* fileName) { + LOG_ALWAYS_FATAL_IF(!fileName, "fileName should never be nullptr"); + size_t insertPos = fileName->find_last_of("."); + if (insertPos == std::string::npos) { + insertPos = fileName->length(); + } + fileName->insert(insertPos, vndkVersionStr()); +} + +static bool readConfig(const std::string& configFile, std::vector<std::string>* soNames) { + // Read list of public native libraries from the config file. + std::string fileContent; + if (!base::ReadFileToString(configFile, &fileContent)) { + return false; + } + + std::vector<std::string> lines = base::Split(fileContent, "\n"); + + for (auto& line : lines) { + auto trimmedLine = base::Trim(line); + if (!trimmedLine.empty()) { + soNames->push_back(trimmedLine); + } + } + + return true; +} + +static const std::string getSystemNativeLibraries(NativeLibrary type) { + static const char* androidRootEnv = getenv("ANDROID_ROOT"); + static const std::string rootDir = androidRootEnv != nullptr ? androidRootEnv : "/system"; + + std::string nativeLibrariesSystemConfig = rootDir + kNativeLibrariesSystemConfigPath[type]; + + insertVndkVersionStr(&nativeLibrariesSystemConfig); + + std::vector<std::string> soNames; + if (!readConfig(nativeLibrariesSystemConfig, &soNames)) { + ALOGE("Failed to retrieve library names from %s", nativeLibrariesSystemConfig.c_str()); + return ""; + } + + return base::Join(soNames, ':'); +} + /*static*/ GraphicsEnv& GraphicsEnv::getInstance() { static GraphicsEnv env; return env; } -void GraphicsEnv::setDriverPath(const std::string path) { - if (!mDriverPath.empty()) { - ALOGV("ignoring attempt to change driver path from '%s' to '%s'", - mDriverPath.c_str(), path.c_str()); - return; +int GraphicsEnv::getCanLoadSystemLibraries() { + if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { + // Return an integer value since this crosses library boundaries + return 1; } - ALOGV("setting driver path to '%s'", path.c_str()); - mDriverPath = path; + return 0; } -void GraphicsEnv::setLayerPaths(android_namespace_t* appNamespace, const std::string layerPaths) { +void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path, + const std::string sphalLibraries) { + if (!mDriverPath.empty() || !mSphalLibraries.empty()) { + ALOGV("ignoring attempt to change driver path from '%s' to '%s' or change sphal libraries " + "from '%s' to '%s'", + mDriverPath.c_str(), path.c_str(), mSphalLibraries.c_str(), sphalLibraries.c_str()); + return; + } + ALOGV("setting driver path to '%s' and sphal libraries to '%s'", path.c_str(), + sphalLibraries.c_str()); + mDriverPath = path; + mSphalLibraries = sphalLibraries; +} + +void GraphicsEnv::hintActivityLaunch() { + ATRACE_CALL(); + + std::thread trySendGpuStatsThread([this]() { + // If there's already graphics driver preloaded in the process, just send + // the stats info to GpuStats directly through async binder. + std::lock_guard<std::mutex> lock(mStatsLock); + if (mGpuStats.glDriverToSend) { + mGpuStats.glDriverToSend = false; + sendGpuStatsLocked(GraphicsEnv::Api::API_GL, true, mGpuStats.glDriverLoadingTime); + } + if (mGpuStats.vkDriverToSend) { + mGpuStats.vkDriverToSend = false; + sendGpuStatsLocked(GraphicsEnv::Api::API_VK, true, mGpuStats.vkDriverLoadingTime); + } + }); + trySendGpuStatsThread.detach(); +} + +void GraphicsEnv::setGpuStats(const std::string& driverPackageName, + const std::string& driverVersionName, uint64_t driverVersionCode, + int64_t driverBuildTime, const std::string& appPackageName, + const int vulkanVersion) { + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mStatsLock); + ALOGV("setGpuStats:\n" + "\tdriverPackageName[%s]\n" + "\tdriverVersionName[%s]\n" + "\tdriverVersionCode[%" PRIu64 "]\n" + "\tdriverBuildTime[%" PRId64 "]\n" + "\tappPackageName[%s]\n" + "\tvulkanVersion[%d]\n", + driverPackageName.c_str(), driverVersionName.c_str(), driverVersionCode, driverBuildTime, + appPackageName.c_str(), vulkanVersion); + + mGpuStats.driverPackageName = driverPackageName; + mGpuStats.driverVersionName = driverVersionName; + mGpuStats.driverVersionCode = driverVersionCode; + mGpuStats.driverBuildTime = driverBuildTime; + mGpuStats.appPackageName = appPackageName; + mGpuStats.vulkanVersion = vulkanVersion; +} + +void GraphicsEnv::setDriverToLoad(GraphicsEnv::Driver driver) { + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mStatsLock); + switch (driver) { + case GraphicsEnv::Driver::GL: + case GraphicsEnv::Driver::GL_UPDATED: + case GraphicsEnv::Driver::ANGLE: { + if (mGpuStats.glDriverToLoad == GraphicsEnv::Driver::NONE || + mGpuStats.glDriverToLoad == GraphicsEnv::Driver::GL) { + mGpuStats.glDriverToLoad = driver; + break; + } + + if (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE) { + mGpuStats.glDriverFallback = driver; + } + break; + } + case Driver::VULKAN: + case Driver::VULKAN_UPDATED: { + if (mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::NONE || + mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::VULKAN) { + mGpuStats.vkDriverToLoad = driver; + break; + } + + if (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE) { + mGpuStats.vkDriverFallback = driver; + } + break; + } + default: + break; + } +} + +void GraphicsEnv::setDriverLoaded(GraphicsEnv::Api api, bool isDriverLoaded, + int64_t driverLoadingTime) { + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mStatsLock); + const bool doNotSend = mGpuStats.appPackageName.empty(); + if (api == GraphicsEnv::Api::API_GL) { + if (doNotSend) mGpuStats.glDriverToSend = true; + mGpuStats.glDriverLoadingTime = driverLoadingTime; + } else { + if (doNotSend) mGpuStats.vkDriverToSend = true; + mGpuStats.vkDriverLoadingTime = driverLoadingTime; + } + + sendGpuStatsLocked(api, isDriverLoaded, driverLoadingTime); +} + +static sp<IGpuService> getGpuService() { + const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu")); + if (!binder) { + ALOGE("Failed to get gpu service"); + return nullptr; + } + + return interface_cast<IGpuService>(binder); +} + +void GraphicsEnv::setTargetStats(const Stats stats, const uint64_t value) { + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mStatsLock); + const sp<IGpuService> gpuService = getGpuService(); + if (gpuService) { + gpuService->setTargetStats(mGpuStats.appPackageName, mGpuStats.driverVersionCode, stats, + value); + } +} + +void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Api api, bool isDriverLoaded, + int64_t driverLoadingTime) { + ATRACE_CALL(); + + // Do not sendGpuStats for those skipping the GraphicsEnvironment setup + if (mGpuStats.appPackageName.empty()) return; + + ALOGV("sendGpuStats:\n" + "\tdriverPackageName[%s]\n" + "\tdriverVersionName[%s]\n" + "\tdriverVersionCode[%" PRIu64 "]\n" + "\tdriverBuildTime[%" PRId64 "]\n" + "\tappPackageName[%s]\n" + "\tvulkanVersion[%d]\n" + "\tapi[%d]\n" + "\tisDriverLoaded[%d]\n" + "\tdriverLoadingTime[%" PRId64 "]", + mGpuStats.driverPackageName.c_str(), mGpuStats.driverVersionName.c_str(), + mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, mGpuStats.appPackageName.c_str(), + mGpuStats.vulkanVersion, static_cast<int32_t>(api), isDriverLoaded, driverLoadingTime); + + GraphicsEnv::Driver driver = GraphicsEnv::Driver::NONE; + bool isIntendedDriverLoaded = false; + if (api == GraphicsEnv::Api::API_GL) { + driver = mGpuStats.glDriverToLoad; + isIntendedDriverLoaded = + isDriverLoaded && (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE); + } else { + driver = mGpuStats.vkDriverToLoad; + isIntendedDriverLoaded = + isDriverLoaded && (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE); + } + + const sp<IGpuService> gpuService = getGpuService(); + if (gpuService) { + gpuService->setGpuStats(mGpuStats.driverPackageName, mGpuStats.driverVersionName, + mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, + mGpuStats.appPackageName, mGpuStats.vulkanVersion, driver, + isIntendedDriverLoaded, driverLoadingTime); + } +} + +void* GraphicsEnv::loadLibrary(std::string name) { + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = getAngleNamespace(), + }; + + std::string libName = std::string("lib") + name + "_angle.so"; + + void* so = android_dlopen_ext(libName.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); + + if (so) { + ALOGD("dlopen_ext from APK (%s) success at %p", libName.c_str(), so); + return so; + } else { + ALOGE("dlopen_ext(\"%s\") failed: %s", libName.c_str(), dlerror()); + } + + return nullptr; +} + +bool GraphicsEnv::checkAngleRules(void* so) { + char manufacturer[PROPERTY_VALUE_MAX]; + char model[PROPERTY_VALUE_MAX]; + property_get("ro.product.manufacturer", manufacturer, "UNSET"); + property_get("ro.product.model", model, "UNSET"); + + auto ANGLEGetFeatureSupportUtilAPIVersion = + (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so, + "ANGLEGetFeatureSupportUtilAPIVersion"); + + if (!ANGLEGetFeatureSupportUtilAPIVersion) { + ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function"); + return false; + } + + // Negotiate the interface version by requesting most recent known to the platform + unsigned int versionToUse = CURRENT_ANGLE_API_VERSION; + if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) { + ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, " + "requested version %u", + versionToUse); + return false; + } + + // Add and remove versions below as needed + bool useAngle = false; + switch (versionToUse) { + case 2: { + ALOGV("Using version %d of ANGLE feature-support library", versionToUse); + void* rulesHandle = nullptr; + int rulesVersion = 0; + void* systemInfoHandle = nullptr; + + // Get the symbols for the feature-support-utility library: +#define GET_SYMBOL(symbol) \ + fp##symbol symbol = (fp##symbol)dlsym(so, #symbol); \ + if (!symbol) { \ + ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \ + break; \ + } + GET_SYMBOL(ANGLEAndroidParseRulesString); + GET_SYMBOL(ANGLEGetSystemInfo); + GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo); + GET_SYMBOL(ANGLEShouldBeUsedForApplication); + GET_SYMBOL(ANGLEFreeRulesHandle); + GET_SYMBOL(ANGLEFreeSystemInfoHandle); + + // Parse the rules, obtain the SystemInfo, and evaluate the + // application against the rules: + if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) { + ALOGW("ANGLE feature-support library cannot parse rules file"); + break; + } + if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) { + ALOGW("ANGLE feature-support library cannot obtain SystemInfo"); + break; + } + if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) { + ALOGW("ANGLE feature-support library cannot add device info to SystemInfo"); + break; + } + useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion, + systemInfoHandle, mAngleAppName.c_str()); + (ANGLEFreeRulesHandle)(rulesHandle); + (ANGLEFreeSystemInfoHandle)(systemInfoHandle); + } break; + + default: + ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse); + } + + ALOGV("Close temporarily-loaded ANGLE opt-in/out logic"); + return useAngle; +} + +bool GraphicsEnv::shouldUseAngle(std::string appName) { + if (appName != mAngleAppName) { + // Make sure we are checking the app we were init'ed for + ALOGE("App name does not match: expected '%s', got '%s'", mAngleAppName.c_str(), + appName.c_str()); + return false; + } + + return shouldUseAngle(); +} + +bool GraphicsEnv::shouldUseAngle() { + // Make sure we are init'ed + if (mAngleAppName.empty()) { + ALOGV("App name is empty. setAngleInfo() has not been called to enable ANGLE."); + return false; + } + + return (mUseAngle == YES) ? true : false; +} + +void GraphicsEnv::updateUseAngle() { + mUseAngle = NO; + + const char* ANGLE_PREFER_ANGLE = "angle"; + const char* ANGLE_PREFER_NATIVE = "native"; + + if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) { + ALOGV("User set \"Developer Options\" to force the use of ANGLE"); + mUseAngle = YES; + } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) { + ALOGV("User set \"Developer Options\" to force the use of Native"); + mUseAngle = NO; + } else { + // The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily + // load ANGLE and call the updatable opt-in/out logic: + void* featureSo = loadLibrary("feature_support"); + if (featureSo) { + ALOGV("loaded ANGLE's opt-in/out logic from namespace"); + mUseAngle = checkAngleRules(featureSo) ? YES : NO; + dlclose(featureSo); + featureSo = nullptr; + } else { + ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE."); + } + } +} + +void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName, + const std::string developerOptIn, const int rulesFd, + const long rulesOffset, const long rulesLength) { + if (mUseAngle != UNKNOWN) { + // We've already figured out an answer for this app, so just return. + ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(), + (mUseAngle == YES) ? "true" : "false"); + return; + } + + ALOGV("setting ANGLE path to '%s'", path.c_str()); + mAnglePath = path; + ALOGV("setting ANGLE app name to '%s'", appName.c_str()); + mAngleAppName = appName; + ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str()); + mAngleDeveloperOptIn = developerOptIn; + + lseek(rulesFd, rulesOffset, SEEK_SET); + mRulesBuffer = std::vector<char>(rulesLength + 1); + ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength); + if (numBytesRead < 0) { + ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead); + numBytesRead = 0; + } else if (numBytesRead == 0) { + ALOGW("Empty rules file"); + } + if (numBytesRead != rulesLength) { + ALOGW("Did not read all of the necessary bytes from the rules file." + "expected: %ld, got: %zd", + rulesLength, numBytesRead); + } + mRulesBuffer[numBytesRead] = '\0'; + + // Update the current status of whether we should use ANGLE or not + updateUseAngle(); +} + +void GraphicsEnv::setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths) { if (mLayerPaths.empty()) { mLayerPaths = layerPaths; mAppNamespace = appNamespace; } else { ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'", - layerPaths.c_str(), appNamespace); + layerPaths.c_str(), appNamespace); } } -android_namespace_t* GraphicsEnv::getAppNamespace() { +NativeLoaderNamespace* GraphicsEnv::getAppNamespace() { return mAppNamespace; } -const std::string GraphicsEnv::getLayerPaths(){ +std::string& GraphicsEnv::getAngleAppName() { + return mAngleAppName; +} + +const std::string& GraphicsEnv::getLayerPaths() { return mLayerPaths; } -const std::string GraphicsEnv::getDebugLayers() { +const std::string& GraphicsEnv::getDebugLayers() { return mDebugLayers; } +const std::string& GraphicsEnv::getDebugLayersGLES() { + return mDebugLayersGLES; +} + void GraphicsEnv::setDebugLayers(const std::string layers) { mDebugLayers = layers; } +void GraphicsEnv::setDebugLayersGLES(const std::string layers) { + mDebugLayersGLES = layers; +} + +// Return true if all the required libraries from vndk and sphal namespace are +// linked to the Game Driver namespace correctly. +bool GraphicsEnv::linkDriverNamespaceLocked(android_namespace_t* vndkNamespace) { + const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK); + if (llndkLibraries.empty()) { + return false; + } + if (!android_link_namespaces(mDriverNamespace, nullptr, llndkLibraries.c_str())) { + ALOGE("Failed to link default namespace[%s]", dlerror()); + return false; + } + + const std::string vndkspLibraries = getSystemNativeLibraries(NativeLibrary::VNDKSP); + if (vndkspLibraries.empty()) { + return false; + } + if (!android_link_namespaces(mDriverNamespace, vndkNamespace, vndkspLibraries.c_str())) { + ALOGE("Failed to link vndk namespace[%s]", dlerror()); + return false; + } + + if (mSphalLibraries.empty()) { + return true; + } + + // Make additional libraries in sphal to be accessible + auto sphalNamespace = android_get_exported_namespace("sphal"); + if (!sphalNamespace) { + ALOGE("Depend on these libraries[%s] in sphal, but failed to get sphal namespace", + mSphalLibraries.c_str()); + return false; + } + + if (!android_link_namespaces(mDriverNamespace, sphalNamespace, mSphalLibraries.c_str())) { + ALOGE("Failed to link sphal namespace[%s]", dlerror()); + return false; + } + + return true; +} + android_namespace_t* GraphicsEnv::getDriverNamespace() { - static std::once_flag once; - std::call_once(once, [this]() { - if (mDriverPath.empty()) - return; - // If the sphal namespace isn't configured for a device, don't support updatable drivers. - // We need a parent namespace to inherit the default search path from. - auto sphalNamespace = android_get_exported_namespace("sphal"); - if (!sphalNamespace) return; - mDriverNamespace = android_create_namespace("gfx driver", - nullptr, // ld_library_path - mDriverPath.c_str(), // default_library_path - ANDROID_NAMESPACE_TYPE_SHARED | - ANDROID_NAMESPACE_TYPE_ISOLATED, - nullptr, // permitted_when_isolated_path - sphalNamespace); - }); + std::lock_guard<std::mutex> lock(mNamespaceMutex); + + if (mDriverNamespace) { + return mDriverNamespace; + } + + if (mDriverPath.empty()) { + return nullptr; + } + + auto vndkNamespace = android_get_exported_namespace("vndk"); + if (!vndkNamespace) { + return nullptr; + } + + mDriverNamespace = android_create_namespace("gfx driver", + mDriverPath.c_str(), // ld_library_path + mDriverPath.c_str(), // default_library_path + ANDROID_NAMESPACE_TYPE_ISOLATED, + nullptr, // permitted_when_isolated_path + nullptr); + + if (!linkDriverNamespaceLocked(vndkNamespace)) { + mDriverNamespace = nullptr; + } + return mDriverNamespace; } -} // namespace android +android_namespace_t* GraphicsEnv::getAngleNamespace() { + std::lock_guard<std::mutex> lock(mNamespaceMutex); -extern "C" android_namespace_t* android_getDriverNamespace() { - return android::GraphicsEnv::getInstance().getDriverNamespace(); + if (mAngleNamespace) { + return mAngleNamespace; + } + + if (mAnglePath.empty()) { + ALOGV("mAnglePath is empty, not creating ANGLE namespace"); + return nullptr; + } + + mAngleNamespace = android_create_namespace("ANGLE", + nullptr, // ld_library_path + mAnglePath.c_str(), // default_library_path + ANDROID_NAMESPACE_TYPE_SHARED | + ANDROID_NAMESPACE_TYPE_ISOLATED, + nullptr, // permitted_when_isolated_path + nullptr); + + ALOGD_IF(!mAngleNamespace, "Could not create ANGLE namespace from default"); + + return mAngleNamespace; } + +} // namespace android
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp new file mode 100644 index 0000000..db16f3c --- /dev/null +++ b/libs/graphicsenv/IGpuService.cpp
@@ -0,0 +1,223 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "GpuService" + +#include <graphicsenv/IGpuService.h> + +#include <binder/IResultReceiver.h> +#include <binder/Parcel.h> + +namespace android { + +class BpGpuService : public BpInterface<IGpuService> { +public: + explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {} + + virtual void setGpuStats(const std::string& driverPackageName, + const std::string& driverVersionName, uint64_t driverVersionCode, + int64_t driverBuildTime, const std::string& appPackageName, + const int32_t vulkanVersion, GraphicsEnv::Driver driver, + bool isDriverLoaded, int64_t driverLoadingTime) { + Parcel data, reply; + data.writeInterfaceToken(IGpuService::getInterfaceDescriptor()); + + data.writeUtf8AsUtf16(driverPackageName); + data.writeUtf8AsUtf16(driverVersionName); + data.writeUint64(driverVersionCode); + data.writeInt64(driverBuildTime); + data.writeUtf8AsUtf16(appPackageName); + data.writeInt32(vulkanVersion); + data.writeInt32(static_cast<int32_t>(driver)); + data.writeBool(isDriverLoaded); + data.writeInt64(driverLoadingTime); + + remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const { + if (!outStats) return UNEXPECTED_NULL; + + Parcel data, reply; + status_t status; + + if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK) + return status; + + if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_GLOBAL_INFO, data, &reply)) != + OK) + return status; + + int32_t result = 0; + if ((status = reply.readInt32(&result)) != OK) return status; + if (result != OK) return result; + + outStats->clear(); + return reply.readParcelableVector(outStats); + } + + virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const { + if (!outStats) return UNEXPECTED_NULL; + + Parcel data, reply; + status_t status; + + if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK) { + return status; + } + + if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_APP_INFO, data, &reply)) != + OK) { + return status; + } + + int32_t result = 0; + if ((status = reply.readInt32(&result)) != OK) return status; + if (result != OK) return result; + + outStats->clear(); + return reply.readParcelableVector(outStats); + } + + virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode, + const GraphicsEnv::Stats stats, const uint64_t value) { + Parcel data, reply; + data.writeInterfaceToken(IGpuService::getInterfaceDescriptor()); + + data.writeUtf8AsUtf16(appPackageName); + data.writeUint64(driverVersionCode); + data.writeInt32(static_cast<int32_t>(stats)); + data.writeUint64(value); + + remote()->transact(BnGpuService::SET_TARGET_STATS, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService"); + +status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + ALOGV("onTransact code[0x%X]", code); + + status_t status; + switch (code) { + case SET_GPU_STATS: { + CHECK_INTERFACE(IGpuService, data, reply); + + std::string driverPackageName; + if ((status = data.readUtf8FromUtf16(&driverPackageName)) != OK) return status; + + std::string driverVersionName; + if ((status = data.readUtf8FromUtf16(&driverVersionName)) != OK) return status; + + uint64_t driverVersionCode; + if ((status = data.readUint64(&driverVersionCode)) != OK) return status; + + int64_t driverBuildTime; + if ((status = data.readInt64(&driverBuildTime)) != OK) return status; + + std::string appPackageName; + if ((status = data.readUtf8FromUtf16(&appPackageName)) != OK) return status; + + int32_t vulkanVersion; + if ((status = data.readInt32(&vulkanVersion)) != OK) return status; + + int32_t driver; + if ((status = data.readInt32(&driver)) != OK) return status; + + bool isDriverLoaded; + if ((status = data.readBool(&isDriverLoaded)) != OK) return status; + + int64_t driverLoadingTime; + if ((status = data.readInt64(&driverLoadingTime)) != OK) return status; + + setGpuStats(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime, + appPackageName, vulkanVersion, static_cast<GraphicsEnv::Driver>(driver), + isDriverLoaded, driverLoadingTime); + + return OK; + } + case GET_GPU_STATS_GLOBAL_INFO: { + CHECK_INTERFACE(IGpuService, data, reply); + + std::vector<GpuStatsGlobalInfo> stats; + const status_t result = getGpuStatsGlobalInfo(&stats); + + if ((status = reply->writeInt32(result)) != OK) return status; + if (result != OK) return result; + + if ((status = reply->writeParcelableVector(stats)) != OK) return status; + + return OK; + } + case GET_GPU_STATS_APP_INFO: { + CHECK_INTERFACE(IGpuService, data, reply); + + std::vector<GpuStatsAppInfo> stats; + const status_t result = getGpuStatsAppInfo(&stats); + + if ((status = reply->writeInt32(result)) != OK) return status; + if (result != OK) return result; + + if ((status = reply->writeParcelableVector(stats)) != OK) return status; + + return OK; + } + case SET_TARGET_STATS: { + CHECK_INTERFACE(IGpuService, data, reply); + + std::string appPackageName; + if ((status = data.readUtf8FromUtf16(&appPackageName)) != OK) return status; + + uint64_t driverVersionCode; + if ((status = data.readUint64(&driverVersionCode)) != OK) return status; + + int32_t stats; + if ((status = data.readInt32(&stats)) != OK) return status; + + uint64_t value; + if ((status = data.readUint64(&value)) != OK) return status; + + setTargetStats(appPackageName, driverVersionCode, + static_cast<GraphicsEnv::Stats>(stats), value); + + return OK; + } + case SHELL_COMMAND_TRANSACTION: { + int in = data.readFileDescriptor(); + int out = data.readFileDescriptor(); + int err = data.readFileDescriptor(); + + std::vector<String16> args; + data.readString16Vector(&args); + + sp<IBinder> unusedCallback; + if ((status = data.readNullableStrongBinder(&unusedCallback)) != OK) return status; + + sp<IResultReceiver> resultReceiver; + if ((status = data.readNullableStrongBinder(&resultReceiver)) != OK) return status; + + status = shellCommand(in, out, err, args); + if (resultReceiver != nullptr) resultReceiver->send(status); + + return OK; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +} // namespace android
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h new file mode 100644 index 0000000..edcccfe --- /dev/null +++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -0,0 +1,75 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string> +#include <vector> + +#include <binder/Parcelable.h> + +namespace android { + +/* + * class for transporting gpu global stats from GpuService to authorized + * recipents. This class is intended to be a data container. + */ +class GpuStatsGlobalInfo : public Parcelable { +public: + GpuStatsGlobalInfo() = default; + GpuStatsGlobalInfo(const GpuStatsGlobalInfo&) = default; + virtual ~GpuStatsGlobalInfo() = default; + virtual status_t writeToParcel(Parcel* parcel) const; + virtual status_t readFromParcel(const Parcel* parcel); + std::string toString() const; + + std::string driverPackageName = ""; + std::string driverVersionName = ""; + uint64_t driverVersionCode = 0; + int64_t driverBuildTime = 0; + int32_t glLoadingCount = 0; + int32_t glLoadingFailureCount = 0; + int32_t vkLoadingCount = 0; + int32_t vkLoadingFailureCount = 0; + int32_t vulkanVersion = 0; + int32_t cpuVulkanVersion = 0; + int32_t glesVersion = 0; + int32_t angleLoadingCount = 0; + int32_t angleLoadingFailureCount = 0; +}; + +/* + * class for transporting gpu app stats from GpuService to authorized recipents. + * This class is intended to be a data container. + */ +class GpuStatsAppInfo : public Parcelable { +public: + GpuStatsAppInfo() = default; + GpuStatsAppInfo(const GpuStatsAppInfo&) = default; + virtual ~GpuStatsAppInfo() = default; + virtual status_t writeToParcel(Parcel* parcel) const; + virtual status_t readFromParcel(const Parcel* parcel); + std::string toString() const; + + std::string appPackageName = ""; + uint64_t driverVersionCode = 0; + std::vector<int64_t> glDriverLoadingTime = {}; + std::vector<int64_t> vkDriverLoadingTime = {}; + std::vector<int64_t> angleDriverLoadingTime = {}; + bool cpuVulkanInUse = false; +}; + +} // namespace android
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 213580c..937bcd9 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -17,53 +17,143 @@ #ifndef ANDROID_UI_GRAPHICS_ENV_H #define ANDROID_UI_GRAPHICS_ENV_H 1 +#include <mutex> #include <string> +#include <vector> struct android_namespace_t; namespace android { +struct NativeLoaderNamespace; + class GraphicsEnv { public: + enum Api { + API_GL = 0, + API_VK = 1, + }; + + enum Driver { + NONE = 0, + GL = 1, + GL_UPDATED = 2, + VULKAN = 3, + VULKAN_UPDATED = 4, + ANGLE = 5, + }; + + enum Stats { + CPU_VULKAN_IN_USE = 0, + }; + +private: + struct GpuStats { + std::string driverPackageName; + std::string driverVersionName; + uint64_t driverVersionCode; + int64_t driverBuildTime; + std::string appPackageName; + int32_t vulkanVersion; + Driver glDriverToLoad; + Driver glDriverFallback; + Driver vkDriverToLoad; + Driver vkDriverFallback; + bool glDriverToSend; + bool vkDriverToSend; + int64_t glDriverLoadingTime; + int64_t vkDriverLoadingTime; + + GpuStats() + : driverPackageName(""), + driverVersionName(""), + driverVersionCode(0), + driverBuildTime(0), + appPackageName(""), + vulkanVersion(0), + glDriverToLoad(Driver::NONE), + glDriverFallback(Driver::NONE), + vkDriverToLoad(Driver::NONE), + vkDriverFallback(Driver::NONE), + glDriverToSend(false), + vkDriverToSend(false), + glDriverLoadingTime(0), + vkDriverLoadingTime(0) {} + }; + +public: static GraphicsEnv& getInstance(); + int getCanLoadSystemLibraries(); + // Set a search path for loading graphics drivers. The path is a list of // directories separated by ':'. A directory can be contained in a zip file // (drivers must be stored uncompressed and page aligned); such elements // in the search path must have a '!' after the zip filename, e.g. // /data/app/com.example.driver/base.apk!/lib/arm64-v8a - void setDriverPath(const std::string path); + // Also set additional required sphal libraries to the linker for loading + // graphics drivers. The string is a list of libraries separated by ':', + // which is required by android_link_namespaces. + void setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries); android_namespace_t* getDriverNamespace(); + void hintActivityLaunch(); + void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, + uint64_t versionCode, int64_t driverBuildTime, + const std::string& appPackageName, const int32_t vulkanVersion); + void setTargetStats(const Stats stats, const uint64_t value = 0); + void setDriverToLoad(Driver driver); + void setDriverLoaded(Api api, bool isDriverLoaded, int64_t driverLoadingTime); + void sendGpuStatsLocked(Api api, bool isDriverLoaded, int64_t driverLoadingTime); - void setLayerPaths(android_namespace_t* appNamespace, const std::string layerPaths); - android_namespace_t* getAppNamespace(); - const std::string getLayerPaths(); + bool shouldUseAngle(std::string appName); + bool shouldUseAngle(); + // Set a search path for loading ANGLE libraries. The path is a list of + // directories separated by ':'. A directory can be contained in a zip file + // (libraries must be stored uncompressed and page aligned); such elements + // in the search path must have a '!' after the zip filename, e.g. + // /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a + void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn, + const int rulesFd, const long rulesOffset, const long rulesLength); + android_namespace_t* getAngleNamespace(); + std::string& getAngleAppName(); + + void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths); + NativeLoaderNamespace* getAppNamespace(); + + const std::string& getLayerPaths(); void setDebugLayers(const std::string layers); - const std::string getDebugLayers(); + void setDebugLayersGLES(const std::string layers); + const std::string& getDebugLayers(); + const std::string& getDebugLayersGLES(); private: + enum UseAngle { UNKNOWN, YES, NO }; + + void* loadLibrary(std::string name); + bool checkAngleRules(void* so); + void updateUseAngle(); + bool linkDriverNamespaceLocked(android_namespace_t* vndkNamespace); + GraphicsEnv() = default; std::string mDriverPath; + std::string mSphalLibraries; + std::mutex mStatsLock; + GpuStats mGpuStats; + std::string mAnglePath; + std::string mAngleAppName; + std::string mAngleDeveloperOptIn; + std::vector<char> mRulesBuffer; + UseAngle mUseAngle = UNKNOWN; std::string mDebugLayers; + std::string mDebugLayersGLES; std::string mLayerPaths; + std::mutex mNamespaceMutex; android_namespace_t* mDriverNamespace = nullptr; - android_namespace_t* mAppNamespace = nullptr; + android_namespace_t* mAngleNamespace = nullptr; + NativeLoaderNamespace* mAppNamespace = nullptr; }; } // namespace android -/* FIXME - * Export an un-mangled function that just does - * return android::GraphicsEnv::getInstance().getDriverNamespace(); - * This allows libEGL to get the function pointer via dlsym, since it can't - * directly link against libgui. In a future release, we'll fix this so that - * libgui does not depend on graphics API libraries, and libEGL can link - * against it. The current dependencies from libgui -> libEGL are: - * - the GLConsumer class, which should be moved to its own library - * - the EGLsyncKHR synchronization in BufferQueue, which is deprecated and - * will be removed soon. - */ -extern "C" android_namespace_t* android_getDriverNamespace(); - #endif // ANDROID_UI_GRAPHICS_ENV_H
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h new file mode 100644 index 0000000..b8d0bd1 --- /dev/null +++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -0,0 +1,71 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <vector> + +#include <binder/IInterface.h> +#include <cutils/compiler.h> +#include <graphicsenv/GpuStatsInfo.h> +#include <graphicsenv/GraphicsEnv.h> + +namespace android { + +/* + * This class defines the Binder IPC interface for GPU-related queries and + * control. + */ +class IGpuService : public IInterface { +public: + DECLARE_META_INTERFACE(GpuService) + + // set GPU stats from GraphicsEnvironment. + virtual void setGpuStats(const std::string& driverPackageName, + const std::string& driverVersionName, uint64_t driverVersionCode, + int64_t driverBuildTime, const std::string& appPackageName, + const int32_t vulkanVersion, GraphicsEnv::Driver driver, + bool isDriverLoaded, int64_t driverLoadingTime) = 0; + + // set target stats. + virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode, + const GraphicsEnv::Stats stats, const uint64_t value = 0) = 0; + + // get GPU global stats from GpuStats module. + virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const = 0; + + // get GPU app stats from GpuStats module. + virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const = 0; +}; + +class BnGpuService : public BnInterface<IGpuService> { +public: + enum IGpuServiceTag { + SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION, + GET_GPU_STATS_GLOBAL_INFO, + GET_GPU_STATS_APP_INFO, + SET_TARGET_STATS, + // Always append new enum to the end. + }; + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0) override; + +protected: + virtual status_t shellCommand(int in, int out, int err, std::vector<String16>& args) = 0; +}; + +} // namespace android
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 73f2147..34575f5 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp
@@ -23,58 +23,100 @@ vndk: { enabled: true, }, + double_loadable: true, + + defaults: ["libgui_bufferqueue-defaults"], + + srcs: [ + "BitTube.cpp", + "BufferHubConsumer.cpp", + "BufferHubProducer.cpp", + "BufferItemConsumer.cpp", + "ConsumerBase.cpp", + "CpuConsumer.cpp", + "DisplayEventReceiver.cpp", + "GLConsumer.cpp", + "GuiConfig.cpp", + "IDisplayEventConnection.cpp", + "IRegionSamplingListener.cpp", + "ISurfaceComposer.cpp", + "ISurfaceComposerClient.cpp", + "ITransactionCompletedListener.cpp", + "LayerDebugInfo.cpp", + "LayerMetadata.cpp", + "LayerState.cpp", + "StreamSplitter.cpp", + "Surface.cpp", + "SurfaceControl.cpp", + "SurfaceComposerClient.cpp", + "SyncFeatures.cpp", + "view/Surface.cpp", + ], + + shared_libs: [ + "android.frameworks.bufferhub@1.0", + "libbufferhub", + "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. + "libinput", + "libpdx_default_transport", + ], + + // bufferhub is not used when building libgui for vendors + target: { + vendor: { + cflags: [ + "-DNO_BUFFERHUB", + "-DNO_INPUT", + ], + exclude_srcs: [ + "BufferHubConsumer.cpp", + "BufferHubProducer.cpp", + ], + exclude_shared_libs: [ + "android.frameworks.bufferhub@1.0", + "libbufferhub", + "libbufferhubqueue", + "libinput", + "libpdx_default_transport", + ], + }, + }, + + header_libs: [ + "libdvr_headers", + "libpdx_headers", + ], +} + +// Used by media codec services exclusively as a static lib for +// core bufferqueue support only. +cc_library_static { + name: "libgui_bufferqueue_static", + vendor_available: true, + + cflags: [ + "-DNO_BUFFERHUB", + ], + + defaults: ["libgui_bufferqueue-defaults"], +} + +// Common build config shared by libgui and libgui_bufferqueue_static. +cc_defaults { + name: "libgui_bufferqueue-defaults", clang: true, cflags: [ "-Wall", "-Werror", ], + cppflags: [ - "-Weverything", - - // The static constructors and destructors in this library have not been noted to - // introduce significant overheads - "-Wno-exit-time-destructors", - "-Wno-global-constructors", - - // We only care about compiling as C++14 - "-Wno-c++98-compat-pedantic", - - // We don't need to enumerate every case in a switch as long as a default case - // is present - "-Wno-switch-enum", - - // Allow calling variadic macros without a __VA_ARGS__ list - "-Wno-gnu-zero-variadic-macro-arguments", - - // Don't warn about struct padding - "-Wno-padded", - - // We are aware of the risks inherent in comparing floats for equality - "-Wno-float-equal", - - // Pure abstract classes trigger this warning - "-Wno-weak-vtables", - - // Allow four-character integer literals - "-Wno-four-char-constants", - - // Allow documentation warnings - "-Wno-documentation", - - // Allow implicit instantiation for templated class function - "-Wno-undefined-func-template", - - // Allow explicitly marking struct as packed even when unnecessary - "-Wno-packed", - + "-Wextra", "-DDEBUG_ONLY_CODE=0", ], product_variables: { - brillo: { - cflags: ["-DHAVE_NO_SURFACE_FLINGER"], - }, eng: { cppflags: [ "-UDEBUG_ONLY_CODE", @@ -84,84 +126,58 @@ }, srcs: [ - "BitTube.cpp", - "BufferHubConsumer.cpp", - "BufferHubProducer.cpp", "BufferItem.cpp", - "BufferItemConsumer.cpp", "BufferQueue.cpp", "BufferQueueConsumer.cpp", "BufferQueueCore.cpp", "BufferQueueProducer.cpp", + "BufferQueueThreadState.cpp", "BufferSlot.cpp", - "ConsumerBase.cpp", - "CpuConsumer.cpp", - "DisplayEventReceiver.cpp", "FrameTimestamps.cpp", - "GLConsumer.cpp", - "GuiConfig.cpp", + "GLConsumerUtils.cpp", "HdrMetadata.cpp", - "IDisplayEventConnection.cpp", "IConsumerListener.cpp", "IGraphicBufferConsumer.cpp", "IGraphicBufferProducer.cpp", "IProducerListener.cpp", - "ISurfaceComposer.cpp", - "ISurfaceComposerClient.cpp", - "LayerDebugInfo.cpp", - "LayerState.cpp", "OccupancyTracker.cpp", - "StreamSplitter.cpp", - "Surface.cpp", - "SurfaceControl.cpp", - "SurfaceComposerClient.cpp", - "SyncFeatures.cpp", - "view/Surface.cpp", "bufferqueue/1.0/B2HProducerListener.cpp", - "bufferqueue/1.0/H2BGraphicBufferProducer.cpp" + "bufferqueue/1.0/Conversion.cpp", + "bufferqueue/1.0/H2BGraphicBufferProducer.cpp", + "bufferqueue/1.0/H2BProducerListener.cpp", + "bufferqueue/1.0/WProducerListener.cpp", + "bufferqueue/2.0/B2HGraphicBufferProducer.cpp", + "bufferqueue/2.0/B2HProducerListener.cpp", + "bufferqueue/2.0/H2BGraphicBufferProducer.cpp", + "bufferqueue/2.0/H2BProducerListener.cpp", + "bufferqueue/2.0/types.cpp", ], shared_libs: [ + "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", "android.hardware.graphics.common@1.1", - "libsync", + "android.hardware.graphics.common@1.2", + "android.hidl.token@1.0-utils", + "libbase", "libbinder", - "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. - "libpdx_default_transport", "libcutils", "libEGL", "libGLESv2", - "libui", - "libutils", - "libnativewindow", - "liblog", "libhidlbase", "libhidltransport", - "android.hidl.token@1.0-utils", - "android.hardware.graphics.bufferqueue@1.0", - "android.hardware.configstore@1.0", - "android.hardware.configstore-utils", + "libhwbinder", + "liblog", + "libnativewindow", + "libsync", + "libui", + "libutils", + "libvndksupport", ], - // bufferhub is not used when building libgui for vendors - target: { - vendor: { - cflags: ["-DNO_BUFFERHUB"], - exclude_srcs: [ - "BufferHubConsumer.cpp", - "BufferHubProducer.cpp", - ], - exclude_shared_libs: [ - "libbufferhubqueue", - "libpdx_default_transport", - ], - }, - }, - header_libs: [ - "libdvr_headers", - "libnativebase_headers", "libgui_headers", - "libpdx_headers", + "libnativebase_headers", ], export_shared_lib_headers: [ @@ -169,9 +185,11 @@ "libEGL", "libnativewindow", "libui", - "android.hidl.token@1.0-utils", "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", + "android.hidl.token@1.0-utils", ], export_header_lib_headers: [
diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp index ae5cca2..4be014f 100644 --- a/libs/gui/BufferHubProducer.cpp +++ b/libs/gui/BufferHubProducer.cpp
@@ -19,6 +19,7 @@ #include <inttypes.h> #include <log/log.h> #include <system/window.h> +#include <ui/BufferHubBuffer.h> namespace android { @@ -63,13 +64,13 @@ } else if (buffers_[slot].mGraphicBuffer != nullptr) { ALOGE("requestBuffer: slot %d is not empty.", slot); return BAD_VALUE; - } else if (buffers_[slot].mBufferProducer == nullptr) { + } else if (buffers_[slot].mProducerBuffer == nullptr) { ALOGE("requestBuffer: slot %d is not dequeued.", slot); return BAD_VALUE; } - const auto& buffer_producer = buffers_[slot].mBufferProducer; - sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer(); + const auto& producer_buffer = buffers_[slot].mProducerBuffer; + sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer(); buffers_[slot].mGraphicBuffer = graphic_buffer; buffers_[slot].mRequestBufferCalled = true; @@ -157,19 +158,19 @@ } size_t slot = 0; - std::shared_ptr<BufferProducer> buffer_producer; + std::shared_ptr<ProducerBuffer> producer_buffer; for (size_t retry = 0; retry < BufferHubQueue::kMaxQueueCapacity; retry++) { LocalHandle fence; auto buffer_status = queue_->Dequeue(dequeue_timeout_ms_, &slot, &fence); if (!buffer_status) return NO_MEMORY; - buffer_producer = buffer_status.take(); - if (!buffer_producer) return NO_MEMORY; + producer_buffer = buffer_status.take(); + if (!producer_buffer) return NO_MEMORY; - if (width == buffer_producer->width() && height == buffer_producer->height() && - uint32_t(format) == buffer_producer->format()) { - // The producer queue returns a buffer producer matches the request. + if (width == producer_buffer->width() && height == producer_buffer->height() && + uint32_t(format) == producer_buffer->format()) { + // The producer queue returns a producer buffer matches the request. break; } @@ -178,8 +179,8 @@ ALOGI("dequeueBuffer: requested buffer (w=%u, h=%u, format=%u) is different " "from the buffer returned at slot: %zu (w=%u, h=%u, format=%u). Need " "re-allocattion.", - width, height, format, slot, buffer_producer->width(), buffer_producer->height(), - buffer_producer->format()); + width, height, format, slot, producer_buffer->width(), producer_buffer->height(), + producer_buffer->format()); // Mark the slot as reallocating, so that later we can set // BUFFER_NEEDS_REALLOCATION when the buffer actually get dequeued. buffers_[slot].mIsReallocating = true; @@ -224,23 +225,172 @@ return ret; } -status_t BufferHubProducer::detachBuffer(int /* slot */) { - ALOGE("BufferHubProducer::detachBuffer not implemented."); +status_t BufferHubProducer::detachBuffer(int slot) { + ALOGV("detachBuffer: slot=%d", slot); + std::unique_lock<std::mutex> lock(mutex_); + + return DetachBufferLocked(static_cast<size_t>(slot)); +} + +status_t BufferHubProducer::DetachBufferLocked(size_t slot) { + if (connected_api_ == kNoConnectedApi) { + ALOGE("detachBuffer: BufferHubProducer is not connected."); + return NO_INIT; + } + + if (slot >= static_cast<size_t>(max_buffer_count_)) { + ALOGE("detachBuffer: slot index %zu out of range [0, %d)", slot, max_buffer_count_); + return BAD_VALUE; + } else if (!buffers_[slot].mBufferState.isDequeued()) { + ALOGE("detachBuffer: slot %zu is not owned by the producer (state = %s)", slot, + buffers_[slot].mBufferState.string()); + return BAD_VALUE; + } else if (!buffers_[slot].mRequestBufferCalled) { + ALOGE("detachBuffer: buffer in slot %zu has not been requested", slot); + return BAD_VALUE; + } + std::shared_ptr<ProducerBuffer> producer_buffer = queue_->GetBuffer(slot); + if (producer_buffer == nullptr || producer_buffer->buffer() == nullptr) { + ALOGE("detachBuffer: Invalid ProducerBuffer at slot %zu.", slot); + return BAD_VALUE; + } + sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer(); + if (graphic_buffer == nullptr) { + ALOGE("detachBuffer: Invalid GraphicBuffer at slot %zu.", slot); + return BAD_VALUE; + } + + // Remove the ProducerBuffer from the ProducerQueue. + status_t error = RemoveBuffer(slot); + if (error != NO_ERROR) { + ALOGE("detachBuffer: Failed to remove buffer, slot=%zu, error=%d.", slot, error); + return error; + } + + // Here we need to convert the existing ProducerBuffer into a DetachedBufferHandle and inject + // the handle into the GraphicBuffer object at the requested slot. + auto status_or_handle = producer_buffer->Detach(); + if (!status_or_handle.ok()) { + ALOGE("detachBuffer: Failed to detach from a ProducerBuffer at slot %zu, error=%d.", slot, + status_or_handle.error()); + return BAD_VALUE; + } + + // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can + // be directly backed by BufferHub. return INVALID_OPERATION; } -status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* /* out_buffer */, - sp<Fence>* /* out_fence */) { - ALOGE("BufferHubProducer::detachNextBuffer not implemented."); - return INVALID_OPERATION; +status_t BufferHubProducer::detachNextBuffer(sp<GraphicBuffer>* out_buffer, sp<Fence>* out_fence) { + ALOGV("detachNextBuffer."); + + if (out_buffer == nullptr || out_fence == nullptr) { + ALOGE("detachNextBuffer: Invalid parameter: out_buffer=%p, out_fence=%p", out_buffer, + out_fence); + return BAD_VALUE; + } + + std::unique_lock<std::mutex> lock(mutex_); + + if (connected_api_ == kNoConnectedApi) { + ALOGE("detachNextBuffer: BufferHubProducer is not connected."); + return NO_INIT; + } + + // detachNextBuffer is equivalent to calling dequeueBuffer, requestBuffer, and detachBuffer in + // sequence, except for two things: + // + // 1) It is unnecessary to know the dimensions, format, or usage of the next buffer, i.e. the + // function just returns whatever ProducerBuffer is available from the ProducerQueue and no + // buffer allocation or re-allocation will happen. + // 2) It will not block, since if it cannot find an appropriate buffer to return, it will return + // an error instead. + size_t slot = 0; + LocalHandle fence; + + // First, dequeue a ProducerBuffer from the ProducerQueue with no timeout. Report error + // immediately if ProducerQueue::Dequeue() fails. + auto status_or_buffer = queue_->Dequeue(/*timeout=*/0, &slot, &fence); + if (!status_or_buffer.ok()) { + ALOGE("detachNextBuffer: Failed to dequeue buffer, error=%d.", status_or_buffer.error()); + return NO_MEMORY; + } + + std::shared_ptr<ProducerBuffer> producer_buffer = status_or_buffer.take(); + if (producer_buffer == nullptr) { + ALOGE("detachNextBuffer: Dequeued buffer is null."); + return NO_MEMORY; + } + + // With the BufferHub backed solution, slot returned from |queue_->Dequeue| is guaranteed to + // be available for producer's use. It's either in free state (if the buffer has never been used + // before) or in queued state (if the buffer has been dequeued and queued back to + // BufferHubQueue). + if (!buffers_[slot].mBufferState.isFree() && !buffers_[slot].mBufferState.isQueued()) { + ALOGE("detachNextBuffer: slot %zu is not free or queued, actual state: %s.", slot, + buffers_[slot].mBufferState.string()); + return BAD_VALUE; + } + if (buffers_[slot].mProducerBuffer == nullptr) { + ALOGE("detachNextBuffer: ProducerBuffer at slot %zu is null.", slot); + return BAD_VALUE; + } + if (buffers_[slot].mProducerBuffer->id() != producer_buffer->id()) { + ALOGE("detachNextBuffer: ProducerBuffer at slot %zu has mismatched id, actual: " + "%d, expected: %d.", + slot, buffers_[slot].mProducerBuffer->id(), producer_buffer->id()); + return BAD_VALUE; + } + + ALOGV("detachNextBuffer: slot=%zu", slot); + buffers_[slot].mBufferState.freeQueued(); + buffers_[slot].mBufferState.dequeue(); + + // Second, request the buffer. + sp<GraphicBuffer> graphic_buffer = producer_buffer->buffer()->buffer(); + buffers_[slot].mGraphicBuffer = producer_buffer->buffer()->buffer(); + + // Finally, detach the buffer and then return. + status_t error = DetachBufferLocked(slot); + if (error == NO_ERROR) { + *out_fence = new Fence(fence.Release()); + *out_buffer = graphic_buffer; + } + return error; } -status_t BufferHubProducer::attachBuffer(int* /* out_slot */, - const sp<GraphicBuffer>& /* buffer */) { - // With this BufferHub backed implementation, we assume (for now) all buffers - // are allocated and owned by the BufferHub. Thus the attempt of transfering - // ownership of a buffer to the buffer queue is intentionally unsupported. - LOG_ALWAYS_FATAL("BufferHubProducer::attachBuffer not supported."); +status_t BufferHubProducer::attachBuffer(int* out_slot, const sp<GraphicBuffer>& buffer) { + // In the BufferHub design, all buffers are allocated and owned by the BufferHub. Thus only + // GraphicBuffers that are originated from BufferHub can be attached to a BufferHubProducer. + ALOGV("queueBuffer: buffer=%p", buffer.get()); + + if (out_slot == nullptr) { + ALOGE("attachBuffer: out_slot cannot be NULL."); + return BAD_VALUE; + } + if (buffer == nullptr) { + ALOGE("attachBuffer: invalid GraphicBuffer."); + return BAD_VALUE; + } + + std::unique_lock<std::mutex> lock(mutex_); + + if (connected_api_ == kNoConnectedApi) { + ALOGE("attachBuffer: BufferQueue has no connected producer"); + return NO_INIT; + } + + // Before attaching the buffer, caller is supposed to call + // IGraphicBufferProducer::setGenerationNumber to inform the + // BufferHubProducer the next generation number. + if (buffer->getGenerationNumber() != generation_number_) { + ALOGE("attachBuffer: Mismatched generation number, buffer: %u, queue: %u.", + buffer->getGenerationNumber(), generation_number_); + return BAD_VALUE; + } + + // TODO(b/70912269): Reimplement BufferHubProducer::DetachBufferLocked() once GraphicBuffer can + // be directly backed by BufferHub. return INVALID_OPERATION; } @@ -302,11 +452,11 @@ return BAD_VALUE; } - // Post the buffer producer with timestamp in the metadata. - const auto& buffer_producer = buffers_[slot].mBufferProducer; + // Post the producer buffer with timestamp in the metadata. + const auto& producer_buffer = buffers_[slot].mProducerBuffer; // Check input crop is not out of boundary of current buffer. - Rect buffer_rect(buffer_producer->width(), buffer_producer->height()); + Rect buffer_rect(producer_buffer->width(), producer_buffer->height()); Rect cropped_rect(Rect::EMPTY_RECT); crop.intersect(buffer_rect, &cropped_rect); if (cropped_rect != crop) { @@ -327,11 +477,11 @@ meta_data.scaling_mode = int32_t(scaling_mode); meta_data.transform = int32_t(transform); - buffer_producer->PostAsync(&meta_data, fence_fd); + producer_buffer->PostAsync(&meta_data, fence_fd); buffers_[slot].mBufferState.queue(); - output->width = buffer_producer->width(); - output->height = buffer_producer->height(); + output->width = producer_buffer->width(); + output->height = producer_buffer->height(); output->transformHint = 0; // default value, we don't use it yet. // |numPendingBuffers| counts of the number of buffers that has been enqueued @@ -369,8 +519,8 @@ return BAD_VALUE; } - auto buffer_producer = buffers_[slot].mBufferProducer; - queue_->Enqueue(buffer_producer, size_t(slot), 0ULL); + auto producer_buffer = buffers_[slot].mProducerBuffer; + queue_->Enqueue(producer_buffer, size_t(slot), 0U); buffers_[slot].mBufferState.cancel(); buffers_[slot].mFence = fence; ALOGV("cancelBuffer: slot %d", slot); @@ -641,12 +791,12 @@ } size_t slot = status.get(); - auto buffer_producer = queue_->GetBuffer(slot); + auto producer_buffer = queue_->GetBuffer(slot); - LOG_ALWAYS_FATAL_IF(buffer_producer == nullptr, "Failed to get buffer producer at slot: %zu", - slot); + LOG_ALWAYS_FATAL_IF(producer_buffer == nullptr, + "Failed to get the producer buffer at slot: %zu", slot); - buffers_[slot].mBufferProducer = buffer_producer; + buffers_[slot].mProducerBuffer = producer_buffer; return NO_ERROR; } @@ -654,26 +804,28 @@ status_t BufferHubProducer::RemoveBuffer(size_t slot) { auto status = queue_->RemoveBuffer(slot); if (!status) { - ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer: %s", - status.GetErrorMessage().c_str()); + ALOGE("BufferHubProducer::RemoveBuffer: Failed to remove buffer at slot: %zu, error: %s.", + slot, status.GetErrorMessage().c_str()); return INVALID_OPERATION; } // Reset in memory objects related the the buffer. - buffers_[slot].mBufferProducer = nullptr; - buffers_[slot].mGraphicBuffer = nullptr; + buffers_[slot].mProducerBuffer = nullptr; buffers_[slot].mBufferState.detachProducer(); + buffers_[slot].mFence = Fence::NO_FENCE; + buffers_[slot].mGraphicBuffer = nullptr; + buffers_[slot].mRequestBufferCalled = false; return NO_ERROR; } status_t BufferHubProducer::FreeAllBuffers() { for (size_t slot = 0; slot < BufferHubQueue::kMaxQueueCapacity; slot++) { // Reset in memory objects related the the buffer. - buffers_[slot].mGraphicBuffer = nullptr; + buffers_[slot].mProducerBuffer = nullptr; buffers_[slot].mBufferState.reset(); - buffers_[slot].mRequestBufferCalled = false; - buffers_[slot].mBufferProducer = nullptr; buffers_[slot].mFence = Fence::NO_FENCE; + buffers_[slot].mGraphicBuffer = nullptr; + buffers_[slot].mRequestBufferCalled = false; } auto status = queue_->FreeAllBuffers();
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp index f50379b..5beba02 100644 --- a/libs/gui/BufferItem.cpp +++ b/libs/gui/BufferItem.cpp
@@ -39,8 +39,8 @@ } BufferItem::BufferItem() : - mGraphicBuffer(NULL), - mFence(NULL), + mGraphicBuffer(nullptr), + mFence(nullptr), mCrop(Rect::INVALID_RECT), mTransform(0), mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), @@ -91,11 +91,11 @@ size_t BufferItem::getFlattenedSize() const { size_t size = sizeof(uint32_t); // Flags - if (mGraphicBuffer != 0) { + if (mGraphicBuffer != nullptr) { size += mGraphicBuffer->getFlattenedSize(); size = FlattenableUtils::align<4>(size); } - if (mFence != 0) { + if (mFence != nullptr) { size += mFence->getFlattenedSize(); size = FlattenableUtils::align<4>(size); } @@ -107,10 +107,10 @@ size_t BufferItem::getFdCount() const { size_t count = 0; - if (mGraphicBuffer != 0) { + if (mGraphicBuffer != nullptr) { count += mGraphicBuffer->getFdCount(); } - if (mFence != 0) { + if (mFence != nullptr) { count += mFence->getFdCount(); } return count; @@ -137,13 +137,13 @@ FlattenableUtils::advance(buffer, size, sizeof(uint32_t)); flags = 0; - if (mGraphicBuffer != 0) { + if (mGraphicBuffer != nullptr) { status_t err = mGraphicBuffer->flatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer); flags |= 1; } - if (mFence != 0) { + if (mFence != nullptr) { status_t err = mFence->flatten(buffer, size, fds, count); if (err) return err; size -= FlattenableUtils::align<4>(buffer);
diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index 89bc0c4..f50bc20 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp
@@ -107,7 +107,7 @@ void BufferItemConsumer::freeBufferLocked(int slotIndex) { sp<BufferFreedListener> listener = mBufferFreedListener.promote(); - if (listener != NULL && mSlots[slotIndex].mGraphicBuffer != NULL) { + if (listener != nullptr && mSlots[slotIndex].mGraphicBuffer != nullptr) { // Fire callback if we have a listener registered and the buffer being freed is valid. BI_LOGV("actually calling onBufferFreed"); listener->onBufferFreed(mSlots[slotIndex].mGraphicBuffer);
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index a8da134..5fb3f0b 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp
@@ -38,7 +38,7 @@ void BufferQueue::ProxyConsumerListener::onDisconnect() { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onDisconnect(); } } @@ -46,7 +46,7 @@ void BufferQueue::ProxyConsumerListener::onFrameAvailable( const BufferItem& item) { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onFrameAvailable(item); } } @@ -54,21 +54,21 @@ void BufferQueue::ProxyConsumerListener::onFrameReplaced( const BufferItem& item) { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onFrameReplaced(item); } } void BufferQueue::ProxyConsumerListener::onBuffersReleased() { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } } void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onSidebandStreamChanged(); } } @@ -85,21 +85,21 @@ void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer, bool consumerIsSurfaceFlinger) { - LOG_ALWAYS_FATAL_IF(outProducer == NULL, + LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL"); - LOG_ALWAYS_FATAL_IF(outConsumer == NULL, + LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL"); sp<BufferQueueCore> core(new BufferQueueCore()); - LOG_ALWAYS_FATAL_IF(core == NULL, + LOG_ALWAYS_FATAL_IF(core == nullptr, "BufferQueue: failed to create BufferQueueCore"); sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger)); - LOG_ALWAYS_FATAL_IF(producer == NULL, + LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer"); sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); - LOG_ALWAYS_FATAL_IF(consumer == NULL, + LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer"); *outProducer = producer; @@ -109,8 +109,8 @@ #ifndef NO_BUFFERHUB void BufferQueue::createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer) { - LOG_ALWAYS_FATAL_IF(outProducer == NULL, "BufferQueue: outProducer must not be NULL"); - LOG_ALWAYS_FATAL_IF(outConsumer == NULL, "BufferQueue: outConsumer must not be NULL"); + LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BufferQueue: outProducer must not be NULL"); + LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BufferQueue: outConsumer must not be NULL"); sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; @@ -118,16 +118,16 @@ dvr::ProducerQueueConfigBuilder configBuilder; std::shared_ptr<dvr::ProducerQueue> producerQueue = dvr::ProducerQueue::Create(configBuilder.Build(), dvr::UsagePolicy{}); - LOG_ALWAYS_FATAL_IF(producerQueue == NULL, "BufferQueue: failed to create ProducerQueue."); + LOG_ALWAYS_FATAL_IF(producerQueue == nullptr, "BufferQueue: failed to create ProducerQueue."); std::shared_ptr<dvr::ConsumerQueue> consumerQueue = producerQueue->CreateConsumerQueue(); - LOG_ALWAYS_FATAL_IF(consumerQueue == NULL, "BufferQueue: failed to create ConsumerQueue."); + LOG_ALWAYS_FATAL_IF(consumerQueue == nullptr, "BufferQueue: failed to create ConsumerQueue."); producer = BufferHubProducer::Create(producerQueue); consumer = BufferHubConsumer::Create(consumerQueue); - LOG_ALWAYS_FATAL_IF(producer == NULL, "BufferQueue: failed to create BufferQueueProducer"); - LOG_ALWAYS_FATAL_IF(consumer == NULL, "BufferQueue: failed to create BufferQueueConsumer"); + LOG_ALWAYS_FATAL_IF(producer == nullptr, "BufferQueue: failed to create BufferQueueProducer"); + LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BufferQueue: failed to create BufferQueueConsumer"); *outProducer = producer; *outConsumer = consumer;
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index d70e142..528bfb1 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp
@@ -34,9 +34,10 @@ #include <gui/IConsumerListener.h> #include <gui/IProducerListener.h> -#include <binder/IPCThreadState.h> +#include <private/gui/BufferQueueThreadState.h> #ifndef __ANDROID_VNDK__ #include <binder/PermissionCache.h> +#include <vndksupport/linker.h> #endif #include <system/window.h> @@ -57,7 +58,7 @@ int numDroppedBuffers = 0; sp<IProducerListener> listener; { - Mutex::Autolock lock(mCore->mMutex); + std::unique_lock<std::mutex> lock(mCore->mMutex); // Check that the consumer doesn't currently have the maximum number of // buffers acquired. We allow the max buffer count to be exceeded by one @@ -93,8 +94,6 @@ // Skip this if we're in shared buffer mode and the queue is empty, // since in that case we'll just return the shared buffer. if (expectedPresent != 0 && !mCore->mQueue.empty()) { - const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second - // The 'expectedPresent' argument indicates when the buffer is expected // to be presented on-screen. If the buffer's desired present time is // earlier (less) than expectedPresent -- meaning it will be displayed @@ -189,6 +188,7 @@ desiredPresent - expectedPresent, systemTime(CLOCK_MONOTONIC), front->mFrameNumber, maxFrameNumber); + ATRACE_NAME("PRESENT_LATER"); return PRESENT_LATER; } @@ -202,7 +202,7 @@ if (sharedBufferAvailable && mCore->mQueue.empty()) { // make sure the buffer has finished allocating before acquiring it - mCore->waitWhileAllocatingLocked(); + mCore->waitWhileAllocatingLocked(lock); slot = mCore->mSharedBufferSlot; @@ -255,7 +255,7 @@ // mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer // on the consumer side if (outBuffer->mAcquireCalled) { - outBuffer->mGraphicBuffer = NULL; + outBuffer->mGraphicBuffer = nullptr; } mCore->mQueue.erase(front); @@ -263,7 +263,7 @@ // We might have freed a slot while dropping old buffers, or the producer // may be blocked waiting for the number of buffers in the queue to // decrease. - mCore->mDequeueCondition.broadcast(); + mCore->mDequeueCondition.notify_all(); ATRACE_INT(mCore->mConsumerName.string(), static_cast<int32_t>(mCore->mQueue.size())); @@ -272,7 +272,7 @@ VALIDATE_CONSISTENCY(); } - if (listener != NULL) { + if (listener != nullptr) { for (int i = 0; i < numDroppedBuffers; ++i) { listener->onBufferReleased(); } @@ -285,7 +285,7 @@ ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); BQ_LOGV("detachBuffer: slot %d", slot); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("detachBuffer: BufferQueue has been abandoned"); @@ -311,7 +311,7 @@ mCore->mActiveBuffers.erase(slot); mCore->mFreeSlots.insert(slot); mCore->clearBufferSlotLocked(slot); - mCore->mDequeueCondition.broadcast(); + mCore->mDequeueCondition.notify_all(); VALIDATE_CONSISTENCY(); return NO_ERROR; @@ -321,15 +321,15 @@ const sp<android::GraphicBuffer>& buffer) { ATRACE_CALL(); - if (outSlot == NULL) { + if (outSlot == nullptr) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; - } else if (buffer == NULL) { + } else if (buffer == nullptr) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); if (mCore->mSharedBufferMode) { BQ_LOGE("attachBuffer: cannot attach a buffer in shared buffer mode"); @@ -413,7 +413,7 @@ ATRACE_BUFFER_INDEX(slot); if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS || - releaseFence == NULL) { + releaseFence == nullptr) { BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot, releaseFence.get()); return BAD_VALUE; @@ -421,7 +421,7 @@ sp<IProducerListener> listener; { // Autolock scope - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); // If the frame number has changed because the buffer has been reallocated, // we can ignore this releaseBuffer for the old buffer. @@ -460,12 +460,12 @@ listener = mCore->mConnectedProducerListener; BQ_LOGV("releaseBuffer: releasing slot %d", slot); - mCore->mDequeueCondition.broadcast(); + mCore->mDequeueCondition.notify_all(); VALIDATE_CONSISTENCY(); } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBufferReleased(); } @@ -476,7 +476,7 @@ const sp<IConsumerListener>& consumerListener, bool controlledByApp) { ATRACE_CALL(); - if (consumerListener == NULL) { + if (consumerListener == nullptr) { BQ_LOGE("connect: consumerListener may not be NULL"); return BAD_VALUE; } @@ -484,7 +484,7 @@ BQ_LOGV("connect: controlledByApp=%s", controlledByApp ? "true" : "false"); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("connect: BufferQueue has been abandoned"); @@ -502,31 +502,31 @@ BQ_LOGV("disconnect"); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); - if (mCore->mConsumerListener == NULL) { + if (mCore->mConsumerListener == nullptr) { BQ_LOGE("disconnect: no consumer is connected"); return BAD_VALUE; } mCore->mIsAbandoned = true; - mCore->mConsumerListener = NULL; + mCore->mConsumerListener = nullptr; mCore->mQueue.clear(); mCore->freeAllBuffersLocked(); mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; - mCore->mDequeueCondition.broadcast(); + mCore->mDequeueCondition.notify_all(); return NO_ERROR; } status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) { ATRACE_CALL(); - if (outSlotMask == NULL) { + if (outSlotMask == nullptr) { BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL"); return BAD_VALUE; } - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("getReleasedBuffers: BufferQueue has been abandoned"); @@ -568,7 +568,7 @@ BQ_LOGV("setDefaultBufferSize: width=%u height=%u", width, height); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mDefaultWidth = width; mCore->mDefaultHeight = height; return NO_ERROR; @@ -582,7 +582,7 @@ return BAD_VALUE; } - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("setMaxBufferCount: producer is already connected"); @@ -622,8 +622,8 @@ sp<IConsumerListener> listener; { // Autolock scope - Mutex::Autolock lock(mCore->mMutex); - mCore->waitWhileAllocatingLocked(); + std::unique_lock<std::mutex> lock(mCore->mMutex); + mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { BQ_LOGE("setMaxAcquiredBufferCount: consumer is abandoned"); @@ -673,7 +673,7 @@ } } // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -683,7 +683,7 @@ status_t BufferQueueConsumer::setConsumerName(const String8& name) { ATRACE_CALL(); BQ_LOGV("setConsumerName: '%s'", name.string()); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mConsumerName = name; mConsumerName = name; return NO_ERROR; @@ -692,7 +692,7 @@ status_t BufferQueueConsumer::setDefaultBufferFormat(PixelFormat defaultFormat) { ATRACE_CALL(); BQ_LOGV("setDefaultBufferFormat: %u", defaultFormat); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mDefaultBufferFormat = defaultFormat; return NO_ERROR; } @@ -701,7 +701,7 @@ android_dataspace defaultDataSpace) { ATRACE_CALL(); BQ_LOGV("setDefaultBufferDataSpace: %u", defaultDataSpace); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mDefaultBufferDataSpace = defaultDataSpace; return NO_ERROR; } @@ -709,7 +709,7 @@ status_t BufferQueueConsumer::setConsumerUsageBits(uint64_t usage) { ATRACE_CALL(); BQ_LOGV("setConsumerUsageBits: %#" PRIx64, usage); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mConsumerUsageBits = usage; return NO_ERROR; } @@ -717,7 +717,7 @@ status_t BufferQueueConsumer::setConsumerIsProtected(bool isProtected) { ATRACE_CALL(); BQ_LOGV("setConsumerIsProtected: %s", isProtected ? "true" : "false"); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mConsumerIsProtected = isProtected; return NO_ERROR; } @@ -725,26 +725,26 @@ status_t BufferQueueConsumer::setTransformHint(uint32_t hint) { ATRACE_CALL(); BQ_LOGV("setTransformHint: %#x", hint); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mTransformHint = hint; return NO_ERROR; } status_t BufferQueueConsumer::getSidebandStream(sp<NativeHandle>* outStream) const { - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); *outStream = mCore->mSidebandStream; return NO_ERROR; } status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush, std::vector<OccupancyTracker::Segment>* outHistory) { - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); *outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush); return NO_ERROR; } status_t BufferQueueConsumer::discardFreeBuffers() { - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->discardFreeBuffersLocked(); return NO_ERROR; } @@ -758,21 +758,31 @@ return savedErrno ? -savedErrno : UNKNOWN_ERROR; } - const IPCThreadState* ipc = IPCThreadState::self(); - const uid_t uid = ipc->getCallingUid(); + bool denied = false; + const uid_t uid = BufferQueueThreadState::getCallingUid(); #ifndef __ANDROID_VNDK__ // permission check can't be done for vendors as vendors have no access to - // the PermissionController - const pid_t pid = ipc->getCallingPid(); - if ((uid != shellUid) && - !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) { - outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer " - "from pid=%d, uid=%d\n", pid, uid); + // the PermissionController. We need to do a runtime check as well, since + // the system variant of libgui can be loaded in a vendor process. For eg: + // if a HAL uses an llndk library that depends on libgui (libmediandk etc). + if (!android_is_in_vendor_process()) { + const pid_t pid = BufferQueueThreadState::getCallingPid(); + if ((uid != shellUid) && + !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) { + outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer " + "from pid=%d, uid=%d\n", + pid, uid); + denied = true; + } + } #else if (uid != shellUid) { + denied = true; + } #endif + if (denied) { android_errorWriteWithInfoLog(0x534e4554, "27046057", - static_cast<int32_t>(uid), NULL, 0); + static_cast<int32_t>(uid), nullptr, 0); return PERMISSION_DENIED; }
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index bb703da..e0e3431 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp
@@ -73,6 +73,8 @@ mActiveBuffers(), mDequeueCondition(), mDequeueBufferCannotBlock(false), + mQueueBufferCanDrop(false), + mLegacyBufferDrop(true), mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888), mDefaultWidth(1), mDefaultHeight(1), @@ -110,13 +112,15 @@ BufferQueueCore::~BufferQueueCore() {} void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const { - Mutex::Autolock lock(mMutex); + std::lock_guard<std::mutex> lock(mMutex); outResult->appendFormat("%s- BufferQueue ", prefix.string()); outResult->appendFormat("mMaxAcquiredBufferCount=%d mMaxDequeuedBufferCount=%d\n", mMaxAcquiredBufferCount, mMaxDequeuedBufferCount); outResult->appendFormat("%s mDequeueBufferCannotBlock=%d mAsyncMode=%d\n", prefix.string(), mDequeueBufferCannotBlock, mAsyncMode); + outResult->appendFormat("%s mQueueBufferCanDrop=%d mLegacyBufferDrop=%d\n", prefix.string(), + mQueueBufferCanDrop, mLegacyBufferDrop); outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.string(), mDefaultWidth, mDefaultHeight, mDefaultBufferFormat); outResult->appendFormat("transform-hint=%02x frame-counter=%" PRIu64, mTransformHint, @@ -306,10 +310,10 @@ return true; } -void BufferQueueCore::waitWhileAllocatingLocked() const { +void BufferQueueCore::waitWhileAllocatingLocked(std::unique_lock<std::mutex>& lock) const { ATRACE_CALL(); while (mIsAllocating) { - mIsAllocatingCondition.wait(mMutex); + mIsAllocatingCondition.wait(lock); } } @@ -349,7 +353,7 @@ BQ_LOGE("Slot %d is in mUnusedSlots but is not FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer != NULL) { + if (mSlots[slot].mGraphicBuffer != nullptr) { BQ_LOGE("Slot %d is in mUnusedSluts but has an active buffer", slot); usleep(PAUSE_TIME); @@ -371,7 +375,7 @@ BQ_LOGE("Slot %d is in mFreeSlots but is not FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer != NULL) { + if (mSlots[slot].mGraphicBuffer != nullptr) { BQ_LOGE("Slot %d is in mFreeSlots but has a buffer", slot); usleep(PAUSE_TIME); @@ -394,7 +398,7 @@ BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer == NULL) { + if (mSlots[slot].mGraphicBuffer == nullptr) { BQ_LOGE("Slot %d is in mFreeBuffers but has no buffer", slot); usleep(PAUSE_TIME); } @@ -418,7 +422,7 @@ BQ_LOGE("Slot %d is in mActiveBuffers but is FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer == NULL && !mIsAllocating) { + if (mSlots[slot].mGraphicBuffer == nullptr && !mIsAllocating) { BQ_LOGE("Slot %d is in mActiveBuffers but has no buffer", slot); usleep(PAUSE_TIME); }
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 9d7f14c..9c311a3 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp
@@ -35,6 +35,7 @@ #include <gui/GLConsumer.h> #include <gui/IConsumerListener.h> #include <gui/IProducerListener.h> +#include <private/gui/BufferQueueThreadState.h> #include <utils/Log.h> #include <utils/Trace.h> @@ -58,14 +59,15 @@ mNextCallbackTicket(0), mCurrentCallbackTicket(0), mCallbackCondition(), - mDequeueTimeout(-1) {} + mDequeueTimeout(-1), + mDequeueWaitingForAllocation(false) {} BufferQueueProducer::~BufferQueueProducer() {} status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { ATRACE_CALL(); BQ_LOGV("requestBuffer: slot %d", slot); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("requestBuffer: BufferQueue has been abandoned"); @@ -100,8 +102,8 @@ sp<IConsumerListener> listener; { // Autolock scope - Mutex::Autolock lock(mCore->mMutex); - mCore->waitWhileAllocatingLocked(); + std::unique_lock<std::mutex> lock(mCore->mMutex); + mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { BQ_LOGE("setMaxDequeuedBufferCount: BufferQueue has been " @@ -162,11 +164,11 @@ if (delta < 0) { listener = mCore->mConsumerListener; } - mCore->mDequeueCondition.broadcast(); + mCore->mDequeueCondition.notify_all(); } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -179,8 +181,8 @@ sp<IConsumerListener> listener; { // Autolock scope - Mutex::Autolock lock(mCore->mMutex); - mCore->waitWhileAllocatingLocked(); + std::unique_lock<std::mutex> lock(mCore->mMutex); + mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { BQ_LOGE("setAsyncMode: BufferQueue has been abandoned"); @@ -214,14 +216,14 @@ } mCore->mAsyncMode = async; VALIDATE_CONSISTENCY(); - mCore->mDequeueCondition.broadcast(); + mCore->mDequeueCondition.notify_all(); if (delta < 0) { listener = mCore->mConsumerListener; } } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; @@ -246,7 +248,7 @@ } status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller, - int* found) const { + std::unique_lock<std::mutex>& lock, int* found) const { auto callerString = (caller == FreeSlotCaller::Dequeue) ? "dequeueBuffer" : "attachBuffer"; bool tryAgain = true; @@ -272,8 +274,12 @@ // This check is only done if a buffer has already been queued if (mCore->mBufferHasBeenQueued && dequeuedCount >= mCore->mMaxDequeuedBufferCount) { - BQ_LOGE("%s: attempting to exceed the max dequeued buffer count " - "(%d)", callerString, mCore->mMaxDequeuedBufferCount); + // Supress error logs when timeout is non-negative. + if (mDequeueTimeout < 0) { + BQ_LOGE("%s: attempting to exceed the max dequeued buffer " + "count (%d)", callerString, + mCore->mMaxDequeuedBufferCount); + } return INVALID_OPERATION; } @@ -333,13 +339,13 @@ return WOULD_BLOCK; } if (mDequeueTimeout >= 0) { - status_t result = mCore->mDequeueCondition.waitRelative( - mCore->mMutex, mDequeueTimeout); - if (result == TIMED_OUT) { - return result; + std::cv_status result = mCore->mDequeueCondition.wait_for(lock, + std::chrono::nanoseconds(mDequeueTimeout)); + if (result == std::cv_status::timeout) { + return TIMED_OUT; } } else { - mCore->mDequeueCondition.wait(mCore->mMutex); + mCore->mDequeueCondition.wait(lock); } } } // while (tryAgain) @@ -353,7 +359,7 @@ FrameEventHistoryDelta* outTimestamps) { ATRACE_CALL(); { // Autolock scope - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; if (mCore->mIsAbandoned) { @@ -380,7 +386,16 @@ bool attachedByConsumer = false; { // Autolock scope - Mutex::Autolock lock(mCore->mMutex); + std::unique_lock<std::mutex> lock(mCore->mMutex); + + // If we don't have a free buffer, but we are currently allocating, we wait until allocation + // is finished such that we don't allocate in parallel. + if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) { + mDequeueWaitingForAllocation = true; + mCore->waitWhileAllocatingLocked(lock); + mDequeueWaitingForAllocation = false; + mDequeueWaitingForAllocationCondition.notify_all(); + } if (format == 0) { format = mCore->mDefaultBufferFormat; @@ -397,8 +412,7 @@ int found = BufferItem::INVALID_BUFFER_SLOT; while (found == BufferItem::INVALID_BUFFER_SLOT) { - status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, - &found); + status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found); if (status != NO_ERROR) { return status; } @@ -449,11 +463,11 @@ mSlots[found].mBufferState.dequeue(); - if ((buffer == NULL) || + if ((buffer == nullptr) || buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) { mSlots[found].mAcquireCalled = false; - mSlots[found].mGraphicBuffer = NULL; + mSlots[found].mGraphicBuffer = nullptr; mSlots[found].mRequestBufferCalled = false; mSlots[found].mEglDisplay = EGL_NO_DISPLAY; mSlots[found].mEglFence = EGL_NO_SYNC_KHR; @@ -471,7 +485,7 @@ BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64, mCore->mBufferAge); - if (CC_UNLIKELY(mSlots[found].mFence == NULL)) { + if (CC_UNLIKELY(mSlots[found].mFence == nullptr)) { BQ_LOGE("dequeueBuffer: about to return a NULL fence - " "slot=%d w=%d h=%d format=%u", found, buffer->width, buffer->height, buffer->format); @@ -505,7 +519,7 @@ status_t error = graphicBuffer->initCheck(); { // Autolock scope - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); if (error == NO_ERROR && !mCore->mIsAbandoned) { graphicBuffer->setGenerationNumber(mCore->mGenerationNumber); @@ -513,7 +527,7 @@ } mCore->mIsAllocating = false; - mCore->mIsAllocatingCondition.broadcast(); + mCore->mIsAllocatingCondition.notify_all(); if (error != NO_ERROR) { mCore->mFreeSlots.insert(*outSlot); @@ -572,7 +586,7 @@ sp<IConsumerListener> listener; { - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("detachBuffer: BufferQueue has been abandoned"); @@ -607,12 +621,12 @@ mCore->mActiveBuffers.erase(slot); mCore->mFreeSlots.insert(slot); mCore->clearBufferSlotLocked(slot); - mCore->mDequeueCondition.broadcast(); + mCore->mDequeueCondition.notify_all(); VALIDATE_CONSISTENCY(); listener = mCore->mConsumerListener; } - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -623,17 +637,17 @@ sp<Fence>* outFence) { ATRACE_CALL(); - if (outBuffer == NULL) { + if (outBuffer == nullptr) { BQ_LOGE("detachNextBuffer: outBuffer must not be NULL"); return BAD_VALUE; - } else if (outFence == NULL) { + } else if (outFence == nullptr) { BQ_LOGE("detachNextBuffer: outFence must not be NULL"); return BAD_VALUE; } sp<IConsumerListener> listener; { - Mutex::Autolock lock(mCore->mMutex); + std::unique_lock<std::mutex> lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned"); @@ -651,7 +665,7 @@ return BAD_VALUE; } - mCore->waitWhileAllocatingLocked(); + mCore->waitWhileAllocatingLocked(lock); if (mCore->mFreeBuffers.empty()) { return NO_MEMORY; @@ -670,7 +684,7 @@ listener = mCore->mConsumerListener; } - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -681,15 +695,15 @@ const sp<android::GraphicBuffer>& buffer) { ATRACE_CALL(); - if (outSlot == NULL) { + if (outSlot == nullptr) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; - } else if (buffer == NULL) { + } else if (buffer == nullptr) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } - Mutex::Autolock lock(mCore->mMutex); + std::unique_lock<std::mutex> lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("attachBuffer: BufferQueue has been abandoned"); @@ -713,11 +727,11 @@ return BAD_VALUE; } - mCore->waitWhileAllocatingLocked(); + mCore->waitWhileAllocatingLocked(lock); status_t returnFlags = NO_ERROR; int found; - status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, &found); + status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Attach, lock, &found); if (status != NO_ERROR) { return status; } @@ -766,7 +780,7 @@ const Region& surfaceDamage = input.getSurfaceDamage(); const HdrMetadata& hdrMetadata = input.getHdrMetadata(); - if (acquireFence == NULL) { + if (acquireFence == nullptr) { BQ_LOGE("queueBuffer: fence is NULL"); return BAD_VALUE; } @@ -790,7 +804,7 @@ uint64_t currentFrameNumber = 0; BufferItem item; { // Autolock scope - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("queueBuffer: BufferQueue has been abandoned"); @@ -872,7 +886,8 @@ item.mFence = acquireFence; item.mFenceTime = acquireFenceTime; item.mIsDroppable = mCore->mAsyncMode || - mCore->mDequeueBufferCannotBlock || + (mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) || + (mCore->mLegacyBufferDrop && mCore->mQueueBufferCanDrop) || (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot); item.mSurfaceDamage = surfaceDamage; item.mQueuedBuffer = true; @@ -931,7 +946,7 @@ } mCore->mBufferHasBeenQueued = true; - mCore->mDequeueCondition.broadcast(); + mCore->mDequeueCondition.notify_all(); mCore->mLastQueuedSlot = slot; output->width = mCore->mDefaultWidth; @@ -957,9 +972,6 @@ item.mGraphicBuffer.clear(); } - // Don't send the slot number through the callback since the consumer shouldn't need it - item.mSlot = BufferItem::INVALID_BUFFER_SLOT; - // Call back without the main BufferQueue lock held, but with the callback // lock held so we can ensure that callbacks occur in order @@ -967,14 +979,14 @@ sp<Fence> lastQueuedFence; { // scope for the lock - Mutex::Autolock lock(mCallbackMutex); + std::unique_lock<std::mutex> lock(mCallbackMutex); while (callbackTicket != mCurrentCallbackTicket) { - mCallbackCondition.wait(mCallbackMutex); + mCallbackCondition.wait(lock); } - if (frameAvailableListener != NULL) { + if (frameAvailableListener != nullptr) { frameAvailableListener->onFrameAvailable(item); - } else if (frameReplacedListener != NULL) { + } else if (frameReplacedListener != nullptr) { frameReplacedListener->onFrameReplaced(item); } @@ -986,7 +998,7 @@ mLastQueuedTransform = item.mTransform; ++mCurrentCallbackTicket; - mCallbackCondition.broadcast(); + mCallbackCondition.notify_all(); } // Update and get FrameEventHistory. @@ -1014,7 +1026,7 @@ status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) { ATRACE_CALL(); BQ_LOGV("cancelBuffer: slot %d", slot); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); if (mCore->mIsAbandoned) { BQ_LOGE("cancelBuffer: BufferQueue has been abandoned"); @@ -1039,7 +1051,7 @@ BQ_LOGE("cancelBuffer: slot %d is not owned by the producer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; - } else if (fence == NULL) { + } else if (fence == nullptr) { BQ_LOGE("cancelBuffer: fence is NULL"); return BAD_VALUE; } @@ -1059,7 +1071,7 @@ } mSlots[slot].mFence = fence; - mCore->mDequeueCondition.broadcast(); + mCore->mDequeueCondition.notify_all(); VALIDATE_CONSISTENCY(); return NO_ERROR; @@ -1067,9 +1079,9 @@ int BufferQueueProducer::query(int what, int *outValue) { ATRACE_CALL(); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); - if (outValue == NULL) { + if (outValue == nullptr) { BQ_LOGE("query: outValue was NULL"); return BAD_VALUE; } @@ -1135,7 +1147,7 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp, QueueBufferOutput *output) { ATRACE_CALL(); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; BQ_LOGV("connect: api=%d producerControlledByApp=%s", api, producerControlledByApp ? "true" : "false"); @@ -1145,12 +1157,12 @@ return NO_INIT; } - if (mCore->mConsumerListener == NULL) { + if (mCore->mConsumerListener == nullptr) { BQ_LOGE("connect: BufferQueue has no consumer"); return NO_INIT; } - if (output == NULL) { + if (output == nullptr) { BQ_LOGE("connect: output was NULL"); return BAD_VALUE; } @@ -1188,10 +1200,10 @@ output->nextFrameNumber = mCore->mFrameCounter + 1; output->bufferReplaced = false; - if (listener != NULL) { + if (listener != nullptr) { // Set up a death notification so that we can disconnect // automatically if the remote producer dies - if (IInterface::asBinder(listener)->remoteBinder() != NULL) { + if (IInterface::asBinder(listener)->remoteBinder() != nullptr) { status = IInterface::asBinder(listener)->linkToDeath( static_cast<IBinder::DeathRecipient*>(this)); if (status != NO_ERROR) { @@ -1210,12 +1222,14 @@ status = BAD_VALUE; break; } - mCore->mConnectedPid = IPCThreadState::self()->getCallingPid(); + mCore->mConnectedPid = BufferQueueThreadState::getCallingPid(); mCore->mBufferHasBeenQueued = false; mCore->mDequeueBufferCannotBlock = false; - if (mDequeueTimeout < 0) { - mCore->mDequeueBufferCannotBlock = - mCore->mConsumerControlledByApp && producerControlledByApp; + mCore->mQueueBufferCanDrop = false; + mCore->mLegacyBufferDrop = true; + if (mCore->mConsumerControlledByApp && producerControlledByApp) { + mCore->mDequeueBufferCannotBlock = mDequeueTimeout < 0; + mCore->mQueueBufferCanDrop = mDequeueTimeout <= 0; } mCore->mAllowAllocation = true; @@ -1230,16 +1244,16 @@ int status = NO_ERROR; sp<IConsumerListener> listener; { // Autolock scope - Mutex::Autolock lock(mCore->mMutex); + std::unique_lock<std::mutex> lock(mCore->mMutex); if (mode == DisconnectMode::AllLocal) { - if (IPCThreadState::self()->getCallingPid() != mCore->mConnectedPid) { + if (BufferQueueThreadState::getCallingPid() != mCore->mConnectedPid) { return NO_ERROR; } api = BufferQueueCore::CURRENTLY_CONNECTED_API; } - mCore->waitWhileAllocatingLocked(); + mCore->waitWhileAllocatingLocked(lock); if (mCore->mIsAbandoned) { // It's not really an error to disconnect after the surface has @@ -1268,7 +1282,7 @@ mCore->freeAllBuffersLocked(); // Remove our death notification callback if we have one - if (mCore->mLinkedToDeath != NULL) { + if (mCore->mLinkedToDeath != nullptr) { sp<IBinder> token = IInterface::asBinder(mCore->mLinkedToDeath); // This can fail if we're here because of the death @@ -1278,12 +1292,12 @@ } mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; - mCore->mLinkedToDeath = NULL; - mCore->mConnectedProducerListener = NULL; + mCore->mLinkedToDeath = nullptr; + mCore->mConnectedProducerListener = nullptr; mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API; mCore->mConnectedPid = -1; mCore->mSidebandStream.clear(); - mCore->mDequeueCondition.broadcast(); + mCore->mDequeueCondition.notify_all(); listener = mCore->mConsumerListener; } else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("disconnect: not connected (req=%d)", api); @@ -1302,7 +1316,7 @@ } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); listener->onDisconnect(); } @@ -1313,12 +1327,12 @@ status_t BufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) { sp<IConsumerListener> listener; { // Autolock scope - Mutex::Autolock _l(mCore->mMutex); + std::lock_guard<std::mutex> _l(mCore->mMutex); mCore->mSidebandStream = stream; listener = mCore->mConsumerListener; } // Autolock scope - if (listener != NULL) { + if (listener != nullptr) { listener->onSidebandStreamChanged(); } return NO_ERROR; @@ -1335,8 +1349,8 @@ uint64_t allocUsage = 0; std::string allocName; { // Autolock scope - Mutex::Autolock lock(mCore->mMutex); - mCore->waitWhileAllocatingLocked(); + std::unique_lock<std::mutex> lock(mCore->mMutex); + mCore->waitWhileAllocatingLocked(lock); if (!mCore->mAllowAllocation) { BQ_LOGE("allocateBuffers: allocation is not allowed for this " @@ -1371,16 +1385,16 @@ if (result != NO_ERROR) { BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format" " %u, usage %#" PRIx64 ")", width, height, format, usage); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mIsAllocating = false; - mCore->mIsAllocatingCondition.broadcast(); + mCore->mIsAllocatingCondition.notify_all(); return; } buffers.push_back(graphicBuffer); } { // Autolock scope - Mutex::Autolock lock(mCore->mMutex); + std::unique_lock<std::mutex> lock(mCore->mMutex); uint32_t checkWidth = width > 0 ? width : mCore->mDefaultWidth; uint32_t checkHeight = height > 0 ? height : mCore->mDefaultHeight; PixelFormat checkFormat = format != 0 ? @@ -1391,7 +1405,7 @@ // Something changed while we released the lock. Retry. BQ_LOGV("allocateBuffers: size/format/usage changed while allocating. Retrying."); mCore->mIsAllocating = false; - mCore->mIsAllocatingCondition.broadcast(); + mCore->mIsAllocatingCondition.notify_all(); continue; } @@ -1419,8 +1433,14 @@ } mCore->mIsAllocating = false; - mCore->mIsAllocatingCondition.broadcast(); + mCore->mIsAllocatingCondition.notify_all(); VALIDATE_CONSISTENCY(); + + // If dequeue is waiting for to allocate a buffer, release the lock until it's not + // waiting anymore so it can use the buffer we just allocated. + while (mDequeueWaitingForAllocation) { + mDequeueWaitingForAllocationCondition.wait(lock); + } } // Autolock scope } } @@ -1429,7 +1449,7 @@ ATRACE_CALL(); BQ_LOGV("allowAllocation: %s", allow ? "true" : "false"); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mAllowAllocation = allow; return NO_ERROR; } @@ -1438,14 +1458,14 @@ ATRACE_CALL(); BQ_LOGV("setGenerationNumber: %u", generationNumber); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mGenerationNumber = generationNumber; return NO_ERROR; } String8 BufferQueueProducer::getConsumerName() const { ATRACE_CALL(); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); BQ_LOGV("getConsumerName: %s", mConsumerName.string()); return mConsumerName; } @@ -1454,7 +1474,7 @@ ATRACE_CALL(); BQ_LOGV("setSharedBufferMode: %d", sharedBufferMode); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); if (!sharedBufferMode) { mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; } @@ -1466,7 +1486,7 @@ ATRACE_CALL(); BQ_LOGV("setAutoRefresh: %d", autoRefresh); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); mCore->mAutoRefresh = autoRefresh; return NO_ERROR; @@ -1476,8 +1496,10 @@ ATRACE_CALL(); BQ_LOGV("setDequeueTimeout: %" PRId64, timeout); - Mutex::Autolock lock(mCore->mMutex); - int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, false, + std::lock_guard<std::mutex> lock(mCore->mMutex); + bool dequeueBufferCannotBlock = + timeout >= 0 ? false : mCore->mDequeueBufferCannotBlock; + int delta = mCore->getMaxBufferCountLocked(mCore->mAsyncMode, dequeueBufferCannotBlock, mCore->mMaxBufferCount) - mCore->getMaxBufferCountLocked(); if (!mCore->adjustAvailableSlotsLocked(delta)) { BQ_LOGE("setDequeueTimeout: BufferQueue failed to adjust the number of " @@ -1486,18 +1508,30 @@ } mDequeueTimeout = timeout; - mCore->mDequeueBufferCannotBlock = false; + mCore->mDequeueBufferCannotBlock = dequeueBufferCannotBlock; + if (timeout > 0) { + mCore->mQueueBufferCanDrop = false; + } VALIDATE_CONSISTENCY(); return NO_ERROR; } +status_t BufferQueueProducer::setLegacyBufferDrop(bool drop) { + ATRACE_CALL(); + BQ_LOGV("setLegacyBufferDrop: drop = %d", drop); + + std::lock_guard<std::mutex> lock(mCore->mMutex); + mCore->mLegacyBufferDrop = drop; + return NO_ERROR; +} + status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) { ATRACE_CALL(); BQ_LOGV("getLastQueuedBuffer"); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; @@ -1533,10 +1567,10 @@ BQ_LOGV("addAndGetFrameTimestamps"); sp<IConsumerListener> listener; { - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); listener = mCore->mConsumerListener; } - if (listener != NULL) { + if (listener != nullptr) { listener->addAndGetFrameTimestamps(newTimestamps, outDelta); } } @@ -1560,7 +1594,7 @@ status_t BufferQueueProducer::getConsumerUsage(uint64_t* outUsage) const { BQ_LOGV("getConsumerUsage"); - Mutex::Autolock lock(mCore->mMutex); + std::lock_guard<std::mutex> lock(mCore->mMutex); *outUsage = mCore->mConsumerUsageBits; return NO_ERROR; }
diff --git a/libs/gui/BufferQueueThreadState.cpp b/libs/gui/BufferQueueThreadState.cpp new file mode 100644 index 0000000..3b531ec --- /dev/null +++ b/libs/gui/BufferQueueThreadState.cpp
@@ -0,0 +1,38 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/IPCThreadState.h> +#include <hwbinder/IPCThreadState.h> +#include <private/gui/BufferQueueThreadState.h> +#include <unistd.h> + +namespace android { + +uid_t BufferQueueThreadState::getCallingUid() { + if (hardware::IPCThreadState::self()->isServingCall()) { + return hardware::IPCThreadState::self()->getCallingUid(); + } + return IPCThreadState::self()->getCallingUid(); +} + +pid_t BufferQueueThreadState::getCallingPid() { + if (hardware::IPCThreadState::self()->isServingCall()) { + return hardware::IPCThreadState::self()->getCallingPid(); + } + return IPCThreadState::self()->getCallingPid(); +} + +} // namespace android
diff --git a/libs/gui/CleanSpec.mk b/libs/gui/CleanSpec.mk deleted file mode 100644 index 5a5144c..0000000 --- a/libs/gui/CleanSpec.mk +++ /dev/null
@@ -1,52 +0,0 @@ -# Copyright (C) 2012 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. -# - -# If you don't need to do a full clean build but would like to touch -# a file or delete some intermediate files, add a clean step to the end -# of the list. These steps will only be run once, if they haven't been -# run before. -# -# E.g.: -# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) -# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) -# -# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with -# files that are missing or have been moved. -# -# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. -# Use $(OUT_DIR) to refer to the "out" directory. -# -# If you need to re-do something that's already mentioned, just copy -# the command and add it to the bottom of the list. E.g., if a change -# that you made last week required touching a file and a change you -# made today requires touching the same file, just copy the old -# touch step and add it to the end of the list. -# -# ************************************************ -# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST -# ************************************************ - -# For example: -#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) -#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) -#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) -#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) - -# ************************************************ -# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST -# ************************************************ -$(call add-clean-step, find $(PRODUCT_OUT) -type f -name "libgui*" -print0 | xargs -0 rm -f) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgui_intermediates) -$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libgui_intermediates)
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index f9e292e..abd9921 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp
@@ -96,7 +96,7 @@ void ConsumerBase::freeBufferLocked(int slotIndex) { CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); - mSlots[slotIndex].mGraphicBuffer = 0; + mSlots[slotIndex].mGraphicBuffer = nullptr; mSlots[slotIndex].mFence = Fence::NO_FENCE; mSlots[slotIndex].mFrameNumber = 0; } @@ -110,7 +110,7 @@ listener = mFrameAvailableListener.promote(); } - if (listener != NULL) { + if (listener != nullptr) { CB_LOGV("actually calling onFrameAvailable"); listener->onFrameAvailable(item); } @@ -125,7 +125,7 @@ listener = mFrameAvailableListener.promote(); } - if (listener != NULL) { + if (listener != nullptr) { CB_LOGV("actually calling onFrameReplaced"); listener->onFrameReplaced(item); } @@ -352,8 +352,8 @@ return err; } - if (item->mGraphicBuffer != NULL) { - if (mSlots[item->mSlot].mGraphicBuffer != NULL) { + if (item->mGraphicBuffer != nullptr) { + if (mSlots[item->mSlot].mGraphicBuffer != nullptr) { freeBufferLocked(item->mSlot); } mSlots[item->mSlot].mGraphicBuffer = item->mGraphicBuffer; @@ -468,7 +468,7 @@ if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) { return false; } - return (mSlots[slot].mGraphicBuffer != NULL && + return (mSlots[slot].mGraphicBuffer != nullptr && mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle); }
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp index 1757ec1..f5cf1c4 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp
@@ -34,9 +34,9 @@ DisplayEventReceiver::DisplayEventReceiver(ISurfaceComposer::VsyncSource vsyncSource) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - if (sf != NULL) { + if (sf != nullptr) { mEventConnection = sf->createDisplayEventConnection(vsyncSource); - if (mEventConnection != NULL) { + if (mEventConnection != nullptr) { mDataChannel = std::make_unique<gui::BitTube>(); mEventConnection->stealReceiveChannel(mDataChannel.get()); } @@ -47,13 +47,13 @@ } status_t DisplayEventReceiver::initCheck() const { - if (mDataChannel != NULL) + if (mDataChannel != nullptr) return NO_ERROR; return NO_INIT; } int DisplayEventReceiver::getFd() const { - if (mDataChannel == NULL) + if (mDataChannel == nullptr) return NO_INIT; return mDataChannel->getFd(); @@ -63,7 +63,7 @@ if (int32_t(count) < 0) return BAD_VALUE; - if (mEventConnection != NULL) { + if (mEventConnection != nullptr) { mEventConnection->setVsyncRate(count); return NO_ERROR; } @@ -71,7 +71,7 @@ } status_t DisplayEventReceiver::requestNextVsync() { - if (mEventConnection != NULL) { + if (mEventConnection != nullptr) { mEventConnection->requestNextVsync(); return NO_ERROR; }
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp index a379ad6..c04d907 100644 --- a/libs/gui/FrameTimestamps.cpp +++ b/libs/gui/FrameTimestamps.cpp
@@ -18,10 +18,10 @@ #define LOG_TAG "FrameEvents" +#include <android-base/stringprintf.h> #include <cutils/compiler.h> // For CC_[UN]LIKELY #include <inttypes.h> #include <utils/Log.h> -#include <utils/String8.h> #include <algorithm> #include <limits> @@ -29,6 +29,7 @@ namespace android { +using base::StringAppendF; // ============================================================================ // FrameEvents @@ -86,50 +87,49 @@ releaseFence->getSignalTime(); } -static void dumpFenceTime(String8& outString, const char* name, - bool pending, const FenceTime& fenceTime) { - outString.appendFormat("--- %s", name); +static void dumpFenceTime(std::string& outString, const char* name, bool pending, + const FenceTime& fenceTime) { + StringAppendF(&outString, "--- %s", name); nsecs_t signalTime = fenceTime.getCachedSignalTime(); if (Fence::isValidTimestamp(signalTime)) { - outString.appendFormat("%" PRId64 "\n", signalTime); + StringAppendF(&outString, "%" PRId64 "\n", signalTime); } else if (pending || signalTime == Fence::SIGNAL_TIME_PENDING) { - outString.appendFormat("Pending\n"); + outString.append("Pending\n"); } else if (&fenceTime == FenceTime::NO_FENCE.get()){ - outString.appendFormat("N/A\n"); + outString.append("N/A\n"); } else { - outString.appendFormat("Error\n"); + outString.append("Error\n"); } } -void FrameEvents::dump(String8& outString) const -{ +void FrameEvents::dump(std::string& outString) const { if (!valid) { return; } - outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber); - outString.appendFormat("--- Posted \t%" PRId64 "\n", postedTime); - outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime); + StringAppendF(&outString, "-- Frame %" PRIu64 "\n", frameNumber); + StringAppendF(&outString, "--- Posted \t%" PRId64 "\n", postedTime); + StringAppendF(&outString, "--- Req. Present\t%" PRId64 "\n", requestedPresentTime); - outString.appendFormat("--- Latched \t"); + outString.append("--- Latched \t"); if (FrameEvents::isValidTimestamp(latchTime)) { - outString.appendFormat("%" PRId64 "\n", latchTime); + StringAppendF(&outString, "%" PRId64 "\n", latchTime); } else { - outString.appendFormat("Pending\n"); + outString.append("Pending\n"); } - outString.appendFormat("--- Refresh (First)\t"); + outString.append("--- Refresh (First)\t"); if (FrameEvents::isValidTimestamp(firstRefreshStartTime)) { - outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime); + StringAppendF(&outString, "%" PRId64 "\n", firstRefreshStartTime); } else { - outString.appendFormat("Pending\n"); + outString.append("Pending\n"); } - outString.appendFormat("--- Refresh (Last)\t"); + outString.append("--- Refresh (Last)\t"); if (FrameEvents::isValidTimestamp(lastRefreshStartTime)) { - outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime); + StringAppendF(&outString, "%" PRId64 "\n", lastRefreshStartTime); } else { - outString.appendFormat("Pending\n"); + outString.append("Pending\n"); } dumpFenceTime(outString, "Acquire \t", @@ -139,11 +139,11 @@ dumpFenceTime(outString, "Display Present \t", !addPostCompositeCalled, *displayPresentFence); - outString.appendFormat("--- DequeueReady \t"); + outString.append("--- DequeueReady \t"); if (FrameEvents::isValidTimestamp(dequeueReadyTime)) { - outString.appendFormat("%" PRId64 "\n", dequeueReadyTime); + StringAppendF(&outString, "%" PRId64 "\n", dequeueReadyTime); } else { - outString.appendFormat("Pending\n"); + outString.append("Pending\n"); } dumpFenceTime(outString, "Release \t", @@ -158,7 +158,7 @@ namespace { struct FrameNumberEqual { - FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {} + explicit FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {} bool operator()(const FrameEvents& frame) { return frame.valid && mFrameNumber == frame.frameNumber; } @@ -206,11 +206,11 @@ return lhs.valid; } -void FrameEventHistory::dump(String8& outString) const { +void FrameEventHistory::dump(std::string& outString) const { auto earliestFrame = std::min_element( mFrames.begin(), mFrames.end(), &FrameNumberLessThan); if (!earliestFrame->valid) { - outString.appendFormat("-- N/A\n"); + outString.append("-- N/A\n"); return; } for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) { @@ -621,7 +621,7 @@ // ============================================================================ FrameEventHistoryDelta& FrameEventHistoryDelta::operator=( - FrameEventHistoryDelta&& src) { + FrameEventHistoryDelta&& src) noexcept { mCompositorTiming = src.mCompositorTiming; if (CC_UNLIKELY(!mDeltas.empty())) {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 885efec..8d66154 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp
@@ -46,7 +46,6 @@ #include <utils/Trace.h> extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); -#define CROP_EXT_STR "EGL_ANDROID_image_crop" #define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" #define EGL_PROTECTED_CONTENT_EXT 0x32C0 @@ -82,26 +81,6 @@ Mutex GLConsumer::sStaticInitLock; sp<GraphicBuffer> GLConsumer::sReleasedTexImageBuffer; -static bool hasEglAndroidImageCropImpl() { - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); - size_t cropExtLen = strlen(CROP_EXT_STR); - size_t extsLen = strlen(exts); - bool equal = !strcmp(CROP_EXT_STR, exts); - bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1); - bool atEnd = (cropExtLen+1) < extsLen && - !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1)); - bool inMiddle = strstr(exts, " " CROP_EXT_STR " "); - return equal || atStart || atEnd || inMiddle; -} - -static bool hasEglAndroidImageCrop() { - // Only compute whether the extension is present once the first time this - // function is called. - static bool hasIt = hasEglAndroidImageCropImpl(); - return hasIt; -} - static bool hasEglProtectedContentImpl() { EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); @@ -122,10 +101,6 @@ return hasIt; } -static bool isEglImageCroppable(const Rect& crop) { - return hasEglAndroidImageCrop() && (crop.left == 0 && crop.top == 0); -} - GLConsumer::GLConsumer(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texTarget, bool useFenceSync, bool isControlledByApp) : ConsumerBase(bq, isControlledByApp), @@ -291,7 +266,7 @@ return err; } - if (mReleasedTexImage == NULL) { + if (mReleasedTexImage == nullptr) { mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); } @@ -321,7 +296,7 @@ sp<GraphicBuffer> GLConsumer::getDebugTexImageBuffer() { Mutex::Autolock _l(sStaticInitLock); - if (CC_UNLIKELY(sReleasedTexImageBuffer == NULL)) { + if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) { // The first time, create the debug texture in case the application // continues to use it. sp<GraphicBuffer> buffer = new GraphicBuffer( @@ -357,7 +332,7 @@ // If item->mGraphicBuffer is not null, this buffer has not been acquired // before, so any prior EglImage created is using a stale buffer. This // replaces any old EglImage with a new one (using the new buffer). - if (item->mGraphicBuffer != NULL) { + if (item->mGraphicBuffer != nullptr) { int slot = item->mSlot; mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer); } @@ -406,7 +381,7 @@ // ConsumerBase. // We may have to do this even when item.mGraphicBuffer == NULL (which // means the buffer was previously acquired). - err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay, item.mCrop); + err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay); if (err != NO_ERROR) { GLC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, slot); @@ -430,8 +405,8 @@ } GLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", - mCurrentTexture, mCurrentTextureImage != NULL ? - mCurrentTextureImage->graphicBufferHandle() : 0, + mCurrentTexture, mCurrentTextureImage != nullptr ? + mCurrentTextureImage->graphicBufferHandle() : nullptr, slot, mSlots[slot].mGraphicBuffer->handle); // Hang onto the pointer so that it isn't freed in the call to @@ -491,13 +466,12 @@ glBindTexture(mTexTarget, mTexName); if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && - mCurrentTextureImage == NULL) { + mCurrentTextureImage == nullptr) { GLC_LOGE("bindTextureImage: no currently-bound texture"); return NO_INIT; } - status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay, - mCurrentCrop); + status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay); if (err != NO_ERROR) { GLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, mCurrentTexture); @@ -511,9 +485,7 @@ // forcing the creation of a new image. if ((error = glGetError()) != GL_NO_ERROR) { glBindTexture(mTexTarget, mTexName); - status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, - mCurrentCrop, - true); + status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true); if (result != NO_ERROR) { GLC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, mCurrentTexture); @@ -655,7 +627,7 @@ mTexName = tex; mAttached = true; - if (mCurrentTextureImage != NULL) { + if (mCurrentTextureImage != nullptr) { // This may wait for a buffer a second time. This is likely required if // this is a different context, since otherwise the wait could be skipped // by bouncing through another context. For the same context the extra @@ -676,7 +648,7 @@ if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (SyncFeatures::getInstance().useNativeFenceSync()) { EGLSyncKHR sync = eglCreateSyncKHR(dpy, - EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); + EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); if (sync == EGL_NO_SYNC_KHR) { GLC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError()); @@ -720,7 +692,7 @@ // Create a fence for the outstanding accesses in the current // OpenGL ES context. - fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); + fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); if (fence == EGL_NO_SYNC_KHR) { GLC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError()); @@ -752,11 +724,11 @@ bool needsRecompute = mFilteringEnabled != enabled; mFilteringEnabled = enabled; - if (needsRecompute && mCurrentTextureImage==NULL) { + if (needsRecompute && mCurrentTextureImage==nullptr) { GLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL"); } - if (needsRecompute && mCurrentTextureImage != NULL) { + if (needsRecompute && mCurrentTextureImage != nullptr) { computeCurrentTransformMatrixLocked(); } } @@ -769,109 +741,10 @@ GLC_LOGD("computeCurrentTransformMatrixLocked: " "mCurrentTextureImage is NULL"); } - computeTransformMatrix(mCurrentTransformMatrix, buf, - isEglImageCroppable(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop, + computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, mFilteringEnabled); } -void GLConsumer::computeTransformMatrix(float outTransform[16], - const sp<GraphicBuffer>& buf, const Rect& cropRect, uint32_t transform, - bool filtering) { - // Transform matrices - static const mat4 mtxFlipH( - -1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 1, 0, 0, 1 - ); - static const mat4 mtxFlipV( - 1, 0, 0, 0, - 0, -1, 0, 0, - 0, 0, 1, 0, - 0, 1, 0, 1 - ); - static const mat4 mtxRot90( - 0, 1, 0, 0, - -1, 0, 0, 0, - 0, 0, 1, 0, - 1, 0, 0, 1 - ); - - mat4 xform; - if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { - xform *= mtxFlipH; - } - if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { - xform *= mtxFlipV; - } - if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - xform *= mtxRot90; - } - - if (!cropRect.isEmpty()) { - float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; - float bufferWidth = buf->getWidth(); - float bufferHeight = buf->getHeight(); - float shrinkAmount = 0.0f; - if (filtering) { - // In order to prevent bilinear sampling beyond the edge of the - // crop rectangle we may need to shrink it by 2 texels in each - // dimension. Normally this would just need to take 1/2 a texel - // off each end, but because the chroma channels of YUV420 images - // are subsampled we may need to shrink the crop region by a whole - // texel on each side. - switch (buf->getPixelFormat()) { - case PIXEL_FORMAT_RGBA_8888: - case PIXEL_FORMAT_RGBX_8888: - case PIXEL_FORMAT_RGBA_FP16: - case PIXEL_FORMAT_RGBA_1010102: - case PIXEL_FORMAT_RGB_888: - case PIXEL_FORMAT_RGB_565: - case PIXEL_FORMAT_BGRA_8888: - // We know there's no subsampling of any channels, so we - // only need to shrink by a half a pixel. - shrinkAmount = 0.5; - break; - - default: - // If we don't recognize the format, we must assume the - // worst case (that we care about), which is YUV420. - shrinkAmount = 1.0; - break; - } - } - - // Only shrink the dimensions that are not the size of the buffer. - if (cropRect.width() < bufferWidth) { - tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; - sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / - bufferWidth; - } - if (cropRect.height() < bufferHeight) { - ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / - bufferHeight; - sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / - bufferHeight; - } - - mat4 crop( - sx, 0, 0, 0, - 0, sy, 0, 0, - 0, 0, 1, 0, - tx, ty, 0, 1 - ); - xform = crop * xform; - } - - // SurfaceFlinger expects the top of its window textures to be at a Y - // coordinate of 0, so GLConsumer must behave the same way. We don't - // want to expose this to applications, however, so we must add an - // additional vertical flip to the transform after all the other transforms. - xform = mtxFlipV * xform; - - memcpy(outTransform, xform.asArray(), sizeof(xform)); -} - Rect GLConsumer::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) { Rect outCrop = crop; @@ -938,7 +811,7 @@ } return (mCurrentTextureImage == nullptr) ? - NULL : mCurrentTextureImage->graphicBuffer(); + nullptr : mCurrentTextureImage->graphicBuffer(); } Rect GLConsumer::getCurrentCrop() const { @@ -1063,8 +936,7 @@ GLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer) : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), - mEglDisplay(EGL_NO_DISPLAY), - mCropRect(Rect::EMPTY_RECT) { + mEglDisplay(EGL_NO_DISPLAY) { } GLConsumer::EglImage::~EglImage() { @@ -1077,13 +949,11 @@ } status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, - const Rect& cropRect, bool forceCreation) { // If there's an image and it's no longer valid, destroy it. bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; bool displayInvalid = mEglDisplay != eglDisplay; - bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect; - if (haveImage && (displayInvalid || cropInvalid || forceCreation)) { + if (haveImage && (displayInvalid || forceCreation)) { if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { ALOGE("createIfNeeded: eglDestroyImageKHR failed"); } @@ -1095,14 +965,12 @@ // If there's no image, create one. if (mEglImage == EGL_NO_IMAGE_KHR) { mEglDisplay = eglDisplay; - mCropRect = cropRect; - mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect); + mEglImage = createImage(mEglDisplay, mGraphicBuffer); } // Fail if we can't create a valid image. if (mEglImage == EGL_NO_IMAGE_KHR) { mEglDisplay = EGL_NO_DISPLAY; - mCropRect.makeInvalid(); const sp<GraphicBuffer>& buffer = mGraphicBuffer; ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", buffer->getWidth(), buffer->getHeight(), buffer->getStride(), @@ -1119,38 +987,19 @@ } EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy, - const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) { + const sp<GraphicBuffer>& graphicBuffer) { EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer()); const bool createProtectedImage = (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, - EGL_IMAGE_CROP_LEFT_ANDROID, crop.left, - EGL_IMAGE_CROP_TOP_ANDROID, crop.top, - EGL_IMAGE_CROP_RIGHT_ANDROID, crop.right, - EGL_IMAGE_CROP_BOTTOM_ANDROID, crop.bottom, + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, createProtectedImage ? EGL_TRUE : EGL_NONE, EGL_NONE, }; - if (!crop.isValid()) { - // No crop rect to set, so leave the crop out of the attrib array. Make - // sure to propagate the protected content attrs if they are set. - attrs[2] = attrs[10]; - attrs[3] = attrs[11]; - attrs[4] = EGL_NONE; - } else if (!isEglImageCroppable(crop)) { - // The crop rect is not at the origin, so we can't set the crop on the - // EGLImage because that's not allowed by the EGL_ANDROID_image_crop - // extension. In the future we can add a layered extension that - // removes this restriction if there is hardware that can support it. - attrs[2] = attrs[10]; - attrs[3] = attrs[11]; - attrs[4] = EGL_NONE; - } - eglInitialize(dpy, 0, 0); + eglInitialize(dpy, nullptr, nullptr); EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); if (image == EGL_NO_IMAGE_KHR) {
diff --git a/libs/gui/GLConsumerUtils.cpp b/libs/gui/GLConsumerUtils.cpp new file mode 100644 index 0000000..7a06c3d --- /dev/null +++ b/libs/gui/GLConsumerUtils.cpp
@@ -0,0 +1,125 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "GLConsumerUtils" +//#define LOG_NDEBUG 0 + +#include <gui/GLConsumer.h> +#include <math/mat4.h> +#include <system/window.h> +#include <utils/Log.h> + +namespace android { + +void GLConsumer::computeTransformMatrix(float outTransform[16], + const sp<GraphicBuffer>& buf, const Rect& cropRect, uint32_t transform, + bool filtering) { + // Transform matrices + static const mat4 mtxFlipH( + -1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 1, 0, 0, 1 + ); + static const mat4 mtxFlipV( + 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 1 + ); + static const mat4 mtxRot90( + 0, 1, 0, 0, + -1, 0, 0, 0, + 0, 0, 1, 0, + 1, 0, 0, 1 + ); + + mat4 xform; + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { + xform *= mtxFlipH; + } + if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { + xform *= mtxFlipV; + } + if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + xform *= mtxRot90; + } + + if (!cropRect.isEmpty()) { + float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f; + float bufferWidth = buf->getWidth(); + float bufferHeight = buf->getHeight(); + float shrinkAmount = 0.0f; + if (filtering) { + // In order to prevent bilinear sampling beyond the edge of the + // crop rectangle we may need to shrink it by 2 texels in each + // dimension. Normally this would just need to take 1/2 a texel + // off each end, but because the chroma channels of YUV420 images + // are subsampled we may need to shrink the crop region by a whole + // texel on each side. + switch (buf->getPixelFormat()) { + case PIXEL_FORMAT_RGBA_8888: + case PIXEL_FORMAT_RGBX_8888: + case PIXEL_FORMAT_RGBA_FP16: + case PIXEL_FORMAT_RGBA_1010102: + case PIXEL_FORMAT_RGB_888: + case PIXEL_FORMAT_RGB_565: + case PIXEL_FORMAT_BGRA_8888: + // We know there's no subsampling of any channels, so we + // only need to shrink by a half a pixel. + shrinkAmount = 0.5; + break; + + default: + // If we don't recognize the format, we must assume the + // worst case (that we care about), which is YUV420. + shrinkAmount = 1.0; + break; + } + } + + // Only shrink the dimensions that are not the size of the buffer. + if (cropRect.width() < bufferWidth) { + tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; + sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / + bufferWidth; + } + if (cropRect.height() < bufferHeight) { + ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / + bufferHeight; + sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / + bufferHeight; + } + + mat4 crop( + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, 1, 0, + tx, ty, 0, 1 + ); + xform = crop * xform; + } + + // GLConsumer uses the GL convention where (0, 0) is the bottom-left + // corner and (1, 1) is the top-right corner. Add an additional vertical + // flip after all other transforms to map from GL convention to buffer + // queue memory layout, where (0, 0) is the top-left corner. + xform = mtxFlipV * xform; + + memcpy(outTransform, xform.asArray(), sizeof(xform)); +} + +}; // namespace android
diff --git a/libs/gui/GuiConfig.cpp b/libs/gui/GuiConfig.cpp index bc0c83c..3ec20ee 100644 --- a/libs/gui/GuiConfig.cpp +++ b/libs/gui/GuiConfig.cpp
@@ -18,8 +18,7 @@ namespace android { -void appendGuiConfigString(String8& configStr) -{ +void appendGuiConfigString(std::string& configStr) { static const char* config = " [libgui" #ifdef DONT_USE_FENCE_SYNC
diff --git a/libs/gui/HdrMetadata.cpp b/libs/gui/HdrMetadata.cpp index b715e43..add3ef0 100644 --- a/libs/gui/HdrMetadata.cpp +++ b/libs/gui/HdrMetadata.cpp
@@ -15,6 +15,7 @@ */ #include <gui/HdrMetadata.h> +#include <limits> namespace android { @@ -26,6 +27,10 @@ if (validTypes & CTA861_3) { size += sizeof(cta8613); } + if (validTypes & HDR10PLUS) { + size += sizeof(size_t); + size += hdr10plus.size(); + } return size; } @@ -41,6 +46,12 @@ if (validTypes & CTA861_3) { FlattenableUtils::write(buffer, size, cta8613); } + if (validTypes & HDR10PLUS) { + size_t metadataSize = hdr10plus.size(); + FlattenableUtils::write(buffer, size, metadataSize); + memcpy(buffer, hdr10plus.data(), metadataSize); + FlattenableUtils::advance(buffer, size, metadataSize); + } return NO_ERROR; } @@ -62,6 +73,22 @@ } FlattenableUtils::read(buffer, size, cta8613); } + if (validTypes & HDR10PLUS) { + if (size < sizeof(size_t)) { + return NO_MEMORY; + } + + size_t metadataSize; + FlattenableUtils::read(buffer, size, metadataSize); + + if (size < metadataSize) { + return NO_MEMORY; + } + + hdr10plus.resize(metadataSize); + memcpy(hdr10plus.data(), buffer, metadataSize); + FlattenableUtils::advance(buffer, size, metadataSize); + } return NO_ERROR; } @@ -91,6 +118,10 @@ } } + if ((validTypes & HDR10PLUS) == HDR10PLUS) { + if (hdr10plus != rhs.hdr10plus) return false; + } + return true; }
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 0b37960..0e03b7d 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -30,16 +30,21 @@ #ifndef NO_BUFFERHUB #include <gui/BufferHubProducer.h> #endif + +#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> +#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h> #include <gui/BufferQueueDefs.h> #include <gui/IGraphicBufferProducer.h> #include <gui/IProducerListener.h> -#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> - namespace android { // ---------------------------------------------------------------------------- -using ::android::hardware::graphics::bufferqueue::V1_0::utils:: +using H2BGraphicBufferProducerV1_0 = + ::android::hardware::graphics::bufferqueue::V1_0::utils:: + H2BGraphicBufferProducer; +using H2BGraphicBufferProducerV2_0 = + ::android::hardware::graphics::bufferqueue::V2_0::utils:: H2BGraphicBufferProducer; enum { @@ -67,6 +72,7 @@ GET_FRAME_TIMESTAMPS, GET_UNIQUE_ID, GET_CONSUMER_USAGE, + SET_LEGACY_BUFFER_DROP, }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -190,10 +196,10 @@ virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { - if (outBuffer == NULL) { + if (outBuffer == nullptr) { ALOGE("detachNextBuffer: outBuffer must not be NULL"); return BAD_VALUE; - } else if (outFence == NULL) { + } else if (outFence == nullptr) { ALOGE("detachNextBuffer: outFence must not be NULL"); return BAD_VALUE; } @@ -301,7 +307,7 @@ int api, bool producerControlledByApp, QueueBufferOutput* output) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); - if (listener != NULL) { + if (listener != nullptr) { data.writeInt32(1); data.writeStrongBinder(IInterface::asBinder(listener)); } else { @@ -355,7 +361,7 @@ data.writeUint32(height); data.writeInt32(static_cast<int32_t>(format)); data.writeUint64(usage); - status_t result = remote()->transact(ALLOCATE_BUFFERS, data, &reply, TF_ONE_WAY); + status_t result = remote()->transact(ALLOCATE_BUFFERS, data, &reply, IBinder::FLAG_ONEWAY); if (result != NO_ERROR) { ALOGE("allocateBuffers failed to transact: %d", result); } @@ -432,6 +438,20 @@ return reply.readInt32(); } + virtual status_t setLegacyBufferDrop(bool drop) { + Parcel data, reply; + data.writeInterfaceToken( + IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(drop); + status_t result = remote()->transact(SET_LEGACY_BUFFER_DROP, + data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32(); + return result; + } + virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) override { Parcel data, reply; @@ -534,9 +554,11 @@ BpGraphicBufferProducer::~BpGraphicBufferProducer() {} class HpGraphicBufferProducer : public HpInterface< - BpGraphicBufferProducer, H2BGraphicBufferProducer> { + BpGraphicBufferProducer, + H2BGraphicBufferProducerV1_0, + H2BGraphicBufferProducerV2_0> { public: - HpGraphicBufferProducer(const sp<IBinder>& base) : PBase(base) {} + explicit HpGraphicBufferProducer(const sp<IBinder>& base) : PBase(base) {} status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override { return mBase->requestBuffer(slot, buf); @@ -630,6 +652,10 @@ return mBase->setDequeueTimeout(timeout); } + status_t setLegacyBufferDrop(bool drop) override { + return mBase->setLegacyBufferDrop(drop); + } + status_t getLastQueuedBuffer( sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, @@ -651,11 +677,17 @@ } }; -IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer, +IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer"); // ---------------------------------------------------------------------- +status_t IGraphicBufferProducer::setLegacyBufferDrop(bool drop) { + // No-op for IGBP other than BufferQueue. + (void) drop; + return INVALID_OPERATION; +} + status_t IGraphicBufferProducer::exportToParcel(Parcel* parcel) { status_t res = OK; res = parcel->writeUint32(USE_BUFFER_QUEUE); @@ -738,8 +770,8 @@ int bufferIdx = data.readInt32(); sp<GraphicBuffer> buffer; int result = requestBuffer(bufferIdx, &buffer); - reply->writeInt32(buffer != 0); - if (buffer != 0) { + reply->writeInt32(buffer != nullptr); + if (buffer != nullptr) { reply->write(*buffer); } reply->writeInt32(result); @@ -774,6 +806,10 @@ int result = dequeueBuffer(&buf, &fence, width, height, format, usage, &bufferAge, getTimestamps ? &frameTimestamps : nullptr); + if (fence == nullptr) { + ALOGE("dequeueBuffer returned a NULL fence, setting to Fence::NO_FENCE"); + fence = Fence::NO_FENCE; + } reply->writeInt32(buf); reply->write(*fence); reply->writeUint64(bufferAge); @@ -797,12 +833,12 @@ int32_t result = detachNextBuffer(&buffer, &fence); reply->writeInt32(result); if (result == NO_ERROR) { - reply->writeInt32(buffer != NULL); - if (buffer != NULL) { + reply->writeInt32(buffer != nullptr); + if (buffer != nullptr) { reply->write(*buffer); } - reply->writeInt32(fence != NULL); - if (fence != NULL) { + reply->writeInt32(fence != nullptr); + if (fence != nullptr) { reply->write(*fence); } } @@ -956,6 +992,10 @@ ALOGE("getLastQueuedBuffer failed to write buffer: %d", result); return result; } + if (fence == nullptr) { + ALOGE("getLastQueuedBuffer returned a NULL fence, setting to Fence::NO_FENCE"); + fence = Fence::NO_FENCE; + } result = reply->write(*fence); if (result != NO_ERROR) { ALOGE("getLastQueuedBuffer failed to write fence: %d", result); @@ -1003,6 +1043,13 @@ } return NO_ERROR; } + case SET_LEGACY_BUFFER_DROP: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + bool drop = data.readInt32(); + int result = setLegacyBufferDrop(drop); + reply->writeInt32(result); + return NO_ERROR; + } } return BBinder::onTransact(code, data, reply, flags); }
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp index 62abfa8..936063a 100644 --- a/libs/gui/IProducerListener.cpp +++ b/libs/gui/IProducerListener.cpp
@@ -15,7 +15,8 @@ */ #include <binder/Parcel.h> - +#include <gui/bufferqueue/1.0/H2BProducerListener.h> +#include <gui/bufferqueue/2.0/H2BProducerListener.h> #include <gui/IProducerListener.h> namespace android { @@ -61,7 +62,24 @@ // translation unit (see clang warning -Wweak-vtables) BpProducerListener::~BpProducerListener() {} -IMPLEMENT_META_INTERFACE(ProducerListener, "android.gui.IProducerListener") +class HpProducerListener : public HpInterface< + BpProducerListener, + hardware::graphics::bufferqueue::V1_0::utils::H2BProducerListener, + hardware::graphics::bufferqueue::V2_0::utils::H2BProducerListener> { +public: + explicit HpProducerListener(const sp<IBinder>& base) : PBase{base} {} + + virtual void onBufferReleased() override { + mBase->onBufferReleased(); + } + + virtual bool needsReleaseNotify() override { + return mBase->needsReleaseNotify(); + } +}; + +IMPLEMENT_HYBRID_META_INTERFACE(ProducerListener, + "android.gui.IProducerListener") status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
diff --git a/libs/gui/IRegionSamplingListener.cpp b/libs/gui/IRegionSamplingListener.cpp new file mode 100644 index 0000000..40cbfce --- /dev/null +++ b/libs/gui/IRegionSamplingListener.cpp
@@ -0,0 +1,64 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "IRegionSamplingListener" +//#define LOG_NDEBUG 0 + +#include <gui/IRegionSamplingListener.h> + +namespace android { + +namespace { // Anonymous + +enum class Tag : uint32_t { + ON_SAMPLE_COLLECTED = IBinder::FIRST_CALL_TRANSACTION, + LAST = ON_SAMPLE_COLLECTED, +}; + +} // Anonymous namespace + +class BpRegionSamplingListener : public SafeBpInterface<IRegionSamplingListener> { +public: + explicit BpRegionSamplingListener(const sp<IBinder>& impl) + : SafeBpInterface<IRegionSamplingListener>(impl, "BpRegionSamplingListener") {} + + ~BpRegionSamplingListener() override; + + void onSampleCollected(float medianLuma) override { + callRemoteAsync<decltype( + &IRegionSamplingListener::onSampleCollected)>(Tag::ON_SAMPLE_COLLECTED, medianLuma); + } +}; + +// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see +// clang warning -Wweak-vtables) +BpRegionSamplingListener::~BpRegionSamplingListener() = default; + +IMPLEMENT_META_INTERFACE(RegionSamplingListener, "android.gui.IRegionSamplingListener"); + +status_t BnRegionSamplingListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { + return BBinder::onTransact(code, data, reply, flags); + } + auto tag = static_cast<Tag>(code); + switch (tag) { + case Tag::ON_SAMPLE_COLLECTED: + return callLocalAsync(data, reply, &IRegionSamplingListener::onSampleCollected); + } +} + +} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index cec86e2..6c9d81a 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp
@@ -26,6 +26,7 @@ #include <gui/IDisplayEventConnection.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/IRegionSamplingListener.h> #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposerClient.h> #include <gui/LayerDebugInfo.h> @@ -63,21 +64,13 @@ return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); } - virtual sp<ISurfaceComposerClient> createScopedConnection( - const sp<IGraphicBufferProducer>& parent) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(IInterface::asBinder(parent)); - remote()->transact(BnSurfaceComposer::CREATE_SCOPED_CONNECTION, data, &reply); - return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); - } - - virtual void setTransactionState( - const Vector<ComposerState>& state, - const Vector<DisplayState>& displays, - uint32_t flags) - { + virtual void setTransactionState(const Vector<ComposerState>& state, + const Vector<DisplayState>& displays, uint32_t flags, + const sp<IBinder>& applyToken, + const InputWindowCommands& commands, + int64_t desiredPresentTime, + const client_cache_t& uncacheBuffer, + const std::vector<ListenerCallbacks>& listenerCallbacks) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -92,6 +85,19 @@ } data.writeUint32(flags); + data.writeStrongBinder(applyToken); + commands.write(data); + data.writeInt64(desiredPresentTime); + data.writeWeakBinder(uncacheBuffer.token); + data.writeUint64(uncacheBuffer.id); + + if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) { + for (const auto& [listener, callbackIds] : listenerCallbacks) { + data.writeStrongBinder(IInterface::asBinder(listener)); + data.writeInt64Vector(callbackIds); + } + } + remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply); } @@ -103,63 +109,94 @@ } virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - bool& outCapturedSecureLayers, - Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, + bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, ISurfaceComposer::Rotation rotation, bool captureSecureLayers) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); + data.writeInt32(static_cast<int32_t>(reqDataspace)); + data.writeInt32(static_cast<int32_t>(reqPixelFormat)); data.write(sourceCrop); data.writeUint32(reqWidth); data.writeUint32(reqHeight); - data.writeInt32(minLayerZ); - data.writeInt32(maxLayerZ); data.writeInt32(static_cast<int32_t>(useIdentityTransform)); data.writeInt32(static_cast<int32_t>(rotation)); data.writeInt32(static_cast<int32_t>(captureSecureLayers)); - status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); - - if (err != NO_ERROR) { - return err; + status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); + if (result != NO_ERROR) { + ALOGE("captureScreen failed to transact: %d", result); + return result; } - - err = reply.readInt32(); - if (err != NO_ERROR) { - return err; + result = reply.readInt32(); + if (result != NO_ERROR) { + ALOGE("captureScreen failed to readInt32: %d", result); + return result; } *outBuffer = new GraphicBuffer(); reply.read(**outBuffer); outCapturedSecureLayers = reply.readBool(); - return err; + return result; } - virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder, - sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop, - float frameScale, bool childrenOnly) { + virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, + sp<GraphicBuffer>* outBuffer) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeUint64(displayOrLayerStack); + status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN_BY_ID, data, &reply); + if (result != NO_ERROR) { + ALOGE("captureScreen failed to transact: %d", result); + return result; + } + result = reply.readInt32(); + if (result != NO_ERROR) { + ALOGE("captureScreen failed to readInt32: %d", result); + return result; + } + + *outDataspace = static_cast<ui::Dataspace>(reply.readInt32()); + *outBuffer = new GraphicBuffer(); + reply.read(**outBuffer); + return result; + } + + virtual status_t captureLayers( + const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, + const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, + const Rect& sourceCrop, + const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeLayers, float frameScale, + bool childrenOnly) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(layerHandleBinder); + data.writeInt32(static_cast<int32_t>(reqDataspace)); + data.writeInt32(static_cast<int32_t>(reqPixelFormat)); data.write(sourceCrop); + data.writeInt32(excludeLayers.size()); + for (auto el : excludeLayers) { + data.writeStrongBinder(el); + } data.writeFloat(frameScale); data.writeBool(childrenOnly); - status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); - - if (err != NO_ERROR) { - return err; + status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); + if (result != NO_ERROR) { + ALOGE("captureLayers failed to transact: %d", result); + return result; } - - err = reply.readInt32(); - if (err != NO_ERROR) { - return err; + result = reply.readInt32(); + if (result != NO_ERROR) { + ALOGE("captureLayers failed to readInt32: %d", result); + return result; } *outBuffer = new GraphicBuffer(); reply.read(**outBuffer); - return err; + return result; } virtual bool authenticateSurfaceTexture( @@ -281,12 +318,25 @@ remote()->transact(BnSurfaceComposer::DESTROY_DISPLAY, data, &reply); } - virtual sp<IBinder> getBuiltInDisplay(int32_t id) - { + virtual std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeInt32(id); - remote()->transact(BnSurfaceComposer::GET_BUILT_IN_DISPLAY, data, &reply); + if (remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS, data, &reply) == + NO_ERROR) { + std::vector<PhysicalDisplayId> displayIds; + if (reply.readUint64Vector(&displayIds) == NO_ERROR) { + return displayIds; + } + } + + return {}; + } + + virtual sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeUint64(displayId); + remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN, data, &reply); return reply.readStrongBinder(); } @@ -336,34 +386,6 @@ return result; } - virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) { - Parcel data, reply; - status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeStrongBinder(display); - if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to writeStrongBinder: %d", result); - return result; - } - result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_VIEWPORT, data, &reply); - if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to transact: %d", result); - return result; - } - result = reply.readInt32(); - if (result == NO_ERROR) { - result = reply.read(*outViewport); - if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to read: %d", result); - return result; - } - } - return result; - } - virtual int getActiveConfig(const sp<IBinder>& display) { Parcel data, reply; @@ -376,10 +398,26 @@ virtual status_t setActiveConfig(const sp<IBinder>& display, int id) { Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - data.writeInt32(id); - remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply); + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("setActiveConfig failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeStrongBinder(display); + if (result != NO_ERROR) { + ALOGE("setActiveConfig failed to writeStrongBinder: %d", result); + return result; + } + result = data.writeInt32(id); + if (result != NO_ERROR) { + ALOGE("setActiveConfig failed to writeInt32: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply); + if (result != NO_ERROR) { + ALOGE("setActiveConfig failed to transact: %d", result); + return result; + } return reply.readInt32(); } @@ -413,6 +451,32 @@ return result; } + virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display, + ui::DisplayPrimaries& primaries) { + Parcel data, reply; + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("getDisplayNativePrimaries failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeStrongBinder(display); + if (result != NO_ERROR) { + ALOGE("getDisplayNativePrimaries failed to writeStrongBinder: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_NATIVE_PRIMARIES, data, &reply); + if (result != NO_ERROR) { + ALOGE("getDisplayNativePrimaries failed to transact: %d", result); + return result; + } + result = reply.readInt32(); + if (result == NO_ERROR) { + memcpy(&primaries, reply.readInplace(sizeof(ui::DisplayPrimaries)), + sizeof(ui::DisplayPrimaries)); + } + return result; + } + virtual ColorMode getActiveColorMode(const sp<IBinder>& display) { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -461,8 +525,16 @@ virtual status_t clearAnimationFrameStats() { Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply); + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("clearAnimationFrameStats failed to writeInterfaceToken: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply); + if (result != NO_ERROR) { + ALOGE("clearAnimationFrameStats failed to transact: %d", result); + return result; + } return reply.readInt32(); } @@ -508,8 +580,8 @@ ALOGE("enableVSyncInjections failed to writeBool: %d", result); return result; } - result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS, - data, &reply, TF_ONE_WAY); + result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS, data, &reply, + IBinder::FLAG_ONEWAY); if (result != NO_ERROR) { ALOGE("enableVSyncInjections failed to transact: %d", result); return result; @@ -529,7 +601,8 @@ ALOGE("injectVSync failed to writeInt64: %d", result); return result; } - result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply, TF_ONE_WAY); + result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply, + IBinder::FLAG_ONEWAY); if (result != NO_ERROR) { ALOGE("injectVSync failed to transact: %d", result); return result; @@ -567,6 +640,342 @@ outLayers->clear(); return reply.readParcelableVector(outLayers); } + + virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace, + ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, + ui::PixelFormat* wideColorGamutPixelFormat) const { + Parcel data, reply; + status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (error != NO_ERROR) { + return error; + } + error = remote()->transact(BnSurfaceComposer::GET_COMPOSITION_PREFERENCE, data, &reply); + if (error != NO_ERROR) { + return error; + } + error = static_cast<status_t>(reply.readInt32()); + if (error == NO_ERROR) { + *defaultDataspace = static_cast<ui::Dataspace>(reply.readInt32()); + *defaultPixelFormat = static_cast<ui::PixelFormat>(reply.readInt32()); + *wideColorGamutDataspace = static_cast<ui::Dataspace>(reply.readInt32()); + *wideColorGamutPixelFormat = static_cast<ui::PixelFormat>(reply.readInt32()); + } + return error; + } + + virtual status_t getColorManagement(bool* outGetColorManagement) const { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::GET_COLOR_MANAGEMENT, data, &reply); + bool result; + status_t err = reply.readBool(&result); + if (err == NO_ERROR) { + *outGetColorManagement = result; + } + return err; + } + + virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display, + ui::PixelFormat* outFormat, + ui::Dataspace* outDataspace, + uint8_t* outComponentMask) const { + if (!outFormat || !outDataspace || !outComponentMask) return BAD_VALUE; + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + + status_t error = + remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES, + data, &reply); + if (error != NO_ERROR) { + return error; + } + + uint32_t value = 0; + error = reply.readUint32(&value); + if (error != NO_ERROR) { + return error; + } + *outFormat = static_cast<ui::PixelFormat>(value); + + error = reply.readUint32(&value); + if (error != NO_ERROR) { + return error; + } + *outDataspace = static_cast<ui::Dataspace>(value); + + error = reply.readUint32(&value); + if (error != NO_ERROR) { + return error; + } + *outComponentMask = static_cast<uint8_t>(value); + return error; + } + + virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, + uint8_t componentMask, + uint64_t maxFrames) const { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + data.writeBool(enable); + data.writeByte(static_cast<int8_t>(componentMask)); + data.writeUint64(maxFrames); + status_t result = + remote()->transact(BnSurfaceComposer::SET_DISPLAY_CONTENT_SAMPLING_ENABLED, data, + &reply); + return result; + } + + virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames, + uint64_t timestamp, + DisplayedFrameStats* outStats) const { + if (!outStats) return BAD_VALUE; + + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + data.writeUint64(maxFrames); + data.writeUint64(timestamp); + + status_t result = + remote()->transact(BnSurfaceComposer::GET_DISPLAYED_CONTENT_SAMPLE, data, &reply); + + if (result != NO_ERROR) { + return result; + } + + result = reply.readUint64(&outStats->numFrames); + if (result != NO_ERROR) { + return result; + } + + result = reply.readUint64Vector(&outStats->component_0_sample); + if (result != NO_ERROR) { + return result; + } + result = reply.readUint64Vector(&outStats->component_1_sample); + if (result != NO_ERROR) { + return result; + } + result = reply.readUint64Vector(&outStats->component_2_sample); + if (result != NO_ERROR) { + return result; + } + result = reply.readUint64Vector(&outStats->component_3_sample); + return result; + } + + virtual status_t getProtectedContentSupport(bool* outSupported) const { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + status_t error = + remote()->transact(BnSurfaceComposer::GET_PROTECTED_CONTENT_SUPPORT, data, &reply); + if (error != NO_ERROR) { + return error; + } + error = reply.readBool(outSupported); + return error; + } + + virtual status_t isWideColorDisplay(const sp<IBinder>& token, + bool* outIsWideColorDisplay) const { + Parcel data, reply; + status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (error != NO_ERROR) { + return error; + } + error = data.writeStrongBinder(token); + if (error != NO_ERROR) { + return error; + } + + error = remote()->transact(BnSurfaceComposer::IS_WIDE_COLOR_DISPLAY, data, &reply); + if (error != NO_ERROR) { + return error; + } + error = reply.readBool(outIsWideColorDisplay); + return error; + } + + virtual status_t addRegionSamplingListener(const Rect& samplingArea, + const sp<IBinder>& stopLayerHandle, + const sp<IRegionSamplingListener>& listener) { + Parcel data, reply; + status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (error != NO_ERROR) { + ALOGE("addRegionSamplingListener: Failed to write interface token"); + return error; + } + error = data.write(samplingArea); + if (error != NO_ERROR) { + ALOGE("addRegionSamplingListener: Failed to write sampling area"); + return error; + } + error = data.writeStrongBinder(stopLayerHandle); + if (error != NO_ERROR) { + ALOGE("addRegionSamplingListener: Failed to write stop layer handle"); + return error; + } + error = data.writeStrongBinder(IInterface::asBinder(listener)); + if (error != NO_ERROR) { + ALOGE("addRegionSamplingListener: Failed to write listener"); + return error; + } + error = remote()->transact(BnSurfaceComposer::ADD_REGION_SAMPLING_LISTENER, data, &reply); + if (error != NO_ERROR) { + ALOGE("addRegionSamplingListener: Failed to transact"); + } + return error; + } + + virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) { + Parcel data, reply; + status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (error != NO_ERROR) { + ALOGE("removeRegionSamplingListener: Failed to write interface token"); + return error; + } + error = data.writeStrongBinder(IInterface::asBinder(listener)); + if (error != NO_ERROR) { + ALOGE("removeRegionSamplingListener: Failed to write listener"); + return error; + } + error = remote()->transact(BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER, data, + &reply); + if (error != NO_ERROR) { + ALOGE("removeRegionSamplingListener: Failed to transact"); + } + return error; + } + + virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken, + const std::vector<int32_t>& allowedConfigs) { + Parcel data, reply; + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("setAllowedDisplayConfigs failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeStrongBinder(displayToken); + if (result != NO_ERROR) { + ALOGE("setAllowedDisplayConfigs failed to writeStrongBinder: %d", result); + return result; + } + result = data.writeInt32Vector(allowedConfigs); + if (result != NO_ERROR) { + ALOGE("setAllowedDisplayConfigs failed to writeInt32Vector: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::SET_ALLOWED_DISPLAY_CONFIGS, data, &reply); + if (result != NO_ERROR) { + ALOGE("setAllowedDisplayConfigs failed to transact: %d", result); + return result; + } + return reply.readInt32(); + } + + virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken, + std::vector<int32_t>* outAllowedConfigs) { + if (!outAllowedConfigs) return BAD_VALUE; + Parcel data, reply; + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("getAllowedDisplayConfigs failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeStrongBinder(displayToken); + if (result != NO_ERROR) { + ALOGE("getAllowedDisplayConfigs failed to writeStrongBinder: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::GET_ALLOWED_DISPLAY_CONFIGS, data, &reply); + if (result != NO_ERROR) { + ALOGE("getAllowedDisplayConfigs failed to transact: %d", result); + return result; + } + result = reply.readInt32Vector(outAllowedConfigs); + if (result != NO_ERROR) { + ALOGE("getAllowedDisplayConfigs failed to readInt32Vector: %d", result); + return result; + } + return reply.readInt32(); + } + + virtual status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, + bool* outSupport) const { + Parcel data, reply; + status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (error != NO_ERROR) { + ALOGE("getDisplayBrightnessSupport: failed to write interface token: %d", error); + return error; + } + error = data.writeStrongBinder(displayToken); + if (error != NO_ERROR) { + ALOGE("getDisplayBrightnessSupport: failed to write display token: %d", error); + return error; + } + error = remote()->transact(BnSurfaceComposer::GET_DISPLAY_BRIGHTNESS_SUPPORT, data, &reply); + if (error != NO_ERROR) { + ALOGE("getDisplayBrightnessSupport: failed to transact: %d", error); + return error; + } + bool support; + error = reply.readBool(&support); + if (error != NO_ERROR) { + ALOGE("getDisplayBrightnessSupport: failed to read support: %d", error); + return error; + } + *outSupport = support; + return NO_ERROR; + } + + virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) const { + Parcel data, reply; + status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (error != NO_ERROR) { + ALOGE("setDisplayBrightness: failed to write interface token: %d", error); + return error; + } + error = data.writeStrongBinder(displayToken); + if (error != NO_ERROR) { + ALOGE("setDisplayBrightness: failed to write display token: %d", error); + return error; + } + error = data.writeFloat(brightness); + if (error != NO_ERROR) { + ALOGE("setDisplayBrightness: failed to write brightness: %d", error); + return error; + } + error = remote()->transact(BnSurfaceComposer::SET_DISPLAY_BRIGHTNESS, data, &reply); + if (error != NO_ERROR) { + ALOGE("setDisplayBrightness: failed to transact: %d", error); + return error; + } + return NO_ERROR; + } + + virtual status_t notifyPowerHint(int32_t hintId) { + Parcel data, reply; + status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (error != NO_ERROR) { + ALOGE("notifyPowerHint: failed to write interface token: %d", error); + return error; + } + error = data.writeInt32(hintId); + if (error != NO_ERROR) { + ALOGE("notifyPowerHint: failed to write hintId: %d", error); + return error; + } + error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_HINT, data, &reply, + IBinder::FLAG_ONEWAY); + if (error != NO_ERROR) { + ALOGE("notifyPowerHint: failed to transact: %d", error); + return error; + } + return NO_ERROR; + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -587,14 +996,6 @@ reply->writeStrongBinder(b); return NO_ERROR; } - case CREATE_SCOPED_CONNECTION: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IGraphicBufferProducer> bufferProducer = - interface_cast<IGraphicBufferProducer>(data.readStrongBinder()); - sp<IBinder> b = IInterface::asBinder(createScopedConnection(bufferProducer)); - reply->writeStrongBinder(b); - return NO_ERROR; - } case SET_TRANSACTION_STATE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -602,10 +1003,10 @@ if (count > data.dataSize()) { return BAD_VALUE; } - ComposerState s; Vector<ComposerState> state; state.setCapacity(count); for (size_t i = 0; i < count; i++) { + ComposerState s; if (s.read(data) == BAD_VALUE) { return BAD_VALUE; } @@ -627,7 +1028,28 @@ } uint32_t stateFlags = data.readUint32(); - setTransactionState(state, displays, stateFlags); + sp<IBinder> applyToken = data.readStrongBinder(); + InputWindowCommands inputWindowCommands; + inputWindowCommands.read(data); + + int64_t desiredPresentTime = data.readInt64(); + + client_cache_t uncachedBuffer; + uncachedBuffer.token = data.readWeakBinder(); + uncachedBuffer.id = data.readUint64(); + + std::vector<ListenerCallbacks> listenerCallbacks; + int32_t listenersSize = data.readInt32(); + for (int32_t i = 0; i < listenersSize; i++) { + auto listener = + interface_cast<ITransactionCompletedListener>(data.readStrongBinder()); + std::vector<CallbackId> callbackIds; + data.readInt64Vector(&callbackIds); + listenerCallbacks.emplace_back(listener, callbackIds); + } + + setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands, + desiredPresentTime, uncachedBuffer, listenerCallbacks); return NO_ERROR; } case BOOT_FINISHED: { @@ -638,21 +1060,23 @@ case CAPTURE_SCREEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = data.readStrongBinder(); + ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32()); + ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32()); sp<GraphicBuffer> outBuffer; Rect sourceCrop(Rect::EMPTY_RECT); data.read(sourceCrop); uint32_t reqWidth = data.readUint32(); uint32_t reqHeight = data.readUint32(); - int32_t minLayerZ = data.readInt32(); - int32_t maxLayerZ = data.readInt32(); bool useIdentityTransform = static_cast<bool>(data.readInt32()); int32_t rotation = data.readInt32(); bool captureSecureLayers = static_cast<bool>(data.readInt32()); bool capturedSecureLayers = false; - status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, sourceCrop, reqWidth, - reqHeight, minLayerZ, maxLayerZ, useIdentityTransform, - static_cast<ISurfaceComposer::Rotation>(rotation), captureSecureLayers); + status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, reqDataspace, + reqPixelFormat, sourceCrop, reqWidth, reqHeight, + useIdentityTransform, + static_cast<ISurfaceComposer::Rotation>(rotation), + captureSecureLayers); reply->writeInt32(res); if (res == NO_ERROR) { @@ -661,17 +1085,41 @@ } return NO_ERROR; } + case CAPTURE_SCREEN_BY_ID: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + uint64_t displayOrLayerStack = data.readUint64(); + ui::Dataspace outDataspace = ui::Dataspace::V0_SRGB; + sp<GraphicBuffer> outBuffer; + status_t res = captureScreen(displayOrLayerStack, &outDataspace, &outBuffer); + reply->writeInt32(res); + if (res == NO_ERROR) { + reply->writeInt32(static_cast<int32_t>(outDataspace)); + reply->write(*outBuffer); + } + return NO_ERROR; + } case CAPTURE_LAYERS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> layerHandleBinder = data.readStrongBinder(); + ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32()); + ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32()); sp<GraphicBuffer> outBuffer; Rect sourceCrop(Rect::EMPTY_RECT); data.read(sourceCrop); + + std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles; + int numExcludeHandles = data.readInt32(); + excludeHandles.reserve(numExcludeHandles); + for (int i = 0; i < numExcludeHandles; i++) { + excludeHandles.emplace(data.readStrongBinder()); + } + float frameScale = data.readFloat(); bool childrenOnly = data.readBool(); - status_t res = captureLayers(layerHandleBinder, &outBuffer, sourceCrop, frameScale, - childrenOnly); + status_t res = + captureLayers(layerHandleBinder, &outBuffer, reqDataspace, reqPixelFormat, + sourceCrop, excludeHandles, frameScale, childrenOnly); reply->writeInt32(res); if (res == NO_ERROR) { reply->write(*outBuffer); @@ -726,10 +1174,10 @@ destroyDisplay(display); return NO_ERROR; } - case GET_BUILT_IN_DISPLAY: { + case GET_PHYSICAL_DISPLAY_TOKEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - int32_t id = data.readInt32(); - sp<IBinder> display(getBuiltInDisplay(id)); + PhysicalDisplayId displayId = data.readUint64(); + sp<IBinder> display = getPhysicalDisplayToken(displayId); reply->writeStrongBinder(display); return NO_ERROR; } @@ -760,26 +1208,6 @@ } return NO_ERROR; } - case GET_DISPLAY_VIEWPORT: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - Rect outViewport; - sp<IBinder> display = nullptr; - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to readStrongBinder: %d", result); - return result; - } - result = getDisplayViewport(display, &outViewport); - result = reply->writeInt32(result); - if (result == NO_ERROR) { - result = reply->write(outViewport); - if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to write: %d", result); - return result; - } - } - return NO_ERROR; - } case GET_ACTIVE_CONFIG: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = data.readStrongBinder(); @@ -814,6 +1242,26 @@ } return NO_ERROR; } + case GET_DISPLAY_NATIVE_PRIMARIES: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + ui::DisplayPrimaries primaries; + sp<IBinder> display = nullptr; + + status_t result = data.readStrongBinder(&display); + if (result != NO_ERROR) { + ALOGE("getDisplayNativePrimaries failed to readStrongBinder: %d", result); + return result; + } + + result = getDisplayNativePrimaries(display, primaries); + reply->writeInt32(result); + if (result == NO_ERROR) { + memcpy(reply->writeInplace(sizeof(ui::DisplayPrimaries)), &primaries, + sizeof(ui::DisplayPrimaries)); + } + + return NO_ERROR; + } case GET_ACTIVE_COLOR_MODE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = nullptr; @@ -914,12 +1362,235 @@ } return result; } + case GET_COMPOSITION_PREFERENCE: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + ui::Dataspace defaultDataspace; + ui::PixelFormat defaultPixelFormat; + ui::Dataspace wideColorGamutDataspace; + ui::PixelFormat wideColorGamutPixelFormat; + status_t error = + getCompositionPreference(&defaultDataspace, &defaultPixelFormat, + &wideColorGamutDataspace, &wideColorGamutPixelFormat); + reply->writeInt32(error); + if (error == NO_ERROR) { + reply->writeInt32(static_cast<int32_t>(defaultDataspace)); + reply->writeInt32(static_cast<int32_t>(defaultPixelFormat)); + reply->writeInt32(static_cast<int32_t>(wideColorGamutDataspace)); + reply->writeInt32(static_cast<int32_t>(wideColorGamutPixelFormat)); + } + return error; + } + case GET_COLOR_MANAGEMENT: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + bool result; + status_t error = getColorManagement(&result); + if (error == NO_ERROR) { + reply->writeBool(result); + } + return error; + } + case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + + sp<IBinder> display = data.readStrongBinder(); + ui::PixelFormat format; + ui::Dataspace dataspace; + uint8_t component = 0; + auto result = + getDisplayedContentSamplingAttributes(display, &format, &dataspace, &component); + if (result == NO_ERROR) { + reply->writeUint32(static_cast<uint32_t>(format)); + reply->writeUint32(static_cast<uint32_t>(dataspace)); + reply->writeUint32(static_cast<uint32_t>(component)); + } + return result; + } + case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + + sp<IBinder> display = nullptr; + bool enable = false; + int8_t componentMask = 0; + uint64_t maxFrames = 0; + status_t result = data.readStrongBinder(&display); + if (result != NO_ERROR) { + ALOGE("setDisplayContentSamplingEnabled failure in reading Display token: %d", + result); + return result; + } + + result = data.readBool(&enable); + if (result != NO_ERROR) { + ALOGE("setDisplayContentSamplingEnabled failure in reading enable: %d", result); + return result; + } + + result = data.readByte(static_cast<int8_t*>(&componentMask)); + if (result != NO_ERROR) { + ALOGE("setDisplayContentSamplingEnabled failure in reading component mask: %d", + result); + return result; + } + + result = data.readUint64(&maxFrames); + if (result != NO_ERROR) { + ALOGE("setDisplayContentSamplingEnabled failure in reading max frames: %d", result); + return result; + } + + return setDisplayContentSamplingEnabled(display, enable, + static_cast<uint8_t>(componentMask), maxFrames); + } + case GET_DISPLAYED_CONTENT_SAMPLE: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + + sp<IBinder> display = data.readStrongBinder(); + uint64_t maxFrames = 0; + uint64_t timestamp = 0; + + status_t result = data.readUint64(&maxFrames); + if (result != NO_ERROR) { + ALOGE("getDisplayedContentSample failure in reading max frames: %d", result); + return result; + } + + result = data.readUint64(×tamp); + if (result != NO_ERROR) { + ALOGE("getDisplayedContentSample failure in reading timestamp: %d", result); + return result; + } + + DisplayedFrameStats stats; + result = getDisplayedContentSample(display, maxFrames, timestamp, &stats); + if (result == NO_ERROR) { + reply->writeUint64(stats.numFrames); + reply->writeUint64Vector(stats.component_0_sample); + reply->writeUint64Vector(stats.component_1_sample); + reply->writeUint64Vector(stats.component_2_sample); + reply->writeUint64Vector(stats.component_3_sample); + } + return result; + } + case GET_PROTECTED_CONTENT_SUPPORT: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + bool result; + status_t error = getProtectedContentSupport(&result); + if (error == NO_ERROR) { + reply->writeBool(result); + } + return error; + } + case IS_WIDE_COLOR_DISPLAY: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> display = nullptr; + status_t error = data.readStrongBinder(&display); + if (error != NO_ERROR) { + return error; + } + bool result; + error = isWideColorDisplay(display, &result); + if (error == NO_ERROR) { + reply->writeBool(result); + } + return error; + } + case GET_PHYSICAL_DISPLAY_IDS: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + return reply->writeUint64Vector(getPhysicalDisplayIds()); + } + case ADD_REGION_SAMPLING_LISTENER: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + Rect samplingArea; + status_t result = data.read(samplingArea); + if (result != NO_ERROR) { + ALOGE("addRegionSamplingListener: Failed to read sampling area"); + return result; + } + sp<IBinder> stopLayerHandle; + result = data.readNullableStrongBinder(&stopLayerHandle); + if (result != NO_ERROR) { + ALOGE("addRegionSamplingListener: Failed to read stop layer handle"); + return result; + } + sp<IRegionSamplingListener> listener; + result = data.readNullableStrongBinder(&listener); + if (result != NO_ERROR) { + ALOGE("addRegionSamplingListener: Failed to read listener"); + return result; + } + return addRegionSamplingListener(samplingArea, stopLayerHandle, listener); + } + case REMOVE_REGION_SAMPLING_LISTENER: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IRegionSamplingListener> listener; + status_t result = data.readNullableStrongBinder(&listener); + if (result != NO_ERROR) { + ALOGE("removeRegionSamplingListener: Failed to read listener"); + return result; + } + return removeRegionSamplingListener(listener); + } + case SET_ALLOWED_DISPLAY_CONFIGS: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> displayToken = data.readStrongBinder(); + std::vector<int32_t> allowedConfigs; + data.readInt32Vector(&allowedConfigs); + status_t result = setAllowedDisplayConfigs(displayToken, allowedConfigs); + reply->writeInt32(result); + return result; + } + case GET_ALLOWED_DISPLAY_CONFIGS: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> displayToken = data.readStrongBinder(); + std::vector<int32_t> allowedConfigs; + status_t result = getAllowedDisplayConfigs(displayToken, &allowedConfigs); + reply->writeInt32Vector(allowedConfigs); + reply->writeInt32(result); + return result; + } + case GET_DISPLAY_BRIGHTNESS_SUPPORT: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> displayToken; + status_t error = data.readNullableStrongBinder(&displayToken); + if (error != NO_ERROR) { + ALOGE("getDisplayBrightnessSupport: failed to read display token: %d", error); + return error; + } + bool support = false; + error = getDisplayBrightnessSupport(displayToken, &support); + reply->writeBool(support); + return error; + } + case SET_DISPLAY_BRIGHTNESS: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> displayToken; + status_t error = data.readNullableStrongBinder(&displayToken); + if (error != NO_ERROR) { + ALOGE("setDisplayBrightness: failed to read display token: %d", error); + return error; + } + float brightness = -1.0f; + error = data.readFloat(&brightness); + if (error != NO_ERROR) { + ALOGE("setDisplayBrightness: failed to read brightness: %d", error); + return error; + } + return setDisplayBrightness(displayToken, brightness); + } + case NOTIFY_POWER_HINT: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + int32_t hintId; + status_t error = data.readInt32(&hintId); + if (error != NO_ERROR) { + ALOGE("notifyPowerHint: failed to read hintId: %d", error); + return error; + } + return notifyPowerHint(hintId); + } default: { return BBinder::onTransact(code, data, reply, flags); } } } -// ---------------------------------------------------------------------------- - -}; +} // namespace android
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index a6890ee..129558b 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -31,7 +31,7 @@ enum class Tag : uint32_t { CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION, - DESTROY_SURFACE, + CREATE_WITH_SURFACE_PARENT, CLEAR_LAYER_FRAME_STATS, GET_LAYER_FRAME_STATS, LAST = GET_LAYER_FRAME_STATS, @@ -47,19 +47,26 @@ ~BpSurfaceComposerClient() override; status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format, - uint32_t flags, const sp<IBinder>& parent, int32_t windowType, - int32_t ownerUid, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp) override { + uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata, + sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp) override { return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE, name, width, height, format, flags, parent, - windowType, ownerUid, + std::move(metadata), handle, gbp); } - status_t destroySurface(const sp<IBinder>& handle) override { - return callRemote<decltype(&ISurfaceComposerClient::destroySurface)>(Tag::DESTROY_SURFACE, - handle); + status_t createWithSurfaceParent(const String8& name, uint32_t width, uint32_t height, + PixelFormat format, uint32_t flags, + const sp<IGraphicBufferProducer>& parent, + LayerMetadata metadata, sp<IBinder>* handle, + sp<IGraphicBufferProducer>* gbp) override { + return callRemote<decltype( + &ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT, + name, width, height, format, + flags, parent, + std::move(metadata), handle, + gbp); } status_t clearLayerFrameStats(const sp<IBinder>& handle) const override { @@ -92,8 +99,8 @@ switch (tag) { case Tag::CREATE_SURFACE: return callLocal(data, reply, &ISurfaceComposerClient::createSurface); - case Tag::DESTROY_SURFACE: - return callLocal(data, reply, &ISurfaceComposerClient::destroySurface); + case Tag::CREATE_WITH_SURFACE_PARENT: + return callLocal(data, reply, &ISurfaceComposerClient::createWithSurfaceParent); case Tag::CLEAR_LAYER_FRAME_STATS: return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats); case Tag::GET_LAYER_FRAME_STATS:
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp new file mode 100644 index 0000000..74cd4f1 --- /dev/null +++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -0,0 +1,197 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ITransactionCompletedListener" +//#define LOG_NDEBUG 0 + +#include <gui/ITransactionCompletedListener.h> + +namespace android { + +namespace { // Anonymous + +enum class Tag : uint32_t { + ON_TRANSACTION_COMPLETED = IBinder::FIRST_CALL_TRANSACTION, + LAST = ON_TRANSACTION_COMPLETED, +}; + +} // Anonymous namespace + +status_t SurfaceStats::writeToParcel(Parcel* output) const { + status_t err = output->writeStrongBinder(surfaceControl); + if (err != NO_ERROR) { + return err; + } + err = output->writeInt64(acquireTime); + if (err != NO_ERROR) { + return err; + } + if (previousReleaseFence) { + err = output->writeBool(true); + if (err != NO_ERROR) { + return err; + } + err = output->write(*previousReleaseFence); + } else { + err = output->writeBool(false); + } + return err; +} + +status_t SurfaceStats::readFromParcel(const Parcel* input) { + status_t err = input->readStrongBinder(&surfaceControl); + if (err != NO_ERROR) { + return err; + } + err = input->readInt64(&acquireTime); + if (err != NO_ERROR) { + return err; + } + bool hasFence = false; + err = input->readBool(&hasFence); + if (err != NO_ERROR) { + return err; + } + if (hasFence) { + previousReleaseFence = new Fence(); + err = input->read(*previousReleaseFence); + if (err != NO_ERROR) { + return err; + } + } + return NO_ERROR; +} + +status_t TransactionStats::writeToParcel(Parcel* output) const { + status_t err = output->writeInt64Vector(callbackIds); + if (err != NO_ERROR) { + return err; + } + err = output->writeInt64(latchTime); + if (err != NO_ERROR) { + return err; + } + if (presentFence) { + err = output->writeBool(true); + if (err != NO_ERROR) { + return err; + } + err = output->write(*presentFence); + } else { + err = output->writeBool(false); + } + if (err != NO_ERROR) { + return err; + } + return output->writeParcelableVector(surfaceStats); +} + +status_t TransactionStats::readFromParcel(const Parcel* input) { + status_t err = input->readInt64Vector(&callbackIds); + if (err != NO_ERROR) { + return err; + } + err = input->readInt64(&latchTime); + if (err != NO_ERROR) { + return err; + } + bool hasFence = false; + err = input->readBool(&hasFence); + if (err != NO_ERROR) { + return err; + } + if (hasFence) { + presentFence = new Fence(); + err = input->read(*presentFence); + if (err != NO_ERROR) { + return err; + } + } + return input->readParcelableVector(&surfaceStats); +} + +status_t ListenerStats::writeToParcel(Parcel* output) const { + status_t err = output->writeInt32(static_cast<int32_t>(transactionStats.size())); + if (err != NO_ERROR) { + return err; + } + for (const auto& stats : transactionStats) { + err = output->writeParcelable(stats); + if (err != NO_ERROR) { + return err; + } + } + return NO_ERROR; +} + +status_t ListenerStats::readFromParcel(const Parcel* input) { + int32_t transactionStats_size = input->readInt32(); + + for (int i = 0; i < transactionStats_size; i++) { + TransactionStats stats; + status_t err = input->readParcelable(&stats); + if (err != NO_ERROR) { + return err; + } + transactionStats.push_back(stats); + } + return NO_ERROR; +} + +ListenerStats ListenerStats::createEmpty(const sp<ITransactionCompletedListener>& listener, + const std::unordered_set<CallbackId>& callbackIds) { + ListenerStats listenerStats; + listenerStats.listener = listener; + listenerStats.transactionStats.emplace_back(callbackIds); + + return listenerStats; +} + +class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> { +public: + explicit BpTransactionCompletedListener(const sp<IBinder>& impl) + : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") { + } + + ~BpTransactionCompletedListener() override; + + void onTransactionCompleted(ListenerStats stats) override { + callRemoteAsync<decltype(&ITransactionCompletedListener:: + onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED, + stats); + } +}; + +// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see +// clang warning -Wweak-vtables) +BpTransactionCompletedListener::~BpTransactionCompletedListener() = default; + +IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener"); + +status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) { + if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { + return BBinder::onTransact(code, data, reply, flags); + } + auto tag = static_cast<Tag>(code); + switch (tag) { + case Tag::ON_TRANSACTION_COMPLETED: + return callLocalAsync(data, reply, + &ITransactionCompletedListener::onTransactionCompleted); + } +} + +}; // namespace android
diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp index d3dc16d..cdde9a2 100644 --- a/libs/gui/LayerDebugInfo.cpp +++ b/libs/gui/LayerDebugInfo.cpp
@@ -16,13 +16,14 @@ #include <gui/LayerDebugInfo.h> +#include <android-base/stringprintf.h> + #include <ui/DebugUtils.h> #include <binder/Parcel.h> -#include <utils/String8.h> - using namespace android; +using android::base::StringAppendF; #define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false) @@ -42,7 +43,6 @@ RETURN_ON_ERROR(parcel->writeInt32(mWidth)); RETURN_ON_ERROR(parcel->writeInt32(mHeight)); RETURN_ON_ERROR(parcel->write(mCrop)); - RETURN_ON_ERROR(parcel->write(mFinalCrop)); RETURN_ON_ERROR(parcel->writeFloat(mColor.r)); RETURN_ON_ERROR(parcel->writeFloat(mColor.g)); RETURN_ON_ERROR(parcel->writeFloat(mColor.b)); @@ -81,7 +81,6 @@ RETURN_ON_ERROR(parcel->readInt32(&mWidth)); RETURN_ON_ERROR(parcel->readInt32(&mHeight)); RETURN_ON_ERROR(parcel->read(mCrop)); - RETURN_ON_ERROR(parcel->read(mFinalCrop)); mColor.r = parcel->readFloat(); RETURN_ON_ERROR(parcel->errorCheck()); mColor.g = parcel->readFloat(); @@ -110,39 +109,37 @@ } std::string to_string(const LayerDebugInfo& info) { - String8 result; + std::string result; - result.appendFormat("+ %s (%s)\n", info.mType.c_str(), info.mName.c_str()); + StringAppendF(&result, "+ %s (%s)\n", info.mType.c_str(), info.mName.c_str()); info.mTransparentRegion.dump(result, "TransparentRegion"); info.mVisibleRegion.dump(result, "VisibleRegion"); info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion"); - result.appendFormat(" layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", - info.mLayerStack, info.mZ, static_cast<double>(info.mX), static_cast<double>(info.mY), - info.mWidth, info.mHeight); + StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", + info.mLayerStack, info.mZ, static_cast<double>(info.mX), + static_cast<double>(info.mY), info.mWidth, info.mHeight); - result.appendFormat("crop=%s, finalCrop=%s, ", - to_string(info.mCrop).c_str(), to_string(info.mFinalCrop).c_str()); - result.appendFormat("isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty); - result.appendFormat("dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str()); - result.appendFormat("pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str()); - result.appendFormat("color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ", - static_cast<double>(info.mColor.r), static_cast<double>(info.mColor.g), - static_cast<double>(info.mColor.b), static_cast<double>(info.mColor.a), - info.mFlags); - result.appendFormat("tr=[%.2f, %.2f][%.2f, %.2f]", - static_cast<double>(info.mMatrix[0][0]), static_cast<double>(info.mMatrix[0][1]), - static_cast<double>(info.mMatrix[1][0]), static_cast<double>(info.mMatrix[1][1])); + StringAppendF(&result, "crop=%s, ", to_string(info.mCrop).c_str()); + StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty); + StringAppendF(&result, "dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str()); + StringAppendF(&result, "pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str()); + StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ", + static_cast<double>(info.mColor.r), static_cast<double>(info.mColor.g), + static_cast<double>(info.mColor.b), static_cast<double>(info.mColor.a), + info.mFlags); + StringAppendF(&result, "tr=[%.2f, %.2f][%.2f, %.2f]", static_cast<double>(info.mMatrix[0][0]), + static_cast<double>(info.mMatrix[0][1]), static_cast<double>(info.mMatrix[1][0]), + static_cast<double>(info.mMatrix[1][1])); result.append("\n"); - result.appendFormat(" parent=%s\n", info.mParentName.c_str()); - result.appendFormat(" activeBuffer=[%4ux%4u:%4u,%s],", - info.mActiveBufferWidth, info.mActiveBufferHeight, - info.mActiveBufferStride, - decodePixelFormat(info.mActiveBufferFormat).c_str()); - result.appendFormat(" queued-frames=%d, mRefreshPending=%d", - info.mNumQueuedFrames, info.mRefreshPending); + StringAppendF(&result, " parent=%s\n", info.mParentName.c_str()); + StringAppendF(&result, " activeBuffer=[%4ux%4u:%4u,%s],", info.mActiveBufferWidth, + info.mActiveBufferHeight, info.mActiveBufferStride, + decodePixelFormat(info.mActiveBufferFormat).c_str()); + StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d", info.mNumQueuedFrames, + info.mRefreshPending); result.append("\n"); - return std::string(result.c_str()); + return result; } } // android
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp new file mode 100644 index 0000000..04d2871 --- /dev/null +++ b/libs/gui/LayerMetadata.cpp
@@ -0,0 +1,129 @@ +/* + * 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. + */ + +#include <android-base/stringprintf.h> +#include <binder/Parcel.h> +#include <gui/LayerMetadata.h> + +using android::base::StringPrintf; + +namespace android { + +LayerMetadata::LayerMetadata() = default; + +LayerMetadata::LayerMetadata(std::unordered_map<uint32_t, std::vector<uint8_t>> map) + : mMap(std::move(map)) {} + +LayerMetadata::LayerMetadata(const LayerMetadata& other) = default; + +LayerMetadata::LayerMetadata(LayerMetadata&& other) = default; + +bool LayerMetadata::merge(const LayerMetadata& other, bool eraseEmpty) { + bool changed = false; + for (const auto& entry : other.mMap) { + auto it = mMap.find(entry.first); + if (it != mMap.cend() && it->second != entry.second) { + if (eraseEmpty && entry.second.empty()) { + mMap.erase(it); + } else { + it->second = entry.second; + } + changed = true; + } else if (it == mMap.cend() && !entry.second.empty()) { + mMap[entry.first] = entry.second; + changed = true; + } + } + return changed; +} + +status_t LayerMetadata::writeToParcel(Parcel* parcel) const { + parcel->writeInt32(static_cast<int>(mMap.size())); + status_t status = OK; + for (const auto& entry : mMap) { + status = parcel->writeUint32(entry.first); + if (status != OK) { + break; + } + status = parcel->writeByteVector(entry.second); + if (status != OK) { + break; + } + } + return status; +} + +status_t LayerMetadata::readFromParcel(const Parcel* parcel) { + int size = parcel->readInt32(); + status_t status = OK; + mMap.clear(); + for (int i = 0; i < size; ++i) { + uint32_t key = parcel->readUint32(); + status = parcel->readByteVector(&mMap[key]); + if (status != OK) { + break; + } + } + return status; +} + +LayerMetadata& LayerMetadata::operator=(const LayerMetadata& other) { + mMap = other.mMap; + return *this; +} + +LayerMetadata& LayerMetadata::operator=(LayerMetadata&& other) { + mMap = std::move(other.mMap); + return *this; +} + +bool LayerMetadata::has(uint32_t key) const { + return mMap.count(key); +} + +int32_t LayerMetadata::getInt32(uint32_t key, int32_t fallback) const { + if (!has(key)) return fallback; + const std::vector<uint8_t>& data = mMap.at(key); + if (data.size() < sizeof(uint32_t)) return fallback; + Parcel p; + p.setData(data.data(), data.size()); + return p.readInt32(); +} + +void LayerMetadata::setInt32(uint32_t key, int32_t value) { + std::vector<uint8_t>& data = mMap[key]; + Parcel p; + p.writeInt32(value); + data.resize(p.dataSize()); + memcpy(data.data(), p.data(), p.dataSize()); +} + +std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const { + if (!has(key)) return std::string(); + switch (key) { + case METADATA_OWNER_UID: + return StringPrintf("ownerUID%s%d", separator, getInt32(key, 0)); + case METADATA_WINDOW_TYPE: + return StringPrintf("windowType%s%d", separator, getInt32(key, 0)); + case METADATA_TASK_ID: + return StringPrintf("taskId%s%d", separator, getInt32(key, 0)); + default: + return StringPrintf("%d%s%dbytes", key, separator, + static_cast<int>(mMap.at(key).size())); + } +} + +} // namespace android
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 01acc2d..6066421 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp
@@ -14,6 +14,10 @@ * limitations under the License. */ +#define LOG_TAG "LayerState" + +#include <inttypes.h> + #include <utils/Errors.h> #include <binder/Parcel.h> #include <gui/ISurfaceComposerClient.h> @@ -25,7 +29,7 @@ status_t layer_state_t::write(Parcel& output) const { output.writeStrongBinder(surface); - output.writeUint32(what); + output.writeUint64(what); output.writeFloat(x); output.writeFloat(y); output.writeInt32(z); @@ -37,26 +41,67 @@ output.writeUint32(mask); *reinterpret_cast<layer_state_t::matrix22_t *>( output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix; - output.write(crop); - output.write(finalCrop); - output.writeStrongBinder(barrierHandle); + output.write(crop_legacy); + output.writeStrongBinder(barrierHandle_legacy); output.writeStrongBinder(reparentHandle); - output.writeUint64(frameNumber); + output.writeUint64(frameNumber_legacy); output.writeInt32(overrideScalingMode); - output.writeStrongBinder(IInterface::asBinder(barrierGbp)); + output.writeStrongBinder(IInterface::asBinder(barrierGbp_legacy)); output.writeStrongBinder(relativeLayerHandle); output.writeStrongBinder(parentHandleForChild); output.writeFloat(color.r); output.writeFloat(color.g); output.writeFloat(color.b); +#ifndef NO_INPUT + inputInfo.write(output); +#endif output.write(transparentRegion); + output.writeUint32(transform); + output.writeBool(transformToDisplayInverse); + output.write(crop); + output.write(frame); + if (buffer) { + output.writeBool(true); + output.write(*buffer); + } else { + output.writeBool(false); + } + if (acquireFence) { + output.writeBool(true); + output.write(*acquireFence); + } else { + output.writeBool(false); + } + output.writeUint32(static_cast<uint32_t>(dataspace)); + output.write(hdrMetadata); + output.write(surfaceDamageRegion); + output.writeInt32(api); + if (sidebandStream) { + output.writeBool(true); + output.writeNativeHandle(sidebandStream->handle()); + } else { + output.writeBool(false); + } + + memcpy(output.writeInplace(16 * sizeof(float)), + colorTransform.asArray(), 16 * sizeof(float)); + output.writeFloat(cornerRadius); + output.writeBool(hasListenerCallbacks); + output.writeWeakBinder(cachedBuffer.token); + output.writeUint64(cachedBuffer.id); + output.writeParcelable(metadata); + + output.writeFloat(bgColorAlpha); + output.writeUint32(static_cast<uint32_t>(bgColorDataspace)); + output.writeBool(colorSpaceAgnostic); + return NO_ERROR; } status_t layer_state_t::read(const Parcel& input) { surface = input.readStrongBinder(); - what = input.readUint32(); + what = input.readUint64(); x = input.readFloat(); y = input.readFloat(); z = input.readInt32(); @@ -72,20 +117,54 @@ } else { return BAD_VALUE; } - input.read(crop); - input.read(finalCrop); - barrierHandle = input.readStrongBinder(); + input.read(crop_legacy); + barrierHandle_legacy = input.readStrongBinder(); reparentHandle = input.readStrongBinder(); - frameNumber = input.readUint64(); + frameNumber_legacy = input.readUint64(); overrideScalingMode = input.readInt32(); - barrierGbp = - interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); + barrierGbp_legacy = interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); relativeLayerHandle = input.readStrongBinder(); parentHandleForChild = input.readStrongBinder(); color.r = input.readFloat(); color.g = input.readFloat(); color.b = input.readFloat(); + +#ifndef NO_INPUT + inputInfo = InputWindowInfo::read(input); +#endif + input.read(transparentRegion); + transform = input.readUint32(); + transformToDisplayInverse = input.readBool(); + input.read(crop); + input.read(frame); + buffer = new GraphicBuffer(); + if (input.readBool()) { + input.read(*buffer); + } + acquireFence = new Fence(); + if (input.readBool()) { + input.read(*acquireFence); + } + dataspace = static_cast<ui::Dataspace>(input.readUint32()); + input.read(hdrMetadata); + input.read(surfaceDamageRegion); + api = input.readInt32(); + if (input.readBool()) { + sidebandStream = NativeHandle::create(input.readNativeHandle(), true); + } + + colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float)))); + cornerRadius = input.readFloat(); + hasListenerCallbacks = input.readBool(); + cachedBuffer.token = input.readWeakBinder(); + cachedBuffer.id = input.readUint64(); + input.readParcelable(&metadata); + + bgColorAlpha = input.readFloat(); + bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32()); + colorSpaceAgnostic = input.readBool(); + return NO_ERROR; } @@ -166,6 +245,7 @@ } if (other.what & eLayerChanged) { what |= eLayerChanged; + what &= ~eRelativeLayerChanged; z = other.z; } if (other.what & eSizeChanged) { @@ -194,19 +274,19 @@ what |= eLayerStackChanged; layerStack = other.layerStack; } - if (other.what & eCropChanged) { - what |= eCropChanged; - crop = other.crop; + if (other.what & eCropChanged_legacy) { + what |= eCropChanged_legacy; + crop_legacy = other.crop_legacy; } - if (other.what & eDeferTransaction) { - what |= eDeferTransaction; - barrierHandle = other.barrierHandle; - barrierGbp = other.barrierGbp; - frameNumber = other.frameNumber; + if (other.what & eCornerRadiusChanged) { + what |= eCornerRadiusChanged; + cornerRadius = other.cornerRadius; } - if (other.what & eFinalCropChanged) { - what |= eFinalCropChanged; - finalCrop = other.finalCrop; + if (other.what & eDeferTransaction_legacy) { + what |= eDeferTransaction_legacy; + barrierHandle_legacy = other.barrierHandle_legacy; + barrierGbp_legacy = other.barrierGbp_legacy; + frameNumber_legacy = other.frameNumber_legacy; } if (other.what & eOverrideScalingModeChanged) { what |= eOverrideScalingModeChanged; @@ -224,6 +304,7 @@ } if (other.what & eRelativeLayerChanged) { what |= eRelativeLayerChanged; + what &= ~eLayerChanged; z = other.z; relativeLayerHandle = other.relativeLayerHandle; } @@ -234,6 +315,124 @@ if (other.what & eDestroySurface) { what |= eDestroySurface; } + if (other.what & eTransformChanged) { + what |= eTransformChanged; + transform = other.transform; + } + if (other.what & eTransformToDisplayInverseChanged) { + what |= eTransformToDisplayInverseChanged; + transformToDisplayInverse = other.transformToDisplayInverse; + } + if (other.what & eCropChanged) { + what |= eCropChanged; + crop = other.crop; + } + if (other.what & eFrameChanged) { + what |= eFrameChanged; + frame = other.frame; + } + if (other.what & eBufferChanged) { + what |= eBufferChanged; + buffer = other.buffer; + } + if (other.what & eAcquireFenceChanged) { + what |= eAcquireFenceChanged; + acquireFence = other.acquireFence; + } + if (other.what & eDataspaceChanged) { + what |= eDataspaceChanged; + dataspace = other.dataspace; + } + if (other.what & eHdrMetadataChanged) { + what |= eHdrMetadataChanged; + hdrMetadata = other.hdrMetadata; + } + if (other.what & eSurfaceDamageRegionChanged) { + what |= eSurfaceDamageRegionChanged; + surfaceDamageRegion = other.surfaceDamageRegion; + } + if (other.what & eApiChanged) { + what |= eApiChanged; + api = other.api; + } + if (other.what & eSidebandStreamChanged) { + what |= eSidebandStreamChanged; + sidebandStream = other.sidebandStream; + } + if (other.what & eColorTransformChanged) { + what |= eColorTransformChanged; + colorTransform = other.colorTransform; + } + if (other.what & eHasListenerCallbacksChanged) { + what |= eHasListenerCallbacksChanged; + hasListenerCallbacks = other.hasListenerCallbacks; + } + +#ifndef NO_INPUT + if (other.what & eInputInfoChanged) { + what |= eInputInfoChanged; + inputInfo = other.inputInfo; + } +#endif + + if (other.what & eCachedBufferChanged) { + what |= eCachedBufferChanged; + cachedBuffer = other.cachedBuffer; + } + if (other.what & eBackgroundColorChanged) { + what |= eBackgroundColorChanged; + color = other.color; + bgColorAlpha = other.bgColorAlpha; + bgColorDataspace = other.bgColorDataspace; + } + if (other.what & eMetadataChanged) { + what |= eMetadataChanged; + metadata.merge(other.metadata); + } + if ((other.what & what) != other.what) { + ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " + "other.what=0x%" PRIu64 " what=0x%" PRIu64, + other.what, what); + } +} + +// ------------------------------- InputWindowCommands ---------------------------------------- + +void InputWindowCommands::merge(const InputWindowCommands& other) { + transferTouchFocusCommands + .insert(transferTouchFocusCommands.end(), + std::make_move_iterator(other.transferTouchFocusCommands.begin()), + std::make_move_iterator(other.transferTouchFocusCommands.end())); + + syncInputWindows |= other.syncInputWindows; +} + +void InputWindowCommands::clear() { + transferTouchFocusCommands.clear(); + syncInputWindows = false; +} + +void InputWindowCommands::write(Parcel& output) const { + output.writeUint32(static_cast<uint32_t>(transferTouchFocusCommands.size())); + for (const auto& transferTouchFocusCommand : transferTouchFocusCommands) { + output.writeStrongBinder(transferTouchFocusCommand.fromToken); + output.writeStrongBinder(transferTouchFocusCommand.toToken); + } + + output.writeBool(syncInputWindows); +} + +void InputWindowCommands::read(const Parcel& input) { + size_t count = input.readUint32(); + transferTouchFocusCommands.clear(); + for (size_t i = 0; i < count; i++) { + TransferTouchFocusCommand transferTouchFocusCommand; + transferTouchFocusCommand.fromToken = input.readStrongBinder(); + transferTouchFocusCommand.toToken = input.readStrongBinder(); + transferTouchFocusCommands.emplace_back(transferTouchFocusCommand); + } + + syncInputWindows = input.readBool(); } }; // namespace android
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS new file mode 100644 index 0000000..73150dc --- /dev/null +++ b/libs/gui/OWNERS
@@ -0,0 +1,7 @@ +jessehall@google.com +jwcai@google.com +lpy@google.com +marissaw@google.com +mathias@google.com +racarr@google.com +stoza@google.com
diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp index 52c9067..2f8e104 100644 --- a/libs/gui/StreamSplitter.cpp +++ b/libs/gui/StreamSplitter.cpp
@@ -38,11 +38,11 @@ status_t StreamSplitter::createSplitter( const sp<IGraphicBufferConsumer>& inputQueue, sp<StreamSplitter>* outSplitter) { - if (inputQueue == NULL) { + if (inputQueue == nullptr) { ALOGE("createSplitter: inputQueue must not be NULL"); return BAD_VALUE; } - if (outSplitter == NULL) { + if (outSplitter == nullptr) { ALOGE("createSplitter: outSplitter must not be NULL"); return BAD_VALUE; } @@ -74,7 +74,7 @@ status_t StreamSplitter::addOutput( const sp<IGraphicBufferProducer>& outputQueue) { - if (outputQueue == NULL) { + if (outputQueue == nullptr) { ALOGE("addOutput: outputQueue must not be NULL"); return BAD_VALUE; }
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 2de14c8..e6eb327 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp
@@ -20,6 +20,11 @@ #include <gui/Surface.h> +#include <condition_variable> +#include <deque> +#include <mutex> +#include <thread> + #include <inttypes.h> #include <android/native_window.h> @@ -39,9 +44,6 @@ #include <gui/ISurfaceComposer.h> #include <private/gui/ComposerService.h> -#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> -#include <configstore/Utils.h> - namespace android { using ui::ColorMode; @@ -156,7 +158,7 @@ ATRACE_CALL(); DisplayStatInfo stats; - status_t result = composerService()->getDisplayStats(NULL, &stats); + status_t result = composerService()->getDisplayStats(nullptr, &stats); if (result != NO_ERROR) { return result; } @@ -321,48 +323,27 @@ return NO_ERROR; } -using namespace android::hardware::configstore; -using namespace android::hardware::configstore::V1_0; - status_t Surface::getWideColorSupport(bool* supported) { ATRACE_CALL(); - sp<IBinder> display( - composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); - Vector<ColorMode> colorModes; - status_t err = - composerService()->getDisplayColorModes(display, &colorModes); - - if (err) - return err; - - bool wideColorBoardConfig = - getBool<ISurfaceFlingerConfigs, - &ISurfaceFlingerConfigs::hasWideColorDisplay>(false); - - *supported = false; - for (ColorMode colorMode : colorModes) { - switch (colorMode) { - case ColorMode::DISPLAY_P3: - case ColorMode::ADOBE_RGB: - case ColorMode::DCI_P3: - if (wideColorBoardConfig) { - *supported = true; - } - break; - default: - break; - } + const sp<IBinder> display = composerService()->getInternalDisplayToken(); + if (display == nullptr) { + return NAME_NOT_FOUND; } - return NO_ERROR; + *supported = false; + status_t error = composerService()->isWideColorDisplay(display, supported); + return error; } status_t Surface::getHdrSupport(bool* supported) { ATRACE_CALL(); - sp<IBinder> display( - composerService()->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + const sp<IBinder> display = composerService()->getInternalDisplayToken(); + if (display == nullptr) { + return NAME_NOT_FOUND; + } + HdrCapabilities hdrCapabilities; status_t err = composerService()->getHdrCapabilities(display, &hdrCapabilities); @@ -474,6 +455,82 @@ return NO_ERROR; } +class FenceMonitor { +public: + explicit FenceMonitor(const char* name) : mName(name), mFencesQueued(0), mFencesSignaled(0) { + std::thread thread(&FenceMonitor::loop, this); + pthread_setname_np(thread.native_handle(), mName); + thread.detach(); + } + + void queueFence(const sp<Fence>& fence) { + char message[64]; + + std::lock_guard<std::mutex> lock(mMutex); + if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { + snprintf(message, sizeof(message), "%s fence %u has signaled", mName, mFencesQueued); + ATRACE_NAME(message); + // Need an increment on both to make the trace number correct. + mFencesQueued++; + mFencesSignaled++; + return; + } + snprintf(message, sizeof(message), "Trace %s fence %u", mName, mFencesQueued); + ATRACE_NAME(message); + + mQueue.push_back(fence); + mCondition.notify_one(); + mFencesQueued++; + ATRACE_INT(mName, int32_t(mQueue.size())); + } + +private: +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" + void loop() { + while (true) { + threadLoop(); + } + } +#pragma clang diagnostic pop + + void threadLoop() { + sp<Fence> fence; + uint32_t fenceNum; + { + std::unique_lock<std::mutex> lock(mMutex); + while (mQueue.empty()) { + mCondition.wait(lock); + } + fence = mQueue[0]; + fenceNum = mFencesSignaled; + } + { + char message[64]; + snprintf(message, sizeof(message), "waiting for %s %u", mName, fenceNum); + ATRACE_NAME(message); + + status_t result = fence->waitForever(message); + if (result != OK) { + ALOGE("Error waiting for fence: %d", result); + } + } + { + std::lock_guard<std::mutex> lock(mMutex); + mQueue.pop_front(); + mFencesSignaled++; + ATRACE_INT(mName, int32_t(mQueue.size())); + } + } + + const char* mName; + uint32_t mFencesQueued; + uint32_t mFencesSignaled; + std::deque<sp<Fence>> mQueue; + std::condition_variable mCondition; + std::mutex mMutex; +}; + int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { ATRACE_CALL(); ALOGV("Surface::dequeueBuffer"); @@ -501,7 +558,7 @@ if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot != BufferItem::INVALID_BUFFER_SLOT) { sp<GraphicBuffer>& gbuf(mSlots[mSharedBufferSlot].buffer); - if (gbuf != NULL) { + if (gbuf != nullptr) { *buffer = gbuf.get(); *fenceFd = -1; return OK; @@ -541,7 +598,12 @@ sp<GraphicBuffer>& gbuf(mSlots[buf].buffer); // this should never happen - ALOGE_IF(fence == NULL, "Surface::dequeueBuffer: received null Fence! buf=%d", buf); + ALOGE_IF(fence == nullptr, "Surface::dequeueBuffer: received null Fence! buf=%d", buf); + + if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) { + static FenceMonitor hwcReleaseThread("HWC release"); + hwcReleaseThread.queueFence(fence); + } if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { freeAllBuffers(); @@ -619,7 +681,7 @@ int Surface::getSlotFromBufferLocked( android_native_buffer_t* buffer) const { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - if (mSlots[i].buffer != NULL && + if (mSlots[i].buffer != nullptr && mSlots[i].buffer->handle == buffer->handle) { return i; } @@ -754,7 +816,7 @@ // The consumer doesn't send it back to prevent us from having two // file descriptors of the same fence. mFrameEventHistory->updateAcquireFence(mNextFrameNumber, - std::make_shared<FenceTime>(std::move(fence))); + std::make_shared<FenceTime>(fence)); // Cache timestamps of signaled fences so we can close their file // descriptors. @@ -767,8 +829,9 @@ mDefaultHeight = output.height; mNextFrameNumber = output.nextFrameNumber; - // Disable transform hint if sticky transform is set. - if (mStickyTransform == 0) { + // Ignore transform hint if sticky transform is set or transform to display inverse flag is + // set. + if (mStickyTransform == 0 && !transformToDisplayInverse()) { mTransformHint = output.transformHint; } @@ -785,6 +848,11 @@ mQueueBufferCondition.broadcast(); + if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) { + static FenceMonitor gpuCompletionThread("GPU completion"); + gpuCompletionThread.queueFence(fence); + } + return err; } @@ -965,6 +1033,9 @@ case NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA: res = dispatchSetBuffersCta8613Metadata(args); break; + case NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA: + res = dispatchSetBuffersHdr10PlusMetadata(args); + break; case NATIVE_WINDOW_SET_SURFACE_DAMAGE: res = dispatchSetSurfaceDamage(args); break; @@ -1120,6 +1191,12 @@ return setBuffersCta8613Metadata(metadata); } +int Surface::dispatchSetBuffersHdr10PlusMetadata(va_list args) { + const size_t size = va_arg(args, size_t); + const uint8_t* metadata = va_arg(args, const uint8_t*); + return setBuffersHdr10PlusMetadata(size, metadata); +} + int Surface::dispatchSetSurfaceDamage(va_list args) { android_native_rect_t* rects = va_arg(args, android_native_rect_t*); size_t numRects = va_arg(args, size_t); @@ -1195,6 +1272,11 @@ return getConsumerUsage(usage); } +bool Surface::transformToDisplayInverse() { + return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) == + NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; +} + int Surface::connect(int api) { static sp<IProducerListener> listener = new DummyProducerListener(); return connect(api, listener); @@ -1217,8 +1299,10 @@ mDefaultHeight = output.height; mNextFrameNumber = output.nextFrameNumber; - // Disable transform hint if sticky transform is set. - if (mStickyTransform == 0) { + // Ignore transform hint if sticky transform is set or transform to display inverse flag is + // set. Transform hint should be ignored if the client is expected to always submit buffers + // in the same orientation. + if (mStickyTransform == 0 && !transformToDisplayInverse()) { mTransformHint = output.transformHint; } @@ -1268,7 +1352,7 @@ ATRACE_CALL(); ALOGV("Surface::detachNextBuffer"); - if (outBuffer == NULL || outFence == NULL) { + if (outBuffer == nullptr || outFence == nullptr) { return BAD_VALUE; } @@ -1277,8 +1361,8 @@ mRemovedBuffers.clear(); } - sp<GraphicBuffer> buffer(NULL); - sp<Fence> fence(NULL); + sp<GraphicBuffer> buffer(nullptr); + sp<Fence> fence(nullptr); status_t result = mGraphicBufferProducer->detachNextBuffer( &buffer, &fence); if (result != NO_ERROR) { @@ -1286,19 +1370,19 @@ } *outBuffer = buffer; - if (fence != NULL && fence->isValid()) { + if (fence != nullptr && fence->isValid()) { *outFence = fence; } else { *outFence = Fence::NO_FENCE; } for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - if (mSlots[i].buffer != NULL && + if (mSlots[i].buffer != nullptr && mSlots[i].buffer->getId() == buffer->getId()) { if (mReportRemovedBuffers) { mRemovedBuffers.push_back(mSlots[i].buffer); } - mSlots[i].buffer = NULL; + mSlots[i].buffer = nullptr; } } @@ -1349,7 +1433,7 @@ ATRACE_CALL(); Rect realRect(Rect::EMPTY_RECT); - if (rect == NULL || rect->isEmpty()) { + if (rect == nullptr || rect->isEmpty()) { realRect.clear(); } else { realRect = *rect; @@ -1515,6 +1599,13 @@ ATRACE_CALL(); ALOGV("Surface::setBuffersTransform"); Mutex::Autolock lock(mMutex); + // Ensure NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY is sticky. If the client sets the flag, do not + // override it until the surface is disconnected. This is a temporary workaround for camera + // until they switch to using Buffer State Layers. Currently if client sets the buffer transform + // it may be overriden by the buffer producer when the producer sets the buffer transform. + if (transformToDisplayInverse()) { + transform |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; + } mTransform = transform; return NO_ERROR; } @@ -1568,6 +1659,19 @@ return NO_ERROR; } +int Surface::setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata) { + ALOGV("Surface::setBuffersBlobMetadata"); + Mutex::Autolock lock(mMutex); + if (size > 0) { + mHdrMetadata.hdr10plus.assign(metadata, metadata + size); + mHdrMetadata.validTypes |= HdrMetadata::HDR10PLUS; + } else { + mHdrMetadata.validTypes &= ~HdrMetadata::HDR10PLUS; + mHdrMetadata.hdr10plus.clear(); + } + return NO_ERROR; +} + Dataspace Surface::getBuffersDataSpace() { ALOGV("Surface::getBuffersDataSpace"); Mutex::Autolock lock(mMutex); @@ -1576,7 +1680,7 @@ void Surface::freeAllBuffers() { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - mSlots[i].buffer = 0; + mSlots[i].buffer = nullptr; } } @@ -1616,12 +1720,12 @@ // src and dst with, height and format must be identical. no verification // is done here. status_t err; - uint8_t* src_bits = NULL; + uint8_t* src_bits = nullptr; err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), reinterpret_cast<void**>(&src_bits)); ALOGE_IF(err, "error locking src buffer %s", strerror(-err)); - uint8_t* dst_bits = NULL; + uint8_t* dst_bits = nullptr; err = dst->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), reinterpret_cast<void**>(&dst_bits), *dstFenceFd); ALOGE_IF(err, "error locking dst buffer %s", strerror(-err)); @@ -1669,7 +1773,7 @@ status_t Surface::lock( ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) { - if (mLockedBuffer != 0) { + if (mLockedBuffer != nullptr) { ALOGE("Surface::lock failed, already locked"); return INVALID_OPERATION; } @@ -1701,7 +1805,7 @@ // figure out if we can copy the frontbuffer back const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); - const bool canCopyBack = (frontBuffer != 0 && + const bool canCopyBack = (frontBuffer != nullptr && backBuffer->width == frontBuffer->width && backBuffer->height == frontBuffer->height && backBuffer->format == frontBuffer->format); @@ -1763,7 +1867,7 @@ status_t Surface::unlockAndPost() { - if (mLockedBuffer == 0) { + if (mLockedBuffer == nullptr) { ALOGE("Surface::unlockAndPost failed, no locked buffer"); return INVALID_OPERATION; } @@ -1777,7 +1881,7 @@ mLockedBuffer->handle, strerror(-err)); mPostedBuffer = mLockedBuffer; - mLockedBuffer = 0; + mLockedBuffer = nullptr; return err; }
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 1002576..def9fe9 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp
@@ -25,7 +25,9 @@ #include <utils/String8.h> #include <utils/threads.h> +#include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <binder/ProcessState.h> #include <system/graphics.h> @@ -40,8 +42,15 @@ #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> +#ifndef NO_INPUT +#include <input/InputWindow.h> +#endif + #include <private/gui/ComposerService.h> +// This server size should always be smaller than the server cache size +#define BUFFER_CACHE_MAX_SIZE 64 + namespace android { using ui::ColorMode; @@ -60,7 +69,7 @@ while (getService(name, &mComposerService) != NO_ERROR) { usleep(250000); } - assert(mComposerService != NULL); + assert(mComposerService != nullptr); // Create the death listener. class DeathObserver : public IBinder::DeathRecipient { @@ -81,9 +90,9 @@ /*static*/ sp<ISurfaceComposer> ComposerService::getComposerService() { ComposerService& instance = ComposerService::getInstance(); Mutex::Autolock _l(instance.mLock); - if (instance.mComposerService == NULL) { + if (instance.mComposerService == nullptr) { ComposerService::getInstance().connectLocked(); - assert(instance.mComposerService != NULL); + assert(instance.mComposerService != nullptr); ALOGD("ComposerService reconnected"); } return instance.mComposerService; @@ -92,19 +101,231 @@ void ComposerService::composerServiceDied() { Mutex::Autolock _l(mLock); - mComposerService = NULL; - mDeathObserver = NULL; + mComposerService = nullptr; + mDeathObserver = nullptr; +} + +class DefaultComposerClient: public Singleton<DefaultComposerClient> { + Mutex mLock; + sp<SurfaceComposerClient> mClient; + friend class Singleton<ComposerService>; +public: + static sp<SurfaceComposerClient> getComposerClient() { + DefaultComposerClient& dc = DefaultComposerClient::getInstance(); + Mutex::Autolock _l(dc.mLock); + if (dc.mClient == nullptr) { + dc.mClient = new SurfaceComposerClient; + } + return dc.mClient; + } +}; +ANDROID_SINGLETON_STATIC_INSTANCE(DefaultComposerClient); + + +sp<SurfaceComposerClient> SurfaceComposerClient::getDefault() { + return DefaultComposerClient::getComposerClient(); } // --------------------------------------------------------------------------- -SurfaceComposerClient::Transaction::Transaction(const Transaction& other) : - mForceSynchronous(other.mForceSynchronous), - mTransactionNestCount(other.mTransactionNestCount), - mAnimation(other.mAnimation), - mEarlyWakeup(other.mEarlyWakeup) { +// TransactionCompletedListener does not use ANDROID_SINGLETON_STATIC_INSTANCE because it needs +// to be able to return a sp<> to its instance to pass to SurfaceFlinger. +// ANDROID_SINGLETON_STATIC_INSTANCE only allows a reference to an instance. + +// 0 is an invalid callback id +TransactionCompletedListener::TransactionCompletedListener() : mCallbackIdCounter(1) {} + +CallbackId TransactionCompletedListener::getNextIdLocked() { + return mCallbackIdCounter++; +} + +sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() { + static sp<TransactionCompletedListener> sInstance = new TransactionCompletedListener; + return sInstance; +} + +sp<ITransactionCompletedListener> TransactionCompletedListener::getIInstance() { + return static_cast<sp<ITransactionCompletedListener>>(getInstance()); +} + +void TransactionCompletedListener::startListeningLocked() { + if (mListening) { + return; + } + ProcessState::self()->startThreadPool(); + mListening = true; +} + +CallbackId TransactionCompletedListener::addCallbackFunction( + const TransactionCompletedCallback& callbackFunction, + const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>& + surfaceControls) { + std::lock_guard<std::mutex> lock(mMutex); + startListeningLocked(); + + CallbackId callbackId = getNextIdLocked(); + mCallbacks[callbackId].callbackFunction = callbackFunction; + + auto& callbackSurfaceControls = mCallbacks[callbackId].surfaceControls; + + for (const auto& surfaceControl : surfaceControls) { + callbackSurfaceControls[surfaceControl->getHandle()] = surfaceControl; + } + + return callbackId; +} + +void TransactionCompletedListener::addSurfaceControlToCallbacks( + const sp<SurfaceControl>& surfaceControl, + const std::unordered_set<CallbackId>& callbackIds) { + std::lock_guard<std::mutex> lock(mMutex); + + for (auto callbackId : callbackIds) { + mCallbacks[callbackId].surfaceControls.emplace(std::piecewise_construct, + std::forward_as_tuple( + surfaceControl->getHandle()), + std::forward_as_tuple(surfaceControl)); + } +} + +void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { + std::lock_guard<std::mutex> lock(mMutex); + + /* This listener knows all the sp<IBinder> to sp<SurfaceControl> for all its registered + * callbackIds, except for when Transactions are merged together. This probably cannot be + * solved before this point because the Transactions could be merged together and applied in a + * different process. + * + * Fortunately, we get all the callbacks for this listener for the same frame together at the + * same time. This means if any Transactions were merged together, we will get their callbacks + * at the same time. We can combine all the sp<IBinder> to sp<SurfaceControl> maps for all the + * callbackIds to generate one super map that contains all the sp<IBinder> to sp<SurfaceControl> + * that could possibly exist for the callbacks. + */ + std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls; + for (const auto& transactionStats : listenerStats.transactionStats) { + for (auto callbackId : transactionStats.callbackIds) { + auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId]; + surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end()); + } + } + + for (const auto& transactionStats : listenerStats.transactionStats) { + for (auto callbackId : transactionStats.callbackIds) { + auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId]; + if (!callbackFunction) { + ALOGE("cannot call null callback function, skipping"); + continue; + } + std::vector<SurfaceControlStats> surfaceControlStats; + for (const auto& surfaceStats : transactionStats.surfaceStats) { + surfaceControlStats.emplace_back(surfaceControls[surfaceStats.surfaceControl], + surfaceStats.acquireTime, + surfaceStats.previousReleaseFence); + } + + callbackFunction(transactionStats.latchTime, transactionStats.presentFence, + surfaceControlStats); + mCallbacks.erase(callbackId); + } + } +} + +// --------------------------------------------------------------------------- + +void bufferCacheCallback(void* /*context*/, uint64_t graphicBufferId); + +class BufferCache : public Singleton<BufferCache> { +public: + BufferCache() : token(new BBinder()) {} + + sp<IBinder> getToken() { + return IInterface::asBinder(TransactionCompletedListener::getIInstance()); + } + + status_t getCacheId(const sp<GraphicBuffer>& buffer, uint64_t* cacheId) { + std::lock_guard<std::mutex> lock(mMutex); + + auto itr = mBuffers.find(buffer->getId()); + if (itr == mBuffers.end()) { + return BAD_VALUE; + } + itr->second = getCounter(); + *cacheId = buffer->getId(); + return NO_ERROR; + } + + uint64_t cache(const sp<GraphicBuffer>& buffer) { + std::lock_guard<std::mutex> lock(mMutex); + + if (mBuffers.size() >= BUFFER_CACHE_MAX_SIZE) { + evictLeastRecentlyUsedBuffer(); + } + + buffer->addDeathCallback(bufferCacheCallback, nullptr); + + mBuffers[buffer->getId()] = getCounter(); + return buffer->getId(); + } + + void uncache(uint64_t cacheId) { + std::lock_guard<std::mutex> lock(mMutex); + uncacheLocked(cacheId); + } + + void uncacheLocked(uint64_t cacheId) REQUIRES(mMutex) { + mBuffers.erase(cacheId); + SurfaceComposerClient::doUncacheBufferTransaction(cacheId); + } + +private: + void evictLeastRecentlyUsedBuffer() REQUIRES(mMutex) { + auto itr = mBuffers.begin(); + uint64_t minCounter = itr->second; + auto minBuffer = itr; + itr++; + + while (itr != mBuffers.end()) { + uint64_t counter = itr->second; + if (counter < minCounter) { + minCounter = counter; + minBuffer = itr; + } + itr++; + } + uncacheLocked(minBuffer->first); + } + + uint64_t getCounter() REQUIRES(mMutex) { + static uint64_t counter = 0; + return counter++; + } + + std::mutex mMutex; + std::map<uint64_t /*Cache id*/, uint64_t /*counter*/> mBuffers GUARDED_BY(mMutex); + + // Used by ISurfaceComposer to identify which process is sending the cached buffer. + sp<IBinder> token; +}; + +ANDROID_SINGLETON_STATIC_INSTANCE(BufferCache); + +void bufferCacheCallback(void* /*context*/, uint64_t graphicBufferId) { + // GraphicBuffer id's are used as the cache ids. + BufferCache::getInstance().uncache(graphicBufferId); +} + +// --------------------------------------------------------------------------- + +SurfaceComposerClient::Transaction::Transaction(const Transaction& other) + : mForceSynchronous(other.mForceSynchronous), + mTransactionNestCount(other.mTransactionNestCount), + mAnimation(other.mAnimation), + mEarlyWakeup(other.mEarlyWakeup), + mDesiredPresentTime(other.mDesiredPresentTime) { mDisplayStates = other.mDisplayStates; mComposerStates = other.mComposerStates; + mInputWindowCommands = other.mInputWindowCommands; } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) { @@ -127,9 +348,96 @@ } other.mDisplayStates.clear(); + for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) { + auto& [callbackIds, surfaceControls] = callbackInfo; + mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator( + callbackIds.begin()), + std::make_move_iterator(callbackIds.end())); + mListenerCallbacks[listener] + .surfaceControls.insert(std::make_move_iterator(surfaceControls.begin()), + std::make_move_iterator(surfaceControls.end())); + } + other.mListenerCallbacks.clear(); + + mInputWindowCommands.merge(other.mInputWindowCommands); + other.mInputWindowCommands.clear(); + + mContainsBuffer = other.mContainsBuffer; + other.mContainsBuffer = false; + + mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup; + other.mEarlyWakeup = false; + return *this; } +void SurfaceComposerClient::doDropReferenceTransaction(const sp<IBinder>& handle, + const sp<ISurfaceComposerClient>& client) { + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + Vector<ComposerState> composerStates; + Vector<DisplayState> displayStates; + + ComposerState s; + s.client = client; + s.state.surface = handle; + s.state.what |= layer_state_t::eReparent; + s.state.parentHandleForChild = nullptr; + + composerStates.add(s); + sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); + sf->setTransactionState(composerStates, displayStates, 0, applyToken, {}, -1, {}, {}); +} + +void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + + client_cache_t uncacheBuffer; + uncacheBuffer.token = BufferCache::getInstance().getToken(); + uncacheBuffer.id = cacheId; + + sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); + sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, {}); +} + +void SurfaceComposerClient::Transaction::cacheBuffers() { + if (!mContainsBuffer) { + return; + } + + size_t count = 0; + for (auto& [sc, cs] : mComposerStates) { + layer_state_t* s = getLayerState(sc); + if (!(s->what & layer_state_t::eBufferChanged)) { + continue; + } + + // Don't try to cache a null buffer. Sending null buffers is cheap so we shouldn't waste + // time trying to cache them. + if (!s->buffer) { + continue; + } + + uint64_t cacheId = 0; + status_t ret = BufferCache::getInstance().getCacheId(s->buffer, &cacheId); + if (ret == NO_ERROR) { + s->what &= ~static_cast<uint64_t>(layer_state_t::eBufferChanged); + s->buffer = nullptr; + } else { + cacheId = BufferCache::getInstance().cache(s->buffer); + } + s->what |= layer_state_t::eCachedBufferChanged; + s->cachedBuffer.token = BufferCache::getInstance().getToken(); + s->cachedBuffer.id = cacheId; + + // If we have more buffers than the size of the cache, we should stop caching so we don't + // evict other buffers in this transaction + count++; + if (count >= BUFFER_CACHE_MAX_SIZE) { + break; + } + } +} + status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { if (mStatus != NO_ERROR) { return mStatus; @@ -137,6 +445,32 @@ sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + std::vector<ListenerCallbacks> listenerCallbacks; + + // For every listener with registered callbacks + for (const auto& [listener, callbackInfo] : mListenerCallbacks) { + auto& [callbackIds, surfaceControls] = callbackInfo; + if (callbackIds.empty()) { + continue; + } + + listenerCallbacks.emplace_back(listener, std::move(callbackIds)); + + // If the listener has any SurfaceControls set on this Transaction update the surface state + for (const auto& surfaceControl : surfaceControls) { + layer_state_t* s = getLayerState(surfaceControl); + if (!s) { + ALOGE("failed to get layer state"); + continue; + } + s->what |= layer_state_t::eHasListenerCallbacksChanged; + s->hasListenerCallbacks = true; + } + } + mListenerCallbacks.clear(); + + cacheBuffers(); + Vector<ComposerState> composerStates; Vector<DisplayState> displayStates; uint32_t flags = 0; @@ -166,7 +500,12 @@ mAnimation = false; mEarlyWakeup = false; - sf->setTransactionState(composerStates, displayStates, flags); + sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); + sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands, + mDesiredPresentTime, + {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/, + listenerCallbacks); + mInputWindowCommands.clear(); mStatus = NO_ERROR; return NO_ERROR; } @@ -182,8 +521,20 @@ return ComposerService::getComposerService()->destroyDisplay(display); } -sp<IBinder> SurfaceComposerClient::getBuiltInDisplay(int32_t id) { - return ComposerService::getComposerService()->getBuiltInDisplay(id); +std::vector<PhysicalDisplayId> SurfaceComposerClient::getPhysicalDisplayIds() { + return ComposerService::getComposerService()->getPhysicalDisplayIds(); +} + +std::optional<PhysicalDisplayId> SurfaceComposerClient::getInternalDisplayId() { + return ComposerService::getComposerService()->getInternalDisplayId(); +} + +sp<IBinder> SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId displayId) { + return ComposerService::getComposerService()->getPhysicalDisplayToken(displayId); +} + +sp<IBinder> SurfaceComposerClient::getInternalDisplayToken() { + return ComposerService::getComposerService()->getInternalDisplayToken(); } void SurfaceComposerClient::Transaction::setAnimationTransaction() { @@ -206,6 +557,15 @@ return &(mComposerStates[sc].state); } +void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback( + const sp<SurfaceControl>& sc) { + auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()]; + callbackInfo.surfaceControls.insert(sc); + + TransactionCompletedListener::getInstance() + ->addSurfaceControlToCallbacks(sc, callbackInfo.callbackIds); +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition( const sp<SurfaceControl>& sc, float x, float y) { layer_state_t* s = getLayerState(sc); @@ -216,6 +576,8 @@ s->what |= layer_state_t::ePositionChanged; s->x = x; s->y = y; + + registerSurfaceControlForCallback(sc); return *this; } @@ -240,9 +602,7 @@ s->w = w; s->h = h; - // Resizing a surface makes the transaction synchronous. - mForceSynchronous = true; - + registerSurfaceControlForCallback(sc); return *this; } @@ -254,7 +614,10 @@ return *this; } s->what |= layer_state_t::eLayerChanged; + s->what &= ~layer_state_t::eRelativeLayerChanged; s->z = z; + + registerSurfaceControlForCallback(sc); return *this; } @@ -265,8 +628,11 @@ mStatus = BAD_INDEX; } s->what |= layer_state_t::eRelativeLayerChanged; + s->what &= ~layer_state_t::eLayerChanged; s->relativeLayerHandle = relativeTo; s->z = z; + + registerSurfaceControlForCallback(sc); return *this; } @@ -286,6 +652,8 @@ s->flags &= ~mask; s->flags |= (flags & mask); s->mask |= mask; + + registerSurfaceControlForCallback(sc); return *this; } @@ -299,6 +667,8 @@ } s->what |= layer_state_t::eTransparentRegionChanged; s->transparentRegion = transparentRegion; + + registerSurfaceControlForCallback(sc); return *this; } @@ -311,6 +681,8 @@ } s->what |= layer_state_t::eAlphaChanged; s->alpha = alpha; + + registerSurfaceControlForCallback(sc); return *this; } @@ -323,6 +695,22 @@ } s->what |= layer_state_t::eLayerStackChanged; s->layerStack = layerStack; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMetadata( + const sp<SurfaceControl>& sc, uint32_t key, std::vector<uint8_t> data) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eMetadataChanged; + s->metadata.mMap[key] = std::move(data); + + registerSurfaceControlForCallback(sc); return *this; } @@ -341,57 +729,68 @@ matrix.dsdy = dsdy; matrix.dtdy = dtdy; s->matrix = matrix; + + registerSurfaceControlForCallback(sc); return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop( +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop_legacy( const sp<SurfaceControl>& sc, const Rect& crop) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eCropChanged; - s->crop = crop; + s->what |= layer_state_t::eCropChanged_legacy; + s->crop_legacy = crop; + + registerSurfaceControlForCallback(sc); return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFinalCrop(const sp<SurfaceControl>& sc, const Rect& crop) { +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCornerRadius( + const sp<SurfaceControl>& sc, float cornerRadius) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eFinalCropChanged; - s->finalCrop = crop; + s->what |= layer_state_t::eCornerRadiusChanged; + s->cornerRadius = cornerRadius; return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::deferTransactionUntil( - const sp<SurfaceControl>& sc, - const sp<IBinder>& handle, uint64_t frameNumber) { +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, + const sp<IBinder>& handle, + uint64_t frameNumber) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eDeferTransaction; - s->barrierHandle = handle; - s->frameNumber = frameNumber; + s->what |= layer_state_t::eDeferTransaction_legacy; + s->barrierHandle_legacy = handle; + s->frameNumber_legacy = frameNumber; + + registerSurfaceControlForCallback(sc); return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::deferTransactionUntil( - const sp<SurfaceControl>& sc, - const sp<Surface>& barrierSurface, uint64_t frameNumber) { +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, + const sp<Surface>& barrierSurface, + uint64_t frameNumber) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eDeferTransaction; - s->barrierGbp = barrierSurface->getIGraphicBufferProducer(); - s->frameNumber = frameNumber; + s->what |= layer_state_t::eDeferTransaction_legacy; + s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer(); + s->frameNumber_legacy = frameNumber; + + registerSurfaceControlForCallback(sc); return *this; } @@ -405,6 +804,8 @@ } s->what |= layer_state_t::eReparentChildren; s->reparentHandle = newParentHandle; + + registerSurfaceControlForCallback(sc); return *this; } @@ -418,6 +819,8 @@ } s->what |= layer_state_t::eReparent; s->parentHandleForChild = newParentHandle; + + registerSurfaceControlForCallback(sc); return *this; } @@ -431,6 +834,219 @@ } s->what |= layer_state_t::eColorChanged; s->color = color; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackgroundColor( + const sp<SurfaceControl>& sc, const half3& color, float alpha, ui::Dataspace dataspace) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eBackgroundColorChanged; + s->color = color; + s->bgColorAlpha = alpha; + s->bgColorDataspace = dataspace; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTransform( + const sp<SurfaceControl>& sc, uint32_t transform) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eTransformChanged; + s->transform = transform; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::setTransformToDisplayInverse(const sp<SurfaceControl>& sc, + bool transformToDisplayInverse) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eTransformToDisplayInverseChanged; + s->transformToDisplayInverse = transformToDisplayInverse; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop( + const sp<SurfaceControl>& sc, const Rect& crop) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eCropChanged; + s->crop = crop; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame( + const sp<SurfaceControl>& sc, const Rect& frame) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eFrameChanged; + s->frame = frame; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer( + const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eBufferChanged; + s->buffer = buffer; + + registerSurfaceControlForCallback(sc); + + mContainsBuffer = true; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAcquireFence( + const sp<SurfaceControl>& sc, const sp<Fence>& fence) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eAcquireFenceChanged; + s->acquireFence = fence; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace( + const sp<SurfaceControl>& sc, ui::Dataspace dataspace) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eDataspaceChanged; + s->dataspace = dataspace; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setHdrMetadata( + const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eHdrMetadataChanged; + s->hdrMetadata = hdrMetadata; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSurfaceDamageRegion( + const sp<SurfaceControl>& sc, const Region& surfaceDamageRegion) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eSurfaceDamageRegionChanged; + s->surfaceDamageRegion = surfaceDamageRegion; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApi( + const sp<SurfaceControl>& sc, int32_t api) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eApiChanged; + s->api = api; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSidebandStream( + const sp<SurfaceControl>& sc, const sp<NativeHandle>& sidebandStream) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eSidebandStreamChanged; + s->sidebandStream = sidebandStream; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime( + nsecs_t desiredPresentTime) { + mDesiredPresentTime = desiredPresentTime; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorSpaceAgnostic( + const sp<SurfaceControl>& sc, const bool agnostic) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eColorSpaceAgnosticChanged; + s->colorSpaceAgnostic = agnostic; + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& +SurfaceComposerClient::Transaction::addTransactionCompletedCallback( + TransactionCompletedCallbackTakesContext callback, void* callbackContext) { + auto listener = TransactionCompletedListener::getInstance(); + + auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + const auto& surfaceControls = + mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls; + + CallbackId callbackId = listener->addCallbackFunction(callbackWithContext, surfaceControls); + + mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace( + callbackId); return *this; } @@ -441,6 +1057,8 @@ mStatus = BAD_INDEX; } s->what |= layer_state_t::eDetachChildren; + + registerSurfaceControlForCallback(sc); return *this; } @@ -468,6 +1086,8 @@ s->what |= layer_state_t::eOverrideScalingModeChanged; s->overrideScalingMode = overrideScalingMode; + + registerSurfaceControlForCallback(sc); return *this; } @@ -479,17 +1099,104 @@ return *this; } s->what |= layer_state_t::eGeometryAppliesWithResize; + + registerSurfaceControlForCallback(sc); return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::destroySurface( - const sp<SurfaceControl>& sc) { +#ifndef NO_INPUT +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo( + const sp<SurfaceControl>& sc, + const InputWindowInfo& info) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eDestroySurface; + s->inputInfo = info; + s->what |= layer_state_t::eInputInfoChanged; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::transferTouchFocus( + const sp<IBinder>& fromToken, const sp<IBinder>& toToken) { + InputWindowCommands::TransferTouchFocusCommand transferTouchFocusCommand; + transferTouchFocusCommand.fromToken = fromToken; + transferTouchFocusCommand.toToken = toToken; + mInputWindowCommands.transferTouchFocusCommands.emplace_back(transferTouchFocusCommand); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() { + mInputWindowCommands.syncInputWindows = true; + return *this; +} + +#endif + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorTransform( + const sp<SurfaceControl>& sc, const mat3& matrix, const vec3& translation) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eColorTransformChanged; + s->colorTransform = mat4(matrix, translation); + + registerSurfaceControlForCallback(sc); + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometry( + const sp<SurfaceControl>& sc, const Rect& source, const Rect& dst, int transform) { + setCrop_legacy(sc, source); + + int x = dst.left; + int y = dst.top; + + float sourceWidth = source.getWidth(); + float sourceHeight = source.getHeight(); + + float xScale = sourceWidth < 0 ? 1.0f : dst.getWidth() / sourceWidth; + float yScale = sourceHeight < 0 ? 1.0f : dst.getHeight() / sourceHeight; + float matrix[4] = {1, 0, 0, 1}; + + switch (transform) { + case NATIVE_WINDOW_TRANSFORM_FLIP_H: + matrix[0] = -xScale; matrix[1] = 0; + matrix[2] = 0; matrix[3] = yScale; + x += source.getWidth(); + break; + case NATIVE_WINDOW_TRANSFORM_FLIP_V: + matrix[0] = xScale; matrix[1] = 0; + matrix[2] = 0; matrix[3] = -yScale; + y += source.getHeight(); + break; + case NATIVE_WINDOW_TRANSFORM_ROT_90: + matrix[0] = 0; matrix[1] = -yScale; + matrix[2] = xScale; matrix[3] = 0; + x += source.getHeight(); + break; + case NATIVE_WINDOW_TRANSFORM_ROT_180: + matrix[0] = -xScale; matrix[1] = 0; + matrix[2] = 0; matrix[3] = -yScale; + x += source.getWidth(); + y += source.getHeight(); + break; + case NATIVE_WINDOW_TRANSFORM_ROT_270: + matrix[0] = 0; matrix[1] = yScale; + matrix[2] = -xScale; matrix[3] = 0; + y += source.getWidth(); + break; + default: + matrix[0] = xScale; matrix[1] = 0; + matrix[2] = 0; matrix[3] = yScale; + break; + } + setMatrix(sc, matrix[0], matrix[1], matrix[2], matrix[3]); + setPosition(sc, x, y); + return *this; } @@ -559,11 +1266,6 @@ { } -SurfaceComposerClient::SurfaceComposerClient(const sp<IGraphicBufferProducer>& root) - : mStatus(NO_INIT), mParent(root) -{ -} - SurfaceComposerClient::SurfaceComposerClient(const sp<ISurfaceComposerClient>& client) : mStatus(NO_ERROR), mClient(client) { @@ -571,12 +1273,10 @@ void SurfaceComposerClient::onFirstRef() { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - if (sf != 0 && mStatus == NO_INIT) { - auto rootProducer = mParent.promote(); + if (sf != nullptr && mStatus == NO_INIT) { sp<ISurfaceComposerClient> conn; - conn = (rootProducer != nullptr) ? sf->createScopedConnection(rootProducer) : - sf->createConnection(); - if (conn != 0) { + conn = sf->createConnection(); + if (conn != nullptr) { mClient = conn; mStatus = NO_ERROR; } @@ -606,39 +1306,49 @@ // this can be called more than once. sp<ISurfaceComposerClient> client; Mutex::Autolock _lm(mLock); - if (mClient != 0) { + if (mClient != nullptr) { client = mClient; // hold ref while lock is held mClient.clear(); } mStatus = NO_INIT; } -sp<SurfaceControl> SurfaceComposerClient::createSurface( - const String8& name, - uint32_t w, - uint32_t h, - PixelFormat format, - uint32_t flags, - SurfaceControl* parent, - int32_t windowType, - int32_t ownerUid) -{ +sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags, + SurfaceControl* parent, + LayerMetadata metadata) { sp<SurfaceControl> s; - createSurfaceChecked(name, w, h, format, &s, flags, parent, windowType, ownerUid); + createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata)); return s; } -status_t SurfaceComposerClient::createSurfaceChecked( - const String8& name, - uint32_t w, - uint32_t h, - PixelFormat format, - sp<SurfaceControl>* outSurface, - uint32_t flags, - SurfaceControl* parent, - int32_t windowType, - int32_t ownerUid) -{ +sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& name, uint32_t w, + uint32_t h, PixelFormat format, + uint32_t flags, Surface* parent, + LayerMetadata metadata) { + sp<SurfaceControl> sur; + status_t err = mStatus; + + if (mStatus == NO_ERROR) { + sp<IBinder> handle; + sp<IGraphicBufferProducer> parentGbp = parent->getIGraphicBufferProducer(); + sp<IGraphicBufferProducer> gbp; + + err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp, + std::move(metadata), &handle, &gbp); + ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err)); + if (err == NO_ERROR) { + return new SurfaceControl(this, handle, gbp, true /* owned */); + } + } + return nullptr; +} + +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) { sp<SurfaceControl> sur; status_t err = mStatus; @@ -650,8 +1360,9 @@ if (parent != nullptr) { parentHandle = parent->getHandle(); } - err = mClient->createSurface(name, w, h, format, flags, parentHandle, - windowType, ownerUid, &handle, &gbp); + + err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata), + &handle, &gbp); ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err)); if (err == NO_ERROR) { *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */); @@ -660,13 +1371,6 @@ return err; } -status_t SurfaceComposerClient::destroySurface(const sp<IBinder>& sid) { - if (mStatus != NO_ERROR) - return mStatus; - status_t err = mClient->destroySurface(sid); - return err; -} - status_t SurfaceComposerClient::clearLayerFrameStats(const sp<IBinder>& token) const { if (mStatus != NO_ERROR) { return mStatus; @@ -718,10 +1422,6 @@ return NO_ERROR; } -status_t SurfaceComposerClient::getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) { - return ComposerService::getComposerService()->getDisplayViewport(display, outViewport); -} - int SurfaceComposerClient::getActiveConfig(const sp<IBinder>& display) { return ComposerService::getComposerService()->getActiveConfig(display); } @@ -730,11 +1430,28 @@ return ComposerService::getComposerService()->setActiveConfig(display, id); } +status_t SurfaceComposerClient::setAllowedDisplayConfigs( + const sp<IBinder>& displayToken, const std::vector<int32_t>& allowedConfigs) { + return ComposerService::getComposerService()->setAllowedDisplayConfigs(displayToken, + allowedConfigs); +} + +status_t SurfaceComposerClient::getAllowedDisplayConfigs(const sp<IBinder>& displayToken, + std::vector<int32_t>* outAllowedConfigs) { + return ComposerService::getComposerService()->getAllowedDisplayConfigs(displayToken, + outAllowedConfigs); +} + status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display, Vector<ColorMode>* outColorModes) { return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes); } +status_t SurfaceComposerClient::getDisplayNativePrimaries(const sp<IBinder>& display, + ui::DisplayPrimaries& outPrimaries) { + return ComposerService::getComposerService()->getDisplayNativePrimaries(display, outPrimaries); +} + ColorMode SurfaceComposerClient::getActiveColorMode(const sp<IBinder>& display) { return ComposerService::getComposerService()->getActiveColorMode(display); } @@ -749,6 +1466,20 @@ ComposerService::getComposerService()->setPowerMode(token, mode); } +status_t SurfaceComposerClient::getCompositionPreference( + ui::Dataspace* defaultDataspace, ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, ui::PixelFormat* wideColorGamutPixelFormat) { + return ComposerService::getComposerService() + ->getCompositionPreference(defaultDataspace, defaultPixelFormat, + wideColorGamutDataspace, wideColorGamutPixelFormat); +} + +bool SurfaceComposerClient::getProtectedContentSupport() { + bool supported = false; + ComposerService::getComposerService()->getProtectedContentSupport(&supported); + return supported; +} + status_t SurfaceComposerClient::clearAnimationFrameStats() { return ComposerService::getComposerService()->clearAnimationFrameStats(); } @@ -763,49 +1494,121 @@ outCapabilities); } +status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display, + ui::PixelFormat* outFormat, + ui::Dataspace* outDataspace, + uint8_t* outComponentMask) { + return ComposerService::getComposerService() + ->getDisplayedContentSamplingAttributes(display, outFormat, outDataspace, + outComponentMask); +} + +status_t SurfaceComposerClient::setDisplayContentSamplingEnabled(const sp<IBinder>& display, + bool enable, uint8_t componentMask, + uint64_t maxFrames) { + return ComposerService::getComposerService()->setDisplayContentSamplingEnabled(display, enable, + componentMask, + maxFrames); +} + +status_t SurfaceComposerClient::getDisplayedContentSample(const sp<IBinder>& display, + uint64_t maxFrames, uint64_t timestamp, + DisplayedFrameStats* outStats) { + return ComposerService::getComposerService()->getDisplayedContentSample(display, maxFrames, + timestamp, outStats); +} + +status_t SurfaceComposerClient::isWideColorDisplay(const sp<IBinder>& display, + bool* outIsWideColorDisplay) { + return ComposerService::getComposerService()->isWideColorDisplay(display, + outIsWideColorDisplay); +} + +status_t SurfaceComposerClient::addRegionSamplingListener( + const Rect& samplingArea, const sp<IBinder>& stopLayerHandle, + const sp<IRegionSamplingListener>& listener) { + return ComposerService::getComposerService()->addRegionSamplingListener(samplingArea, + stopLayerHandle, + listener); +} + +status_t SurfaceComposerClient::removeRegionSamplingListener( + const sp<IRegionSamplingListener>& listener) { + return ComposerService::getComposerService()->removeRegionSamplingListener(listener); +} + +bool SurfaceComposerClient::getDisplayBrightnessSupport(const sp<IBinder>& displayToken) { + bool support = false; + ComposerService::getComposerService()->getDisplayBrightnessSupport(displayToken, &support); + return support; +} + +status_t SurfaceComposerClient::setDisplayBrightness(const sp<IBinder>& displayToken, + float brightness) { + return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness); +} + +status_t SurfaceComposerClient::notifyPowerHint(int32_t hintId) { + return ComposerService::getComposerService()->notifyPowerHint(hintId); +} + // ---------------------------------------------------------------------------- -status_t ScreenshotClient::capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, - bool useIdentityTransform, uint32_t rotation, - bool captureSecureLayers, sp<GraphicBuffer>* outBuffer, - bool& outCapturedSecureLayers) { +status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, + uint32_t rotation, bool captureSecureLayers, + sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == NULL) return NO_INIT; - status_t ret = s->captureScreen(display, outBuffer, outCapturedSecureLayers, sourceCrop, - reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform, - static_cast<ISurfaceComposer::Rotation>(rotation), - captureSecureLayers); + if (s == nullptr) return NO_INIT; + status_t ret = + s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace, + reqPixelFormat, sourceCrop, reqWidth, reqHeight, useIdentityTransform, + static_cast<ISurfaceComposer::Rotation>(rotation), + captureSecureLayers); if (ret != NO_ERROR) { return ret; } return ret; } -status_t ScreenshotClient::capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, - bool useIdentityTransform, uint32_t rotation, - sp<GraphicBuffer>* outBuffer) { +status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, + uint32_t rotation, sp<GraphicBuffer>* outBuffer) { bool ignored; - return capture(display, sourceCrop, reqWidth, reqHeight, - minLayerZ, maxLayerZ, useIdentityTransform, rotation, false, outBuffer, ignored); + return capture(display, reqDataSpace, reqPixelFormat, sourceCrop, reqWidth, reqHeight, + useIdentityTransform, rotation, false, outBuffer, ignored); } -status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, +status_t ScreenshotClient::capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, + sp<GraphicBuffer>* outBuffer) { + sp<ISurfaceComposer> s(ComposerService::getComposerService()); + if (s == nullptr) return NO_INIT; + return s->captureScreen(displayOrLayerStack, outDataspace, outBuffer); +} + +status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, + const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, float frameScale, sp<GraphicBuffer>* outBuffer) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == NULL) return NO_INIT; - status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale, - false /* childrenOnly */); + if (s == nullptr) return NO_INIT; + status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, + sourceCrop, {}, frameScale, false /* childrenOnly */); return ret; } -status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, - float frameScale, sp<GraphicBuffer>* outBuffer) { +status_t ScreenshotClient::captureChildLayers( + const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles, + float frameScale, sp<GraphicBuffer>* outBuffer) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == NULL) return NO_INIT; - status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale, - true /* childrenOnly */); + if (s == nullptr) return NO_INIT; + status_t ret = + s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop, + excludeHandles, frameScale, true /* childrenOnly */); return ret; } // ----------------------------------------------------------------------------
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 5eafbb3..55488da 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp
@@ -54,19 +54,34 @@ { } +SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) { + mClient = other->mClient; + mHandle = other->mHandle; + mGraphicBufferProducer = other->mGraphicBufferProducer; + mOwned = false; +} + SurfaceControl::~SurfaceControl() { - destroy(); + // Avoid reparenting the server-side surface to null if we are not the owner of it, + // meaning that we retrieved it from another process. + if (mClient != nullptr && mHandle != nullptr && mOwned) { + SurfaceComposerClient::doDropReferenceTransaction(mHandle, mClient->getClient()); + } + release(); } void SurfaceControl::destroy() { - // Avoid destroying the server-side surface if we are not the owner of it, meaning that we - // retrieved it from another process. - if (isValid() && mOwned) { - mClient->destroySurface(mHandle); + if (isValid()) { + SurfaceComposerClient::Transaction().reparent(this, nullptr).apply(); } - // clear all references and trigger an IPC now, to make sure things + release(); +} + +void SurfaceControl::release() +{ + // Trigger an IPC now, to make sure things // happen without delay, since these resources are quite heavy. mClient.clear(); mHandle.clear(); @@ -74,19 +89,8 @@ IPCThreadState::self()->flushCommands(); } -void SurfaceControl::clear() -{ - // here, the window manager tells us explicitly that we should destroy - // the surface's resource. Soon after this call, it will also release - // its last reference (which will call the dtor); however, it is possible - // that a client living in the same process still holds references which - // would delay the call to the dtor -- that is why we need this explicit - // "clear()" call. - destroy(); -} - void SurfaceControl::disconnect() { - if (mGraphicBufferProducer != NULL) { + if (mGraphicBufferProducer != nullptr) { mGraphicBufferProducer->disconnect( BufferQueueCore::CURRENTLY_CONNECTED_API); } @@ -95,28 +99,28 @@ bool SurfaceControl::isSameSurface( const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) { - if (lhs == 0 || rhs == 0) + if (lhs == nullptr || rhs == nullptr) return false; return lhs->mHandle == rhs->mHandle; } status_t SurfaceControl::clearLayerFrameStats() const { status_t err = validate(); - if (err < 0) return err; + if (err != NO_ERROR) return err; const sp<SurfaceComposerClient>& client(mClient); return client->clearLayerFrameStats(mHandle); } status_t SurfaceControl::getLayerFrameStats(FrameStats* outStats) const { status_t err = validate(); - if (err < 0) return err; + if (err != NO_ERROR) return err; const sp<SurfaceComposerClient>& client(mClient); return client->getLayerFrameStats(mHandle, outStats); } status_t SurfaceControl::validate() const { - if (mHandle==0 || mClient==0) { + if (mHandle==nullptr || mClient==nullptr) { ALOGE("invalid handle (%p) or client (%p)", mHandle.get(), mClient.get()); return NO_INIT; @@ -128,7 +132,7 @@ const sp<SurfaceControl>& control, Parcel* parcel) { sp<IGraphicBufferProducer> bp; - if (control != NULL) { + if (control != nullptr) { bp = control->mGraphicBufferProducer; } return parcel->writeStrongBinder(IInterface::asBinder(bp)); @@ -146,7 +150,7 @@ sp<Surface> SurfaceControl::getSurface() const { Mutex::Autolock _l(mLock); - if (mSurfaceData == 0) { + if (mSurfaceData == nullptr) { return generateSurfaceLocked(); } return mSurfaceData; @@ -164,6 +168,12 @@ return mHandle; } +sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer() const +{ + Mutex::Autolock _l(mLock); + return mGraphicBufferProducer; +} + sp<SurfaceComposerClient> SurfaceControl::getClient() const { return mClient;
diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp index afa15c5..fcae05c 100644 --- a/libs/gui/SyncFeatures.cpp +++ b/libs/gui/SyncFeatures.cpp
@@ -41,7 +41,7 @@ // This can only be called after EGL has been initialized; otherwise the // check below will abort. const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); - LOG_ALWAYS_FATAL_IF(exts == NULL, "eglQueryStringImplementationANDROID failed"); + LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryStringImplementationANDROID failed"); if (strstr(exts, "EGL_ANDROID_native_fence_sync")) { // This makes GLConsumer use the EGL_ANDROID_native_fence_sync // extension to create Android native fences to signal when all
diff --git a/libs/gui/bufferqueue/1.0/Conversion.cpp b/libs/gui/bufferqueue/1.0/Conversion.cpp new file mode 100644 index 0000000..5cb3593 --- /dev/null +++ b/libs/gui/bufferqueue/1.0/Conversion.cpp
@@ -0,0 +1,1542 @@ +/* + * Copyright 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gui/bufferqueue/1.0/Conversion.h> + +namespace android { +namespace conversion { + +// native_handle_t helper functions. + +/** + * \brief Take an fd and create a native handle containing only the given fd. + * The created handle will need to be deleted manually with + * `native_handle_delete()`. + * + * \param[in] fd The source file descriptor (of type `int`). + * \return The create `native_handle_t*` that contains the given \p fd. If the + * supplied \p fd is negative, the created native handle will contain no file + * descriptors. + * + * If the native handle cannot be created, the return value will be + * `nullptr`. + * + * This function does not duplicate the file descriptor. + */ +native_handle_t* native_handle_create_from_fd(int fd) { + if (fd < 2) { + return native_handle_create(0, 0); + } + native_handle_t* nh = native_handle_create(1, 0); + if (nh == nullptr) { + return nullptr; + } + nh->data[0] = fd; + return nh; +} + +/** + * \brief Extract a file descriptor from a native handle. + * + * \param[in] nh The source `native_handle_t*`. + * \param[in] index The index of the file descriptor in \p nh to read from. This + * input has the default value of `0`. + * \return The `index`-th file descriptor in \p nh. If \p nh does not have + * enough file descriptors, the returned value will be `-1`. + * + * This function does not duplicate the file descriptor. + */ +int native_handle_read_fd(native_handle_t const* nh, int index) { + return ((nh == nullptr) || (nh->numFds == 0) || + (nh->numFds <= index) || (index < 0)) ? + -1 : nh->data[index]; +} + +/** + * Conversion functions + * ==================== + * + * There are two main directions of conversion: + * - `inTargetType(...)`: Create a wrapper whose lifetime depends on the + * input. The wrapper has type `TargetType`. + * - `toTargetType(...)`: Create a standalone object of type `TargetType` that + * corresponds to the input. The lifetime of the output does not depend on the + * lifetime of the input. + * - `wrapIn(TargetType*, ...)`: Same as `inTargetType()`, but for `TargetType` + * that cannot be copied and/or moved efficiently, or when there are multiple + * output arguments. + * - `convertTo(TargetType*, ...)`: Same as `toTargetType()`, but for + * `TargetType` that cannot be copied and/or moved efficiently, or when there + * are multiple output arguments. + * + * `wrapIn()` and `convertTo()` functions will take output arguments before + * input arguments. Some of these functions might return a value to indicate + * success or error. + * + * In converting or wrapping something as a Treble type that contains a + * `hidl_handle`, `native_handle_t*` will need to be created and returned as + * an additional output argument, hence only `wrapIn()` or `convertTo()` would + * be available. The caller must call `native_handle_delete()` to deallocate the + * returned native handle when it is no longer needed. + * + * For types that contain file descriptors, `inTargetType()` and `wrapAs()` do + * not perform duplication of file descriptors, while `toTargetType()` and + * `convertTo()` do. + */ + +/** + * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls. + * + * \param[in] t The source `Return<void>`. + * \return The corresponding `status_t`. + */ +// convert: Return<void> -> status_t +status_t toStatusT(Return<void> const& t) { + return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR); +} + +/** + * \brief Convert `Return<void>` to `binder::Status`. + * + * \param[in] t The source `Return<void>`. + * \return The corresponding `binder::Status`. + */ +// convert: Return<void> -> ::android::binder::Status +::android::binder::Status toBinderStatus( + Return<void> const& t) { + return ::android::binder::Status::fromExceptionCode( + toStatusT(t), + t.description().c_str()); +} + +/** + * \brief Wrap `native_handle_t*` in `hidl_handle`. + * + * \param[in] nh The source `native_handle_t*`. + * \return The `hidl_handle` that points to \p nh. + */ +// wrap: native_handle_t* -> hidl_handle +hidl_handle inHidlHandle(native_handle_t const* nh) { + return hidl_handle(nh); +} + +/** + * \brief Convert `int32_t` to `Dataspace`. + * + * \param[in] l The source `int32_t`. + * \result The corresponding `Dataspace`. + */ +// convert: int32_t -> Dataspace +Dataspace toHardwareDataspace(int32_t l) { + return static_cast<Dataspace>(l); +} + +/** + * \brief Convert `Dataspace` to `int32_t`. + * + * \param[in] t The source `Dataspace`. + * \result The corresponding `int32_t`. + */ +// convert: Dataspace -> int32_t +int32_t toRawDataspace(Dataspace const& t) { + return static_cast<int32_t>(t); +} + +/** + * \brief Wrap an opaque buffer inside a `hidl_vec<uint8_t>`. + * + * \param[in] l The pointer to the beginning of the opaque buffer. + * \param[in] size The size of the buffer. + * \return A `hidl_vec<uint8_t>` that points to the buffer. + */ +// wrap: void*, size_t -> hidl_vec<uint8_t> +hidl_vec<uint8_t> inHidlBytes(void const* l, size_t size) { + hidl_vec<uint8_t> t; + t.setToExternal(static_cast<uint8_t*>(const_cast<void*>(l)), size, false); + return t; +} + +/** + * \brief Create a `hidl_vec<uint8_t>` that is a copy of an opaque buffer. + * + * \param[in] l The pointer to the beginning of the opaque buffer. + * \param[in] size The size of the buffer. + * \return A `hidl_vec<uint8_t>` that is a copy of the input buffer. + */ +// convert: void*, size_t -> hidl_vec<uint8_t> +hidl_vec<uint8_t> toHidlBytes(void const* l, size_t size) { + hidl_vec<uint8_t> t; + t.resize(size); + uint8_t const* src = static_cast<uint8_t const*>(l); + std::copy(src, src + size, t.data()); + return t; +} + +/** + * \brief Wrap `GraphicBuffer` in `AnwBuffer`. + * + * \param[out] t The wrapper of type `AnwBuffer`. + * \param[in] l The source `GraphicBuffer`. + */ +// wrap: GraphicBuffer -> AnwBuffer +void wrapAs(AnwBuffer* t, GraphicBuffer const& l) { + t->attr.width = l.getWidth(); + t->attr.height = l.getHeight(); + t->attr.stride = l.getStride(); + t->attr.format = static_cast<PixelFormat>(l.getPixelFormat()); + t->attr.layerCount = l.getLayerCount(); + t->attr.usage = static_cast<uint32_t>(l.getUsage()); + t->attr.id = l.getId(); + t->attr.generationNumber = l.getGenerationNumber(); + t->nativeHandle = hidl_handle(l.handle); +} + +/** + * \brief Convert `AnwBuffer` to `GraphicBuffer`. + * + * \param[out] l The destination `GraphicBuffer`. + * \param[in] t The source `AnwBuffer`. + * + * This function will duplicate all file descriptors in \p t. + */ +// convert: AnwBuffer -> GraphicBuffer +// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten +bool convertTo(GraphicBuffer* l, AnwBuffer const& t) { + native_handle_t* handle = t.nativeHandle == nullptr ? + nullptr : native_handle_clone(t.nativeHandle); + + size_t const numInts = 12 + static_cast<size_t>(handle ? handle->numInts : 0); + int32_t* ints = new int32_t[numInts]; + + size_t numFds = static_cast<size_t>(handle ? handle->numFds : 0); + int* fds = new int[numFds]; + + ints[0] = 'GBFR'; + ints[1] = static_cast<int32_t>(t.attr.width); + ints[2] = static_cast<int32_t>(t.attr.height); + ints[3] = static_cast<int32_t>(t.attr.stride); + ints[4] = static_cast<int32_t>(t.attr.format); + ints[5] = static_cast<int32_t>(t.attr.layerCount); + ints[6] = static_cast<int32_t>(t.attr.usage); + ints[7] = static_cast<int32_t>(t.attr.id >> 32); + ints[8] = static_cast<int32_t>(t.attr.id & 0xFFFFFFFF); + ints[9] = static_cast<int32_t>(t.attr.generationNumber); + ints[10] = 0; + ints[11] = 0; + if (handle) { + ints[10] = static_cast<int32_t>(handle->numFds); + ints[11] = static_cast<int32_t>(handle->numInts); + int* intsStart = handle->data + handle->numFds; + std::copy(handle->data, intsStart, fds); + std::copy(intsStart, intsStart + handle->numInts, &ints[12]); + } + + void const* constBuffer = static_cast<void const*>(ints); + size_t size = numInts * sizeof(int32_t); + int const* constFds = static_cast<int const*>(fds); + status_t status = l->unflatten(constBuffer, size, constFds, numFds); + + delete [] fds; + delete [] ints; + native_handle_delete(handle); + return status == NO_ERROR; +} + +/** + * Conversion functions for types outside media + * ============================================ + * + * Some objects in libui and libgui that were made to go through binder calls do + * not expose ways to read or write their fields to the public. To pass an + * object of this kind through the HIDL boundary, translation functions need to + * work around the access restriction by using the publicly available + * `flatten()` and `unflatten()` functions. + * + * All `flatten()` and `unflatten()` overloads follow the same convention as + * follows: + * + * status_t flatten(ObjectType const& object, + * [OtherType const& other, ...] + * void*& buffer, size_t& size, + * int*& fds, size_t& numFds) + * + * status_t unflatten(ObjectType* object, + * [OtherType* other, ...,] + * void*& buffer, size_t& size, + * int*& fds, size_t& numFds) + * + * The number of `other` parameters varies depending on the `ObjectType`. For + * example, in the process of unflattening an object that contains + * `hidl_handle`, `other` is needed to hold `native_handle_t` objects that will + * be created. + * + * The last four parameters always work the same way in all overloads of + * `flatten()` and `unflatten()`: + * - For `flatten()`, `buffer` is the pointer to the non-fd buffer to be filled, + * `size` is the size (in bytes) of the non-fd buffer pointed to by `buffer`, + * `fds` is the pointer to the fd buffer to be filled, and `numFds` is the + * size (in ints) of the fd buffer pointed to by `fds`. + * - For `unflatten()`, `buffer` is the pointer to the non-fd buffer to be read + * from, `size` is the size (in bytes) of the non-fd buffer pointed to by + * `buffer`, `fds` is the pointer to the fd buffer to be read from, and + * `numFds` is the size (in ints) of the fd buffer pointed to by `fds`. + * - After a successful call to `flatten()` or `unflatten()`, `buffer` and `fds` + * will be advanced, while `size` and `numFds` will be decreased to reflect + * how much storage/data of the two buffers (fd and non-fd) have been used. + * - After an unsuccessful call, the values of `buffer`, `size`, `fds` and + * `numFds` are invalid. + * + * The return value of a successful `flatten()` or `unflatten()` call will be + * `OK` (also aliased as `NO_ERROR`). Any other values indicate a failure. + * + * For each object type that supports flattening, there will be two accompanying + * functions: `getFlattenedSize()` and `getFdCount()`. `getFlattenedSize()` will + * return the size of the non-fd buffer that the object will need for + * flattening. `getFdCount()` will return the size of the fd buffer that the + * object will need for flattening. + * + * The set of these four functions, `getFlattenedSize()`, `getFdCount()`, + * `flatten()` and `unflatten()`, are similar to functions of the same name in + * the abstract class `Flattenable`. The only difference is that functions in + * this file are not member functions of the object type. For example, we write + * + * flatten(x, buffer, size, fds, numFds) + * + * instead of + * + * x.flatten(buffer, size, fds, numFds) + * + * because we cannot modify the type of `x`. + * + * There is one exception to the naming convention: `hidl_handle` that + * represents a fence. The four functions for this "Fence" type have the word + * "Fence" attched to their names because the object type, which is + * `hidl_handle`, does not carry the special meaning that the object itself can + * only contain zero or one file descriptor. + */ + +// Ref: frameworks/native/libs/ui/Fence.cpp + +/** + * \brief Return the size of the non-fd buffer required to flatten a fence. + * + * \param[in] fence The input fence of type `hidl_handle`. + * \return The required size of the flat buffer. + * + * The current version of this function always returns 4, which is the number of + * bytes required to store the number of file descriptors contained in the fd + * part of the flat buffer. + */ +size_t getFenceFlattenedSize(hidl_handle const& /* fence */) { + return 4; +}; + +/** + * \brief Return the number of file descriptors contained in a fence. + * + * \param[in] fence The input fence of type `hidl_handle`. + * \return `0` if \p fence does not contain a valid file descriptor, or `1` + * otherwise. + */ +size_t getFenceFdCount(hidl_handle const& fence) { + return native_handle_read_fd(fence) == -1 ? 0 : 1; +} + +/** + * \brief Unflatten `Fence` to `hidl_handle`. + * + * \param[out] fence The destination `hidl_handle`. + * \param[out] nh The underlying native handle. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * If the return value is `NO_ERROR`, \p nh will point to a newly created + * native handle, which needs to be deleted with `native_handle_delete()` + * afterwards. + */ +status_t unflattenFence(hidl_handle* fence, native_handle_t** nh, + void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { + if (size < 4) { + return NO_MEMORY; + } + + uint32_t numFdsInHandle; + FlattenableUtils::read(buffer, size, numFdsInHandle); + + if (numFdsInHandle > 1) { + return BAD_VALUE; + } + + if (numFds < numFdsInHandle) { + return NO_MEMORY; + } + + if (numFdsInHandle) { + *nh = native_handle_create_from_fd(*fds); + if (*nh == nullptr) { + return NO_MEMORY; + } + *fence = *nh; + ++fds; + --numFds; + } else { + *nh = nullptr; + *fence = hidl_handle(); + } + + return NO_ERROR; +} + +/** + * \brief Flatten `hidl_handle` as `Fence`. + * + * \param[in] t The source `hidl_handle`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + */ +status_t flattenFence(hidl_handle const& fence, + void*& buffer, size_t& size, int*& fds, size_t& numFds) { + if (size < getFenceFlattenedSize(fence) || + numFds < getFenceFdCount(fence)) { + return NO_MEMORY; + } + // Cast to uint32_t since the size of a size_t can vary between 32- and + // 64-bit processes + FlattenableUtils::write(buffer, size, + static_cast<uint32_t>(getFenceFdCount(fence))); + int fd = native_handle_read_fd(fence); + if (fd != -1) { + *fds = fd; + ++fds; + --numFds; + } + return NO_ERROR; +} + +/** + * \brief Wrap `Fence` in `hidl_handle`. + * + * \param[out] t The wrapper of type `hidl_handle`. + * \param[out] nh The native handle pointed to by \p t. + * \param[in] l The source `Fence`. + * + * On success, \p nh will hold a newly created native handle, which must be + * deleted manually with `native_handle_delete()` afterwards. + */ +// wrap: Fence -> hidl_handle +bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l) { + size_t const baseSize = l.getFlattenedSize(); + std::unique_ptr<uint8_t[]> baseBuffer( + new (std::nothrow) uint8_t[baseSize]); + if (!baseBuffer) { + return false; + } + + size_t const baseNumFds = l.getFdCount(); + std::unique_ptr<int[]> baseFds( + new (std::nothrow) int[baseNumFds]); + if (!baseFds) { + return false; + } + + void* buffer = static_cast<void*>(baseBuffer.get()); + size_t size = baseSize; + int* fds = static_cast<int*>(baseFds.get()); + size_t numFds = baseNumFds; + if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) { + return false; + } + + void const* constBuffer = static_cast<void const*>(baseBuffer.get()); + size = baseSize; + int const* constFds = static_cast<int const*>(baseFds.get()); + numFds = baseNumFds; + if (unflattenFence(t, nh, constBuffer, size, constFds, numFds) + != NO_ERROR) { + return false; + } + + return true; +} + +/** + * \brief Convert `hidl_handle` to `Fence`. + * + * \param[out] l The destination `Fence`. `l` must not have been used + * (`l->isValid()` must return `false`) before this function is called. + * \param[in] t The source `hidl_handle`. + * + * If \p t contains a valid file descriptor, it will be duplicated. + */ +// convert: hidl_handle -> Fence +bool convertTo(Fence* l, hidl_handle const& t) { + int fd = native_handle_read_fd(t); + if (fd != -1) { + fd = dup(fd); + if (fd == -1) { + return false; + } + } + native_handle_t* nh = native_handle_create_from_fd(fd); + if (nh == nullptr) { + if (fd != -1) { + close(fd); + } + return false; + } + + size_t const baseSize = getFenceFlattenedSize(t); + std::unique_ptr<uint8_t[]> baseBuffer( + new (std::nothrow) uint8_t[baseSize]); + if (!baseBuffer) { + native_handle_delete(nh); + return false; + } + + size_t const baseNumFds = getFenceFdCount(t); + std::unique_ptr<int[]> baseFds( + new (std::nothrow) int[baseNumFds]); + if (!baseFds) { + native_handle_delete(nh); + return false; + } + + void* buffer = static_cast<void*>(baseBuffer.get()); + size_t size = baseSize; + int* fds = static_cast<int*>(baseFds.get()); + size_t numFds = baseNumFds; + if (flattenFence(hidl_handle(nh), buffer, size, fds, numFds) != NO_ERROR) { + native_handle_delete(nh); + return false; + } + native_handle_delete(nh); + + void const* constBuffer = static_cast<void const*>(baseBuffer.get()); + size = baseSize; + int const* constFds = static_cast<int const*>(baseFds.get()); + numFds = baseNumFds; + if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { + return false; + } + + return true; +} + +// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot + +/** + * \brief Return the size of the non-fd buffer required to flatten + * `FenceTimeSnapshot`. + * + * \param[in] t The input `FenceTimeSnapshot`. + * \return The required size of the flat buffer. + */ +size_t getFlattenedSize( + HGraphicBufferProducer::FenceTimeSnapshot const& t) { + constexpr size_t min = sizeof(t.state); + switch (t.state) { + case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY: + return min; + case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE: + return min + getFenceFlattenedSize(t.fence); + case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME: + return min + sizeof( + ::android::FenceTime::Snapshot::signalTime); + } + return 0; +} + +/** + * \brief Return the number of file descriptors contained in + * `FenceTimeSnapshot`. + * + * \param[in] t The input `FenceTimeSnapshot`. + * \return The number of file descriptors contained in \p snapshot. + */ +size_t getFdCount( + HGraphicBufferProducer::FenceTimeSnapshot const& t) { + return t.state == + HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE ? + getFenceFdCount(t.fence) : 0; +} + +/** + * \brief Flatten `FenceTimeSnapshot`. + * + * \param[in] t The source `FenceTimeSnapshot`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * This function will duplicate the file descriptor in `t.fence` if `t.state == + * FENCE`. + */ +status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t, + void*& buffer, size_t& size, int*& fds, size_t& numFds) { + if (size < getFlattenedSize(t)) { + return NO_MEMORY; + } + + switch (t.state) { + case HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY: + FlattenableUtils::write(buffer, size, + ::android::FenceTime::Snapshot::State::EMPTY); + return NO_ERROR; + case HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE: + FlattenableUtils::write(buffer, size, + ::android::FenceTime::Snapshot::State::FENCE); + return flattenFence(t.fence, buffer, size, fds, numFds); + case HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME: + FlattenableUtils::write(buffer, size, + ::android::FenceTime::Snapshot::State::SIGNAL_TIME); + FlattenableUtils::write(buffer, size, t.signalTimeNs); + return NO_ERROR; + } + return NO_ERROR; +} + +/** + * \brief Unflatten `FenceTimeSnapshot`. + * + * \param[out] t The destination `FenceTimeSnapshot`. + * \param[out] nh The underlying native handle. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * If the return value is `NO_ERROR` and the constructed snapshot contains a + * file descriptor, \p nh will be created to hold that file descriptor. In this + * case, \p nh needs to be deleted with `native_handle_delete()` afterwards. + */ +status_t unflatten( + HGraphicBufferProducer::FenceTimeSnapshot* t, native_handle_t** nh, + void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { + if (size < sizeof(t->state)) { + return NO_MEMORY; + } + + *nh = nullptr; + ::android::FenceTime::Snapshot::State state; + FlattenableUtils::read(buffer, size, state); + switch (state) { + case ::android::FenceTime::Snapshot::State::EMPTY: + t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::EMPTY; + return NO_ERROR; + case ::android::FenceTime::Snapshot::State::FENCE: + t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::FENCE; + return unflattenFence(&t->fence, nh, buffer, size, fds, numFds); + case ::android::FenceTime::Snapshot::State::SIGNAL_TIME: + t->state = HGraphicBufferProducer::FenceTimeSnapshot::State::SIGNAL_TIME; + if (size < sizeof(t->signalTimeNs)) { + return NO_MEMORY; + } + FlattenableUtils::read(buffer, size, t->signalTimeNs); + return NO_ERROR; + } + return NO_ERROR; +} + +// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta + +/** + * \brief Return a lower bound on the size of the non-fd buffer required to + * flatten `FrameEventsDelta`. + * + * \param[in] t The input `FrameEventsDelta`. + * \return A lower bound on the size of the flat buffer. + */ +constexpr size_t minFlattenedSize( + HGraphicBufferProducer::FrameEventsDelta const& /* t */) { + return sizeof(uint64_t) + // mFrameNumber + sizeof(uint8_t) + // mIndex + sizeof(uint8_t) + // mAddPostCompositeCalled + sizeof(uint8_t) + // mAddRetireCalled + sizeof(uint8_t) + // mAddReleaseCalled + sizeof(nsecs_t) + // mPostedTime + sizeof(nsecs_t) + // mRequestedPresentTime + sizeof(nsecs_t) + // mLatchTime + sizeof(nsecs_t) + // mFirstRefreshStartTime + sizeof(nsecs_t); // mLastRefreshStartTime +} + +/** + * \brief Return the size of the non-fd buffer required to flatten + * `FrameEventsDelta`. + * + * \param[in] t The input `FrameEventsDelta`. + * \return The required size of the flat buffer. + */ +size_t getFlattenedSize( + HGraphicBufferProducer::FrameEventsDelta const& t) { + return minFlattenedSize(t) + + getFlattenedSize(t.gpuCompositionDoneFence) + + getFlattenedSize(t.displayPresentFence) + + getFlattenedSize(t.displayRetireFence) + + getFlattenedSize(t.releaseFence); +}; + +/** + * \brief Return the number of file descriptors contained in + * `FrameEventsDelta`. + * + * \param[in] t The input `FrameEventsDelta`. + * \return The number of file descriptors contained in \p t. + */ +size_t getFdCount( + HGraphicBufferProducer::FrameEventsDelta const& t) { + return getFdCount(t.gpuCompositionDoneFence) + + getFdCount(t.displayPresentFence) + + getFdCount(t.displayRetireFence) + + getFdCount(t.releaseFence); +}; + +/** + * \brief Unflatten `FrameEventsDelta`. + * + * \param[out] t The destination `FrameEventsDelta`. + * \param[out] nh The underlying array of native handles. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * If the return value is `NO_ERROR`, \p nh will have length 4, and it will be + * populated with `nullptr` or newly created handles. Each non-null slot in \p + * nh will need to be deleted manually with `native_handle_delete()`. + */ +status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t, + std::vector<native_handle_t*>* nh, + void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { + if (size < minFlattenedSize(*t)) { + return NO_MEMORY; + } + FlattenableUtils::read(buffer, size, t->frameNumber); + + // These were written as uint8_t for alignment. + uint8_t temp = 0; + FlattenableUtils::read(buffer, size, temp); + size_t index = static_cast<size_t>(temp); + if (index >= ::android::FrameEventHistory::MAX_FRAME_HISTORY) { + return BAD_VALUE; + } + t->index = static_cast<uint32_t>(index); + + FlattenableUtils::read(buffer, size, temp); + t->addPostCompositeCalled = static_cast<bool>(temp); + FlattenableUtils::read(buffer, size, temp); + t->addRetireCalled = static_cast<bool>(temp); + FlattenableUtils::read(buffer, size, temp); + t->addReleaseCalled = static_cast<bool>(temp); + + FlattenableUtils::read(buffer, size, t->postedTimeNs); + FlattenableUtils::read(buffer, size, t->requestedPresentTimeNs); + FlattenableUtils::read(buffer, size, t->latchTimeNs); + FlattenableUtils::read(buffer, size, t->firstRefreshStartTimeNs); + FlattenableUtils::read(buffer, size, t->lastRefreshStartTimeNs); + FlattenableUtils::read(buffer, size, t->dequeueReadyTime); + + // Fences + HGraphicBufferProducer::FenceTimeSnapshot* tSnapshot[4]; + tSnapshot[0] = &t->gpuCompositionDoneFence; + tSnapshot[1] = &t->displayPresentFence; + tSnapshot[2] = &t->displayRetireFence; + tSnapshot[3] = &t->releaseFence; + nh->resize(4); + for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) { + status_t status = unflatten( + tSnapshot[snapshotIndex], &((*nh)[snapshotIndex]), + buffer, size, fds, numFds); + if (status != NO_ERROR) { + while (snapshotIndex > 0) { + --snapshotIndex; + if ((*nh)[snapshotIndex] != nullptr) { + native_handle_delete((*nh)[snapshotIndex]); + } + } + return status; + } + } + return NO_ERROR; +} + +/** + * \brief Flatten `FrameEventsDelta`. + * + * \param[in] t The source `FrameEventsDelta`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * This function will duplicate file descriptors contained in \p t. + */ +// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp: +// FrameEventsDelta::flatten +status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t, + void*& buffer, size_t& size, int*& fds, size_t numFds) { + // Check that t.index is within a valid range. + if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY) + || t.index > std::numeric_limits<uint8_t>::max()) { + return BAD_VALUE; + } + + FlattenableUtils::write(buffer, size, t.frameNumber); + + // These are static_cast to uint8_t for alignment. + FlattenableUtils::write(buffer, size, static_cast<uint8_t>(t.index)); + FlattenableUtils::write( + buffer, size, static_cast<uint8_t>(t.addPostCompositeCalled)); + FlattenableUtils::write( + buffer, size, static_cast<uint8_t>(t.addRetireCalled)); + FlattenableUtils::write( + buffer, size, static_cast<uint8_t>(t.addReleaseCalled)); + + FlattenableUtils::write(buffer, size, t.postedTimeNs); + FlattenableUtils::write(buffer, size, t.requestedPresentTimeNs); + FlattenableUtils::write(buffer, size, t.latchTimeNs); + FlattenableUtils::write(buffer, size, t.firstRefreshStartTimeNs); + FlattenableUtils::write(buffer, size, t.lastRefreshStartTimeNs); + FlattenableUtils::write(buffer, size, t.dequeueReadyTime); + + // Fences + HGraphicBufferProducer::FenceTimeSnapshot const* tSnapshot[4]; + tSnapshot[0] = &t.gpuCompositionDoneFence; + tSnapshot[1] = &t.displayPresentFence; + tSnapshot[2] = &t.displayRetireFence; + tSnapshot[3] = &t.releaseFence; + for (size_t snapshotIndex = 0; snapshotIndex < 4; ++snapshotIndex) { + status_t status = flatten( + *(tSnapshot[snapshotIndex]), buffer, size, fds, numFds); + if (status != NO_ERROR) { + return status; + } + } + return NO_ERROR; +} + +// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta + +/** + * \brief Return the size of the non-fd buffer required to flatten + * `HGraphicBufferProducer::FrameEventHistoryDelta`. + * + * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. + * \return The required size of the flat buffer. + */ +size_t getFlattenedSize( + HGraphicBufferProducer::FrameEventHistoryDelta const& t) { + size_t size = 4 + // mDeltas.size() + sizeof(t.compositorTiming); + for (size_t i = 0; i < t.deltas.size(); ++i) { + size += getFlattenedSize(t.deltas[i]); + } + return size; +} + +/** + * \brief Return the number of file descriptors contained in + * `HGraphicBufferProducer::FrameEventHistoryDelta`. + * + * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. + * \return The number of file descriptors contained in \p t. + */ +size_t getFdCount( + HGraphicBufferProducer::FrameEventHistoryDelta const& t) { + size_t numFds = 0; + for (size_t i = 0; i < t.deltas.size(); ++i) { + numFds += getFdCount(t.deltas[i]); + } + return numFds; +} + +/** + * \brief Unflatten `FrameEventHistoryDelta`. + * + * \param[out] t The destination `FrameEventHistoryDelta`. + * \param[out] nh The underlying array of arrays of native handles. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * If the return value is `NO_ERROR`, \p nh will be populated with `nullptr` or + * newly created handles. The second dimension of \p nh will be 4. Each non-null + * slot in \p nh will need to be deleted manually with `native_handle_delete()`. + */ +status_t unflatten( + HGraphicBufferProducer::FrameEventHistoryDelta* t, + std::vector<std::vector<native_handle_t*> >* nh, + void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { + if (size < 4) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, t->compositorTiming); + + uint32_t deltaCount = 0; + FlattenableUtils::read(buffer, size, deltaCount); + if (static_cast<size_t>(deltaCount) > + ::android::FrameEventHistory::MAX_FRAME_HISTORY) { + return BAD_VALUE; + } + t->deltas.resize(deltaCount); + nh->resize(deltaCount); + for (size_t deltaIndex = 0; deltaIndex < deltaCount; ++deltaIndex) { + status_t status = unflatten( + &(t->deltas[deltaIndex]), &((*nh)[deltaIndex]), + buffer, size, fds, numFds); + if (status != NO_ERROR) { + return status; + } + } + return NO_ERROR; +} + +/** + * \brief Flatten `FrameEventHistoryDelta`. + * + * \param[in] t The source `FrameEventHistoryDelta`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * This function will duplicate file descriptors contained in \p t. + */ +status_t flatten( + HGraphicBufferProducer::FrameEventHistoryDelta const& t, + void*& buffer, size_t& size, int*& fds, size_t& numFds) { + if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) { + return BAD_VALUE; + } + if (size < getFlattenedSize(t)) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, t.compositorTiming); + + FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.deltas.size())); + for (size_t deltaIndex = 0; deltaIndex < t.deltas.size(); ++deltaIndex) { + status_t status = flatten(t.deltas[deltaIndex], buffer, size, fds, numFds); + if (status != NO_ERROR) { + return status; + } + } + return NO_ERROR; +} + +/** + * \brief Wrap `::android::FrameEventHistoryData` in + * `HGraphicBufferProducer::FrameEventHistoryDelta`. + * + * \param[out] t The wrapper of type + * `HGraphicBufferProducer::FrameEventHistoryDelta`. + * \param[out] nh The array of array of native handles that are referred to by + * members of \p t. + * \param[in] l The source `::android::FrameEventHistoryDelta`. + * + * On success, each member of \p nh will be either `nullptr` or a newly created + * native handle. All the non-`nullptr` elements must be deleted individually + * with `native_handle_delete()`. + */ +bool wrapAs(HGraphicBufferProducer::FrameEventHistoryDelta* t, + std::vector<std::vector<native_handle_t*> >* nh, + ::android::FrameEventHistoryDelta const& l) { + + size_t const baseSize = l.getFlattenedSize(); + std::unique_ptr<uint8_t[]> baseBuffer( + new (std::nothrow) uint8_t[baseSize]); + if (!baseBuffer) { + return false; + } + + size_t const baseNumFds = l.getFdCount(); + std::unique_ptr<int[]> baseFds( + new (std::nothrow) int[baseNumFds]); + if (!baseFds) { + return false; + } + + void* buffer = static_cast<void*>(baseBuffer.get()); + size_t size = baseSize; + int* fds = baseFds.get(); + size_t numFds = baseNumFds; + if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) { + return false; + } + + void const* constBuffer = static_cast<void const*>(baseBuffer.get()); + size = baseSize; + int const* constFds = static_cast<int const*>(baseFds.get()); + numFds = baseNumFds; + if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) { + return false; + } + + return true; +} + +/** + * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to + * `::android::FrameEventHistoryDelta`. + * + * \param[out] l The destination `::android::FrameEventHistoryDelta`. + * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`. + * + * This function will duplicate all file descriptors contained in \p t. + */ +bool convertTo( + ::android::FrameEventHistoryDelta* l, + HGraphicBufferProducer::FrameEventHistoryDelta const& t) { + + size_t const baseSize = getFlattenedSize(t); + std::unique_ptr<uint8_t[]> baseBuffer( + new (std::nothrow) uint8_t[baseSize]); + if (!baseBuffer) { + return false; + } + + size_t const baseNumFds = getFdCount(t); + std::unique_ptr<int[]> baseFds( + new (std::nothrow) int[baseNumFds]); + if (!baseFds) { + return false; + } + + void* buffer = static_cast<void*>(baseBuffer.get()); + size_t size = baseSize; + int* fds = static_cast<int*>(baseFds.get()); + size_t numFds = baseNumFds; + if (flatten(t, buffer, size, fds, numFds) != NO_ERROR) { + return false; + } + + void const* constBuffer = static_cast<void const*>(baseBuffer.get()); + size = baseSize; + int const* constFds = static_cast<int const*>(baseFds.get()); + numFds = baseNumFds; + if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { + return false; + } + + return true; +} + +// Ref: frameworks/native/libs/ui/Region.cpp + +/** + * \brief Return the size of the buffer required to flatten `Region`. + * + * \param[in] t The input `Region`. + * \return The required size of the flat buffer. + */ +size_t getFlattenedSize(Region const& t) { + return sizeof(uint32_t) + t.size() * sizeof(::android::Rect); +} + +/** + * \brief Unflatten `Region`. + * + * \param[out] t The destination `Region`. + * \param[in,out] buffer The pointer to the flat buffer. + * \param[in,out] size The size of the flat buffer. + * \return `NO_ERROR` on success; other value on failure. + */ +status_t unflatten(Region* t, void const*& buffer, size_t& size) { + if (size < sizeof(uint32_t)) { + return NO_MEMORY; + } + + uint32_t numRects = 0; + FlattenableUtils::read(buffer, size, numRects); + if (size < numRects * sizeof(Rect)) { + return NO_MEMORY; + } + if (numRects > (UINT32_MAX / sizeof(Rect))) { + return NO_MEMORY; + } + + t->resize(numRects); + for (size_t r = 0; r < numRects; ++r) { + ::android::Rect rect(::android::Rect::EMPTY_RECT); + status_t status = rect.unflatten(buffer, size); + if (status != NO_ERROR) { + return status; + } + FlattenableUtils::advance(buffer, size, sizeof(rect)); + (*t)[r] = Rect{ + static_cast<int32_t>(rect.left), + static_cast<int32_t>(rect.top), + static_cast<int32_t>(rect.right), + static_cast<int32_t>(rect.bottom)}; + } + return NO_ERROR; +} + +/** + * \brief Flatten `Region`. + * + * \param[in] t The source `Region`. + * \param[in,out] buffer The pointer to the flat buffer. + * \param[in,out] size The size of the flat buffer. + * \return `NO_ERROR` on success; other value on failure. + */ +status_t flatten(Region const& t, void*& buffer, size_t& size) { + if (size < getFlattenedSize(t)) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, static_cast<uint32_t>(t.size())); + for (size_t r = 0; r < t.size(); ++r) { + ::android::Rect rect( + static_cast<int32_t>(t[r].left), + static_cast<int32_t>(t[r].top), + static_cast<int32_t>(t[r].right), + static_cast<int32_t>(t[r].bottom)); + status_t status = rect.flatten(buffer, size); + if (status != NO_ERROR) { + return status; + } + FlattenableUtils::advance(buffer, size, sizeof(rect)); + } + return NO_ERROR; +} + +/** + * \brief Convert `::android::Region` to `Region`. + * + * \param[out] t The destination `Region`. + * \param[in] l The source `::android::Region`. + */ +// convert: ::android::Region -> Region +bool convertTo(Region* t, ::android::Region const& l) { + size_t const baseSize = l.getFlattenedSize(); + std::unique_ptr<uint8_t[]> baseBuffer( + new (std::nothrow) uint8_t[baseSize]); + if (!baseBuffer) { + return false; + } + + void* buffer = static_cast<void*>(baseBuffer.get()); + size_t size = baseSize; + if (l.flatten(buffer, size) != NO_ERROR) { + return false; + } + + void const* constBuffer = static_cast<void const*>(baseBuffer.get()); + size = baseSize; + if (unflatten(t, constBuffer, size) != NO_ERROR) { + return false; + } + + return true; +} + +/** + * \brief Convert `Region` to `::android::Region`. + * + * \param[out] l The destination `::android::Region`. + * \param[in] t The source `Region`. + */ +// convert: Region -> ::android::Region +bool convertTo(::android::Region* l, Region const& t) { + size_t const baseSize = getFlattenedSize(t); + std::unique_ptr<uint8_t[]> baseBuffer( + new (std::nothrow) uint8_t[baseSize]); + if (!baseBuffer) { + return false; + } + + void* buffer = static_cast<void*>(baseBuffer.get()); + size_t size = baseSize; + if (flatten(t, buffer, size) != NO_ERROR) { + return false; + } + + void const* constBuffer = static_cast<void const*>(baseBuffer.get()); + size = baseSize; + if (l->unflatten(constBuffer, size) != NO_ERROR) { + return false; + } + + return true; +} + +// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp: +// BGraphicBufferProducer::QueueBufferInput + +/** + * \brief Return a lower bound on the size of the buffer required to flatten + * `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. + * \return A lower bound on the size of the flat buffer. + */ +constexpr size_t minFlattenedSize( + HGraphicBufferProducer::QueueBufferInput const& /* t */) { + return sizeof(int64_t) + // timestamp + sizeof(int) + // isAutoTimestamp + sizeof(android_dataspace) + // dataSpace + sizeof(::android::Rect) + // crop + sizeof(int) + // scalingMode + sizeof(uint32_t) + // transform + sizeof(uint32_t) + // stickyTransform + sizeof(bool); // getFrameTimestamps +} + +/** + * \brief Return the size of the buffer required to flatten + * `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. + * \return The required size of the flat buffer. + */ +size_t getFlattenedSize(HGraphicBufferProducer::QueueBufferInput const& t) { + return minFlattenedSize(t) + + getFenceFlattenedSize(t.fence) + + getFlattenedSize(t.surfaceDamage) + + sizeof(HdrMetadata::validTypes); +} + +/** + * \brief Return the number of file descriptors contained in + * `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. + * \return The number of file descriptors contained in \p t. + */ +size_t getFdCount( + HGraphicBufferProducer::QueueBufferInput const& t) { + return getFenceFdCount(t.fence); +} + +/** + * \brief Flatten `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`. + * \param[out] nh The native handle cloned from `t.fence`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * This function will duplicate the file descriptor in `t.fence`. */ +status_t flatten(HGraphicBufferProducer::QueueBufferInput const& t, + native_handle_t** nh, + void*& buffer, size_t& size, int*& fds, size_t& numFds) { + if (size < getFlattenedSize(t)) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, t.timestamp); + FlattenableUtils::write(buffer, size, static_cast<int>(t.isAutoTimestamp)); + FlattenableUtils::write(buffer, size, + static_cast<android_dataspace_t>(t.dataSpace)); + FlattenableUtils::write(buffer, size, ::android::Rect( + static_cast<int32_t>(t.crop.left), + static_cast<int32_t>(t.crop.top), + static_cast<int32_t>(t.crop.right), + static_cast<int32_t>(t.crop.bottom))); + FlattenableUtils::write(buffer, size, static_cast<int>(t.scalingMode)); + FlattenableUtils::write(buffer, size, t.transform); + FlattenableUtils::write(buffer, size, t.stickyTransform); + FlattenableUtils::write(buffer, size, t.getFrameTimestamps); + + *nh = t.fence.getNativeHandle() == nullptr ? + nullptr : native_handle_clone(t.fence); + status_t status = flattenFence(hidl_handle(*nh), buffer, size, fds, numFds); + if (status != NO_ERROR) { + return status; + } + status = flatten(t.surfaceDamage, buffer, size); + if (status != NO_ERROR) { + return status; + } + FlattenableUtils::write(buffer, size, decltype(HdrMetadata::validTypes)(0)); + return NO_ERROR; +} + +/** + * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`. + * \param[out] nh The underlying native handle for `t->fence`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * If the return value is `NO_ERROR` and `t->fence` contains a valid file + * descriptor, \p nh will be a newly created native handle holding that file + * descriptor. \p nh needs to be deleted with `native_handle_delete()` + * afterwards. + */ +status_t unflatten( + HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh, + void const*& buffer, size_t& size, int const*& fds, size_t& numFds) { + if (size < minFlattenedSize(*t)) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, t->timestamp); + int lIsAutoTimestamp; + FlattenableUtils::read(buffer, size, lIsAutoTimestamp); + t->isAutoTimestamp = static_cast<int32_t>(lIsAutoTimestamp); + android_dataspace_t lDataSpace; + FlattenableUtils::read(buffer, size, lDataSpace); + t->dataSpace = static_cast<Dataspace>(lDataSpace); + Rect lCrop; + FlattenableUtils::read(buffer, size, lCrop); + t->crop = Rect{ + static_cast<int32_t>(lCrop.left), + static_cast<int32_t>(lCrop.top), + static_cast<int32_t>(lCrop.right), + static_cast<int32_t>(lCrop.bottom)}; + int lScalingMode; + FlattenableUtils::read(buffer, size, lScalingMode); + t->scalingMode = static_cast<int32_t>(lScalingMode); + FlattenableUtils::read(buffer, size, t->transform); + FlattenableUtils::read(buffer, size, t->stickyTransform); + FlattenableUtils::read(buffer, size, t->getFrameTimestamps); + + status_t status = unflattenFence(&(t->fence), nh, + buffer, size, fds, numFds); + if (status != NO_ERROR) { + return status; + } + // HdrMetadata ignored + return unflatten(&(t->surfaceDamage), buffer, size); +} + +/** + * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in + * `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[out] t The wrapper of type + * `HGraphicBufferProducer::QueueBufferInput`. + * \param[out] nh The underlying native handle for `t->fence`. + * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`. + * + * If the return value is `true` and `t->fence` contains a valid file + * descriptor, \p nh will be a newly created native handle holding that file + * descriptor. \p nh needs to be deleted with `native_handle_delete()` + * afterwards. + */ +bool wrapAs( + HGraphicBufferProducer::QueueBufferInput* t, + native_handle_t** nh, + BGraphicBufferProducer::QueueBufferInput const& l) { + + size_t const baseSize = l.getFlattenedSize(); + std::unique_ptr<uint8_t[]> baseBuffer( + new (std::nothrow) uint8_t[baseSize]); + if (!baseBuffer) { + return false; + } + + size_t const baseNumFds = l.getFdCount(); + std::unique_ptr<int[]> baseFds( + new (std::nothrow) int[baseNumFds]); + if (!baseFds) { + return false; + } + + void* buffer = static_cast<void*>(baseBuffer.get()); + size_t size = baseSize; + int* fds = baseFds.get(); + size_t numFds = baseNumFds; + if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) { + return false; + } + + void const* constBuffer = static_cast<void const*>(baseBuffer.get()); + size = baseSize; + int const* constFds = static_cast<int const*>(baseFds.get()); + numFds = baseNumFds; + if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) { + return false; + } + + return true; +} + +/** + * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to + * `BGraphicBufferProducer::QueueBufferInput`. + * + * \param[out] l The destination `BGraphicBufferProducer::QueueBufferInput`. + * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`. + * + * If `t.fence` has a valid file descriptor, it will be duplicated. + */ +bool convertTo( + BGraphicBufferProducer::QueueBufferInput* l, + HGraphicBufferProducer::QueueBufferInput const& t) { + + size_t const baseSize = getFlattenedSize(t); + std::unique_ptr<uint8_t[]> baseBuffer( + new (std::nothrow) uint8_t[baseSize]); + if (!baseBuffer) { + return false; + } + + size_t const baseNumFds = getFdCount(t); + std::unique_ptr<int[]> baseFds( + new (std::nothrow) int[baseNumFds]); + if (!baseFds) { + return false; + } + + void* buffer = static_cast<void*>(baseBuffer.get()); + size_t size = baseSize; + int* fds = baseFds.get(); + size_t numFds = baseNumFds; + native_handle_t* nh; + if (flatten(t, &nh, buffer, size, fds, numFds) != NO_ERROR) { + return false; + } + + void const* constBuffer = static_cast<void const*>(baseBuffer.get()); + size = baseSize; + int const* constFds = static_cast<int const*>(baseFds.get()); + numFds = baseNumFds; + if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { + if (nh != nullptr) { + native_handle_close(nh); + native_handle_delete(nh); + } + return false; + } + + native_handle_delete(nh); + return true; +} + +// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp: +// BGraphicBufferProducer::QueueBufferOutput + +/** + * \brief Wrap `BGraphicBufferProducer::QueueBufferOutput` in + * `HGraphicBufferProducer::QueueBufferOutput`. + * + * \param[out] t The wrapper of type + * `HGraphicBufferProducer::QueueBufferOutput`. + * \param[out] nh The array of array of native handles that are referred to by + * members of \p t. + * \param[in] l The source `BGraphicBufferProducer::QueueBufferOutput`. + * + * On success, each member of \p nh will be either `nullptr` or a newly created + * native handle. All the non-`nullptr` elements must be deleted individually + * with `native_handle_delete()`. + */ +// wrap: BGraphicBufferProducer::QueueBufferOutput -> +// HGraphicBufferProducer::QueueBufferOutput +bool wrapAs(HGraphicBufferProducer::QueueBufferOutput* t, + std::vector<std::vector<native_handle_t*> >* nh, + BGraphicBufferProducer::QueueBufferOutput const& l) { + if (!wrapAs(&(t->frameTimestamps), nh, l.frameTimestamps)) { + return false; + } + t->width = l.width; + t->height = l.height; + t->transformHint = l.transformHint; + t->numPendingBuffers = l.numPendingBuffers; + t->nextFrameNumber = l.nextFrameNumber; + t->bufferReplaced = l.bufferReplaced; + return true; +} + +/** + * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to + * `BGraphicBufferProducer::QueueBufferOutput`. + * + * \param[out] l The destination `BGraphicBufferProducer::QueueBufferOutput`. + * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`. + * + * This function will duplicate all file descriptors contained in \p t. + */ +// convert: HGraphicBufferProducer::QueueBufferOutput -> +// BGraphicBufferProducer::QueueBufferOutput +bool convertTo( + BGraphicBufferProducer::QueueBufferOutput* l, + HGraphicBufferProducer::QueueBufferOutput const& t) { + if (!convertTo(&(l->frameTimestamps), t.frameTimestamps)) { + return false; + } + l->width = t.width; + l->height = t.height; + l->transformHint = t.transformHint; + l->numPendingBuffers = t.numPendingBuffers; + l->nextFrameNumber = t.nextFrameNumber; + l->bufferReplaced = t.bufferReplaced; + return true; +} + +/** + * \brief Convert `BGraphicBufferProducer::DisconnectMode` to + * `HGraphicBufferProducer::DisconnectMode`. + * + * \param[in] l The source `BGraphicBufferProducer::DisconnectMode`. + * \return The corresponding `HGraphicBufferProducer::DisconnectMode`. + */ +HGraphicBufferProducer::DisconnectMode toHidlDisconnectMode( + BGraphicBufferProducer::DisconnectMode l) { + switch (l) { + case BGraphicBufferProducer::DisconnectMode::Api: + return HGraphicBufferProducer::DisconnectMode::API; + case BGraphicBufferProducer::DisconnectMode::AllLocal: + return HGraphicBufferProducer::DisconnectMode::ALL_LOCAL; + } + return HGraphicBufferProducer::DisconnectMode::API; +} + +/** + * \brief Convert `HGraphicBufferProducer::DisconnectMode` to + * `BGraphicBufferProducer::DisconnectMode`. + * + * \param[in] l The source `HGraphicBufferProducer::DisconnectMode`. + * \return The corresponding `BGraphicBufferProducer::DisconnectMode`. + */ +BGraphicBufferProducer::DisconnectMode toGuiDisconnectMode( + HGraphicBufferProducer::DisconnectMode t) { + switch (t) { + case HGraphicBufferProducer::DisconnectMode::API: + return BGraphicBufferProducer::DisconnectMode::Api; + case HGraphicBufferProducer::DisconnectMode::ALL_LOCAL: + return BGraphicBufferProducer::DisconnectMode::AllLocal; + } + return BGraphicBufferProducer::DisconnectMode::Api; +} + +} // namespace conversion +} // namespace android +
diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp index fe99620..cee1b81 100644 --- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp
@@ -100,7 +100,12 @@ */ // convert: Return<Status> -> status_t inline status_t toStatusT(Return<Status> const& t) { - return t.isOk() ? static_cast<status_t>(static_cast<Status>(t)) : UNKNOWN_ERROR; + if (t.isOk()) { + return static_cast<status_t>(static_cast<Status>(t)); + } else if (t.isDeadObject()) { + return DEAD_OBJECT; + } + return UNKNOWN_ERROR; } /** @@ -111,7 +116,7 @@ */ // convert: Return<void> -> status_t inline status_t toStatusT(Return<void> const& t) { - return t.isOk() ? OK : UNKNOWN_ERROR; + return t.isOk() ? OK : (t.isDeadObject() ? DEAD_OBJECT : UNKNOWN_ERROR); } /** @@ -896,7 +901,7 @@ int const* constFds = static_cast<int const*>(baseFds.get()); numFds = baseNumFds; if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) { - for (auto nhA : nhAA) { + for (const auto& nhA : nhAA) { for (auto nh : nhA) { if (nh != nullptr) { native_handle_close(nh); @@ -907,8 +912,8 @@ return false; } - for (auto nhA : nhAA) { - for (auto nh : nhA) { + for (const auto& nhA : nhAA) { + for (const auto& nh : nhA) { if (nh != nullptr) { native_handle_delete(nh); }
diff --git a/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp new file mode 100644 index 0000000..2712f42 --- /dev/null +++ b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
@@ -0,0 +1,59 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "H2BProducerListener@1.0" + +#include <android-base/logging.h> + +#include <gui/bufferqueue/1.0/H2BProducerListener.h> +#include <hidl/Status.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V1_0 { +namespace utils { + +using ::android::hardware::Return; + +H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base) + : CBase{base} { +} + +void H2BProducerListener::onBufferReleased() { + if (!mBase->onBufferReleased().isOk()) { + LOG(ERROR) << "onBufferReleased: transaction failed."; + } +} + +bool H2BProducerListener::needsReleaseNotify() { + Return<bool> transResult = mBase->needsReleaseNotify(); + if (!transResult.isOk()) { + LOG(ERROR) << "needsReleaseNotify: transaction failed."; + return false; + } + return static_cast<bool>(transResult); +} + +} // namespace utils +} // namespace V1_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android +
diff --git a/libs/gui/bufferqueue/1.0/WProducerListener.cpp b/libs/gui/bufferqueue/1.0/WProducerListener.cpp new file mode 100644 index 0000000..78dc4e8 --- /dev/null +++ b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
@@ -0,0 +1,50 @@ +/* + * Copyright 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gui/bufferqueue/1.0/WProducerListener.h> + +namespace android { + +// TWProducerListener +TWProducerListener::TWProducerListener( + sp<BProducerListener> const& base): + mBase(base) { +} + +Return<void> TWProducerListener::onBufferReleased() { + mBase->onBufferReleased(); + return Void(); +} + +Return<bool> TWProducerListener::needsReleaseNotify() { + return mBase->needsReleaseNotify(); +} + +// LWProducerListener +LWProducerListener::LWProducerListener( + sp<HProducerListener> const& base): + mBase(base) { +} + +void LWProducerListener::onBufferReleased() { + mBase->onBufferReleased(); +} + +bool LWProducerListener::needsReleaseNotify() { + return static_cast<bool>(mBase->needsReleaseNotify()); +} + +} // namespace android
diff --git a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp new file mode 100644 index 0000000..e891ec5 --- /dev/null +++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
@@ -0,0 +1,339 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "B2HGraphicBufferProducer@2.0" + +#include <android-base/logging.h> + +#include <android/hardware/graphics/bufferqueue/2.0/types.h> +#include <android/hardware/graphics/common/1.2/types.h> +#include <gui/bufferqueue/2.0/H2BProducerListener.h> +#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h> +#include <gui/bufferqueue/2.0/types.h> +#include <ui/GraphicBuffer.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <vndk/hardware_buffer.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +namespace /* unnamed */ { + +using BQueueBufferInput = ::android:: + IGraphicBufferProducer::QueueBufferInput; +using HQueueBufferInput = ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer::QueueBufferInput; +using BQueueBufferOutput = ::android:: + IGraphicBufferProducer::QueueBufferOutput; +using HQueueBufferOutput = ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer::QueueBufferOutput; + +using ::android::hardware::graphics::bufferqueue::V2_0::utils::b2h; +using ::android::hardware::graphics::bufferqueue::V2_0::utils::h2b; + +bool b2h(BQueueBufferOutput const& from, HQueueBufferOutput* to) { + to->width = from.width; + to->height = from.height; + to->transformHint = static_cast<int32_t>(from.transformHint); + to->numPendingBuffers = from.numPendingBuffers; + to->nextFrameNumber = from.nextFrameNumber; + to->bufferReplaced = from.bufferReplaced; + return true; +} + +} // unnamed namespace + +// B2HGraphicBufferProducer +// ======================== + +B2HGraphicBufferProducer::B2HGraphicBufferProducer( + sp<BGraphicBufferProducer> const& base) + : mBase{base} { +} + +Return<HStatus> B2HGraphicBufferProducer::setMaxDequeuedBufferCount( + int32_t maxDequeuedBuffers) { + HStatus hStatus{}; + bool converted = b2h( + mBase->setMaxDequeuedBufferCount( + static_cast<int>(maxDequeuedBuffers)), + &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<void> B2HGraphicBufferProducer::requestBuffer( + int32_t slot, + requestBuffer_cb _hidl_cb) { + sp<GraphicBuffer> bBuffer; + HStatus hStatus{}; + HardwareBuffer hBuffer{}; + uint32_t hGenerationNumber{}; + bool converted = + b2h(mBase->requestBuffer( + static_cast<int>(slot), &bBuffer), + &hStatus) && + b2h(bBuffer, &hBuffer, &hGenerationNumber); + _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, + hBuffer, hGenerationNumber); + return {}; +} + +Return<HStatus> B2HGraphicBufferProducer::setAsyncMode(bool async) { + HStatus hStatus{}; + bool converted = b2h(mBase->setAsyncMode(async), &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<void> B2HGraphicBufferProducer::dequeueBuffer( + DequeueBufferInput const& input, + dequeueBuffer_cb _hidl_cb) { + int bSlot{}; + sp<BFence> bFence; + HStatus hStatus{}; + DequeueBufferOutput hOutput{}; + HFenceWrapper hFenceWrapper; + bool converted = + b2h(mBase->dequeueBuffer( + &bSlot, + &bFence, + input.width, + input.height, + static_cast<PixelFormat>(input.format), + input.usage, + &hOutput.bufferAge, + nullptr /* outTimestamps */), + &hStatus, + &hOutput.bufferNeedsReallocation, + &hOutput.releaseAllBuffers) && + b2h(bFence, &hFenceWrapper); + hOutput.fence = hFenceWrapper.getHandle(); + _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, + static_cast<int32_t>(bSlot), + hOutput); + return {}; +} + +Return<HStatus> B2HGraphicBufferProducer::detachBuffer(int32_t slot) { + HStatus hStatus{}; + bool converted = b2h( + mBase->detachBuffer(static_cast<int>(slot)), &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<void> B2HGraphicBufferProducer::detachNextBuffer( + detachNextBuffer_cb _hidl_cb) { + sp<GraphicBuffer> bBuffer; + sp<BFence> bFence; + HStatus hStatus{}; + HardwareBuffer hBuffer{}; + HFenceWrapper hFenceWrapper; + bool converted = + b2h(mBase->detachNextBuffer(&bBuffer, &bFence), &hStatus) && + b2h(bBuffer, &hBuffer) && + b2h(bFence, &hFenceWrapper); + _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, + hBuffer, + hFenceWrapper.getHandle()); + return {}; +} + +Return<void> B2HGraphicBufferProducer::attachBuffer( + HardwareBuffer const& hBuffer, + uint32_t generationNumber, + attachBuffer_cb _hidl_cb) { + sp<GraphicBuffer> bBuffer; + if (!h2b(hBuffer, &bBuffer) || !bBuffer) { + _hidl_cb(HStatus::UNKNOWN_ERROR, + static_cast<int32_t>(SlotIndex::INVALID), + false); + return {}; + } + bBuffer->setGenerationNumber(generationNumber); + + int bSlot{}; + HStatus hStatus{}; + bool releaseAllBuffers{}; + bool converted = b2h( + mBase->attachBuffer(&bSlot, bBuffer), &hStatus, + nullptr /* bufferNeedsReallocation */, + &releaseAllBuffers); + _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, + static_cast<int32_t>(bSlot), + releaseAllBuffers); + return {}; +} + +Return<void> B2HGraphicBufferProducer::queueBuffer( + int32_t slot, + QueueBufferInput const& hInput, + queueBuffer_cb _hidl_cb) { + BQueueBufferInput bInput{ + hInput.timestamp, + hInput.isAutoTimestamp, + static_cast<android_dataspace>(hInput.dataSpace), + {}, /* crop */ + 0 /* scalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE */, + static_cast<uint32_t>(hInput.transform), + {}, /* fence */ + static_cast<uint32_t>(hInput.stickyTransform), + false /* getFrameTimestamps */}; + + // Convert crop. + if (!h2b(hInput.crop, &bInput.crop)) { + _hidl_cb(HStatus::UNKNOWN_ERROR, QueueBufferOutput{}); + return {}; + } + + // Convert surfaceDamage. + if (!h2b(hInput.surfaceDamage, &bInput.surfaceDamage)) { + _hidl_cb(HStatus::UNKNOWN_ERROR, QueueBufferOutput{}); + return {}; + } + + // Convert fence. + if (!h2b(hInput.fence, &bInput.fence)) { + _hidl_cb(HStatus::UNKNOWN_ERROR, QueueBufferOutput{}); + return {}; + } + + BQueueBufferOutput bOutput{}; + HStatus hStatus{}; + QueueBufferOutput hOutput{}; + bool converted = + b2h( + mBase->queueBuffer(static_cast<int>(slot), bInput, &bOutput), + &hStatus) && + b2h(bOutput, &hOutput); + + _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, hOutput); + return {}; +} + +Return<HStatus> B2HGraphicBufferProducer::cancelBuffer( + int32_t slot, + hidl_handle const& fence) { + sp<BFence> bFence; + if (!h2b(fence.getNativeHandle(), &bFence)) { + return {HStatus::UNKNOWN_ERROR}; + } + HStatus hStatus{}; + bool converted = b2h( + mBase->cancelBuffer(static_cast<int>(slot), bFence), + &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<void> B2HGraphicBufferProducer::query(int32_t what, query_cb _hidl_cb) { + int value{}; + int result = mBase->query(static_cast<int>(what), &value); + _hidl_cb(static_cast<int32_t>(result), static_cast<int32_t>(value)); + return {}; +} + +Return<void> B2HGraphicBufferProducer::connect( + sp<HProducerListener> const& hListener, + HConnectionType hConnectionType, + bool producerControlledByApp, + connect_cb _hidl_cb) { + sp<BProducerListener> bListener = new H2BProducerListener(hListener); + int bConnectionType{}; + if (!bListener || !h2b(hConnectionType, &bConnectionType)) { + _hidl_cb(HStatus::UNKNOWN_ERROR, QueueBufferOutput{}); + return {}; + } + BQueueBufferOutput bOutput{}; + HStatus hStatus{}; + QueueBufferOutput hOutput{}; + bool converted = + b2h(mBase->connect(bListener, + bConnectionType, + producerControlledByApp, + &bOutput), + &hStatus) && + b2h(bOutput, &hOutput); + _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, hOutput); + return {}; +} + +Return<HStatus> B2HGraphicBufferProducer::disconnect( + HConnectionType hConnectionType) { + int bConnectionType; + if (!h2b(hConnectionType, &bConnectionType)) { + return {HStatus::UNKNOWN_ERROR}; + } + HStatus hStatus{}; + bool converted = b2h(mBase->disconnect(bConnectionType), &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<HStatus> B2HGraphicBufferProducer::allocateBuffers( + uint32_t width, uint32_t height, + uint32_t format, uint64_t usage) { + mBase->allocateBuffers( + width, height, static_cast<PixelFormat>(format), usage); + return {HStatus::OK}; +} + +Return<HStatus> B2HGraphicBufferProducer::allowAllocation(bool allow) { + HStatus hStatus{}; + bool converted = b2h(mBase->allowAllocation(allow), &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<HStatus> B2HGraphicBufferProducer::setGenerationNumber( + uint32_t generationNumber) { + HStatus hStatus{}; + bool converted = b2h( + mBase->setGenerationNumber(generationNumber), + &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<HStatus> B2HGraphicBufferProducer::setDequeueTimeout( + int64_t timeoutNs) { + HStatus hStatus{}; + bool converted = b2h( + mBase->setDequeueTimeout(static_cast<nsecs_t>(timeoutNs)), + &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<uint64_t> B2HGraphicBufferProducer::getUniqueId() { + uint64_t outId{}; + HStatus hStatus{}; + bool converted = b2h(mBase->getUniqueId(&outId), &hStatus); + return {converted ? outId : 0}; +} + +Return<void> B2HGraphicBufferProducer::getConsumerName( + getConsumerName_cb _hidl_cb) { + _hidl_cb(hidl_string{mBase->getConsumerName().c_str()}); + return {}; +} + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android +
diff --git a/libs/gui/bufferqueue/2.0/B2HProducerListener.cpp b/libs/gui/bufferqueue/2.0/B2HProducerListener.cpp new file mode 100644 index 0000000..c4c96eb --- /dev/null +++ b/libs/gui/bufferqueue/2.0/B2HProducerListener.cpp
@@ -0,0 +1,47 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gui/bufferqueue/2.0/B2HProducerListener.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +// B2HProducerListener +B2HProducerListener::B2HProducerListener(sp<BProducerListener> const& base) + : mBase{base}, + mNeedsReleaseNotify{base ? base->needsReleaseNotify() : false} { +} + +Return<void> B2HProducerListener::onBuffersReleased(uint32_t count) { + if (mNeedsReleaseNotify) { + for (; count > 0; --count) { + mBase->onBufferReleased(); + } + } + return {}; +} + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android +
diff --git a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp new file mode 100644 index 0000000..2f5b73c --- /dev/null +++ b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp
@@ -0,0 +1,506 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "H2BGraphicBufferProducer@2.0" + +#include <android-base/logging.h> + +#include <android/hardware/graphics/common/1.2/types.h> +#include <gui/bufferqueue/2.0/B2HProducerListener.h> +#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h> +#include <gui/bufferqueue/2.0/types.h> +#include <ui/GraphicBuffer.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <vndk/hardware_buffer.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +namespace /* unnamed */ { + +using BQueueBufferInput = ::android:: + IGraphicBufferProducer::QueueBufferInput; +using HQueueBufferInput = ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer::QueueBufferInput; +using BQueueBufferOutput = ::android:: + IGraphicBufferProducer::QueueBufferOutput; +using HQueueBufferOutput = ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer::QueueBufferOutput; + +using ::android::hardware::graphics::bufferqueue::V2_0::utils::b2h; +using ::android::hardware::graphics::bufferqueue::V2_0::utils::h2b; + +bool b2h(BQueueBufferInput const& from, HQueueBufferInput* to, + HFenceWrapper* hFenceWrapper) { + to->timestamp = from.timestamp; + to->isAutoTimestamp = static_cast<bool>(from.isAutoTimestamp); + to->dataSpace = static_cast<int32_t>(from.dataSpace); + to->transform = static_cast<int32_t>(from.transform); + to->stickyTransform = static_cast<int32_t>(from.stickyTransform); + if (!b2h(from.crop, &to->crop) || + !b2h(from.surfaceDamage, &to->surfaceDamage) || + !b2h(from.fence, hFenceWrapper)) { + return false; + } + to->fence = hFenceWrapper->getHandle(); + return true; +} + +bool h2b(HQueueBufferOutput const& from, BQueueBufferOutput* to) { + to->width = from.width; + to->height = from.height; + to->transformHint = static_cast<uint32_t>(from.transformHint); + to->numPendingBuffers = from.numPendingBuffers; + to->nextFrameNumber = from.nextFrameNumber; + to->bufferReplaced = from.bufferReplaced; + return true; +} + +} // unnamed namespace + +// H2BGraphicBufferProducer +// ======================== + +status_t H2BGraphicBufferProducer::requestBuffer(int slot, + sp<GraphicBuffer>* bBuffer) { + bool converted{}; + status_t bStatus{}; + Return<void> transResult = mBase->requestBuffer(slot, + [&converted, &bStatus, bBuffer]( + HStatus hStatus, + HardwareBuffer const& hBuffer, + uint32_t generationNumber) { + converted = + h2b(hStatus, &bStatus) && + h2b(hBuffer, bBuffer); + if (*bBuffer) { + (*bBuffer)->setGenerationNumber(generationNumber); + } + }); + if (!transResult.isOk()) { + LOG(ERROR) << "requestBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!converted) { + LOG(ERROR) << "requestBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::setMaxDequeuedBufferCount( + int maxDequeuedBuffers) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->setMaxDequeuedBufferCount( + static_cast<int32_t>(maxDequeuedBuffers)); + if (!transResult.isOk()) { + LOG(ERROR) << "setMaxDequeuedBufferCount: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "setMaxDequeuedBufferCount: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::setAsyncMode(bool async) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->setAsyncMode(async); + if (!transResult.isOk()) { + LOG(ERROR) << "setAsyncMode: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "setAsyncMode: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::dequeueBuffer( + int* slot, sp<BFence>* fence, + uint32_t w, uint32_t h, + PixelFormat format, uint64_t usage, + uint64_t* outBufferAge, FrameEventHistoryDelta* /* outTimestamps */) { + + using HInput = HGraphicBufferProducer::DequeueBufferInput; + HInput input{w, h, static_cast<uint32_t>(format), usage}; + + using HOutput = HGraphicBufferProducer::DequeueBufferOutput; + bool converted{}; + status_t bStatus{}; + Return<void> transResult = mBase->dequeueBuffer(input, + [&converted, &bStatus, slot, fence, outBufferAge] ( + HStatus hStatus, int32_t hSlot, HOutput const& hOutput) { + converted = h2b(hStatus, &bStatus); + if (!converted || bStatus != OK) { + return; + } + *slot = hSlot; + *outBufferAge = hOutput.bufferAge; + bStatus = + (hOutput.bufferNeedsReallocation ? + BUFFER_NEEDS_REALLOCATION : 0) | + (hOutput.releaseAllBuffers ? + RELEASE_ALL_BUFFERS : 0); + converted = h2b(hOutput.fence, fence); + }); + if (!transResult.isOk()) { + LOG(ERROR) << "dequeueBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!converted) { + LOG(ERROR) << "dequeueBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::detachBuffer(int slot) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->detachBuffer( + static_cast<int32_t>(slot)); + if (!transResult.isOk()) { + LOG(ERROR) << "detachBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "detachBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::detachNextBuffer( + sp<GraphicBuffer>* outBuffer, sp<BFence>* outFence) { + bool converted{}; + status_t bStatus{}; + Return<void> transResult = mBase->detachNextBuffer( + [&converted, &bStatus, outBuffer, outFence] ( + HStatus hStatus, + HardwareBuffer const& hBuffer, + hidl_handle const& hFence) { + converted = h2b(hStatus, &bStatus) && + h2b(hBuffer, outBuffer) && + h2b(hFence, outFence); + }); + if (!transResult.isOk()) { + LOG(ERROR) << "detachNextBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!converted) { + LOG(ERROR) << "detachNextBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::attachBuffer( + int* outSlot, sp<GraphicBuffer> const& buffer) { + HardwareBuffer hBuffer{}; + uint32_t hGenerationNumber{}; + if (!b2h(buffer, &hBuffer, &hGenerationNumber)) { + LOG(ERROR) << "attachBuffer: invalid input buffer."; + return BAD_VALUE; + } + + bool converted{}; + status_t bStatus{}; + Return<void> transResult = mBase->attachBuffer(hBuffer, hGenerationNumber, + [&converted, &bStatus, outSlot]( + HStatus hStatus, int32_t hSlot, bool releaseAllBuffers) { + converted = h2b(hStatus, &bStatus); + *outSlot = static_cast<int>(hSlot); + if (converted && releaseAllBuffers && bStatus == OK) { + bStatus = IGraphicBufferProducer::RELEASE_ALL_BUFFERS; + } + }); + if (!transResult.isOk()) { + LOG(ERROR) << "attachBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!converted) { + LOG(ERROR) << "attachBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::queueBuffer( + int slot, + QueueBufferInput const& input, + QueueBufferOutput* output) { + HQueueBufferInput hInput{}; + HFenceWrapper hFenceWrapper; + if (!b2h(input, &hInput, &hFenceWrapper)) { + LOG(ERROR) << "queueBuffer: corrupted input."; + return UNKNOWN_ERROR; + } + + bool converted{}; + status_t bStatus{}; + Return<void> transResult = mBase->queueBuffer( + static_cast<int32_t>(slot), + hInput, + [&converted, &bStatus, output]( + HStatus hStatus, + HQueueBufferOutput const& hOutput) { + converted = h2b(hStatus, &bStatus) && h2b(hOutput, output); + }); + + if (!transResult.isOk()) { + LOG(ERROR) << "queueBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!converted) { + LOG(ERROR) << "queueBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::cancelBuffer(int slot, sp<BFence> const& fence) { + HFenceWrapper hFenceWrapper; + if (!b2h(fence, &hFenceWrapper)) { + LOG(ERROR) << "cancelBuffer: corrupted input fence."; + return UNKNOWN_ERROR; + } + status_t bStatus{}; + Return<HStatus> transResult = mBase->cancelBuffer( + static_cast<int32_t>(slot), + hFenceWrapper.getHandle()); + if (!transResult.isOk()) { + LOG(ERROR) << "cancelBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "cancelBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +int H2BGraphicBufferProducer::query(int what, int* value) { + int result{}; + Return<void> transResult = mBase->query( + static_cast<int32_t>(what), + [&result, value](int32_t r, int32_t v) { + result = static_cast<int>(r); + *value = static_cast<int>(v); + }); + if (!transResult.isOk()) { + LOG(ERROR) << "query: transaction failed."; + return FAILED_TRANSACTION; + } + return result; +} + +status_t H2BGraphicBufferProducer::connect( + sp<IProducerListener> const& listener, int api, + bool producerControlledByApp, QueueBufferOutput* output) { + HConnectionType hConnectionType; + if (!b2h(api, &hConnectionType)) { + LOG(ERROR) << "connect: corrupted input connection type."; + return UNKNOWN_ERROR; + } + sp<HProducerListener> hListener = nullptr; + if (listener && listener->needsReleaseNotify()) { + hListener = new B2HProducerListener(listener); + if (!hListener) { + LOG(ERROR) << "connect: failed to wrap listener."; + return UNKNOWN_ERROR; + } + } + + bool converted{}; + status_t bStatus{}; + Return<void> transResult = mBase->connect( + hListener, + hConnectionType, + producerControlledByApp, + [&converted, &bStatus, output]( + HStatus hStatus, + HQueueBufferOutput const& hOutput) { + converted = h2b(hStatus, &bStatus) && h2b(hOutput, output); + }); + if (!transResult.isOk()) { + LOG(ERROR) << "connect: transaction failed."; + return FAILED_TRANSACTION; + } + if (!converted) { + LOG(ERROR) << "connect: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; + +} + +status_t H2BGraphicBufferProducer::disconnect(int api, DisconnectMode mode) { + HConnectionType hConnectionType; + if (mode == DisconnectMode::AllLocal) { + hConnectionType = HConnectionType::CURRENTLY_CONNECTED; + } else if (!b2h(api, &hConnectionType)) { + LOG(ERROR) << "connect: corrupted input connection type."; + return UNKNOWN_ERROR; + } + + status_t bStatus{}; + Return<HStatus> transResult = mBase->disconnect(hConnectionType); + if (!transResult.isOk()) { + LOG(ERROR) << "disconnect: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "disconnect: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::setSidebandStream( + sp<NativeHandle> const& stream) { + if (stream) { + LOG(INFO) << "setSidebandStream: not supported."; + return INVALID_OPERATION; + } + return OK; +} + +void H2BGraphicBufferProducer::allocateBuffers( + uint32_t width, uint32_t height, + PixelFormat format, uint64_t usage) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->allocateBuffers( + width, height, static_cast<uint32_t>(format), usage); + if (!transResult.isOk()) { + LOG(ERROR) << "allocateBuffer: transaction failed."; + return; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "allocateBuffer: corrupted transaction."; + return; + } +} + +status_t H2BGraphicBufferProducer::allowAllocation(bool allow) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->allowAllocation(allow); + if (!transResult.isOk()) { + LOG(ERROR) << "allowAllocation: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "allowAllocation: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::setGenerationNumber( + uint32_t generationNumber) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->setGenerationNumber(generationNumber); + if (!transResult.isOk()) { + LOG(ERROR) << "setGenerationNumber: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "setGenerationNumber: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +String8 H2BGraphicBufferProducer::getConsumerName() const { + String8 bName; + Return<void> transResult = mBase->getConsumerName( + [&bName](hidl_string const& name) { + bName = name.c_str(); + }); + return bName; +} + +status_t H2BGraphicBufferProducer::setSharedBufferMode(bool sharedBufferMode) { + if (sharedBufferMode) { + LOG(INFO) << "setSharedBufferMode: not supported."; + return INVALID_OPERATION; + } + return OK; +} + +status_t H2BGraphicBufferProducer::setAutoRefresh(bool autoRefresh) { + if (autoRefresh) { + LOG(INFO) << "setAutoRefresh: not supported."; + return INVALID_OPERATION; + } + return OK; +} + +status_t H2BGraphicBufferProducer::setDequeueTimeout(nsecs_t timeout) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->setDequeueTimeout( + static_cast<int64_t>(timeout)); + if (!transResult.isOk()) { + LOG(ERROR) << "setDequeueTimeout: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "setDequeueTimeout: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::getLastQueuedBuffer( + sp<GraphicBuffer>*, + sp<BFence>*, + float[16]) { + LOG(INFO) << "getLastQueuedBuffer: not supported."; + return INVALID_OPERATION; +} + +void H2BGraphicBufferProducer::getFrameTimestamps(FrameEventHistoryDelta*) { + LOG(INFO) << "getFrameTimestamps: not supported."; +} + +status_t H2BGraphicBufferProducer::getUniqueId(uint64_t* outId) const { + Return<uint64_t> transResult = mBase->getUniqueId(); + if (!transResult.isOk()) { + LOG(ERROR) << "getUniqueId: transaction failed."; + return FAILED_TRANSACTION; + } + *outId = static_cast<uint64_t>(transResult); + return OK; +} + +status_t H2BGraphicBufferProducer::getConsumerUsage(uint64_t*) const { + LOG(INFO) << "getConsumerUsage: not supported."; + return INVALID_OPERATION; +} + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android
diff --git a/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp new file mode 100644 index 0000000..b81a357 --- /dev/null +++ b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
@@ -0,0 +1,57 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "H2BProducerListener@2.0" + +#include <android-base/logging.h> + +#include <gui/bufferqueue/2.0/H2BProducerListener.h> +#include <hidl/Status.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +using ::android::hardware::Return; + +H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base) + : CBase{base} { +} + +void H2BProducerListener::onBufferReleased() { + if (mBase) { + Return<void> transResult = mBase->onBuffersReleased(1); + if (!transResult.isOk()) { + LOG(ERROR) << "onBuffersReleased: transaction failed."; + } + } +} + +bool H2BProducerListener::needsReleaseNotify() { + return static_cast<bool>(mBase); +} + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android +
diff --git a/libs/gui/bufferqueue/2.0/types.cpp b/libs/gui/bufferqueue/2.0/types.cpp new file mode 100644 index 0000000..cbd6cad --- /dev/null +++ b/libs/gui/bufferqueue/2.0/types.cpp
@@ -0,0 +1,302 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cutils/native_handle.h> +#include <gui/BufferQueueCore.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/bufferqueue/2.0/types.h> +#include <system/window.h> +#include <vndk/hardware_buffer.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +// Status +// ====== + +bool b2h(status_t from, HStatus* to, + bool* bufferNeedsReallocation, bool* releaseAllBuffers) { + switch (from) { + case OK: + *to = HStatus::OK; break; + case NO_MEMORY: + *to = HStatus::NO_MEMORY; break; + case NO_INIT: + *to = HStatus::NO_INIT; break; + case BAD_VALUE: + *to = HStatus::BAD_VALUE; break; + case DEAD_OBJECT: + *to = HStatus::DEAD_OBJECT; break; + case INVALID_OPERATION: + *to = HStatus::INVALID_OPERATION; break; + case TIMED_OUT: + *to = HStatus::TIMED_OUT; break; + case WOULD_BLOCK: + *to = HStatus::WOULD_BLOCK; break; + case UNKNOWN_ERROR: + *to = HStatus::UNKNOWN_ERROR; break; + default: + using BGBP = ::android::IGraphicBufferProducer; + status_t mask = + (bufferNeedsReallocation ? BGBP::BUFFER_NEEDS_REALLOCATION : 0) + | (releaseAllBuffers ? BGBP::RELEASE_ALL_BUFFERS : 0); + if (from & ~mask) { + *to = static_cast<HStatus>(from); + } else { + *to = HStatus::OK; + if (bufferNeedsReallocation) { + *bufferNeedsReallocation = from & BGBP::BUFFER_NEEDS_REALLOCATION; + } + if (releaseAllBuffers) { + *releaseAllBuffers = from & BGBP::RELEASE_ALL_BUFFERS; + } + } + } + return true; +} + +bool h2b(HStatus from, status_t* to) { + switch (from) { + case HStatus::OK: + *to = OK; break; + case HStatus::NO_MEMORY: + *to = NO_MEMORY; break; + case HStatus::NO_INIT: + *to = NO_INIT; break; + case HStatus::BAD_VALUE: + *to = BAD_VALUE; break; + case HStatus::DEAD_OBJECT: + *to = DEAD_OBJECT; break; + case HStatus::INVALID_OPERATION: + *to = INVALID_OPERATION; break; + case HStatus::TIMED_OUT: + *to = TIMED_OUT; break; + case HStatus::WOULD_BLOCK: + *to = WOULD_BLOCK; break; + case HStatus::UNKNOWN_ERROR: + *to = UNKNOWN_ERROR; break; + default: + *to = static_cast<status_t>(from); + } + return true; +} + +// Fence +// ===== + +HFenceWrapper::HFenceWrapper(native_handle_t* h) : mHandle{h} { +} + +HFenceWrapper::~HFenceWrapper() { + native_handle_delete(mHandle); +} + +HFenceWrapper& HFenceWrapper::set(native_handle_t* h) { + native_handle_delete(mHandle); + mHandle = h; + return *this; +} + +HFenceWrapper& HFenceWrapper::operator=(native_handle_t* h) { + return set(h); +} + +hidl_handle HFenceWrapper::getHandle() const { + return hidl_handle{mHandle}; +} + +HFenceWrapper::operator hidl_handle() const { + return getHandle(); +} + +bool b2h(sp<BFence> const& from, HFenceWrapper* to) { + if (!from) { + to->set(nullptr); + return true; + } + int fenceFd = from->get(); + if (fenceFd == -1) { + to->set(nullptr); + return true; + } + native_handle_t* nh = native_handle_create(1, 0); + if (!nh) { + return false; + } + nh->data[0] = fenceFd; + to->set(nh); + return true; +} + +bool h2b(native_handle_t const* from, sp<BFence>* to) { + if (!from || from->numFds == 0) { + *to = new ::android::Fence(); + return true; + } + if (from->numFds != 1 || from->numInts != 0) { + return false; + } + *to = new BFence(dup(from->data[0])); + return true; +} + +// ConnectionType +// ============== + +bool b2h(int from, HConnectionType* to) { + *to = static_cast<HConnectionType>(from); + switch (from) { + case BufferQueueCore::CURRENTLY_CONNECTED_API: + *to = HConnectionType::CURRENTLY_CONNECTED; break; + case NATIVE_WINDOW_API_EGL: + *to = HConnectionType::EGL; break; + case NATIVE_WINDOW_API_CPU: + *to = HConnectionType::CPU; break; + case NATIVE_WINDOW_API_MEDIA: + *to = HConnectionType::MEDIA; break; + case NATIVE_WINDOW_API_CAMERA: + *to = HConnectionType::CAMERA; break; + } + return true; +} + +bool h2b(HConnectionType from, int* to) { + *to = static_cast<int>(from); + switch (from) { + case HConnectionType::CURRENTLY_CONNECTED: + *to = BufferQueueCore::CURRENTLY_CONNECTED_API; break; + case HConnectionType::EGL: + *to = NATIVE_WINDOW_API_EGL; break; + case HConnectionType::CPU: + *to = NATIVE_WINDOW_API_CPU; break; + case HConnectionType::MEDIA: + *to = NATIVE_WINDOW_API_MEDIA; break; + case HConnectionType::CAMERA: + *to = NATIVE_WINDOW_API_CAMERA; break; + } + return true; +} + +// Rect +// ==== + +bool b2h(BRect const& from, HRect* to) { + BRect* dst = reinterpret_cast<BRect*>(to->data()); + dst->left = from.left; + dst->top = from.top; + dst->right = from.right; + dst->bottom = from.bottom; + return true; +} + +bool h2b(HRect const& from, BRect* to) { + BRect const* src = reinterpret_cast<BRect const*>(from.data()); + to->left = src->left; + to->top = src->top; + to->right = src->right; + to->bottom = src->bottom; + return true; +} + +// Region +// ====== + +bool b2h(BRegion const& from, HRegion* to) { + size_t numRects; + BRect const* rectArray = from.getArray(&numRects); + to->resize(numRects); + for (size_t i = 0; i < numRects; ++i) { + if (!b2h(rectArray[i], &(*to)[i])) { + return false; + } + } + return true; +} + +bool h2b(HRegion const& from, BRegion* to) { + if (from.size() > 0) { + BRect bRect; + if (!h2b(from[0], &bRect)) { + return false; + } + to->set(bRect); + for (size_t i = 1; i < from.size(); ++i) { + if (!h2b(from[i], &bRect)) { + return false; + } + to->addRectUnchecked( + static_cast<int>(bRect.left), + static_cast<int>(bRect.top), + static_cast<int>(bRect.right), + static_cast<int>(bRect.bottom)); + } + } else { + to->clear(); + } + return true; +} + +// GraphicBuffer +// ============= + +// The handle is not cloned. Its lifetime is tied to the original GraphicBuffer. +bool b2h(sp<GraphicBuffer> const& from, HardwareBuffer* to, + uint32_t* toGenerationNumber) { + if (!from) { + return false; + } + AHardwareBuffer* hwBuffer = from->toAHardwareBuffer(); + to->nativeHandle.setTo( + const_cast<native_handle_t*>( + AHardwareBuffer_getNativeHandle(hwBuffer)), + false); + AHardwareBuffer_describe( + hwBuffer, + reinterpret_cast<AHardwareBuffer_Desc*>(to->description.data())); + if (toGenerationNumber) { + *toGenerationNumber = from->getGenerationNumber(); + } + return true; +} + +// The handle is cloned. +bool h2b(HardwareBuffer const& from, sp<GraphicBuffer>* to) { + AHardwareBuffer_Desc const* desc = + reinterpret_cast<AHardwareBuffer_Desc const*>( + from.description.data()); + native_handle_t const* handle = from.nativeHandle; + AHardwareBuffer* hwBuffer; + if (AHardwareBuffer_createFromHandle( + desc, handle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, + &hwBuffer) != OK) { + return false; + } + *to = GraphicBuffer::fromAHardwareBuffer(hwBuffer); + AHardwareBuffer_release(hwBuffer); + return true; +} + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android +
diff --git a/libs/gui/bufferqueue/OWNERS b/libs/gui/bufferqueue/OWNERS new file mode 100644 index 0000000..cbe9317 --- /dev/null +++ b/libs/gui/bufferqueue/OWNERS
@@ -0,0 +1,5 @@ +chz@google.com +lajos@google.com +pawin@google.com +taklee@google.com +wonsik@google.com
diff --git a/libs/gui/include/gui/BufferHubProducer.h b/libs/gui/include/gui/BufferHubProducer.h index 23c9909..0e925ce 100644 --- a/libs/gui/include/gui/BufferHubProducer.h +++ b/libs/gui/include/gui/BufferHubProducer.h
@@ -165,6 +165,10 @@ // buffers are acquired by the consumer, we can't . status_t FreeAllBuffers(); + // Helper function that implements the detachBuffer() call, but assuming |mutex_| has been + // locked already. + status_t DetachBufferLocked(size_t slot); + // Concreate implementation backed by BufferHubBuffer. std::shared_ptr<dvr::ProducerQueue> queue_; @@ -199,10 +203,10 @@ // requested buffer usage or geometry differs from that of the buffer // allocated to a slot. struct BufferHubSlot : public BufferSlot { - BufferHubSlot() : mBufferProducer(nullptr), mIsReallocating(false) {} + BufferHubSlot() : mProducerBuffer(nullptr), mIsReallocating(false) {} // BufferSlot comes from android framework, using m prefix to comply with // the name convention with the reset of data fields from BufferSlot. - std::shared_ptr<dvr::BufferProducer> mBufferProducer; + std::shared_ptr<dvr::ProducerBuffer> mProducerBuffer; bool mIsReallocating; }; BufferHubSlot buffers_[dvr::BufferHubQueue::kMaxQueueCapacity];
diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h index d108120..7db69ec 100644 --- a/libs/gui/include/gui/BufferQueueConsumer.h +++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -31,7 +31,7 @@ class BufferQueueConsumer : public BnGraphicBufferConsumer { public: - BufferQueueConsumer(const sp<BufferQueueCore>& core); + explicit BufferQueueConsumer(const sp<BufferQueueCore>& core); ~BufferQueueConsumer() override; // acquireBuffer attempts to acquire ownership of the next pending buffer in @@ -171,6 +171,9 @@ // End functions required for backwards compatibility + // Value used to determine if present time is valid. + constexpr static int MAX_REASONABLE_NSEC = 1'000'000'000ULL; // 1 second + private: sp<BufferQueueCore> mCore;
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h index 537c957..690a85f 100644 --- a/libs/gui/include/gui/BufferQueueCore.h +++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -22,8 +22,6 @@ #include <gui/BufferSlot.h> #include <gui/OccupancyTracker.h> -#include <utils/Condition.h> -#include <utils/Mutex.h> #include <utils/NativeHandle.h> #include <utils/RefBase.h> #include <utils/String8.h> @@ -33,6 +31,8 @@ #include <list> #include <set> +#include <mutex> +#include <condition_variable> #define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__) #define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__) @@ -40,13 +40,14 @@ #define BQ_LOGW(x, ...) ALOGW("[%s] " x, mConsumerName.string(), ##__VA_ARGS__) #define BQ_LOGE(x, ...) ALOGE("[%s] " x, mConsumerName.string(), ##__VA_ARGS__) -#define ATRACE_BUFFER_INDEX(index) \ - if (ATRACE_ENABLED()) { \ - char ___traceBuf[1024]; \ - snprintf(___traceBuf, 1024, "%s: %d", \ - mCore->mConsumerName.string(), (index)); \ - android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \ - } +#define ATRACE_BUFFER_INDEX(index) \ + do { \ + if (ATRACE_ENABLED()) { \ + char ___traceBuf[1024]; \ + snprintf(___traceBuf, 1024, "%s: %d", mCore->mConsumerName.string(), (index)); \ + android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf); \ + } \ + } while (false) namespace android { @@ -133,7 +134,7 @@ bool adjustAvailableSlotsLocked(int delta); // waitWhileAllocatingLocked blocks until mIsAllocating is false. - void waitWhileAllocatingLocked() const; + void waitWhileAllocatingLocked(std::unique_lock<std::mutex>& lock) const; #if DEBUG_ONLY_CODE // validateConsistencyLocked ensures that the free lists are in sync with @@ -144,7 +145,7 @@ // mMutex is the mutex used to prevent concurrent access to the member // variables of BufferQueueCore objects. It must be locked whenever any // member variable is accessed. - mutable Mutex mMutex; + mutable std::mutex mMutex; // mIsAbandoned indicates that the BufferQueue will no longer be used to // consume image buffers pushed to it using the IGraphicBufferProducer @@ -218,13 +219,24 @@ // mDequeueCondition is a condition variable used for dequeueBuffer in // synchronous mode. - mutable Condition mDequeueCondition; + mutable std::condition_variable mDequeueCondition; // mDequeueBufferCannotBlock indicates whether dequeueBuffer is allowed to // block. This flag is set during connect when both the producer and // consumer are controlled by the application. bool mDequeueBufferCannotBlock; + // mQueueBufferCanDrop indicates whether queueBuffer is allowed to drop + // buffers in non-async mode. This flag is set during connect when both the + // producer and consumer are controlled by application. + bool mQueueBufferCanDrop; + + // mLegacyBufferDrop indicates whether mQueueBufferCanDrop is in effect. + // If this flag is set mQueueBufferCanDrop is working as explained. If not + // queueBuffer will not drop buffers unless consumer is SurfaceFlinger and + // mQueueBufferCanDrop is set. + bool mLegacyBufferDrop; + // mDefaultBufferFormat can be set so it will override the buffer format // when it isn't specified in dequeueBuffer. PixelFormat mDefaultBufferFormat; @@ -281,7 +293,7 @@ // mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating // becomes false. - mutable Condition mIsAllocatingCondition; + mutable std::condition_variable mIsAllocatingCondition; // mAllowAllocation determines whether dequeueBuffer is allowed to allocate // new buffers
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h index 5c7ffb4..d2a47a6 100644 --- a/libs/gui/include/gui/BufferQueueProducer.h +++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -29,7 +29,8 @@ public: friend class BufferQueue; // Needed to access binderDied - BufferQueueProducer(const sp<BufferQueueCore>& core, bool consumerIsSurfaceFlinger = false); + explicit BufferQueueProducer(const sp<BufferQueueCore>& core, + bool consumerIsSurfaceFlinger = false); ~BufferQueueProducer() override; // requestBuffer returns the GraphicBuffer for slot N. @@ -173,6 +174,9 @@ // See IGraphicBufferProducer::setDequeueTimeout virtual status_t setDequeueTimeout(nsecs_t timeout) override; + // see IGraphicBufferProducer::setLegacyBufferDrop + virtual status_t setLegacyBufferDrop(bool drop); + // See IGraphicBufferProducer::getLastQueuedBuffer virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) override; @@ -210,7 +214,8 @@ Dequeue, Attach, }; - status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, int* found) const; + status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, std::unique_lock<std::mutex>& lock, + int* found) const; sp<BufferQueueCore> mCore; @@ -242,15 +247,22 @@ // (mCore->mMutex) is held, a ticket is retained by the producer. After // dropping the BufferQueue lock, the producer must wait on the condition // variable until the current callback ticket matches its retained ticket. - Mutex mCallbackMutex; + std::mutex mCallbackMutex; int mNextCallbackTicket; // Protected by mCore->mMutex int mCurrentCallbackTicket; // Protected by mCallbackMutex - Condition mCallbackCondition; + std::condition_variable mCallbackCondition; // Sets how long dequeueBuffer or attachBuffer will block if a buffer or // slot is not yet available. nsecs_t mDequeueTimeout; + // If set to true, dequeueBuffer() is currently waiting for buffer allocation to complete. + bool mDequeueWaitingForAllocation; + + // Condition variable to signal allocateBuffers() that dequeueBuffer() is no longer waiting for + // allocation to complete. + std::condition_variable mDequeueWaitingForAllocationCondition; + }; // class BufferQueueProducer } // namespace android
diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h index d375611..806fbe8 100644 --- a/libs/gui/include/gui/CpuConsumer.h +++ b/libs/gui/include/gui/CpuConsumer.h
@@ -70,7 +70,7 @@ uint32_t chromaStep; LockedBuffer() : - data(NULL), + data(nullptr), width(0), height(0), format(PIXEL_FORMAT_NONE), @@ -82,8 +82,8 @@ dataSpace(HAL_DATASPACE_UNKNOWN), frameNumber(0), flexFormat(PIXEL_FORMAT_NONE), - dataCb(NULL), - dataCr(NULL), + dataCb(nullptr), + dataCr(nullptr), chromaStride(0), chromaStep(0) {}
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 32ce59a..22de751 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -52,13 +52,14 @@ enum { DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'), DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'), + DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'), }; struct Event { struct Header { uint32_t type; - uint32_t id; + PhysicalDisplayId displayId; nsecs_t timestamp __attribute__((aligned(8))); }; @@ -70,10 +71,15 @@ bool connected; }; + struct Config { + int32_t configId; + }; + Header header; union { VSync vsync; Hotplug hotplug; + Config config; }; }; @@ -84,7 +90,7 @@ * or requestNextVsync to receive them. * Other events start being delivered immediately. */ - DisplayEventReceiver( + explicit DisplayEventReceiver( ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp); /*
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h index 9716be4..df02494 100644 --- a/libs/gui/include/gui/FrameTimestamps.h +++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -30,7 +30,6 @@ struct FrameEvents; class FrameEventHistoryDelta; -class String8; // Identifiers for all the events that may be recorded or reported. @@ -72,7 +71,7 @@ bool hasDequeueReadyInfo() const; void checkFencesForCompletion(); - void dump(String8& outString) const; + void dump(std::string& outString) const; bool valid{false}; int connectId{0}; @@ -112,7 +111,7 @@ FrameEvents* getFrame(uint64_t frameNumber); FrameEvents* getFrame(uint64_t frameNumber, size_t* iHint); void checkFencesForCompletion(); - void dump(String8& outString) const; + void dump(std::string& outString) const; static constexpr size_t MAX_FRAME_HISTORY = 8; @@ -311,7 +310,7 @@ // Movable. FrameEventHistoryDelta(FrameEventHistoryDelta&& src) = default; - FrameEventHistoryDelta& operator=(FrameEventHistoryDelta&& src); + FrameEventHistoryDelta& operator=(FrameEventHistoryDelta&& src) noexcept; // Not copyable. FrameEventHistoryDelta(const FrameEventHistoryDelta& src) = delete; FrameEventHistoryDelta& operator=(
diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index 71ed3bf..ddd868d 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h
@@ -140,7 +140,8 @@ // Scale the crop down horizontally or vertically such that it has the // same aspect ratio as the buffer does. - static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight); + static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, + uint32_t bufferHeight); // getTimestamp retrieves the timestamp associated with the texture image // set by the most recent call to updateTexImage. @@ -300,12 +301,11 @@ // also only creating new EGLImages from buffers when required. class EglImage : public LightRefBase<EglImage> { public: - EglImage(sp<GraphicBuffer> graphicBuffer); + explicit EglImage(sp<GraphicBuffer> graphicBuffer); // createIfNeeded creates an EGLImage if required (we haven't created // one yet, or the EGLDisplay or crop-rect has changed). status_t createIfNeeded(EGLDisplay display, - const Rect& cropRect, bool forceCreate = false); // This calls glEGLImageTargetTexture2DOES to bind the image to the @@ -314,7 +314,7 @@ const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; } const native_handle* graphicBufferHandle() { - return mGraphicBuffer == NULL ? NULL : mGraphicBuffer->handle; + return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle; } private: @@ -324,7 +324,7 @@ // createImage creates a new EGLImage from a GraphicBuffer. EGLImageKHR createImage(EGLDisplay dpy, - const sp<GraphicBuffer>& graphicBuffer, const Rect& crop); + const sp<GraphicBuffer>& graphicBuffer); // Disallow copying EglImage(const EglImage& rhs);
diff --git a/libs/gui/include/gui/GuiConfig.h b/libs/gui/include/gui/GuiConfig.h index b020ed9..7aa5432 100644 --- a/libs/gui/include/gui/GuiConfig.h +++ b/libs/gui/include/gui/GuiConfig.h
@@ -17,12 +17,12 @@ #ifndef ANDROID_GUI_CONFIG_H #define ANDROID_GUI_CONFIG_H -#include <utils/String8.h> +#include <string> namespace android { // Append the libgui configuration details to configStr. -void appendGuiConfigString(String8& configStr); +void appendGuiConfigString(std::string& configStr); }; // namespace android
diff --git a/libs/gui/include/gui/HdrMetadata.h b/libs/gui/include/gui/HdrMetadata.h index 9800602..1e9c3e7 100644 --- a/libs/gui/include/gui/HdrMetadata.h +++ b/libs/gui/include/gui/HdrMetadata.h
@@ -17,6 +17,7 @@ #pragma once #include <stdint.h> +#include <vector> #include <system/graphics.h> #include <utils/Flattenable.h> @@ -26,12 +27,15 @@ struct HdrMetadata : public LightFlattenable<HdrMetadata> { enum Type : uint32_t { SMPTE2086 = 1 << 0, - CTA861_3 = 1 << 1, + CTA861_3 = 1 << 1, + HDR10PLUS = 1 << 2, }; + uint32_t validTypes{0}; android_smpte2086_metadata smpte2086{}; android_cta861_3_metadata cta8613{}; + std::vector<uint8_t> hdr10plus{}; // LightFlattenable bool isFixedSize() const { return false; }
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 887654e..3dde8c8 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -25,6 +25,7 @@ #include <binder/IInterface.h> +#include <ui/BufferQueueDefs.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> #include <ui/Rect.h> @@ -35,6 +36,7 @@ #include <hidl/HybridInterface.h> #include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h> +#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h> namespace android { // ---------------------------------------------------------------------------- @@ -42,8 +44,6 @@ class IProducerListener; class NativeHandle; class Surface; -typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer - HGraphicBufferProducer; /* * This class defines the Binder IPC interface for the producer side of @@ -62,15 +62,24 @@ class IGraphicBufferProducer : public IInterface { public: - DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer) + using HGraphicBufferProducerV1_0 = + ::android::hardware::graphics::bufferqueue::V1_0:: + IGraphicBufferProducer; + using HGraphicBufferProducerV2_0 = + ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer; + + DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer, + HGraphicBufferProducerV1_0, + HGraphicBufferProducerV2_0) enum { // A flag returned by dequeueBuffer when the client needs to call // requestBuffer immediately thereafter. - BUFFER_NEEDS_REALLOCATION = 0x1, + BUFFER_NEEDS_REALLOCATION = BufferQueueDefs::BUFFER_NEEDS_REALLOCATION, // A flag returned by dequeueBuffer when all mirrored slots should be // released by the client. This flag should always be processed first. - RELEASE_ALL_BUFFERS = 0x2, + RELEASE_ALL_BUFFERS = BufferQueueDefs::RELEASE_ALL_BUFFERS, }; enum { @@ -345,7 +354,7 @@ *outScalingMode = scalingMode; *outTransform = transform; *outFence = fence; - if (outStickyTransform != NULL) { + if (outStickyTransform != nullptr) { *outStickyTransform = stickyTransform; } if (outGetFrameTimestamps) { @@ -366,7 +375,6 @@ const HdrMetadata& getHdrMetadata() const { return hdrMetadata; } void setHdrMetadata(const HdrMetadata& metadata) { hdrMetadata = metadata; } - private: int64_t timestamp{0}; int isAutoTimestamp{0}; android_dataspace dataSpace{HAL_DATASPACE_UNKNOWN}; @@ -584,12 +592,20 @@ // non-blocking mode and its corresponding spare buffer (which is used to // ensure a buffer is always available). // + // Note well: queueBuffer will stop buffer dropping behavior if timeout is + // strictly positive. If timeout is zero or negative, previous buffer + // dropping behavior will not be changed. + // // Return of a value other than NO_ERROR means an error has occurred: // * BAD_VALUE - Failure to adjust the number of available slots. This can // happen because of trying to allocate/deallocate the async // buffer. virtual status_t setDequeueTimeout(nsecs_t timeout) = 0; + // Used to enable/disable buffer drop behavior of queueBuffer. + // If it's not used, legacy drop behavior will be retained. + virtual status_t setLegacyBufferDrop(bool drop); + // Returns the last queued buffer along with a fence which must signal // before the contents of the buffer are read. If there are no buffers in // the queue, outBuffer will be populated with nullptr and outFence will be
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h index e808bd3..a13d8e4 100644 --- a/libs/gui/include/gui/IProducerListener.h +++ b/libs/gui/include/gui/IProducerListener.h
@@ -17,8 +17,10 @@ #ifndef ANDROID_GUI_IPRODUCERLISTENER_H #define ANDROID_GUI_IPRODUCERLISTENER_H +#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h> +#include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h> #include <binder/IInterface.h> - +#include <hidl/HybridInterface.h> #include <utils/RefBase.h> namespace android { @@ -47,7 +49,14 @@ class IProducerListener : public ProducerListener, public IInterface { public: - DECLARE_META_INTERFACE(ProducerListener) + using HProducerListener1 = + ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener; + using HProducerListener2 = + ::android::hardware::graphics::bufferqueue::V2_0::IProducerListener; + DECLARE_HYBRID_META_INTERFACE( + ProducerListener, + HProducerListener1, + HProducerListener2) }; class BnProducerListener : public BnInterface<IProducerListener>
diff --git a/libs/gui/include/gui/IRegionSamplingListener.h b/libs/gui/include/gui/IRegionSamplingListener.h new file mode 100644 index 0000000..1803d9a --- /dev/null +++ b/libs/gui/include/gui/IRegionSamplingListener.h
@@ -0,0 +1,43 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> +#include <vector> + +#include <binder/IInterface.h> +#include <binder/SafeInterface.h> + +namespace android { + +class IRegionSamplingListener : public IInterface { +public: + DECLARE_META_INTERFACE(RegionSamplingListener) + + virtual void onSampleCollected(float medianLuma) = 0; +}; + +class BnRegionSamplingListener : public SafeBnInterface<IRegionSamplingListener> { +public: + BnRegionSamplingListener() + : SafeBnInterface<IRegionSamplingListener>("BnRegionSamplingListener") {} + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0) override; +}; + +} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 0db21a5..e2f7736 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -20,32 +20,42 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/RefBase.h> +#include <binder/IBinder.h> +#include <binder/IInterface.h> + +#include <gui/ITransactionCompletedListener.h> + +#include <ui/ConfigStoreTypes.h> +#include <ui/DisplayedFrameStats.h> +#include <ui/FrameStats.h> +#include <ui/GraphicBuffer.h> +#include <ui/GraphicTypes.h> +#include <ui/PixelFormat.h> + #include <utils/Errors.h> +#include <utils/RefBase.h> #include <utils/Timers.h> #include <utils/Vector.h> -#include <binder/IInterface.h> - -#include <ui/FrameStats.h> -#include <ui/PixelFormat.h> -#include <ui/GraphicBuffer.h> -#include <ui/GraphicTypes.h> - +#include <optional> +#include <unordered_set> #include <vector> namespace android { // ---------------------------------------------------------------------------- +struct client_cache_t; struct ComposerState; struct DisplayState; struct DisplayInfo; struct DisplayStatInfo; +struct InputWindowCommands; class LayerDebugInfo; class HdrCapabilities; class IDisplayEventConnection; class IGraphicBufferProducer; class ISurfaceComposerClient; +class IRegionSamplingListener; class Rect; enum class FrameEvent; @@ -68,11 +78,6 @@ eEarlyWakeup = 0x04 }; - enum { - eDisplayIdMain = 0, - eDisplayIdHdmi = 1 - }; - enum Rotation { eRotateNone = 0, eRotate90 = 1, @@ -85,22 +90,11 @@ eVsyncSourceSurfaceFlinger = 1 }; - /* create connection with surface flinger, requires - * ACCESS_SURFACE_FLINGER permission + /* + * Create a connection with SurfaceFlinger. */ virtual sp<ISurfaceComposerClient> createConnection() = 0; - /** create a scoped connection with surface flinger. - * Surfaces produced with this connection will act - * as children of the passed in GBP. That is to say - * SurfaceFlinger will draw them relative and confined to - * drawing of buffers from the layer associated with parent. - * As this is graphically equivalent in reach to just drawing - * pixels into the parent buffers, it requires no special permission. - */ - virtual sp<ISurfaceComposerClient> createScopedConnection( - const sp<IGraphicBufferProducer>& parent) = 0; - /* return an IDisplayEventConnection */ virtual sp<IDisplayEventConnection> createDisplayEventConnection( VsyncSource vsyncSource = eVsyncSourceApp) = 0; @@ -116,14 +110,35 @@ */ virtual void destroyDisplay(const sp<IBinder>& display) = 0; - /* get the token for the existing default displays. possible values - * for id are eDisplayIdMain and eDisplayIdHdmi. + /* get stable IDs for connected physical displays. */ - virtual sp<IBinder> getBuiltInDisplay(int32_t id) = 0; + virtual std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const = 0; + + // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic. + std::optional<PhysicalDisplayId> getInternalDisplayId() const { + const auto displayIds = getPhysicalDisplayIds(); + return displayIds.empty() ? std::nullopt : std::make_optional(displayIds.front()); + } + + /* get token for a physical display given its stable ID obtained via getPhysicalDisplayIds or a + * DisplayEventReceiver hotplug event. + */ + virtual sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const = 0; + + // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic. + sp<IBinder> getInternalDisplayToken() const { + const auto displayId = getInternalDisplayId(); + return displayId ? getPhysicalDisplayToken(*displayId) : nullptr; + } /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */ virtual void setTransactionState(const Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags) = 0; + const Vector<DisplayState>& displays, uint32_t flags, + const sp<IBinder>& applyToken, + const InputWindowCommands& inputWindowCommands, + int64_t desiredPresentTime, + const client_cache_t& uncacheBuffer, + const std::vector<ListenerCallbacks>& listenerCallbacks) = 0; /* signal that we're done booting. * Requires ACCESS_SURFACE_FLINGER permission @@ -157,9 +172,6 @@ virtual status_t getDisplayStats(const sp<IBinder>& display, DisplayStatInfo* stats) = 0; - /* returns display viewport information of the given display */ - virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) = 0; - /* indicates which of the configurations returned by getDisplayInfo is * currently active */ virtual int getActiveConfig(const sp<IBinder>& display) = 0; @@ -170,36 +182,103 @@ virtual status_t getDisplayColorModes(const sp<IBinder>& display, Vector<ui::ColorMode>* outColorModes) = 0; + virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display, + ui::DisplayPrimaries& primaries) = 0; virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& display) = 0; virtual status_t setActiveColorMode(const sp<IBinder>& display, ui::ColorMode colorMode) = 0; - /* Capture the specified screen. requires READ_FRAME_BUFFER permission - * This function will fail if there is a secure window on screen. + /** + * Capture the specified screen. This requires READ_FRAME_BUFFER + * permission. This function will fail if there is a secure window on + * screen. + * + * This function can capture a subregion (the source crop) of the screen. + * The subregion can be optionally rotated. It will also be scaled to + * match the size of the output buffer. + * + * reqDataspace and reqPixelFormat specify the data space and pixel format + * of the buffer. The caller should pick the data space and pixel format + * that it can consume. + * + * sourceCrop is the crop on the logical display. + * + * reqWidth and reqHeight specifies the size of the buffer. When either + * of them is 0, they are set to the size of the logical display viewport. + * + * When useIdentityTransform is true, layer transformations are disabled. + * + * rotation specifies the rotation of the source crop (and the pixels in + * it) around its center. */ virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - bool& outCapturedSecureLayers, Rect sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ, - int32_t maxLayerZ, bool useIdentityTransform, + bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, Rotation rotation = eRotateNone, bool captureSecureLayers = false) = 0; - + /** + * Capture the specified screen. This requires READ_FRAME_BUFFER + * permission. This function will fail if there is a secure window on + * screen. + * + * This function can capture a subregion (the source crop) of the screen + * into an sRGB buffer with RGBA_8888 pixel format. + * The subregion can be optionally rotated. It will also be scaled to + * match the size of the output buffer. + * + * At the moment, sourceCrop is ignored and is always set to the visible + * region (projected display viewport) of the screen. + * + * reqWidth and reqHeight specifies the size of the buffer. When either + * of them is 0, they are set to the size of the logical display viewport. + * + * When useIdentityTransform is true, layer transformations are disabled. + * + * rotation specifies the rotation of the source crop (and the pixels in + * it) around its center. + */ virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - Rect sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, int32_t minLayerZ, - int32_t maxLayerZ, bool useIdentityTransform, - Rotation rotation = eRotateNone, - bool captureSecureLayers = false) { - bool ignored; - return captureScreen(display, outBuffer, ignored, sourceCrop, reqWidth, reqHeight, minLayerZ, - maxLayerZ, useIdentityTransform, rotation, captureSecureLayers); + Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, + bool useIdentityTransform, Rotation rotation = eRotateNone) { + bool outIgnored; + return captureScreen(display, outBuffer, outIgnored, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, sourceCrop, reqWidth, reqHeight, + useIdentityTransform, rotation); } + + virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, + sp<GraphicBuffer>* outBuffer) = 0; + + template <class AA> + struct SpHash { + size_t operator()(const sp<AA>& k) const { return std::hash<AA*>()(k.get()); } + }; + /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. + * + * reqDataspace and reqPixelFormat specify the data space and pixel format + * of the buffer. The caller should pick the data space and pixel format + * that it can consume. */ - virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder, - sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop, - float frameScale = 1.0, bool childrenOnly = false) = 0; + virtual status_t captureLayers( + const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, + const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, + const Rect& sourceCrop, + const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeHandles, + float frameScale = 1.0, bool childrenOnly = false) = 0; + + /** + * Capture a subtree of the layer hierarchy into an sRGB buffer with RGBA_8888 pixel format, + * potentially ignoring the root node. + */ + status_t captureLayers(const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, + const Rect& sourceCrop, float frameScale = 1.0, + bool childrenOnly = false) { + return captureLayers(layerHandleBinder, outBuffer, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, sourceCrop, {}, frameScale, childrenOnly); + } /* Clears the frame statistics for animations. * @@ -229,29 +308,156 @@ * Requires the ACCESS_SURFACE_FLINGER permission. */ virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0; + + virtual status_t getColorManagement(bool* outGetColorManagement) const = 0; + + /* Gets the composition preference of the default data space and default pixel format, + * as well as the wide color gamut data space and wide color gamut pixel format. + * If the wide color gamut data space is V0_SRGB, then it implies that the platform + * has no wide color gamut support. + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t getCompositionPreference(ui::Dataspace* defaultDataspace, + ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, + ui::PixelFormat* wideColorGamutPixelFormat) const = 0; + /* + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display, + ui::PixelFormat* outFormat, + ui::Dataspace* outDataspace, + uint8_t* outComponentMask) const = 0; + + /* Turns on the color sampling engine on the display. + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, + uint8_t componentMask, + uint64_t maxFrames) const = 0; + + /* Returns statistics on the color profile of the last frame displayed for a given display + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames, + uint64_t timestamp, + DisplayedFrameStats* outStats) const = 0; + + /* + * Gets whether SurfaceFlinger can support protected content in GPU composition. + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t getProtectedContentSupport(bool* outSupported) const = 0; + + /* + * Queries whether the given display is a wide color display. + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t isWideColorDisplay(const sp<IBinder>& token, + bool* outIsWideColorDisplay) const = 0; + + /* Registers a listener to stream median luma updates from SurfaceFlinger. + * + * The sampling area is bounded by both samplingArea and the given stopLayerHandle + * (i.e., only layers behind the stop layer will be captured and sampled). + * + * Multiple listeners may be provided so long as they have independent listeners. + * If multiple listeners are provided, the effective sampling region for each listener will + * be bounded by whichever stop layer has a lower Z value. + * + * Requires the same permissions as captureLayers and captureScreen. + */ + virtual status_t addRegionSamplingListener(const Rect& samplingArea, + const sp<IBinder>& stopLayerHandle, + const sp<IRegionSamplingListener>& listener) = 0; + + /* + * Removes a listener that was streaming median luma updates from SurfaceFlinger. + */ + virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0; + + /* + * Sets the allowed display configurations to be used. + * The allowedConfigs in a vector of indexes corresponding to the configurations + * returned from getDisplayConfigs(). + */ + virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken, + const std::vector<int32_t>& allowedConfigs) = 0; + + /* + * Returns the allowed display configurations currently set. + * The allowedConfigs in a vector of indexes corresponding to the configurations + * returned from getDisplayConfigs(). + */ + virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken, + std::vector<int32_t>* outAllowedConfigs) = 0; + /* + * Gets whether brightness operations are supported on a display. + * + * displayToken + * The token of the display. + * outSupport + * An output parameter for whether brightness operations are supported. + * + * Returns NO_ERROR upon success. Otherwise, + * NAME_NOT_FOUND if the display is invalid, or + * BAD_VALUE if the output parameter is invalid. + */ + virtual status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, + bool* outSupport) const = 0; + + /* + * Sets the brightness of a display. + * + * displayToken + * The token of the display whose brightness is set. + * brightness + * A number between 0.0f (minimum brightness) and 1.0 (maximum brightness), or -1.0f to + * turn the backlight off. + * + * Returns NO_ERROR upon success. Otherwise, + * NAME_NOT_FOUND if the display is invalid, or + * BAD_VALUE if the brightness is invalid, or + * INVALID_OPERATION if brightness operations are not supported. + */ + virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, + float brightness) const = 0; + + /* + * Sends a power hint to the composer. This function is asynchronous. + * + * hintId + * hint id according to android::hardware::power::V1_0::PowerHint + * + * Returns NO_ERROR upon success. + */ + virtual status_t notifyPowerHint(int32_t hintId) = 0; }; // ---------------------------------------------------------------------------- class BnSurfaceComposer: public BnInterface<ISurfaceComposer> { public: - enum { + enum ISurfaceComposerTag { // Note: BOOT_FINISHED must remain this value, it is called from // Java by ActivityManagerService. BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION, CREATE_CONNECTION, - UNUSED, // formerly CREATE_GRAPHIC_BUFFER_ALLOC + CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED, // unused, fails permissions check CREATE_DISPLAY_EVENT_CONNECTION, CREATE_DISPLAY, DESTROY_DISPLAY, - GET_BUILT_IN_DISPLAY, + GET_PHYSICAL_DISPLAY_TOKEN, SET_TRANSACTION_STATE, AUTHENTICATE_SURFACE, GET_SUPPORTED_FRAME_TIMESTAMPS, GET_DISPLAY_CONFIGS, GET_ACTIVE_CONFIG, SET_ACTIVE_CONFIG, - CONNECT_DISPLAY, + CONNECT_DISPLAY_UNUSED, // unused, fails permissions check CAPTURE_SCREEN, CAPTURE_LAYERS, CLEAR_ANIMATION_FRAME_STATS, @@ -265,8 +471,24 @@ ENABLE_VSYNC_INJECTIONS, INJECT_VSYNC, GET_LAYER_DEBUG_INFO, - CREATE_SCOPED_CONNECTION, - GET_DISPLAY_VIEWPORT + GET_COMPOSITION_PREFERENCE, + GET_COLOR_MANAGEMENT, + GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES, + SET_DISPLAY_CONTENT_SAMPLING_ENABLED, + GET_DISPLAYED_CONTENT_SAMPLE, + GET_PROTECTED_CONTENT_SUPPORT, + IS_WIDE_COLOR_DISPLAY, + GET_DISPLAY_NATIVE_PRIMARIES, + GET_PHYSICAL_DISPLAY_IDS, + ADD_REGION_SAMPLING_LISTENER, + REMOVE_REGION_SAMPLING_LISTENER, + SET_ALLOWED_DISPLAY_CONFIGS, + GET_ALLOWED_DISPLAY_CONFIGS, + GET_DISPLAY_BRIGHTNESS_SUPPORT, + SET_DISPLAY_BRIGHTNESS, + CAPTURE_SCREEN_BY_ID, + NOTIFY_POWER_HINT, + // Always append new enum to the end. }; virtual status_t onTransact(uint32_t code, const Parcel& data,
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h index 8dfc99a..32ac9e8 100644 --- a/libs/gui/include/gui/ISurfaceComposerClient.h +++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -18,8 +18,11 @@ #include <binder/IInterface.h> #include <binder/SafeInterface.h> +#include <gui/LayerMetadata.h> #include <ui/PixelFormat.h> +#include <unordered_map> + namespace android { class FrameStats; @@ -40,8 +43,10 @@ eProtectedByDRM = 0x00001000, eCursorWindow = 0x00002000, - eFXSurfaceNormal = 0x00000000, + eFXSurfaceBufferQueue = 0x00000000, eFXSurfaceColor = 0x00020000, + eFXSurfaceBufferState = 0x00040000, + eFXSurfaceContainer = 0x00080000, eFXSurfaceMask = 0x000F0000, }; @@ -49,14 +54,18 @@ * Requires ACCESS_SURFACE_FLINGER permission */ virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format, - uint32_t flags, const sp<IBinder>& parent, int32_t windowType, - int32_t ownerUid, sp<IBinder>* handle, + uint32_t flags, const sp<IBinder>& parent, + LayerMetadata metadata, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp) = 0; /* * Requires ACCESS_SURFACE_FLINGER permission */ - virtual status_t destroySurface(const sp<IBinder>& handle) = 0; + virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags, + const sp<IGraphicBufferProducer>& parent, + LayerMetadata metadata, sp<IBinder>* handle, + sp<IGraphicBufferProducer>* gbp) = 0; /* * Requires ACCESS_SURFACE_FLINGER permission
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h new file mode 100644 index 0000000..774ad46 --- /dev/null +++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -0,0 +1,113 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <binder/SafeInterface.h> + +#include <ui/Fence.h> +#include <utils/Timers.h> + +#include <cstdint> +#include <unordered_map> +#include <unordered_set> + +namespace android { + +class ITransactionCompletedListener; + +using CallbackId = int64_t; + +class SurfaceStats : public Parcelable { +public: + status_t writeToParcel(Parcel* output) const override; + status_t readFromParcel(const Parcel* input) override; + + SurfaceStats() = default; + SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence) + : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {} + + sp<IBinder> surfaceControl; + nsecs_t acquireTime = -1; + sp<Fence> previousReleaseFence; +}; + +class TransactionStats : public Parcelable { +public: + status_t writeToParcel(Parcel* output) const override; + status_t readFromParcel(const Parcel* input) override; + + TransactionStats() = default; + TransactionStats(const std::vector<CallbackId>& ids) : callbackIds(ids) {} + TransactionStats(const std::unordered_set<CallbackId>& ids) + : callbackIds(ids.begin(), ids.end()) {} + TransactionStats(const std::vector<CallbackId>& ids, nsecs_t latch, const sp<Fence>& present, + const std::vector<SurfaceStats>& surfaces) + : callbackIds(ids), latchTime(latch), presentFence(present), surfaceStats(surfaces) {} + + std::vector<CallbackId> callbackIds; + nsecs_t latchTime = -1; + sp<Fence> presentFence = nullptr; + std::vector<SurfaceStats> surfaceStats; +}; + +class ListenerStats : public Parcelable { +public: + status_t writeToParcel(Parcel* output) const override; + status_t readFromParcel(const Parcel* input) override; + + static ListenerStats createEmpty(const sp<ITransactionCompletedListener>& listener, + const std::unordered_set<CallbackId>& callbackIds); + + sp<ITransactionCompletedListener> listener; + std::vector<TransactionStats> transactionStats; +}; + +class ITransactionCompletedListener : public IInterface { +public: + DECLARE_META_INTERFACE(TransactionCompletedListener) + + virtual void onTransactionCompleted(ListenerStats stats) = 0; +}; + +class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> { +public: + BnTransactionCompletedListener() + : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {} + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0) override; +}; + +class ListenerCallbacks { +public: + ListenerCallbacks(const sp<ITransactionCompletedListener>& listener, + const std::unordered_set<CallbackId>& callbacks) + : transactionCompletedListener(listener), + callbackIds(callbacks.begin(), callbacks.end()) {} + + ListenerCallbacks(const sp<ITransactionCompletedListener>& listener, + const std::vector<CallbackId>& ids) + : transactionCompletedListener(listener), callbackIds(ids) {} + + sp<ITransactionCompletedListener> transactionCompletedListener; + std::vector<CallbackId> callbackIds; +}; + +} // namespace android
diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h index 92bd8c5..66a7b4d 100644 --- a/libs/gui/include/gui/LayerDebugInfo.h +++ b/libs/gui/include/gui/LayerDebugInfo.h
@@ -52,7 +52,6 @@ int32_t mWidth = -1; int32_t mHeight = -1; Rect mCrop = Rect::INVALID_RECT; - Rect mFinalCrop = Rect::INVALID_RECT; half4 mColor = half4(1.0_hf, 1.0_hf, 1.0_hf, 0.0_hf); uint32_t mFlags = 0; PixelFormat mPixelFormat = PIXEL_FORMAT_NONE;
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h new file mode 100644 index 0000000..47f0ced --- /dev/null +++ b/libs/gui/include/gui/LayerMetadata.h
@@ -0,0 +1,51 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/Parcelable.h> + +#include <unordered_map> + +namespace android { + +enum { METADATA_OWNER_UID = 1, METADATA_WINDOW_TYPE = 2, METADATA_TASK_ID = 3 }; + +struct LayerMetadata : public Parcelable { + std::unordered_map<uint32_t, std::vector<uint8_t>> mMap; + + LayerMetadata(); + LayerMetadata(const LayerMetadata& other); + LayerMetadata(LayerMetadata&& other); + explicit LayerMetadata(std::unordered_map<uint32_t, std::vector<uint8_t>> map); + LayerMetadata& operator=(const LayerMetadata& other); + LayerMetadata& operator=(LayerMetadata&& other); + + // Merges other into this LayerMetadata. If eraseEmpty is true, any entries in + // in this whose keys are paired with empty values in other will be erased. + bool merge(const LayerMetadata& other, bool eraseEmpty = false); + + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + + bool has(uint32_t key) const; + int32_t getInt32(uint32_t key, int32_t fallback) const; + void setInt32(uint32_t key, int32_t value); + + std::string itemToString(uint32_t key, const char* separator) const; +}; + +} // namespace android
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 788962e..f438eb3 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h
@@ -22,127 +22,211 @@ #include <utils/Errors.h> -#include <ui/Region.h> -#include <ui/Rect.h> #include <gui/IGraphicBufferProducer.h> +#include <math/mat4.h> + +#ifndef NO_INPUT +#include <input/InputWindow.h> +#endif + +#include <gui/LayerMetadata.h> #include <math/vec3.h> +#include <ui/GraphicTypes.h> +#include <ui/Rect.h> +#include <ui/Region.h> namespace android { class Parcel; class ISurfaceComposerClient; +struct client_cache_t { + wp<IBinder> token = nullptr; + uint64_t id; + + bool operator==(const client_cache_t& other) const { return id == other.id; } + + bool isValid() const { return token != nullptr; } +}; + /* * Used to communicate layer information between SurfaceFlinger and its clients. */ struct layer_state_t { - - enum { - eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java - eLayerOpaque = 0x02, // SURFACE_OPAQUE - eLayerSecure = 0x80, // SECURE + eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java + eLayerOpaque = 0x02, // SURFACE_OPAQUE + eLayerSecure = 0x80, // SECURE }; enum { - ePositionChanged = 0x00000001, - eLayerChanged = 0x00000002, - eSizeChanged = 0x00000004, - eAlphaChanged = 0x00000008, - eMatrixChanged = 0x00000010, - eTransparentRegionChanged = 0x00000020, - eFlagsChanged = 0x00000040, - eLayerStackChanged = 0x00000080, - eCropChanged = 0x00000100, - eDeferTransaction = 0x00000200, - eFinalCropChanged = 0x00000400, - eOverrideScalingModeChanged = 0x00000800, - eGeometryAppliesWithResize = 0x00001000, - eReparentChildren = 0x00002000, - eDetachChildren = 0x00004000, - eRelativeLayerChanged = 0x00008000, - eReparent = 0x00010000, - eColorChanged = 0x00020000, - eDestroySurface = 0x00040000 + ePositionChanged = 0x00000001, + eLayerChanged = 0x00000002, + eSizeChanged = 0x00000004, + eAlphaChanged = 0x00000008, + eMatrixChanged = 0x00000010, + eTransparentRegionChanged = 0x00000020, + eFlagsChanged = 0x00000040, + eLayerStackChanged = 0x00000080, + eCropChanged_legacy = 0x00000100, + eDeferTransaction_legacy = 0x00000200, + eOverrideScalingModeChanged = 0x00000400, + eGeometryAppliesWithResize = 0x00000800, + eReparentChildren = 0x00001000, + eDetachChildren = 0x00002000, + eRelativeLayerChanged = 0x00004000, + eReparent = 0x00008000, + eColorChanged = 0x00010000, + eDestroySurface = 0x00020000, + eTransformChanged = 0x00040000, + eTransformToDisplayInverseChanged = 0x00080000, + eCropChanged = 0x00100000, + eBufferChanged = 0x00200000, + eAcquireFenceChanged = 0x00400000, + eDataspaceChanged = 0x00800000, + eHdrMetadataChanged = 0x01000000, + eSurfaceDamageRegionChanged = 0x02000000, + eApiChanged = 0x04000000, + eSidebandStreamChanged = 0x08000000, + eColorTransformChanged = 0x10000000, + eHasListenerCallbacksChanged = 0x20000000, + eInputInfoChanged = 0x40000000, + eCornerRadiusChanged = 0x80000000, + eFrameChanged = 0x1'00000000, + eCachedBufferChanged = 0x2'00000000, + eBackgroundColorChanged = 0x4'00000000, + eMetadataChanged = 0x8'00000000, + eColorSpaceAgnosticChanged = 0x10'00000000, }; layer_state_t() - : what(0), - x(0), y(0), z(0), w(0), h(0), layerStack(0), - alpha(0), flags(0), mask(0), - reserved(0), crop(Rect::INVALID_RECT), - finalCrop(Rect::INVALID_RECT), frameNumber(0), - overrideScalingMode(-1) - { + : what(0), + x(0), + y(0), + z(0), + w(0), + h(0), + layerStack(0), + alpha(0), + flags(0), + mask(0), + reserved(0), + crop_legacy(Rect::INVALID_RECT), + cornerRadius(0.0f), + frameNumber_legacy(0), + overrideScalingMode(-1), + transform(0), + transformToDisplayInverse(false), + crop(Rect::INVALID_RECT), + frame(Rect::INVALID_RECT), + dataspace(ui::Dataspace::UNKNOWN), + surfaceDamageRegion(), + api(-1), + colorTransform(mat4()), + hasListenerCallbacks(false), + bgColorAlpha(0), + bgColorDataspace(ui::Dataspace::UNKNOWN), + colorSpaceAgnostic(false) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; + hdrMetadata.validTypes = 0; } void merge(const layer_state_t& other); - status_t write(Parcel& output) const; - status_t read(const Parcel& input); + status_t write(Parcel& output) const; + status_t read(const Parcel& input); - struct matrix22_t { - float dsdx{0}; - float dtdx{0}; - float dtdy{0}; - float dsdy{0}; - }; - sp<IBinder> surface; - uint32_t what; - float x; - float y; - int32_t z; - uint32_t w; - uint32_t h; - uint32_t layerStack; - float alpha; - uint8_t flags; - uint8_t mask; - uint8_t reserved; - matrix22_t matrix; - Rect crop; - Rect finalCrop; - sp<IBinder> barrierHandle; - sp<IBinder> reparentHandle; - uint64_t frameNumber; - int32_t overrideScalingMode; + struct matrix22_t { + float dsdx{0}; + float dtdx{0}; + float dtdy{0}; + float dsdy{0}; + }; + sp<IBinder> surface; + uint64_t what; + float x; + float y; + int32_t z; + uint32_t w; + uint32_t h; + uint32_t layerStack; + float alpha; + uint8_t flags; + uint8_t mask; + uint8_t reserved; + matrix22_t matrix; + Rect crop_legacy; + float cornerRadius; + sp<IBinder> barrierHandle_legacy; + sp<IBinder> reparentHandle; + uint64_t frameNumber_legacy; + int32_t overrideScalingMode; - sp<IGraphicBufferProducer> barrierGbp; + sp<IGraphicBufferProducer> barrierGbp_legacy; - sp<IBinder> relativeLayerHandle; + sp<IBinder> relativeLayerHandle; - sp<IBinder> parentHandleForChild; + sp<IBinder> parentHandleForChild; - half3 color; + half3 color; - // non POD must be last. see write/read - Region transparentRegion; + // non POD must be last. see write/read + Region transparentRegion; + + uint32_t transform; + bool transformToDisplayInverse; + Rect crop; + Rect frame; + sp<GraphicBuffer> buffer; + sp<Fence> acquireFence; + ui::Dataspace dataspace; + HdrMetadata hdrMetadata; + Region surfaceDamageRegion; + int32_t api; + sp<NativeHandle> sidebandStream; + mat4 colorTransform; + + bool hasListenerCallbacks; +#ifndef NO_INPUT + InputWindowInfo inputInfo; +#endif + + client_cache_t cachedBuffer; + + LayerMetadata metadata; + + // The following refer to the alpha, and dataspace, respectively of + // the background color layer + float bgColorAlpha; + ui::Dataspace bgColorDataspace; + + // A color space agnostic layer means the color of this layer can be + // interpreted in any color space. + bool colorSpaceAgnostic; }; struct ComposerState { sp<ISurfaceComposerClient> client; layer_state_t state; - status_t write(Parcel& output) const; - status_t read(const Parcel& input); + status_t write(Parcel& output) const; + status_t read(const Parcel& input); }; struct DisplayState { - enum { - eOrientationDefault = 0, - eOrientation90 = 1, - eOrientation180 = 2, - eOrientation270 = 3, - eOrientationUnchanged = 4, - eOrientationSwapMask = 0x01 + eOrientationDefault = 0, + eOrientation90 = 1, + eOrientation180 = 2, + eOrientation270 = 3, + eOrientationUnchanged = 4, + eOrientationSwapMask = 0x01 }; enum { - eSurfaceChanged = 0x01, - eLayerStackChanged = 0x02, - eDisplayProjectionChanged = 0x04, - eDisplaySizeChanged = 0x08 + eSurfaceChanged = 0x01, + eLayerStackChanged = 0x02, + eDisplayProjectionChanged = 0x04, + eDisplaySizeChanged = 0x08 }; DisplayState(); @@ -152,29 +236,55 @@ sp<IBinder> token; sp<IGraphicBufferProducer> surface; uint32_t layerStack; + + // These states define how layers are projected onto the physical display. + // + // Layers are first clipped to `viewport'. They are then translated and + // scaled from `viewport' to `frame'. Finally, they are rotated according + // to `orientation', `width', and `height'. + // + // For example, assume viewport is Rect(0, 0, 200, 100), frame is Rect(20, + // 10, 420, 210), and the size of the display is WxH. When orientation is + // 0, layers will be scaled by a factor of 2 and translated by (20, 10). + // When orientation is 1, layers will be additionally rotated by 90 + // degrees around the origin clockwise and translated by (W, 0). uint32_t orientation; Rect viewport; Rect frame; + uint32_t width, height; + status_t write(Parcel& output) const; status_t read(const Parcel& input); }; -static inline -int compare_type(const ComposerState& lhs, const ComposerState& rhs) { +struct InputWindowCommands { + struct TransferTouchFocusCommand { + sp<IBinder> fromToken; + sp<IBinder> toToken; + }; + + std::vector<TransferTouchFocusCommand> transferTouchFocusCommands; + bool syncInputWindows{false}; + + void merge(const InputWindowCommands& other); + void clear(); + void write(Parcel& output) const; + void read(const Parcel& input); +}; + +static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) { if (lhs.client < rhs.client) return -1; if (lhs.client > rhs.client) return 1; - if (lhs.state.surface < rhs.state.surface) return -1; - if (lhs.state.surface > rhs.state.surface) return 1; + if (lhs.state.surface < rhs.state.surface) return -1; + if (lhs.state.surface > rhs.state.surface) return 1; return 0; } -static inline -int compare_type(const DisplayState& lhs, const DisplayState& rhs) { +static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs) { return compare_type(lhs.token, rhs.token); } }; // namespace android #endif // ANDROID_SF_LAYER_STATE_H -
diff --git a/libs/gui/include/gui/StreamSplitter.h b/libs/gui/include/gui/StreamSplitter.h index 8f47eb4..b4eef29 100644 --- a/libs/gui/include/gui/StreamSplitter.h +++ b/libs/gui/include/gui/StreamSplitter.h
@@ -128,7 +128,7 @@ class BufferTracker : public LightRefBase<BufferTracker> { public: - BufferTracker(const sp<GraphicBuffer>& buffer); + explicit BufferTracker(const sp<GraphicBuffer>& buffer); const sp<GraphicBuffer>& getBuffer() const { return mBuffer; } const sp<Fence>& getMergedFence() const { return mMergedFence; } @@ -154,7 +154,7 @@ }; // Only called from createSplitter - StreamSplitter(const sp<IGraphicBufferConsumer>& inputQueue); + explicit StreamSplitter(const sp<IGraphicBufferConsumer>& inputQueue); // Must be accessed through RefBase virtual ~StreamSplitter();
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 9aeafae..0c471bb 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h
@@ -80,7 +80,7 @@ /* convenience function to check that the given surface is non NULL as * well as its IGraphicBufferProducer */ static bool isValid(const sp<Surface>& surface) { - return surface != NULL && surface->getIGraphicBufferProducer() != NULL; + return surface != nullptr && surface->getIGraphicBufferProducer() != nullptr; } /* Attaches a sideband buffer stream to the Surface's IGraphicBufferProducer. @@ -218,6 +218,7 @@ int dispatchSetBuffersDataSpace(va_list args); int dispatchSetBuffersSmpte2086Metadata(va_list args); int dispatchSetBuffersCta8613Metadata(va_list args); + int dispatchSetBuffersHdr10PlusMetadata(va_list args); int dispatchSetSurfaceDamage(va_list args); int dispatchSetSharedBufferMode(va_list args); int dispatchSetAutoRefresh(va_list args); @@ -229,6 +230,7 @@ int dispatchGetWideColorSupport(va_list args); int dispatchGetHdrSupport(va_list args); int dispatchGetConsumerUsage64(va_list args); + bool transformToDisplayInverse(); protected: virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); @@ -249,6 +251,7 @@ virtual int setBuffersDataSpace(ui::Dataspace dataSpace); virtual int setBuffersSmpte2086Metadata(const android_smpte2086_metadata* metadata); virtual int setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata); + virtual int setBuffersHdr10PlusMetadata(const size_t size, const uint8_t* metadata); virtual int setCrop(Rect const* rect); virtual int setUsage(uint64_t reqUsage); virtual void setSurfaceDamage(android_native_rect_t* rects, size_t numRects);
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 49bb687..0e17c7b 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -19,7 +19,9 @@ #include <stdint.h> #include <sys/types.h> +#include <set> #include <unordered_map> +#include <unordered_set> #include <binder/IBinder.h> @@ -28,14 +30,18 @@ #include <utils/SortedVector.h> #include <utils/threads.h> +#include <ui/ConfigStoreTypes.h> +#include <ui/DisplayedFrameStats.h> #include <ui/FrameStats.h> #include <ui/GraphicTypes.h> #include <ui/PixelFormat.h> #include <gui/CpuConsumer.h> +#include <gui/ISurfaceComposer.h> +#include <gui/ITransactionCompletedListener.h> +#include <gui/LayerState.h> #include <gui/SurfaceControl.h> #include <math/vec3.h> -#include <gui/LayerState.h> namespace android { @@ -45,17 +51,37 @@ class HdrCapabilities; class ISurfaceComposerClient; class IGraphicBufferProducer; +class IRegionSamplingListener; class Region; // --------------------------------------------------------------------------- +struct SurfaceControlStats { + SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t time, + const sp<Fence>& prevReleaseFence) + : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {} + + sp<SurfaceControl> surfaceControl; + nsecs_t acquireTime = -1; + sp<Fence> previousReleaseFence; +}; + +using TransactionCompletedCallbackTakesContext = + std::function<void(void* /*context*/, nsecs_t /*latchTime*/, + const sp<Fence>& /*presentFence*/, + const std::vector<SurfaceControlStats>& /*stats*/)>; +using TransactionCompletedCallback = + std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/, + const std::vector<SurfaceControlStats>& /*stats*/)>; + +// --------------------------------------------------------------------------- + class SurfaceComposerClient : public RefBase { friend class Composer; public: SurfaceComposerClient(); SurfaceComposerClient(const sp<ISurfaceComposerClient>& client); - SurfaceComposerClient(const sp<IGraphicBufferProducer>& parent); virtual ~SurfaceComposerClient(); // Always make sure we could initialize @@ -69,7 +95,7 @@ // callback when the composer is dies status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient, - void* cookie = NULL, uint32_t flags = 0); + void* cookie = nullptr, uint32_t flags = 0); // Get a list of supported configurations for a given display static status_t getDisplayConfigs(const sp<IBinder>& display, @@ -79,9 +105,6 @@ static status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info); - // Get the display viewport for the given display - static status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport); - // Get the index of the current active configuration (relative to the list // returned by getDisplayInfo) static int getActiveConfig(const sp<IBinder>& display); @@ -90,10 +113,26 @@ // returned by getDisplayInfo static status_t setActiveConfig(const sp<IBinder>& display, int id); + // Sets the allowed display configurations to be used. + // The allowedConfigs in a vector of indexes corresponding to the configurations + // returned from getDisplayConfigs(). + static status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken, + const std::vector<int32_t>& allowedConfigs); + + // Returns the allowed display configurations currently set. + // The allowedConfigs in a vector of indexes corresponding to the configurations + // returned from getDisplayConfigs(). + static status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken, + std::vector<int32_t>* outAllowedConfigs); + // Gets the list of supported color modes for the given display static status_t getDisplayColorModes(const sp<IBinder>& display, Vector<ui::ColorMode>* outColorModes); + // Get the coordinates of the display's native color primaries + static status_t getDisplayNativePrimaries(const sp<IBinder>& display, + ui::DisplayPrimaries& outPrimaries); + // Gets the active color mode for the given display static ui::ColorMode getActiveColorMode(const sp<IBinder>& display); @@ -104,31 +143,107 @@ /* Triggers screen on/off or low power mode and waits for it to complete */ static void setDisplayPowerMode(const sp<IBinder>& display, int mode); + /* Returns the composition preference of the default data space and default pixel format, + * as well as the wide color gamut data space and wide color gamut pixel format. + * If the wide color gamut data space is V0_SRGB, then it implies that the platform + * has no wide color gamut support. + */ + static status_t getCompositionPreference(ui::Dataspace* defaultDataspace, + ui::PixelFormat* defaultPixelFormat, + ui::Dataspace* wideColorGamutDataspace, + ui::PixelFormat* wideColorGamutPixelFormat); + + /* + * Gets whether SurfaceFlinger can support protected content in GPU composition. + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + static bool getProtectedContentSupport(); + + /** + * Called from SurfaceControl d'tor to 'destroy' the surface (or rather, reparent it + * to null), but without needing an sp<SurfaceControl> to avoid infinite ressurection. + */ + static void doDropReferenceTransaction(const sp<IBinder>& handle, + const sp<ISurfaceComposerClient>& client); + + /** + * Uncaches a buffer in ISurfaceComposer. It must be uncached via a transaction so that it is + * in order with other transactions that use buffers. + */ + static void doUncacheBufferTransaction(uint64_t cacheId); + + // Queries whether a given display is wide color display. + static status_t isWideColorDisplay(const sp<IBinder>& display, bool* outIsWideColorDisplay); + + /* + * Returns whether brightness operations are supported on a display. + * + * displayToken + * The token of the display. + * + * Returns whether brightness operations are supported on a display or not. + */ + static bool getDisplayBrightnessSupport(const sp<IBinder>& displayToken); + + /* + * Sets the brightness of a display. + * + * displayToken + * The token of the display whose brightness is set. + * brightness + * A number between 0.0 (minimum brightness) and 1.0 (maximum brightness), or -1.0f to + * turn the backlight off. + * + * Returns NO_ERROR upon success. Otherwise, + * NAME_NOT_FOUND if the display handle is invalid, or + * BAD_VALUE if the brightness value is invalid, or + * INVALID_OPERATION if brightness operaetions are not supported. + */ + static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness); + + /* + * Sends a power hint to the composer. This function is asynchronous. + * + * hintId + * hint id according to android::hardware::power::V1_0::PowerHint + * + * Returns NO_ERROR upon success. + */ + static status_t notifyPowerHint(int32_t hintId); + // ------------------------------------------------------------------------ // surface creation / destruction + 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 - int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.) - int32_t ownerUid = -1 // UID of the task + 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 ); - status_t createSurfaceChecked( - const String8& name,// name of the surface - uint32_t w, // width in pixel - uint32_t h, // height in pixel - PixelFormat format, // pixel-format desired - sp<SurfaceControl>* outSurface, - uint32_t flags = 0, // usage flags - SurfaceControl* parent = nullptr, // parent - int32_t windowType = -1, // from WindowManager.java (STATUS_BAR, INPUT_METHOD, etc.) - int32_t ownerUid = -1 // UID of the task + status_t createSurfaceChecked(const String8& name, // name of the surface + uint32_t w, // width in pixel + 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 + ); + + //! Create a surface + sp<SurfaceControl> createWithSurfaceParent(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 + Surface* parent = nullptr, // parent + LayerMetadata metadata = LayerMetadata() // metadata ); //! Create a virtual display @@ -137,9 +252,13 @@ //! Destroy a virtual display static void destroyDisplay(const sp<IBinder>& display); - //! Get the token for the existing default displays. - //! Possible values for id are eDisplayIdMain and eDisplayIdHdmi. - static sp<IBinder> getBuiltInDisplay(int32_t id); + //! Get stable IDs for connected physical displays + static std::vector<PhysicalDisplayId> getPhysicalDisplayIds(); + static std::optional<PhysicalDisplayId> getInternalDisplayId(); + + //! Get token for a physical display given its stable ID + static sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId); + static sp<IBinder> getInternalDisplayToken(); static status_t enableVSyncInjections(bool enable); @@ -151,19 +270,56 @@ } }; + struct TCLHash { + std::size_t operator()(const sp<ITransactionCompletedListener>& tcl) const { + return std::hash<IBinder*>{}((tcl) ? IInterface::asBinder(tcl).get() : nullptr); + } + }; + + struct CallbackInfo { + // All the callbacks that have been requested for a TransactionCompletedListener in the + // Transaction + std::unordered_set<CallbackId> callbackIds; + // All the SurfaceControls that have been modified in this TransactionCompletedListener's + // process that require a callback if there is one or more callbackIds set. + std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls; + }; + class Transaction { std::unordered_map<sp<SurfaceControl>, ComposerState, SCHash> mComposerStates; SortedVector<DisplayState > mDisplayStates; + std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> + mListenerCallbacks; + uint32_t mForceSynchronous = 0; uint32_t mTransactionNestCount = 0; bool mAnimation = false; bool mEarlyWakeup = false; + // Indicates that the Transaction contains a buffer that should be cached + bool mContainsBuffer = false; + + // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction + // to be presented. When it is not possible to present at exactly that time, it will be + // presented after the time has passed. + // + // Desired present times that are more than 1 second in the future may be ignored. + // When a desired present time has already passed, the transaction will be presented as soon + // as possible. + // + // Transactions from the same process are presented in the same order that they are applied. + // The desired present time does not affect this ordering. + int64_t mDesiredPresentTime = -1; + + InputWindowCommands mInputWindowCommands; int mStatus = NO_ERROR; layer_state_t* getLayerState(const sp<SurfaceControl>& sc); DisplayState& getDisplayState(const sp<IBinder>& token); + void cacheBuffers(); + void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc); + public: Transaction() = default; virtual ~Transaction() = default; @@ -203,22 +359,23 @@ float alpha); Transaction& setMatrix(const sp<SurfaceControl>& sc, float dsdx, float dtdx, float dtdy, float dsdy); - Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop); - Transaction& setFinalCrop(const sp<SurfaceControl>& sc, const Rect& crop); + Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop); + Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius); Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack); + Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, + std::vector<uint8_t> data); // Defers applying any changes made in this transaction until the Layer // identified by handle reaches the given frameNumber. If the Layer identified // by handle is removed, then we will apply this transaction regardless of // what frame number has been reached. - Transaction& deferTransactionUntil(const sp<SurfaceControl>& sc, - const sp<IBinder>& handle, - uint64_t frameNumber); - // A variant of deferTransactionUntil which identifies the Layer we wait for by + Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, + const sp<IBinder>& handle, uint64_t frameNumber); + // A variant of deferTransactionUntil_legacy which identifies the Layer we wait for by // Surface instead of Handle. Useful for clients which may not have the // SurfaceControl for some of their Surfaces. Otherwise behaves identically. - Transaction& deferTransactionUntil(const sp<SurfaceControl>& sc, - const sp<Surface>& barrierSurface, - uint64_t frameNumber); + Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, + const sp<Surface>& barrierSurface, + uint64_t frameNumber); // Reparents all children of this layer to the new parent handle. Transaction& reparentChildren(const sp<SurfaceControl>& sc, const sp<IBinder>& newParentHandle); @@ -231,6 +388,31 @@ Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color); + // Sets the background color of a layer with the specified color, alpha, and dataspace + Transaction& setBackgroundColor(const sp<SurfaceControl>& sc, const half3& color, + float alpha, ui::Dataspace dataspace); + + Transaction& setTransform(const sp<SurfaceControl>& sc, uint32_t transform); + Transaction& setTransformToDisplayInverse(const sp<SurfaceControl>& sc, + bool transformToDisplayInverse); + Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop); + Transaction& setFrame(const sp<SurfaceControl>& sc, const Rect& frame); + Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer); + Transaction& setCachedBuffer(const sp<SurfaceControl>& sc, int32_t bufferId); + Transaction& setAcquireFence(const sp<SurfaceControl>& sc, const sp<Fence>& fence); + Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace); + Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata); + Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc, + const Region& surfaceDamageRegion); + Transaction& setApi(const sp<SurfaceControl>& sc, int32_t api); + Transaction& setSidebandStream(const sp<SurfaceControl>& sc, + const sp<NativeHandle>& sidebandStream); + Transaction& setDesiredPresentTime(nsecs_t desiredPresentTime); + Transaction& setColorSpaceAgnostic(const sp<SurfaceControl>& sc, const bool agnostic); + + Transaction& addTransactionCompletedCallback( + TransactionCompletedCallbackTakesContext callback, void* callbackContext); + // Detaches all child surfaces (and their children recursively) // from their SurfaceControl. // The child SurfaceControls will not throw exceptions or return errors, @@ -254,7 +436,18 @@ // freezing the total geometry of a surface until a resize is completed. Transaction& setGeometryAppliesWithResize(const sp<SurfaceControl>& sc); - Transaction& destroySurface(const sp<SurfaceControl>& sc); +#ifndef NO_INPUT + Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info); + Transaction& transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken); + Transaction& syncInputWindows(); +#endif + + // Set a color transform matrix on the given layer on the built-in display. + Transaction& setColorTransform(const sp<SurfaceControl>& sc, const mat3& matrix, + const vec3& translation); + + Transaction& setGeometry(const sp<SurfaceControl>& sc, + const Rect& source, const Rect& dst, int transform); status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); @@ -280,8 +473,6 @@ void setEarlyWakeup(); }; - status_t destroySurface(const sp<IBinder>& id); - status_t clearLayerFrameStats(const sp<IBinder>& token) const; status_t getLayerFrameStats(const sp<IBinder>& token, FrameStats* outStats) const; static status_t clearAnimationFrameStats(); @@ -297,13 +488,26 @@ inline sp<ISurfaceComposerClient> getClient() { return mClient; } + static status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display, + ui::PixelFormat* outFormat, + ui::Dataspace* outDataspace, + uint8_t* outComponentMask); + static status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, + uint8_t componentMask, uint64_t maxFrames); + + static status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames, + uint64_t timestamp, DisplayedFrameStats* outStats); + static status_t addRegionSamplingListener(const Rect& samplingArea, + const sp<IBinder>& stopLayerHandle, + const sp<IRegionSamplingListener>& listener); + static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener); + private: virtual void onFirstRef(); mutable Mutex mLock; status_t mStatus; sp<ISurfaceComposerClient> mClient; - wp<IGraphicBufferProducer> mParent; }; // --------------------------------------------------------------------------- @@ -312,22 +516,74 @@ public: // if cropping isn't required, callers may pass in a default Rect, e.g.: // capture(display, producer, Rect(), reqWidth, ...); - static status_t capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, - bool useIdentityTransform, uint32_t rotation, - bool captureSecureLayers, sp<GraphicBuffer>* outBuffer, - bool& outCapturedSecureLayers); - static status_t capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, - bool useIdentityTransform, uint32_t rotation, + static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, + uint32_t rotation, bool captureSecureLayers, + sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers); + static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, + uint32_t rotation, sp<GraphicBuffer>* outBuffer); + static status_t capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, sp<GraphicBuffer>* outBuffer); - static status_t captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, float frameScale, - sp<GraphicBuffer>* outBuffer); - static status_t captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, - float frameScale, sp<GraphicBuffer>* outBuffer); + static status_t captureLayers(const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + float frameScale, sp<GraphicBuffer>* outBuffer); + static status_t captureChildLayers( + const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& + excludeHandles, + float frameScale, sp<GraphicBuffer>* outBuffer); }; // --------------------------------------------------------------------------- + +class TransactionCompletedListener : public BnTransactionCompletedListener { + TransactionCompletedListener(); + + CallbackId getNextIdLocked() REQUIRES(mMutex); + + std::mutex mMutex; + + bool mListening GUARDED_BY(mMutex) = false; + + CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1; + + struct IBinderHash { + std::size_t operator()(const sp<IBinder>& iBinder) const { + return std::hash<IBinder*>{}(iBinder.get()); + } + }; + + struct CallbackTranslation { + TransactionCompletedCallback callbackFunction; + std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls; + }; + + std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex); + +public: + static sp<TransactionCompletedListener> getInstance(); + static sp<ITransactionCompletedListener> getIInstance(); + + void startListeningLocked() REQUIRES(mMutex); + + CallbackId addCallbackFunction( + const TransactionCompletedCallback& callbackFunction, + const std::unordered_set<sp<SurfaceControl>, SurfaceComposerClient::SCHash>& + surfaceControls); + + void addSurfaceControlToCallbacks(const sp<SurfaceControl>& surfaceControl, + const std::unordered_set<CallbackId>& callbackIds); + + // Overrides BnTransactionCompletedListener's onTransactionCompleted + void onTransactionCompleted(ListenerStats stats) override; +}; + +// --------------------------------------------------------------------------- + }; // namespace android #endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index 38de299..23bfc02 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h
@@ -48,18 +48,22 @@ void writeToParcel(Parcel* parcel); static bool isValid(const sp<SurfaceControl>& surface) { - return (surface != 0) && surface->isValid(); + return (surface != nullptr) && surface->isValid(); } bool isValid() { - return mHandle!=0 && mClient!=0; + return mHandle!=nullptr && mClient!=nullptr; } static bool isSameSurface( const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs); - // release surface data from java - void clear(); + // Release the handles assosciated with the SurfaceControl, without reparenting + // them off-screen. At the moment if this isn't executed before ~SurfaceControl + // is called then the destructor will reparent the layer off-screen for you. + void release(); + // Reparent off-screen and release. This is invoked by the destructor. + void destroy(); // disconnect any api that's connected void disconnect(); @@ -71,15 +75,18 @@ sp<Surface> createSurface() const; sp<IBinder> getHandle() const; + sp<IGraphicBufferProducer> getIGraphicBufferProducer() const; + status_t clearLayerFrameStats() const; status_t getLayerFrameStats(FrameStats* outStats) const; sp<SurfaceComposerClient> getClient() const; + + explicit SurfaceControl(const sp<SurfaceControl>& other); - SurfaceControl(const sp<SurfaceComposerClient>& client, - const sp<IBinder>& handle, - const sp<IGraphicBufferProducer>& gbp, - bool owned); + SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle, + const sp<IGraphicBufferProducer>& gbp, bool owned); + private: // can't be copied SurfaceControl& operator = (SurfaceControl& rhs); @@ -92,7 +99,6 @@ sp<Surface> generateSurfaceLocked() const; status_t validate() const; - void destroy(); sp<SurfaceComposerClient> mClient; sp<IBinder> mHandle;
diff --git a/libs/gui/include/gui/bufferqueue/1.0/B2HProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/B2HProducerListener.h index fa6c2d9..0f6fb45 100644 --- a/libs/gui/include/gui/bufferqueue/1.0/B2HProducerListener.h +++ b/libs/gui/include/gui/bufferqueue/1.0/B2HProducerListener.h
@@ -50,7 +50,7 @@ struct B2HProducerListener : public HProducerListener { sp<BProducerListener> mBase; - B2HProducerListener(sp<BProducerListener> const& base); + explicit B2HProducerListener(sp<BProducerListener> const& base); Return<void> onBufferReleased() override; Return<bool> needsReleaseNotify() override; };
diff --git a/libs/gui/include/gui/bufferqueue/1.0/Conversion.h b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h new file mode 100644 index 0000000..627845c --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
@@ -0,0 +1,765 @@ +/* + * Copyright 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_CONVERSION_H_ +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_CONVERSION_H_ + +#include <vector> +#include <list> + +#include <unistd.h> + +#include <hidl/MQDescriptor.h> +#include <hidl/Status.h> + +#include <binder/Binder.h> +#include <binder/Status.h> +#include <ui/FenceTime.h> +#include <cutils/native_handle.h> +#include <gui/IGraphicBufferProducer.h> + +#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h> + +namespace android { +namespace conversion { + +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::hidl_handle; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::sp; +using ::android::status_t; + +using ::android::String8; + +using ::android::hardware::media::V1_0::Rect; +using ::android::hardware::media::V1_0::Region; + +using ::android::hardware::graphics::common::V1_0::Dataspace; + +using ::android::hardware::graphics::common::V1_0::PixelFormat; + +using ::android::hardware::media::V1_0::AnwBuffer; +using ::android::GraphicBuffer; + +typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer + HGraphicBufferProducer; +typedef ::android::IGraphicBufferProducer + BGraphicBufferProducer; + +// native_handle_t helper functions. + +/** + * \brief Take an fd and create a native handle containing only the given fd. + * The created handle will need to be deleted manually with + * `native_handle_delete()`. + * + * \param[in] fd The source file descriptor (of type `int`). + * \return The create `native_handle_t*` that contains the given \p fd. If the + * supplied \p fd is negative, the created native handle will contain no file + * descriptors. + * + * If the native handle cannot be created, the return value will be + * `nullptr`. + * + * This function does not duplicate the file descriptor. + */ +native_handle_t* native_handle_create_from_fd(int fd); + +/** + * \brief Extract a file descriptor from a native handle. + * + * \param[in] nh The source `native_handle_t*`. + * \param[in] index The index of the file descriptor in \p nh to read from. This + * input has the default value of `0`. + * \return The `index`-th file descriptor in \p nh. If \p nh does not have + * enough file descriptors, the returned value will be `-1`. + * + * This function does not duplicate the file descriptor. + */ +int native_handle_read_fd(native_handle_t const* nh, int index = 0); + +/** + * Conversion functions + * ==================== + * + * There are two main directions of conversion: + * - `inTargetType(...)`: Create a wrapper whose lifetime depends on the + * input. The wrapper has type `TargetType`. + * - `toTargetType(...)`: Create a standalone object of type `TargetType` that + * corresponds to the input. The lifetime of the output does not depend on the + * lifetime of the input. + * - `wrapIn(TargetType*, ...)`: Same as `inTargetType()`, but for `TargetType` + * that cannot be copied and/or moved efficiently, or when there are multiple + * output arguments. + * - `convertTo(TargetType*, ...)`: Same as `toTargetType()`, but for + * `TargetType` that cannot be copied and/or moved efficiently, or when there + * are multiple output arguments. + * + * `wrapIn()` and `convertTo()` functions will take output arguments before + * input arguments. Some of these functions might return a value to indicate + * success or error. + * + * In converting or wrapping something as a Treble type that contains a + * `hidl_handle`, `native_handle_t*` will need to be created and returned as + * an additional output argument, hence only `wrapIn()` or `convertTo()` would + * be available. The caller must call `native_handle_delete()` to deallocate the + * returned native handle when it is no longer needed. + * + * For types that contain file descriptors, `inTargetType()` and `wrapAs()` do + * not perform duplication of file descriptors, while `toTargetType()` and + * `convertTo()` do. + */ + +/** + * \brief Convert `Return<void>` to `binder::Status`. + * + * \param[in] t The source `Return<void>`. + * \return The corresponding `binder::Status`. + */ +// convert: Return<void> -> ::android::binder::Status +::android::binder::Status toBinderStatus(Return<void> const& t); + +/** + * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls. + * + * \param[in] t The source `Return<void>`. + * \return The corresponding `status_t`. + */ +// convert: Return<void> -> status_t +status_t toStatusT(Return<void> const& t); + +/** + * \brief Wrap `native_handle_t*` in `hidl_handle`. + * + * \param[in] nh The source `native_handle_t*`. + * \return The `hidl_handle` that points to \p nh. + */ +// wrap: native_handle_t* -> hidl_handle +hidl_handle inHidlHandle(native_handle_t const* nh); + +/** + * \brief Convert `int32_t` to `Dataspace`. + * + * \param[in] l The source `int32_t`. + * \result The corresponding `Dataspace`. + */ +// convert: int32_t -> Dataspace +Dataspace toHardwareDataspace(int32_t l); + +/** + * \brief Convert `Dataspace` to `int32_t`. + * + * \param[in] t The source `Dataspace`. + * \result The corresponding `int32_t`. + */ +// convert: Dataspace -> int32_t +int32_t toRawDataspace(Dataspace const& t); + +/** + * \brief Wrap an opaque buffer inside a `hidl_vec<uint8_t>`. + * + * \param[in] l The pointer to the beginning of the opaque buffer. + * \param[in] size The size of the buffer. + * \return A `hidl_vec<uint8_t>` that points to the buffer. + */ +// wrap: void*, size_t -> hidl_vec<uint8_t> +hidl_vec<uint8_t> inHidlBytes(void const* l, size_t size); + +/** + * \brief Create a `hidl_vec<uint8_t>` that is a copy of an opaque buffer. + * + * \param[in] l The pointer to the beginning of the opaque buffer. + * \param[in] size The size of the buffer. + * \return A `hidl_vec<uint8_t>` that is a copy of the input buffer. + */ +// convert: void*, size_t -> hidl_vec<uint8_t> +hidl_vec<uint8_t> toHidlBytes(void const* l, size_t size); + +/** + * \brief Wrap `GraphicBuffer` in `AnwBuffer`. + * + * \param[out] t The wrapper of type `AnwBuffer`. + * \param[in] l The source `GraphicBuffer`. + */ +// wrap: GraphicBuffer -> AnwBuffer +void wrapAs(AnwBuffer* t, GraphicBuffer const& l); + +/** + * \brief Convert `AnwBuffer` to `GraphicBuffer`. + * + * \param[out] l The destination `GraphicBuffer`. + * \param[in] t The source `AnwBuffer`. + * + * This function will duplicate all file descriptors in \p t. + */ +// convert: AnwBuffer -> GraphicBuffer +// Ref: frameworks/native/libs/ui/GraphicBuffer.cpp: GraphicBuffer::flatten +bool convertTo(GraphicBuffer* l, AnwBuffer const& t); + +/** + * Conversion functions for types outside media + * ============================================ + * + * Some objects in libui and libgui that were made to go through binder calls do + * not expose ways to read or write their fields to the public. To pass an + * object of this kind through the HIDL boundary, translation functions need to + * work around the access restriction by using the publicly available + * `flatten()` and `unflatten()` functions. + * + * All `flatten()` and `unflatten()` overloads follow the same convention as + * follows: + * + * status_t flatten(ObjectType const& object, + * [OtherType const& other, ...] + * void*& buffer, size_t& size, + * int*& fds, size_t& numFds) + * + * status_t unflatten(ObjectType* object, + * [OtherType* other, ...,] + * void*& buffer, size_t& size, + * int*& fds, size_t& numFds) + * + * The number of `other` parameters varies depending on the `ObjectType`. For + * example, in the process of unflattening an object that contains + * `hidl_handle`, `other` is needed to hold `native_handle_t` objects that will + * be created. + * + * The last four parameters always work the same way in all overloads of + * `flatten()` and `unflatten()`: + * - For `flatten()`, `buffer` is the pointer to the non-fd buffer to be filled, + * `size` is the size (in bytes) of the non-fd buffer pointed to by `buffer`, + * `fds` is the pointer to the fd buffer to be filled, and `numFds` is the + * size (in ints) of the fd buffer pointed to by `fds`. + * - For `unflatten()`, `buffer` is the pointer to the non-fd buffer to be read + * from, `size` is the size (in bytes) of the non-fd buffer pointed to by + * `buffer`, `fds` is the pointer to the fd buffer to be read from, and + * `numFds` is the size (in ints) of the fd buffer pointed to by `fds`. + * - After a successful call to `flatten()` or `unflatten()`, `buffer` and `fds` + * will be advanced, while `size` and `numFds` will be decreased to reflect + * how much storage/data of the two buffers (fd and non-fd) have been used. + * - After an unsuccessful call, the values of `buffer`, `size`, `fds` and + * `numFds` are invalid. + * + * The return value of a successful `flatten()` or `unflatten()` call will be + * `OK` (also aliased as `NO_ERROR`). Any other values indicate a failure. + * + * For each object type that supports flattening, there will be two accompanying + * functions: `getFlattenedSize()` and `getFdCount()`. `getFlattenedSize()` will + * return the size of the non-fd buffer that the object will need for + * flattening. `getFdCount()` will return the size of the fd buffer that the + * object will need for flattening. + * + * The set of these four functions, `getFlattenedSize()`, `getFdCount()`, + * `flatten()` and `unflatten()`, are similar to functions of the same name in + * the abstract class `Flattenable`. The only difference is that functions in + * this file are not member functions of the object type. For example, we write + * + * flatten(x, buffer, size, fds, numFds) + * + * instead of + * + * x.flatten(buffer, size, fds, numFds) + * + * because we cannot modify the type of `x`. + * + * There is one exception to the naming convention: `hidl_handle` that + * represents a fence. The four functions for this "Fence" type have the word + * "Fence" attched to their names because the object type, which is + * `hidl_handle`, does not carry the special meaning that the object itself can + * only contain zero or one file descriptor. + */ + +// Ref: frameworks/native/libs/ui/Fence.cpp + +/** + * \brief Return the size of the non-fd buffer required to flatten a fence. + * + * \param[in] fence The input fence of type `hidl_handle`. + * \return The required size of the flat buffer. + * + * The current version of this function always returns 4, which is the number of + * bytes required to store the number of file descriptors contained in the fd + * part of the flat buffer. + */ +size_t getFenceFlattenedSize(hidl_handle const& fence); + +/** + * \brief Return the number of file descriptors contained in a fence. + * + * \param[in] fence The input fence of type `hidl_handle`. + * \return `0` if \p fence does not contain a valid file descriptor, or `1` + * otherwise. + */ +size_t getFenceFdCount(hidl_handle const& fence); + +/** + * \brief Unflatten `Fence` to `hidl_handle`. + * + * \param[out] fence The destination `hidl_handle`. + * \param[out] nh The underlying native handle. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * If the return value is `NO_ERROR`, \p nh will point to a newly created + * native handle, which needs to be deleted with `native_handle_delete()` + * afterwards. + */ +status_t unflattenFence(hidl_handle* fence, native_handle_t** nh, + void const*& buffer, size_t& size, int const*& fds, size_t& numFds); + +/** + * \brief Flatten `hidl_handle` as `Fence`. + * + * \param[in] t The source `hidl_handle`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + */ +status_t flattenFence(hidl_handle const& fence, + void*& buffer, size_t& size, int*& fds, size_t& numFds); + +/** + * \brief Wrap `Fence` in `hidl_handle`. + * + * \param[out] t The wrapper of type `hidl_handle`. + * \param[out] nh The native handle pointed to by \p t. + * \param[in] l The source `Fence`. + * + * On success, \p nh will hold a newly created native handle, which must be + * deleted manually with `native_handle_delete()` afterwards. + */ +// wrap: Fence -> hidl_handle +bool wrapAs(hidl_handle* t, native_handle_t** nh, Fence const& l); + +/** + * \brief Convert `hidl_handle` to `Fence`. + * + * \param[out] l The destination `Fence`. `l` must not have been used + * (`l->isValid()` must return `false`) before this function is called. + * \param[in] t The source `hidl_handle`. + * + * If \p t contains a valid file descriptor, it will be duplicated. + */ +// convert: hidl_handle -> Fence +bool convertTo(Fence* l, hidl_handle const& t); + +// Ref: frameworks/native/libs/ui/FenceTime.cpp: FenceTime::Snapshot + +/** + * \brief Return the size of the non-fd buffer required to flatten + * `FenceTimeSnapshot`. + * + * \param[in] t The input `FenceTimeSnapshot`. + * \return The required size of the flat buffer. + */ +size_t getFlattenedSize(HGraphicBufferProducer::FenceTimeSnapshot const& t); + +/** + * \brief Return the number of file descriptors contained in + * `FenceTimeSnapshot`. + * + * \param[in] t The input `FenceTimeSnapshot`. + * \return The number of file descriptors contained in \p snapshot. + */ +size_t getFdCount(HGraphicBufferProducer::FenceTimeSnapshot const& t); + +/** + * \brief Flatten `FenceTimeSnapshot`. + * + * \param[in] t The source `FenceTimeSnapshot`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * This function will duplicate the file descriptor in `t.fence` if `t.state == + * FENCE`. + */ +status_t flatten(HGraphicBufferProducer::FenceTimeSnapshot const& t, + void*& buffer, size_t& size, int*& fds, size_t& numFds); + +/** + * \brief Unflatten `FenceTimeSnapshot`. + * + * \param[out] t The destination `FenceTimeSnapshot`. + * \param[out] nh The underlying native handle. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * If the return value is `NO_ERROR` and the constructed snapshot contains a + * file descriptor, \p nh will be created to hold that file descriptor. In this + * case, \p nh needs to be deleted with `native_handle_delete()` afterwards. + */ +status_t unflatten( + HGraphicBufferProducer::FenceTimeSnapshot* t, native_handle_t** nh, + void const*& buffer, size_t& size, int const*& fds, size_t& numFds); + +// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventsDelta + +/** + * \brief Return the size of the non-fd buffer required to flatten + * `FrameEventsDelta`. + * + * \param[in] t The input `FrameEventsDelta`. + * \return The required size of the flat buffer. + */ +size_t getFlattenedSize(HGraphicBufferProducer::FrameEventsDelta const& t); + +/** + * \brief Return the number of file descriptors contained in + * `FrameEventsDelta`. + * + * \param[in] t The input `FrameEventsDelta`. + * \return The number of file descriptors contained in \p t. + */ +size_t getFdCount(HGraphicBufferProducer::FrameEventsDelta const& t); + +/** + * \brief Unflatten `FrameEventsDelta`. + * + * \param[out] t The destination `FrameEventsDelta`. + * \param[out] nh The underlying array of native handles. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * If the return value is `NO_ERROR`, \p nh will have length 4, and it will be + * populated with `nullptr` or newly created handles. Each non-null slot in \p + * nh will need to be deleted manually with `native_handle_delete()`. + */ +status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t, + std::vector<native_handle_t*>* nh, + void const*& buffer, size_t& size, int const*& fds, size_t& numFds); + +/** + * \brief Flatten `FrameEventsDelta`. + * + * \param[in] t The source `FrameEventsDelta`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * This function will duplicate file descriptors contained in \p t. + */ +// Ref: frameworks/native/libs/gui/FrameTimestamp.cpp: +// FrameEventsDelta::flatten +status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t, + void*& buffer, size_t& size, int*& fds, size_t numFds); + +// Ref: frameworks/native/libs/gui/FrameTimestamps.cpp: FrameEventHistoryDelta + +/** + * \brief Return the size of the non-fd buffer required to flatten + * `HGraphicBufferProducer::FrameEventHistoryDelta`. + * + * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. + * \return The required size of the flat buffer. + */ +size_t getFlattenedSize( + HGraphicBufferProducer::FrameEventHistoryDelta const& t); + +/** + * \brief Return the number of file descriptors contained in + * `HGraphicBufferProducer::FrameEventHistoryDelta`. + * + * \param[in] t The input `HGraphicBufferProducer::FrameEventHistoryDelta`. + * \return The number of file descriptors contained in \p t. + */ +size_t getFdCount( + HGraphicBufferProducer::FrameEventHistoryDelta const& t); + +/** + * \brief Unflatten `FrameEventHistoryDelta`. + * + * \param[out] t The destination `FrameEventHistoryDelta`. + * \param[out] nh The underlying array of arrays of native handles. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * If the return value is `NO_ERROR`, \p nh will be populated with `nullptr` or + * newly created handles. The second dimension of \p nh will be 4. Each non-null + * slot in \p nh will need to be deleted manually with `native_handle_delete()`. + */ +status_t unflatten( + HGraphicBufferProducer::FrameEventHistoryDelta* t, + std::vector<std::vector<native_handle_t*> >* nh, + void const*& buffer, size_t& size, int const*& fds, size_t& numFds); + +/** + * \brief Flatten `FrameEventHistoryDelta`. + * + * \param[in] t The source `FrameEventHistoryDelta`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * This function will duplicate file descriptors contained in \p t. + */ +status_t flatten( + HGraphicBufferProducer::FrameEventHistoryDelta const& t, + void*& buffer, size_t& size, int*& fds, size_t& numFds); + +/** + * \brief Wrap `::android::FrameEventHistoryData` in + * `HGraphicBufferProducer::FrameEventHistoryDelta`. + * + * \param[out] t The wrapper of type + * `HGraphicBufferProducer::FrameEventHistoryDelta`. + * \param[out] nh The array of array of native handles that are referred to by + * members of \p t. + * \param[in] l The source `::android::FrameEventHistoryDelta`. + * + * On success, each member of \p nh will be either `nullptr` or a newly created + * native handle. All the non-`nullptr` elements must be deleted individually + * with `native_handle_delete()`. + */ +bool wrapAs(HGraphicBufferProducer::FrameEventHistoryDelta* t, + std::vector<std::vector<native_handle_t*> >* nh, + ::android::FrameEventHistoryDelta const& l); + +/** + * \brief Convert `HGraphicBufferProducer::FrameEventHistoryDelta` to + * `::android::FrameEventHistoryDelta`. + * + * \param[out] l The destination `::android::FrameEventHistoryDelta`. + * \param[in] t The source `HGraphicBufferProducer::FrameEventHistoryDelta`. + * + * This function will duplicate all file descriptors contained in \p t. + */ +bool convertTo( + ::android::FrameEventHistoryDelta* l, + HGraphicBufferProducer::FrameEventHistoryDelta const& t); + +// Ref: frameworks/native/libs/ui/Region.cpp + +/** + * \brief Return the size of the buffer required to flatten `Region`. + * + * \param[in] t The input `Region`. + * \return The required size of the flat buffer. + */ +size_t getFlattenedSize(Region const& t); + +/** + * \brief Unflatten `Region`. + * + * \param[out] t The destination `Region`. + * \param[in,out] buffer The pointer to the flat buffer. + * \param[in,out] size The size of the flat buffer. + * \return `NO_ERROR` on success; other value on failure. + */ +status_t unflatten(Region* t, void const*& buffer, size_t& size); + +/** + * \brief Flatten `Region`. + * + * \param[in] t The source `Region`. + * \param[in,out] buffer The pointer to the flat buffer. + * \param[in,out] size The size of the flat buffer. + * \return `NO_ERROR` on success; other value on failure. + */ +status_t flatten(Region const& t, void*& buffer, size_t& size); + +/** + * \brief Convert `::android::Region` to `Region`. + * + * \param[out] t The destination `Region`. + * \param[in] l The source `::android::Region`. + */ +// convert: ::android::Region -> Region +bool convertTo(Region* t, ::android::Region const& l); + +/** + * \brief Convert `Region` to `::android::Region`. + * + * \param[out] l The destination `::android::Region`. + * \param[in] t The source `Region`. + */ +// convert: Region -> ::android::Region +bool convertTo(::android::Region* l, Region const& t); + +// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp: +// BGraphicBufferProducer::QueueBufferInput + +/** + * \brief Return the size of the buffer required to flatten + * `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. + * \return The required size of the flat buffer. + */ +size_t getFlattenedSize(HGraphicBufferProducer::QueueBufferInput const& t); + +/** + * \brief Return the number of file descriptors contained in + * `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[in] t The input `HGraphicBufferProducer::QueueBufferInput`. + * \return The number of file descriptors contained in \p t. + */ +size_t getFdCount( + HGraphicBufferProducer::QueueBufferInput const& t); +/** + * \brief Flatten `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`. + * \param[out] nh The native handle cloned from `t.fence`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * This function will duplicate the file descriptor in `t.fence`. */ +status_t flatten(HGraphicBufferProducer::QueueBufferInput const& t, + native_handle_t** nh, + void*& buffer, size_t& size, int*& fds, size_t& numFds); + +/** + * \brief Unflatten `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[out] t The destination `HGraphicBufferProducer::QueueBufferInput`. + * \param[out] nh The underlying native handle for `t->fence`. + * \param[in,out] buffer The pointer to the flat non-fd buffer. + * \param[in,out] size The size of the flat non-fd buffer. + * \param[in,out] fds The pointer to the flat fd buffer. + * \param[in,out] numFds The size of the flat fd buffer. + * \return `NO_ERROR` on success; other value on failure. + * + * If the return value is `NO_ERROR` and `t->fence` contains a valid file + * descriptor, \p nh will be a newly created native handle holding that file + * descriptor. \p nh needs to be deleted with `native_handle_delete()` + * afterwards. + */ +status_t unflatten( + HGraphicBufferProducer::QueueBufferInput* t, native_handle_t** nh, + void const*& buffer, size_t& size, int const*& fds, size_t& numFds); + +/** + * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in + * `HGraphicBufferProducer::QueueBufferInput`. + * + * \param[out] t The wrapper of type + * `HGraphicBufferProducer::QueueBufferInput`. + * \param[out] nh The underlying native handle for `t->fence`. + * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`. + * + * If the return value is `true` and `t->fence` contains a valid file + * descriptor, \p nh will be a newly created native handle holding that file + * descriptor. \p nh needs to be deleted with `native_handle_delete()` + * afterwards. + */ +bool wrapAs( + HGraphicBufferProducer::QueueBufferInput* t, + native_handle_t** nh, + BGraphicBufferProducer::QueueBufferInput const& l); + +/** + * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to + * `BGraphicBufferProducer::QueueBufferInput`. + * + * \param[out] l The destination `BGraphicBufferProducer::QueueBufferInput`. + * \param[in] t The source `HGraphicBufferProducer::QueueBufferInput`. + * + * If `t.fence` has a valid file descriptor, it will be duplicated. + */ +bool convertTo( + BGraphicBufferProducer::QueueBufferInput* l, + HGraphicBufferProducer::QueueBufferInput const& t); + +// Ref: frameworks/native/libs/gui/BGraphicBufferProducer.cpp: +// BGraphicBufferProducer::QueueBufferOutput + +/** + * \brief Wrap `BGraphicBufferProducer::QueueBufferOutput` in + * `HGraphicBufferProducer::QueueBufferOutput`. + * + * \param[out] t The wrapper of type + * `HGraphicBufferProducer::QueueBufferOutput`. + * \param[out] nh The array of array of native handles that are referred to by + * members of \p t. + * \param[in] l The source `BGraphicBufferProducer::QueueBufferOutput`. + * + * On success, each member of \p nh will be either `nullptr` or a newly created + * native handle. All the non-`nullptr` elements must be deleted individually + * with `native_handle_delete()`. + */ +// wrap: BGraphicBufferProducer::QueueBufferOutput -> +// HGraphicBufferProducer::QueueBufferOutput +bool wrapAs(HGraphicBufferProducer::QueueBufferOutput* t, + std::vector<std::vector<native_handle_t*> >* nh, + BGraphicBufferProducer::QueueBufferOutput const& l); + +/** + * \brief Convert `HGraphicBufferProducer::QueueBufferOutput` to + * `BGraphicBufferProducer::QueueBufferOutput`. + * + * \param[out] l The destination `BGraphicBufferProducer::QueueBufferOutput`. + * \param[in] t The source `HGraphicBufferProducer::QueueBufferOutput`. + * + * This function will duplicate all file descriptors contained in \p t. + */ +// convert: HGraphicBufferProducer::QueueBufferOutput -> +// BGraphicBufferProducer::QueueBufferOutput +bool convertTo( + BGraphicBufferProducer::QueueBufferOutput* l, + HGraphicBufferProducer::QueueBufferOutput const& t); + +/** + * \brief Convert `BGraphicBufferProducer::DisconnectMode` to + * `HGraphicBufferProducer::DisconnectMode`. + * + * \param[in] l The source `BGraphicBufferProducer::DisconnectMode`. + * \return The corresponding `HGraphicBufferProducer::DisconnectMode`. + */ +HGraphicBufferProducer::DisconnectMode toHidlDisconnectMode( + BGraphicBufferProducer::DisconnectMode l); + +/** + * \brief Convert `HGraphicBufferProducer::DisconnectMode` to + * `BGraphicBufferProducer::DisconnectMode`. + * + * \param[in] l The source `HGraphicBufferProducer::DisconnectMode`. + * \return The corresponding `BGraphicBufferProducer::DisconnectMode`. + */ +BGraphicBufferProducer::DisconnectMode toGuiDisconnectMode( + HGraphicBufferProducer::DisconnectMode t); + +} // namespace conversion +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_CONVERSION_H_
diff --git a/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h index 74850b4..c4d0245 100644 --- a/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h +++ b/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h
@@ -57,9 +57,8 @@ struct H2BGraphicBufferProducer : public ::android::H2BConverter< HGraphicBufferProducer, - BGraphicBufferProducer, BnGraphicBufferProducer> { - H2BGraphicBufferProducer(sp<HGraphicBufferProducer> const& base) : CBase(base) {} + explicit H2BGraphicBufferProducer(sp<HGraphicBufferProducer> const& base) : CBase(base) {} status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override; status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override;
diff --git a/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h new file mode 100644 index 0000000..211fdd5 --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
@@ -0,0 +1,52 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BPRODUCERLISTENER_H +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BPRODUCERLISTENER_H + +#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h> +#include <gui/IProducerListener.h> +#include <hidl/HybridInterface.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V1_0 { +namespace utils { + +using HProducerListener = ::android::hardware::graphics::bufferqueue::V1_0:: + IProducerListener; + +using BProducerListener = ::android::IProducerListener; + +class H2BProducerListener + : public H2BConverter<HProducerListener, BnProducerListener> { +public: + H2BProducerListener(sp<HProducerListener> const& base); + virtual void onBufferReleased() override; + virtual bool needsReleaseNotify() override; +}; + +} // namespace utils +} // namespace V1_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BPRODUCERLISTENER_H +
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h new file mode 100644 index 0000000..029dcc0 --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
@@ -0,0 +1,380 @@ +/* + * Copyright 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WGRAPHICBUFFERPRODUCER_H_ +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WGRAPHICBUFFERPRODUCER_H_ + +#include <hidl/MQDescriptor.h> +#include <hidl/Status.h> + +#include <binder/Binder.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/IProducerListener.h> +#include <gui/bufferqueue/1.0/Conversion.h> +#include <gui/bufferqueue/1.0/WProducerListener.h> +#include <system/window.h> + +#include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h> + +namespace android { + +using ::android::hardware::media::V1_0::AnwBuffer; +using ::android::hidl::base::V1_0::IBase; +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_handle; +using ::android::hardware::hidl_memory; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::sp; + +typedef ::android::hardware::graphics::bufferqueue::V1_0:: + IGraphicBufferProducer HGraphicBufferProducer; +typedef ::android::hardware::graphics::bufferqueue::V1_0:: + IProducerListener HProducerListener; + +typedef ::android::IGraphicBufferProducer BGraphicBufferProducer; +typedef ::android::IProducerListener BProducerListener; +using ::android::BnGraphicBufferProducer; + +#ifndef LOG +struct LOG_dummy { + template <typename T> + LOG_dummy& operator<< (const T&) { return *this; } +}; + +#define LOG(x) LOG_dummy() +#endif + +// Instantiate only if HGraphicBufferProducer is base of BASE. +template <typename BASE, + typename = typename std::enable_if<std::is_base_of<HGraphicBufferProducer, BASE>::value>::type> +struct TWGraphicBufferProducer : public BASE { + TWGraphicBufferProducer(sp<BGraphicBufferProducer> const& base) : mBase(base) {} + Return<void> requestBuffer(int32_t slot, HGraphicBufferProducer::requestBuffer_cb _hidl_cb) override { + sp<GraphicBuffer> buf; + status_t status = mBase->requestBuffer(slot, &buf); + AnwBuffer anwBuffer{}; + if (buf != nullptr) { + ::android::conversion::wrapAs(&anwBuffer, *buf); + } + _hidl_cb(static_cast<int32_t>(status), anwBuffer); + return Void(); + } + + Return<int32_t> setMaxDequeuedBufferCount(int32_t maxDequeuedBuffers) override { + return static_cast<int32_t>(mBase->setMaxDequeuedBufferCount( + static_cast<int>(maxDequeuedBuffers))); + } + + Return<int32_t> setAsyncMode(bool async) override { + return static_cast<int32_t>(mBase->setAsyncMode(async)); + } + + Return<void> dequeueBuffer( + uint32_t width, uint32_t height, + ::android::hardware::graphics::common::V1_0::PixelFormat format, uint32_t usage, + bool getFrameTimestamps, HGraphicBufferProducer::dequeueBuffer_cb _hidl_cb) override { + int slot{}; + sp<Fence> fence; + ::android::FrameEventHistoryDelta outTimestamps; + status_t status = mBase->dequeueBuffer( + &slot, &fence, width, height, + static_cast<::android::PixelFormat>(format), usage, nullptr, + getFrameTimestamps ? &outTimestamps : nullptr); + hidl_handle tFence{}; + HGraphicBufferProducer::FrameEventHistoryDelta tOutTimestamps{}; + + native_handle_t* nh = nullptr; + if ((fence == nullptr) || !::android::conversion::wrapAs(&tFence, &nh, *fence)) { + LOG(ERROR) << "TWGraphicBufferProducer::dequeueBuffer - " + "Invalid output fence"; + _hidl_cb(static_cast<int32_t>(status), + static_cast<int32_t>(slot), + tFence, + tOutTimestamps); + return Void(); + } + std::vector<std::vector<native_handle_t*> > nhAA; + if (getFrameTimestamps && !::android::conversion::wrapAs(&tOutTimestamps, &nhAA, outTimestamps)) { + LOG(ERROR) << "TWGraphicBufferProducer::dequeueBuffer - " + "Invalid output timestamps"; + _hidl_cb(static_cast<int32_t>(status), + static_cast<int32_t>(slot), + tFence, + tOutTimestamps); + native_handle_delete(nh); + return Void(); + } + + _hidl_cb(static_cast<int32_t>(status), + static_cast<int32_t>(slot), + tFence, + tOutTimestamps); + native_handle_delete(nh); + if (getFrameTimestamps) { + for (auto& nhA : nhAA) { + for (auto& handle : nhA) { + native_handle_delete(handle); + } + } + } + return Void(); + } + + Return<int32_t> detachBuffer(int32_t slot) override { + return static_cast<int32_t>(mBase->detachBuffer(slot)); + } + + Return<void> detachNextBuffer(HGraphicBufferProducer::detachNextBuffer_cb _hidl_cb) override { + sp<GraphicBuffer> outBuffer; + sp<Fence> outFence; + status_t status = mBase->detachNextBuffer(&outBuffer, &outFence); + AnwBuffer tBuffer{}; + hidl_handle tFence{}; + + if (outBuffer == nullptr) { + LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - " + "Invalid output buffer"; + _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence); + return Void(); + } + ::android::conversion::wrapAs(&tBuffer, *outBuffer); + native_handle_t* nh = nullptr; + if ((outFence != nullptr) && !::android::conversion::wrapAs(&tFence, &nh, *outFence)) { + LOG(ERROR) << "TWGraphicBufferProducer::detachNextBuffer - " + "Invalid output fence"; + _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence); + return Void(); + } + + _hidl_cb(static_cast<int32_t>(status), tBuffer, tFence); + native_handle_delete(nh); + return Void(); + } + + Return<void> attachBuffer(const AnwBuffer& buffer, HGraphicBufferProducer::attachBuffer_cb _hidl_cb) override { + int outSlot; + sp<GraphicBuffer> lBuffer = new GraphicBuffer(); + if (!::android::conversion::convertTo(lBuffer.get(), buffer)) { + LOG(ERROR) << "TWGraphicBufferProducer::attachBuffer - " + "Invalid input native window buffer"; + _hidl_cb(static_cast<int32_t>(BAD_VALUE), -1); + return Void(); + } + status_t status = mBase->attachBuffer(&outSlot, lBuffer); + + _hidl_cb(static_cast<int32_t>(status), static_cast<int32_t>(outSlot)); + return Void(); + } + + Return<void> queueBuffer( + int32_t slot, const HGraphicBufferProducer::QueueBufferInput& input, + HGraphicBufferProducer::queueBuffer_cb _hidl_cb) override { + HGraphicBufferProducer::QueueBufferOutput tOutput{}; + BGraphicBufferProducer::QueueBufferInput lInput( + 0, false, HAL_DATASPACE_UNKNOWN, + ::android::Rect(0, 0, 1, 1), + NATIVE_WINDOW_SCALING_MODE_FREEZE, + 0, ::android::Fence::NO_FENCE); + if (!::android::conversion::convertTo(&lInput, input)) { + LOG(ERROR) << "TWGraphicBufferProducer::queueBuffer - " + "Invalid input"; + _hidl_cb(static_cast<int32_t>(BAD_VALUE), tOutput); + return Void(); + } + BGraphicBufferProducer::QueueBufferOutput lOutput; + status_t status = mBase->queueBuffer( + static_cast<int>(slot), lInput, &lOutput); + + std::vector<std::vector<native_handle_t*> > nhAA; + if (!::android::conversion::wrapAs(&tOutput, &nhAA, lOutput)) { + LOG(ERROR) << "TWGraphicBufferProducer::queueBuffer - " + "Invalid output"; + _hidl_cb(static_cast<int32_t>(BAD_VALUE), tOutput); + return Void(); + } + + _hidl_cb(static_cast<int32_t>(status), tOutput); + for (auto& nhA : nhAA) { + for (auto& nh : nhA) { + native_handle_delete(nh); + } + } + return Void(); + } + + Return<int32_t> cancelBuffer(int32_t slot, const hidl_handle& fence) override { + sp<Fence> lFence = new Fence(); + if (!::android::conversion::convertTo(lFence.get(), fence)) { + LOG(ERROR) << "TWGraphicBufferProducer::cancelBuffer - " + "Invalid input fence"; + return static_cast<int32_t>(BAD_VALUE); + } + return static_cast<int32_t>(mBase->cancelBuffer(static_cast<int>(slot), lFence)); + } + + Return<void> query(int32_t what, HGraphicBufferProducer::query_cb _hidl_cb) override { + int lValue; + int lReturn = mBase->query(static_cast<int>(what), &lValue); + _hidl_cb(static_cast<int32_t>(lReturn), static_cast<int32_t>(lValue)); + return Void(); + } + + Return<void> connect(const sp<HProducerListener>& listener, + int32_t api, bool producerControlledByApp, + HGraphicBufferProducer::connect_cb _hidl_cb) override { + sp<BProducerListener> lListener = listener == nullptr ? + nullptr : new LWProducerListener(listener); + BGraphicBufferProducer::QueueBufferOutput lOutput; + status_t status = mBase->connect(lListener, + static_cast<int>(api), + producerControlledByApp, + &lOutput); + + HGraphicBufferProducer::QueueBufferOutput tOutput{}; + std::vector<std::vector<native_handle_t*> > nhAA; + if (!::android::conversion::wrapAs(&tOutput, &nhAA, lOutput)) { + LOG(ERROR) << "TWGraphicBufferProducer::connect - " + "Invalid output"; + _hidl_cb(static_cast<int32_t>(status), tOutput); + return Void(); + } + + _hidl_cb(static_cast<int32_t>(status), tOutput); + for (auto& nhA : nhAA) { + for (auto& nh : nhA) { + native_handle_delete(nh); + } + } + return Void(); + } + + Return<int32_t> disconnect( + int32_t api, + HGraphicBufferProducer::DisconnectMode mode) override { + return static_cast<int32_t>(mBase->disconnect( + static_cast<int>(api), + ::android::conversion::toGuiDisconnectMode(mode))); + } + + Return<int32_t> setSidebandStream(const hidl_handle& stream) override { + return static_cast<int32_t>(mBase->setSidebandStream(NativeHandle::create( + stream ? native_handle_clone(stream) : NULL, true))); + } + + Return<void> allocateBuffers( + uint32_t width, uint32_t height, + ::android::hardware::graphics::common::V1_0::PixelFormat format, + uint32_t usage) override { + mBase->allocateBuffers( + width, height, + static_cast<::android::PixelFormat>(format), + usage); + return Void(); + } + + Return<int32_t> allowAllocation(bool allow) override { + return static_cast<int32_t>(mBase->allowAllocation(allow)); + } + + Return<int32_t> setGenerationNumber(uint32_t generationNumber) override { + return static_cast<int32_t>(mBase->setGenerationNumber(generationNumber)); + } + + Return<void> getConsumerName(HGraphicBufferProducer::getConsumerName_cb _hidl_cb) override { + _hidl_cb(mBase->getConsumerName().string()); + return Void(); + } + + Return<int32_t> setSharedBufferMode(bool sharedBufferMode) override { + return static_cast<int32_t>(mBase->setSharedBufferMode(sharedBufferMode)); + } + + Return<int32_t> setAutoRefresh(bool autoRefresh) override { + return static_cast<int32_t>(mBase->setAutoRefresh(autoRefresh)); + } + + Return<int32_t> setDequeueTimeout(int64_t timeoutNs) override { + return static_cast<int32_t>(mBase->setDequeueTimeout(timeoutNs)); + } + + Return<void> getLastQueuedBuffer(HGraphicBufferProducer::getLastQueuedBuffer_cb _hidl_cb) override { + sp<GraphicBuffer> lOutBuffer = new GraphicBuffer(); + sp<Fence> lOutFence = new Fence(); + float lOutTransformMatrix[16]; + status_t status = mBase->getLastQueuedBuffer( + &lOutBuffer, &lOutFence, lOutTransformMatrix); + + AnwBuffer tOutBuffer{}; + if (lOutBuffer != nullptr) { + ::android::conversion::wrapAs(&tOutBuffer, *lOutBuffer); + } + hidl_handle tOutFence{}; + native_handle_t* nh = nullptr; + if ((lOutFence == nullptr) || !::android::conversion::wrapAs(&tOutFence, &nh, *lOutFence)) { + LOG(ERROR) << "TWGraphicBufferProducer::getLastQueuedBuffer - " + "Invalid output fence"; + _hidl_cb(static_cast<int32_t>(status), + tOutBuffer, + tOutFence, + hidl_array<float, 16>()); + return Void(); + } + hidl_array<float, 16> tOutTransformMatrix(lOutTransformMatrix); + + _hidl_cb(static_cast<int32_t>(status), tOutBuffer, tOutFence, tOutTransformMatrix); + native_handle_delete(nh); + return Void(); + } + + Return<void> getFrameTimestamps(HGraphicBufferProducer::getFrameTimestamps_cb _hidl_cb) override { + ::android::FrameEventHistoryDelta lDelta; + mBase->getFrameTimestamps(&lDelta); + + HGraphicBufferProducer::FrameEventHistoryDelta tDelta{}; + std::vector<std::vector<native_handle_t*> > nhAA; + if (!::android::conversion::wrapAs(&tDelta, &nhAA, lDelta)) { + LOG(ERROR) << "TWGraphicBufferProducer::getFrameTimestamps - " + "Invalid output frame timestamps"; + _hidl_cb(tDelta); + return Void(); + } + + _hidl_cb(tDelta); + for (auto& nhA : nhAA) { + for (auto& nh : nhA) { + native_handle_delete(nh); + } + } + return Void(); + } + + Return<void> getUniqueId(HGraphicBufferProducer::getUniqueId_cb _hidl_cb) override { + uint64_t outId{}; + status_t status = mBase->getUniqueId(&outId); + _hidl_cb(static_cast<int32_t>(status), outId); + return Void(); + } + +private: + sp<BGraphicBufferProducer> mBase; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WGRAPHICBUFFERPRODUCER_H_
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h new file mode 100644 index 0000000..51dff5b --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
@@ -0,0 +1,62 @@ +/* + * Copyright 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WPRODUCERLISTENER_H_ +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WPRODUCERLISTENER_H_ + +#include <hidl/MQDescriptor.h> +#include <hidl/Status.h> + +#include <binder/IBinder.h> +#include <gui/IProducerListener.h> + +#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h> + +namespace android { + +using ::android::hidl::base::V1_0::IBase; +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_memory; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::sp; + +typedef ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener + HProducerListener; +typedef ::android::IProducerListener + BProducerListener; +using ::android::BnProducerListener; + +struct TWProducerListener : public HProducerListener { + sp<BProducerListener> mBase; + TWProducerListener(sp<BProducerListener> const& base); + Return<void> onBufferReleased() override; + Return<bool> needsReleaseNotify() override; +}; + +class LWProducerListener : public BnProducerListener { +public: + sp<HProducerListener> mBase; + LWProducerListener(sp<HProducerListener> const& base); + void onBufferReleased() override; + bool needsReleaseNotify() override; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_WPRODUCERLISTENER_H_
diff --git a/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h new file mode 100644 index 0000000..1c58167 --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
@@ -0,0 +1,121 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HGRAPHICBUFFERPRODUCER_H +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HGRAPHICBUFFERPRODUCER_H + +#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/bufferqueue/2.0/types.h> +#include <hidl/HidlSupport.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +using HGraphicBufferProducer = + ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer; +using BGraphicBufferProducer = + ::android:: + IGraphicBufferProducer; +using HProducerListener = + ::android::hardware::graphics::bufferqueue::V2_0:: + IProducerListener; + +using ::android::hardware::Return; +using ::android::hardware::hidl_handle; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; + +using ::android::hardware::graphics::common::V1_2::HardwareBuffer; + +class B2HGraphicBufferProducer : public HGraphicBufferProducer { +public: + B2HGraphicBufferProducer(sp<BGraphicBufferProducer> const& base); + + virtual Return<HStatus> setMaxDequeuedBufferCount( + int32_t maxDequeuedBuffers) override; + + virtual Return<void> requestBuffer( + int32_t slot, + requestBuffer_cb _hidl_cb) override; + + virtual Return<HStatus> setAsyncMode(bool async) override; + + virtual Return<void> dequeueBuffer( + DequeueBufferInput const& input, + dequeueBuffer_cb _hidl_cb) override; + + virtual Return<HStatus> detachBuffer(int32_t slot) override; + + virtual Return<void> detachNextBuffer( + detachNextBuffer_cb _hidl_cb) override; + + virtual Return<void> attachBuffer( + HardwareBuffer const& buffer, + uint32_t generationNumber, + attachBuffer_cb _hidl_cb) override; + + virtual Return<void> queueBuffer( + int32_t slot, + QueueBufferInput const& input, + queueBuffer_cb _hidl_cb) override; + + virtual Return<HStatus> cancelBuffer( + int32_t slot, + hidl_handle const& fence) override; + + virtual Return<void> query(int32_t what, query_cb _hidl_cb) override; + + virtual Return<void> connect( + sp<HProducerListener> const& listener, + HConnectionType api, + bool producerControlledByApp, + connect_cb _hidl_cb) override; + + virtual Return<HStatus> disconnect(HConnectionType api) override; + + virtual Return<HStatus> allocateBuffers( + uint32_t width, uint32_t height, + uint32_t format, uint64_t usage) override; + + virtual Return<HStatus> allowAllocation(bool allow) override; + + virtual Return<HStatus> setGenerationNumber(uint32_t generationNumber) override; + + virtual Return<HStatus> setDequeueTimeout(int64_t timeoutNs) override; + + virtual Return<uint64_t> getUniqueId() override; + + virtual Return<void> getConsumerName(getConsumerName_cb _hidl_cb) override; + +protected: + sp<BGraphicBufferProducer> mBase; +}; + + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HGRAPHICBUFFERPRODUCER_H
diff --git a/libs/gui/include/gui/bufferqueue/2.0/B2HProducerListener.h b/libs/gui/include/gui/bufferqueue/2.0/B2HProducerListener.h new file mode 100644 index 0000000..b48a473 --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/2.0/B2HProducerListener.h
@@ -0,0 +1,57 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HPRODUCERLISTENER_H +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HPRODUCERLISTENER_H + +#include <android/hidl/base/1.0/IBase.h> +#include <binder/IBinder.h> +#include <gui/IProducerListener.h> +#include <hidl/Status.h> + +#include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +using ::android::hardware::Return; + +using HProducerListener = ::android::hardware::graphics::bufferqueue::V2_0:: + IProducerListener; + +using BProducerListener = ::android::IProducerListener; + +struct B2HProducerListener : public HProducerListener { + explicit B2HProducerListener(sp<BProducerListener> const& base); + Return<void> onBuffersReleased(uint32_t count) override; +protected: + sp<BProducerListener> mBase; + bool mNeedsReleaseNotify; +}; + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HPRODUCERLISTENER_H +
diff --git a/libs/gui/include/gui/bufferqueue/2.0/H2BGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/2.0/H2BGraphicBufferProducer.h new file mode 100644 index 0000000..7dd1617 --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/2.0/H2BGraphicBufferProducer.h
@@ -0,0 +1,107 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BGRAPHICBUFFERPRODUCER_H +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BGRAPHICBUFFERPRODUCER_H + +#include <gui/IGraphicBufferProducer.h> +#include <gui/IProducerListener.h> +#include <hidl/HybridInterface.h> +#include <ui/Fence.h> + +#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +using ::android::BnGraphicBufferProducer; +using ::android::IProducerListener; +using Fence = ::android::Fence; + +using HGraphicBufferProducer = + ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer; +using HProducerListener = + ::android::hardware::graphics::bufferqueue::V2_0:: + IProducerListener; +using BGraphicBufferProducer = + ::android::IGraphicBufferProducer; + +struct H2BGraphicBufferProducer + : public ::android::H2BConverter<HGraphicBufferProducer, + BnGraphicBufferProducer> { + explicit H2BGraphicBufferProducer( + sp<HGraphicBufferProducer> const& base) : CBase(base) {} + + virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override; + virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override; + virtual status_t setAsyncMode(bool async) override; + virtual status_t dequeueBuffer( + int* slot, sp<Fence>* fence, + uint32_t width, uint32_t height, + PixelFormat format, uint64_t usage, + uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) override; + virtual status_t detachBuffer(int slot) override; + virtual status_t detachNextBuffer( + sp<GraphicBuffer>* outBuffer, + sp<Fence>* outFence) override; + virtual status_t attachBuffer( + int* outSlot, + sp<GraphicBuffer> const& buffer) override; + virtual status_t queueBuffer( + int slot, + QueueBufferInput const& input, + QueueBufferOutput* output) override; + virtual status_t cancelBuffer(int slot, sp<Fence> const& fence) override; + virtual int query(int what, int* value) override; + virtual status_t connect( + sp<IProducerListener> const& listener, + int api, + bool producerControlledByApp, + QueueBufferOutput* output) override; + virtual status_t disconnect( + int api, + DisconnectMode mode = DisconnectMode::Api) override; + virtual status_t setSidebandStream(sp<NativeHandle> const& stream) override; + virtual void allocateBuffers( + uint32_t width, uint32_t height, + PixelFormat format, uint64_t usage) override; + virtual status_t allowAllocation(bool allow) override; + virtual status_t setGenerationNumber(uint32_t generationNumber) override; + virtual String8 getConsumerName() const override; + virtual status_t setSharedBufferMode(bool sharedBufferMode) override; + virtual status_t setAutoRefresh(bool autoRefresh) override; + virtual status_t setDequeueTimeout(nsecs_t timeout) override; + virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, + sp<Fence>* outFence, float outTransformMatrix[16]) override; + virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override; + virtual status_t getUniqueId(uint64_t* outId) const override; + virtual status_t getConsumerUsage(uint64_t* outUsage) const override; +}; + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BGRAPHICBUFFERPRODUCER_H
diff --git a/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h new file mode 100644 index 0000000..898920b --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
@@ -0,0 +1,52 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BPRODUCERLISTENER_H +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BPRODUCERLISTENER_H + +#include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h> +#include <gui/IProducerListener.h> +#include <hidl/HybridInterface.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +using HProducerListener = ::android::hardware::graphics::bufferqueue::V2_0:: + IProducerListener; + +using BProducerListener = ::android::IProducerListener; + +class H2BProducerListener + : public H2BConverter<HProducerListener, BnProducerListener> { +public: + H2BProducerListener(sp<HProducerListener> const& base); + virtual void onBufferReleased() override; + virtual bool needsReleaseNotify() override; +}; + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BPRODUCERLISTENER_H +
diff --git a/libs/gui/include/gui/bufferqueue/2.0/types.h b/libs/gui/include/gui/bufferqueue/2.0/types.h new file mode 100644 index 0000000..62176ce --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/2.0/types.h
@@ -0,0 +1,129 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_TYPES_H +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_TYPES_H + +#include <android/hardware/graphics/bufferqueue/2.0/types.h> +#include <android/hardware/graphics/common/1.2/types.h> +#include <hidl/HidlSupport.h> +#include <ui/Fence.h> +#include <ui/GraphicBuffer.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +// Status +// ====== + +using HStatus = ::android::hardware::graphics::bufferqueue::V2_0:: + Status; + +// A status_t value may have flags encoded. These flags are decoded into boolean +// values if their corresponding output pointers are not null. +bool b2h(status_t from, HStatus* to, + bool* bufferNeedsReallocation = nullptr, + bool* releaseAllBuffers = nullptr); +// Simple 1-to-1 mapping. If BUFFER_NEEDS_REALLOCATION or RELEASE_ALL_BUFFERS +// needs to be added, it must be done manually afterwards. +bool h2b(HStatus from, status_t* to); + +// Fence +// ===== + +using BFence = ::android::Fence; +// This class manages the lifetime of a copied handle. Its destructor calls +// native_handle_delete() but not native_handle_close(). +struct HFenceWrapper { + HFenceWrapper() = default; + // Sets mHandle to a new value. + HFenceWrapper(native_handle_t* h); + // Deletes mHandle without closing. + ~HFenceWrapper(); + // Deletes mHandle without closing, then sets mHandle to a new value. + HFenceWrapper& set(native_handle_t* h); + HFenceWrapper& operator=(native_handle_t* h); + // Returns a non-owning hidl_handle pointing to mHandle. + hidl_handle getHandle() const; + operator hidl_handle() const; +protected: + native_handle_t* mHandle{nullptr}; +}; + +// Does not clone the fd---only copy the fd. The returned HFenceWrapper should +// not outlive the input Fence object. +bool b2h(sp<BFence> const& from, HFenceWrapper* to); +// Clones the fd and puts it in a new Fence object. +bool h2b(native_handle_t const* from, sp<BFence>* to); + +// ConnectionType +// ============== + +using HConnectionType = ::android::hardware::graphics::bufferqueue::V2_0:: + ConnectionType; + +bool b2h(int from, HConnectionType* to); +bool h2b(HConnectionType from, int* to); + +// Rect +// ==== + +using BRect = ::android::Rect; +using HRect = ::android::hardware::graphics::common::V1_2::Rect; + +bool b2h(BRect const& from, HRect* to); +bool h2b(HRect const& from, BRect* to); + +// Region +// ====== + +using BRegion = ::android::Region; +using HRegion = ::android::hardware::hidl_vec<HRect>; + +bool b2h(BRegion const& from, HRegion* to); +bool h2b(HRegion const& from, BRegion* to); + +// GraphicBuffer +// ============= + +using HardwareBuffer = ::android::hardware::graphics::common::V1_2:: + HardwareBuffer; +using HardwareBufferDescription = ::android::hardware::graphics::common::V1_2:: + HardwareBufferDescription; + +// Does not clone the handle. The returned HardwareBuffer should not outlive the +// input GraphicBuffer. Note that HardwareBuffer does not carry the generation +// number, so this function needs another output argument. +bool b2h(sp<GraphicBuffer> const& from, HardwareBuffer* to, + uint32_t* toGenerationNumber = nullptr); +// Clones the handle and creates a new GraphicBuffer from the cloned handle. +// Note that the generation number of the GraphicBuffer has to be set manually +// afterwards because HardwareBuffer does not have such information. +bool h2b(HardwareBuffer const& from, sp<GraphicBuffer>* to); + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_TYPES_H +
diff --git a/libs/gui/include/private/gui/BufferQueueThreadState.h b/libs/gui/include/private/gui/BufferQueueThreadState.h new file mode 100644 index 0000000..67dcf62 --- /dev/null +++ b/libs/gui/include/private/gui/BufferQueueThreadState.h
@@ -0,0 +1,30 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> +#include <unistd.h> + +namespace android { + +// TODO: Replace this with b/127962003 +class BufferQueueThreadState { +public: + static pid_t getCallingPid(); + static uid_t getCallingUid(); +}; + +} // namespace android
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 01e90e0..ab6dcaa 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp
@@ -16,11 +16,14 @@ "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", "CpuConsumer_test.cpp", + "EndToEndNativeInputTest.cpp", + "DisplayedContentSampling_test.cpp", "FillBuffer.cpp", "GLTest.cpp", "IGraphicBufferProducer_test.cpp", "Malicious.cpp", "MultiTextureConsumer_test.cpp", + "RegionSampling_test.cpp", "StreamSplitter_test.cpp", "SurfaceTextureClient_test.cpp", "SurfaceTextureFBO_test.cpp", @@ -35,6 +38,7 @@ shared_libs: [ "android.hardware.configstore@1.0", "android.hardware.configstore-utils", + "libbase", "liblog", "libEGL", "libGLESv1_CM", @@ -44,15 +48,19 @@ "libgui", "libhidlbase", "libhidltransport", + "libinput", "libui", "libutils", "libnativewindow" ], } -// Build a separate binary for each source file to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +// Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +// This test has a main method, and requires a separate binary to be built. +// To add move tests like this, just add additional cc_test statements, +// as opposed to adding more source files to this one. cc_test { - name: "libgui_separate_binary_test", + name: "SurfaceParcelable_test", test_suites: ["device-tests"], clang: true, @@ -61,7 +69,6 @@ "-Werror", ], - test_per_src: true, srcs: [ "SurfaceParcelable_test.cpp", ], @@ -81,3 +88,26 @@ "libdvr_headers", ], } + +cc_test { + name: "SamplingDemo", + + clang: true, + cflags: [ + "-Wall", + "-Werror", + ], + + srcs: [ + "SamplingDemo.cpp", + ], + + shared_libs: [ + "libbinder", + "libcutils", + "libgui", + "liblog", + "libui", + "libutils", + ] +}
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 9a20859..119e888 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -61,7 +61,7 @@ } void GetMinUndequeuedBufferCount(int* bufferCount) { - ASSERT_TRUE(bufferCount != NULL); + ASSERT_TRUE(bufferCount != nullptr); ASSERT_EQ(OK, mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, bufferCount)); ASSERT_GE(*bufferCount, 0); @@ -82,7 +82,7 @@ sp<Fence> fence; input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop, - &scalingMode, &transform, &fence, NULL); + &scalingMode, &transform, &fence, nullptr); ASSERT_EQ(timestamp, item.mTimestamp); ASSERT_EQ(isAutoTimestamp, item.mIsAutoTimestamp); ASSERT_EQ(dataSpace, item.mDataSpace); @@ -128,17 +128,17 @@ sp<IBinder> binderProducer = serviceManager->getService(PRODUCER_NAME); mProducer = interface_cast<IGraphicBufferProducer>(binderProducer); - EXPECT_TRUE(mProducer != NULL); + EXPECT_TRUE(mProducer != nullptr); sp<IBinder> binderConsumer = serviceManager->getService(CONSUMER_NAME); mConsumer = interface_cast<IGraphicBufferConsumer>(binderConsumer); - EXPECT_TRUE(mConsumer != NULL); + EXPECT_TRUE(mConsumer != nullptr); sp<DummyConsumer> dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, - mProducer->connect(NULL, NATIVE_WINDOW_API_CPU, false, &output)); + mProducer->connect(nullptr, NATIVE_WINDOW_API_CPU, false, &output)); int slot; sp<Fence> fence; @@ -353,8 +353,8 @@ ASSERT_EQ(OK, buffer->unlock()); int newSlot; - ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(NULL, safeToClobberBuffer)); - ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, NULL)); + ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(nullptr, safeToClobberBuffer)); + ASSERT_EQ(BAD_VALUE, mProducer->attachBuffer(&newSlot, nullptr)); ASSERT_EQ(OK, mProducer->attachBuffer(&newSlot, buffer)); IGraphicBufferProducer::QueueBufferInput input(0, false, @@ -412,8 +412,8 @@ int newSlot; sp<GraphicBuffer> safeToClobberBuffer; - ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(NULL, safeToClobberBuffer)); - ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, NULL)); + ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(nullptr, safeToClobberBuffer)); + ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, nullptr)); ASSERT_EQ(OK, mConsumer->attachBuffer(&newSlot, item.mGraphicBuffer)); ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, EGL_NO_DISPLAY,
diff --git a/libs/gui/tests/CpuConsumer_test.cpp b/libs/gui/tests/CpuConsumer_test.cpp index 36be7d9..00e32d9 100644 --- a/libs/gui/tests/CpuConsumer_test.cpp +++ b/libs/gui/tests/CpuConsumer_test.cpp
@@ -484,12 +484,12 @@ err = native_window_dequeue_buffer_and_wait(anw.get(), &anb); ASSERT_NO_ERROR(err, "dequeueBuffer error: "); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); *stride = buf->getStride(); - uint8_t* img = NULL; + uint8_t* img = nullptr; ALOGVV("Lock buffer from %p for write", anw.get()); err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); @@ -554,7 +554,7 @@ err = mCC->lockNextBuffer(&b); ASSERT_NO_ERROR(err, "getNextBuffer error: "); - ASSERT_TRUE(b.data != NULL); + ASSERT_TRUE(b.data != nullptr); EXPECT_EQ(params.width, b.width); EXPECT_EQ(params.height, b.height); EXPECT_EQ(params.format, b.format); @@ -595,7 +595,7 @@ err = mCC->lockNextBuffer(&b); ASSERT_NO_ERROR(err, "getNextBuffer error: "); - ASSERT_TRUE(b.data != NULL); + ASSERT_TRUE(b.data != nullptr); EXPECT_EQ(params.width, b.width); EXPECT_EQ(params.height, b.height); EXPECT_EQ(params.format, b.format); @@ -637,7 +637,7 @@ err = mCC->lockNextBuffer(&b[i]); ASSERT_NO_ERROR(err, "getNextBuffer error: "); - ASSERT_TRUE(b[i].data != NULL); + ASSERT_TRUE(b[i].data != nullptr); EXPECT_EQ(params.width, b[i].width); EXPECT_EQ(params.height, b[i].height); EXPECT_EQ(params.format, b[i].format); @@ -660,7 +660,7 @@ err = mCC->lockNextBuffer(&bTooMuch); ASSERT_NO_ERROR(err, "Did not allow new lock after unlock"); - ASSERT_TRUE(bTooMuch.data != NULL); + ASSERT_TRUE(bTooMuch.data != nullptr); EXPECT_EQ(params.width, bTooMuch.width); EXPECT_EQ(params.height, bTooMuch.height); EXPECT_EQ(params.format, bTooMuch.format);
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp new file mode 100644 index 0000000..b647aab --- /dev/null +++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -0,0 +1,122 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <binder/ProcessState.h> +#include <gui/ISurfaceComposer.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <inttypes.h> + +namespace android { + +using Transaction = SurfaceComposerClient::Transaction; + +static constexpr uint32_t INVALID_MASK = 0x10; +class DisplayedContentSamplingTest : public ::testing::Test { +protected: + void SetUp() { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(OK, mComposerClient->initCheck()); + mDisplayToken = mComposerClient->getInternalDisplayToken(); + ASSERT_TRUE(mDisplayToken); + } + + bool shouldSkipTest() { + ui::PixelFormat format; + ui::Dataspace dataspace; + status_t status = + mComposerClient->getDisplayedContentSamplingAttributes(mDisplayToken, &format, + &dataspace, &componentMask); + if (status == PERMISSION_DENIED) { + SUCCEED() << "permissions denial, skipping test"; + return true; + } + if (status == INVALID_OPERATION) { + SUCCEED() << "optional function not supported, skipping test"; + return true; + } + return false; + } + + sp<SurfaceComposerClient> mComposerClient; + sp<IBinder> mDisplayToken; + uint8_t componentMask = 0; +}; + +TEST_F(DisplayedContentSamplingTest, GetDisplayedContentSamplingAttributesAreSane) { + // tradefed infrastructure does not support use of GTEST_SKIP + if (shouldSkipTest()) return; + + ui::PixelFormat format; + ui::Dataspace dataspace; + status_t status = + mComposerClient->getDisplayedContentSamplingAttributes(mDisplayToken, &format, + &dataspace, &componentMask); + EXPECT_EQ(OK, status); + EXPECT_LE(componentMask, INVALID_MASK); +} + +TEST_F(DisplayedContentSamplingTest, EnableWithInvalidMaskReturnsBadValue) { + if (shouldSkipTest()) return; + + status_t status = + mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, true, INVALID_MASK, 0); + EXPECT_EQ(BAD_VALUE, status); +} + +TEST_F(DisplayedContentSamplingTest, EnableAndDisableSucceed) { + if (shouldSkipTest()) return; + + status_t status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, true, + componentMask, 10); + EXPECT_EQ(OK, status); + + status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, false, componentMask, + 0); + EXPECT_EQ(OK, status); +} + +TEST_F(DisplayedContentSamplingTest, SelectivelyDisableComponentOk) { + if (shouldSkipTest()) return; + + status_t status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, true, + componentMask, 0); + EXPECT_EQ(OK, status); + + // Clear the lowest bit. + componentMask &= (componentMask - 1); + status = mComposerClient->setDisplayContentSamplingEnabled(mDisplayToken, false, componentMask, + 0); + EXPECT_EQ(OK, status); +} + +TEST_F(DisplayedContentSamplingTest, SampleCollectionCoherentWithSupportMask) { + if (shouldSkipTest()) return; + + DisplayedFrameStats stats; + status_t status = mComposerClient->getDisplayedContentSample(mDisplayToken, 0, 0, &stats); + EXPECT_EQ(OK, status); + if (stats.numFrames <= 0) return; + + if (componentMask & (0x1 << 0)) EXPECT_NE(0, stats.component_0_sample.size()); + if (componentMask & (0x1 << 1)) EXPECT_NE(0, stats.component_1_sample.size()); + if (componentMask & (0x1 << 2)) EXPECT_NE(0, stats.component_2_sample.size()); + if (componentMask & (0x1 << 3)) EXPECT_NE(0, stats.component_3_sample.size()); +} + +} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp new file mode 100644 index 0000000..ff1ba0a --- /dev/null +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -0,0 +1,522 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/types.h> +#include <stdio.h> +#include <poll.h> + +#include <memory> + +#include <android/native_window.h> + +#include <binder/Binder.h> +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/ProcessState.h> + +#include <gui/ISurfaceComposer.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/SurfaceControl.h> + +#include <input/InputWindow.h> +#include <input/IInputFlinger.h> +#include <input/InputTransport.h> +#include <input/Input.h> + +#include <ui/DisplayInfo.h> +#include <ui/Rect.h> +#include <ui/Region.h> + + +namespace android { +namespace test { + +using Transaction = SurfaceComposerClient::Transaction; + +sp<IInputFlinger> getInputFlinger() { + sp<IBinder> input(defaultServiceManager()->getService( + String16("inputflinger"))); + if (input == nullptr) { + ALOGE("Failed to link to input service"); + } else { ALOGE("Linked to input"); } + return interface_cast<IInputFlinger>(input); +} + +// We use the top 10 layers as a way to haphazardly place ourselves above anything else. +static const int LAYER_BASE = INT32_MAX - 10; + +class InputSurface { +public: + InputSurface(const sp<SurfaceControl> &sc, int width, int height) { + mSurfaceControl = sc; + + InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel); + mServerChannel->setToken(new BBinder()); + + mInputFlinger = getInputFlinger(); + mInputFlinger->registerInputChannel(mServerChannel); + + populateInputInfo(width, height); + + mInputConsumer = new InputConsumer(mClientChannel); + } + + static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient> &scc, + int width, int height) { + sp<SurfaceControl> surfaceControl = + scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */, + PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor); + return std::make_unique<InputSurface>(surfaceControl, width, height); + } + + static std::unique_ptr<InputSurface> makeBufferInputSurface( + const sp<SurfaceComposerClient> &scc, int width, int height) { + sp<SurfaceControl> surfaceControl = + scc->createSurface(String8("Test Buffer Surface"), width, height, + PIXEL_FORMAT_RGBA_8888, 0 /* flags */); + return std::make_unique<InputSurface>(surfaceControl, width, height); + } + + static std::unique_ptr<InputSurface> makeContainerInputSurface( + const sp<SurfaceComposerClient> &scc, int width, int height) { + sp<SurfaceControl> surfaceControl = + scc->createSurface(String8("Test Container Surface"), 0 /* bufHeight */, + 0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceContainer); + return std::make_unique<InputSurface>(surfaceControl, width, height); + } + + InputEvent* consumeEvent() { + waitForEventAvailable(); + + InputEvent *ev; + uint32_t seqId; + status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev); + if (consumed != OK) { + return nullptr; + } + mInputConsumer->sendFinishedSignal(seqId, true); + return ev; + } + + void expectTap(int x, int y) { + InputEvent* ev = consumeEvent(); + EXPECT_TRUE(ev != nullptr); + EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION); + MotionEvent* mev = static_cast<MotionEvent*>(ev); + EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction()); + EXPECT_EQ(x, mev->getX(0)); + EXPECT_EQ(y, mev->getY(0)); + + ev = consumeEvent(); + EXPECT_TRUE(ev != nullptr); + EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION); + mev = static_cast<MotionEvent*>(ev); + EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction()); + } + + void expectMotionEvent(int motionEventType, int x, int y) { + InputEvent *ev = consumeEvent(); + ASSERT_NE(ev, nullptr); + ASSERT_EQ(ev->getType(), AINPUT_EVENT_TYPE_MOTION); + MotionEvent *mev = static_cast<MotionEvent *>(ev); + EXPECT_EQ(motionEventType, mev->getAction()); + EXPECT_EQ(x, mev->getX(0)); + EXPECT_EQ(y, mev->getY(0)); + } + + void expectNoMotionEvent(int motionEventType) { + InputEvent *ev = consumeEvent(); + if (ev == nullptr || ev->getType() != AINPUT_EVENT_TYPE_MOTION) { + // Didn't find an event or a motion event so assume action didn't occur. + return; + } + + MotionEvent *mev = static_cast<MotionEvent *>(ev); + EXPECT_NE(motionEventType, mev->getAction()); + } + + ~InputSurface() { + mInputFlinger->unregisterInputChannel(mServerChannel); + } + + void doTransaction(std::function<void(SurfaceComposerClient::Transaction&, + const sp<SurfaceControl>&)> transactionBody) { + SurfaceComposerClient::Transaction t; + transactionBody(t, mSurfaceControl); + t.apply(true); + } + + void showAt(int x, int y) { + SurfaceComposerClient::Transaction t; + t.show(mSurfaceControl); + t.setInputWindowInfo(mSurfaceControl, mInputInfo); + t.setLayer(mSurfaceControl, LAYER_BASE); + t.setPosition(mSurfaceControl, x, y); + t.setCrop_legacy(mSurfaceControl, Rect(0, 0, 100, 100)); + t.setAlpha(mSurfaceControl, 1); + t.apply(true); + } + +private: + void waitForEventAvailable() { + struct pollfd fd; + + fd.fd = mClientChannel->getFd(); + fd.events = POLLIN; + poll(&fd, 1, 3000); + } + + void populateInputInfo(int width, int height) { + mInputInfo.token = mServerChannel->getToken(); + mInputInfo.name = "Test info"; + mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL; + mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION; + mInputInfo.dispatchingTimeout = 100000; + mInputInfo.globalScaleFactor = 1.0; + mInputInfo.canReceiveKeys = true; + mInputInfo.hasFocus = true; + mInputInfo.hasWallpaper = false; + mInputInfo.paused = false; + + mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height)); + + // TODO: Fill in from SF? + mInputInfo.ownerPid = 11111; + mInputInfo.ownerUid = 11111; + mInputInfo.inputFeatures = 0; + mInputInfo.displayId = 0; + + InputApplicationInfo aInfo; + aInfo.token = new BBinder(); + aInfo.name = "Test app info"; + aInfo.dispatchingTimeout = 100000; + + mInputInfo.applicationInfo = aInfo; + } +public: + sp<SurfaceControl> mSurfaceControl; + sp<InputChannel> mServerChannel, mClientChannel; + sp<IInputFlinger> mInputFlinger; + + InputWindowInfo mInputInfo; + + PreallocatedInputEventFactory mInputEventFactory; + InputConsumer* mInputConsumer; +}; + +class InputSurfacesTest : public ::testing::Test { +public: + InputSurfacesTest() { + ProcessState::self()->startThreadPool(); + } + + void SetUp() { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + const auto display = mComposerClient->getInternalDisplayToken(); + ASSERT_FALSE(display == nullptr); + + DisplayInfo info; + ASSERT_EQ(NO_ERROR, mComposerClient->getDisplayInfo(display, &info)); + + // After a new buffer is queued, SurfaceFlinger is notified and will + // latch the new buffer on next vsync. Let's heuristically wait for 3 + // vsyncs. + mBufferPostDelay = int32_t(1e6 / info.fps) * 3; + } + + void TearDown() { + mComposerClient->dispose(); + } + + std::unique_ptr<InputSurface> makeSurface(int width, int height) { + return InputSurface::makeColorInputSurface(mComposerClient, width, height); + } + + void postBuffer(const sp<SurfaceControl> &layer) { + // wait for previous transactions (such as setSize) to complete + Transaction().apply(true); + ANativeWindow_Buffer buffer = {}; + EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr)); + ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost()); + // Request an empty transaction to get applied synchronously to ensure the buffer is + // latched. + Transaction().apply(true); + usleep(mBufferPostDelay); + } + + sp<SurfaceComposerClient> mComposerClient; + int32_t mBufferPostDelay; +}; + +void injectTap(int x, int y) { + char *buf1, *buf2; + asprintf(&buf1, "%d", x); + asprintf(&buf2, "%d", y); + if (fork() == 0) { + execlp("input", "input", "tap", buf1, buf2, NULL); + } +} + +void injectMotionEvent(std::string event, int x, int y) { + char *buf1, *buf2; + asprintf(&buf1, "%d", x); + asprintf(&buf2, "%d", y); + if (fork() == 0) { + execlp("input", "input", "motionevent", event.c_str(), buf1, buf2, NULL); + } +} + +TEST_F(InputSurfacesTest, can_receive_input) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(100, 100); + + injectTap(101, 101); + + EXPECT_TRUE(surface->consumeEvent() != nullptr); +} + +TEST_F(InputSurfacesTest, input_respects_positioning) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(100, 100); + + std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100); + surface2->showAt(200, 200); + + injectTap(201, 201); + surface2->expectTap(1, 1); + + injectTap(101, 101); + surface->expectTap(1, 1); + + surface2->doTransaction([](auto &t, auto &sc) { + t.setPosition(sc, 100, 100); + }); + surface->doTransaction([](auto &t, auto &sc) { + t.setPosition(sc, 200, 200); + }); + + injectTap(101, 101); + surface2->expectTap(1, 1); + + injectTap(201, 201); + surface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, input_respects_layering) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100); + + surface->showAt(10, 10); + surface2->showAt(10, 10); + + surface->doTransaction([](auto &t, auto &sc) { + t.setLayer(sc, LAYER_BASE + 1); + }); + + injectTap(11, 11); + surface->expectTap(1, 1); + + surface2->doTransaction([](auto &t, auto &sc) { + t.setLayer(sc, LAYER_BASE + 1); + }); + + injectTap(11, 11); + surface2->expectTap(1, 1); + + surface2->doTransaction([](auto &t, auto &sc) { + t.hide(sc); + }); + + injectTap(11, 11); + surface->expectTap(1, 1); +} + +// Surface Insets are set to offset the client content and draw a border around the client surface +// (such as shadows in dialogs). Inputs sent to the client are offset such that 0,0 is the start +// of the client content. +TEST_F(InputSurfacesTest, input_respects_surface_insets) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); + bgSurface->showAt(100, 100); + + fgSurface->mInputInfo.surfaceInset = 5; + fgSurface->showAt(100, 100); + + injectTap(106, 106); + fgSurface->expectTap(1, 1); + + injectTap(101, 101); + bgSurface->expectTap(1, 1); +} + +// Ensure a surface whose insets are cropped, handles the touch offset correctly. ref:b/120413463 +TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { + std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100); + parentSurface->showAt(100, 100); + + childSurface->mInputInfo.surfaceInset = 10; + childSurface->showAt(100, 100); + + childSurface->doTransaction([&](auto &t, auto &sc) { + t.setPosition(sc, -5, -5); + t.reparent(sc, parentSurface->mSurfaceControl->getHandle()); + }); + + injectTap(106, 106); + childSurface->expectTap(1, 1); + + injectTap(101, 101); + parentSurface->expectTap(1, 1); +} + +// Ensure a surface whose insets are scaled, handles the touch offset correctly. +TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); + bgSurface->showAt(100, 100); + + fgSurface->mInputInfo.surfaceInset = 5; + fgSurface->showAt(100, 100); + + fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); }); + + // expect = touch / scale - inset + injectTap(112, 124); + fgSurface->expectTap(1, 1); + + injectTap(101, 101); + bgSurface->expectTap(1, 1); +} + +// Ensure we ignore transparent region when getting screen bounds when positioning input frame. +TEST_F(InputSurfacesTest, input_ignores_transparent_region) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->doTransaction([](auto &t, auto &sc) { + Region transparentRegion(Rect(0, 0, 10, 10)); + t.setTransparentRegionHint(sc, transparentRegion); + }); + surface->showAt(100, 100); + injectTap(101, 101); + surface->expectTap(1, 1); +} + +// Ensure we send the input to the right surface when the surface visibility changes due to the +// first buffer being submitted. ref: b/120839715 +TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> bufferSurface = + InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); + + bgSurface->showAt(10, 10); + bufferSurface->showAt(10, 10); + + injectTap(11, 11); + bgSurface->expectTap(1, 1); + + postBuffer(bufferSurface->mSurfaceControl); + injectTap(11, 11); + bufferSurface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> bufferSurface = + InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); + postBuffer(bufferSurface->mSurfaceControl); + + bgSurface->showAt(10, 10); + bufferSurface->showAt(10, 10); + + injectTap(11, 11); + bufferSurface->expectTap(1, 1); + + bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); }); + + injectTap(11, 11); + bgSurface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); + + bgSurface->showAt(10, 10); + fgSurface->showAt(10, 10); + + injectTap(11, 11); + fgSurface->expectTap(1, 1); + + fgSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); }); + + injectTap(11, 11); + bgSurface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> containerSurface = + InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); + + bgSurface->showAt(10, 10); + containerSurface->showAt(10, 10); + + injectTap(11, 11); + containerSurface->expectTap(1, 1); + + containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); }); + + injectTap(11, 11); + bgSurface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, transfer_touch_focus) { + std::unique_ptr<InputSurface> fromSurface = makeSurface(100, 100); + + fromSurface->showAt(10, 10); + injectMotionEvent("DOWN", 11, 11); + fromSurface->expectMotionEvent(AMOTION_EVENT_ACTION_DOWN, 1, 1); + + std::unique_ptr<InputSurface> toSurface = makeSurface(100, 100); + toSurface->showAt(10, 10); + + sp<IBinder> fromToken = fromSurface->mServerChannel->getToken(); + sp<IBinder> toToken = toSurface->mServerChannel->getToken(); + SurfaceComposerClient::Transaction t; + t.transferTouchFocus(fromToken, toToken).apply(true); + + injectMotionEvent("UP", 11, 11); + toSurface->expectMotionEvent(AMOTION_EVENT_ACTION_UP, 1, 1); + fromSurface->expectNoMotionEvent(AMOTION_EVENT_ACTION_UP); +} + +TEST_F(InputSurfacesTest, input_respects_outscreen) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(-1, -1); + + injectTap(0, 0); + surface->expectTap(1, 1); +} +} +}
diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp index ccd674f..b60995a 100644 --- a/libs/gui/tests/FillBuffer.cpp +++ b/libs/gui/tests/FillBuffer.cpp
@@ -93,11 +93,11 @@ android_native_buffer_t* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); - uint8_t* img = NULL; + uint8_t* img = nullptr; ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img))); fillRGBA8Buffer(img, buf->getWidth(), buf->getHeight(), buf->getStride());
diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp index a91552f..a1405fc 100644 --- a/libs/gui/tests/GLTest.cpp +++ b/libs/gui/tests/GLTest.cpp
@@ -50,7 +50,7 @@ ASSERT_EQ(EGL_SUCCESS, eglGetError()); char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS"); - if (displaySecsEnv != NULL) { + if (displaySecsEnv != nullptr) { mDisplaySecs = atoi(displaySecsEnv); if (mDisplaySecs < 0) { mDisplaySecs = 0; @@ -67,7 +67,7 @@ String8("Test Surface"), getSurfaceWidth(), getSurfaceHeight(), PIXEL_FORMAT_RGB_888, 0); - ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl != nullptr); ASSERT_TRUE(mSurfaceControl->isValid()); Transaction t; @@ -117,7 +117,7 @@ sleep(mDisplaySecs); } - if (mComposerClient != NULL) { + if (mComposerClient != nullptr) { mComposerClient->dispose(); } if (mEglContext != EGL_NO_CONTEXT) { @@ -171,7 +171,7 @@ EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config, sp<ANativeWindow>& window) const { - return eglCreateWindowSurface(display, config, window.get(), NULL); + return eglCreateWindowSurface(display, config, window.get(), nullptr); } ::testing::AssertionResult GLTest::checkPixel(int x, int y, @@ -256,7 +256,7 @@ GLuint shader = glCreateShader(shaderType); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); if (shader) { - glShaderSource(shader, 1, &pSource, NULL); + glShaderSource(shader, 1, &pSource, nullptr); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); glCompileShader(shader); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); @@ -270,7 +270,7 @@ if (infoLen) { char* buf = (char*) malloc(infoLen); if (buf) { - glGetShaderInfoLog(shader, infoLen, NULL, buf); + glGetShaderInfoLog(shader, infoLen, nullptr, buf); printf("Shader compile log:\n%s\n", buf); free(buf); FAIL(); @@ -278,7 +278,7 @@ } else { char* buf = (char*) malloc(0x1000); if (buf) { - glGetShaderInfoLog(shader, 0x1000, NULL, buf); + glGetShaderInfoLog(shader, 0x1000, nullptr, buf); printf("Shader compile log:\n%s\n", buf); free(buf); FAIL(); @@ -322,7 +322,7 @@ if (bufLength) { char* buf = (char*) malloc(bufLength); if (buf) { - glGetProgramInfoLog(program, bufLength, NULL, buf); + glGetProgramInfoLog(program, bufLength, nullptr, buf); printf("Program link log:\n%s\n", buf); free(buf); FAIL();
diff --git a/libs/gui/tests/GLTest.h b/libs/gui/tests/GLTest.h index f0d27a8..f290b3c 100644 --- a/libs/gui/tests/GLTest.h +++ b/libs/gui/tests/GLTest.h
@@ -39,7 +39,7 @@ mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT), - mGlConfig(NULL) { + mGlConfig(nullptr) { } virtual void SetUp();
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp index a35cf11..aef7aed 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -228,9 +228,9 @@ void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence, sp<GraphicBuffer> *buffer) { - ASSERT_TRUE(slot != NULL); - ASSERT_TRUE(fence != NULL); - ASSERT_TRUE(buffer != NULL); + ASSERT_TRUE(slot != nullptr); + ASSERT_TRUE(fence != nullptr); + ASSERT_TRUE(buffer != nullptr); ASSERT_NO_FATAL_FAILURE(ConnectProducer()); @@ -263,7 +263,7 @@ EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN, TEST_API, TEST_CONTROLLED_BY_APP, - /*output*/NULL)); + /*output*/nullptr)); // Invalid API returns bad value EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN, @@ -359,7 +359,7 @@ // TODO: Consider documented the above enums as unsupported or make a new enum for IGBP // Value was NULL - EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/NULL)); + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/nullptr)); ASSERT_OK(mConsumer->consumerDisconnect()); @@ -465,7 +465,7 @@ // Fence was NULL { - sp<Fence> nullFence = NULL; + sp<Fence> nullFence = nullptr; IGraphicBufferProducer::QueueBufferInput input = QueueBufferInputBuilder().setFence(nullFence).build(); @@ -695,10 +695,7 @@ sp<Fence> fence; sp<GraphicBuffer> buffer; - if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) { - // TODO(b/38137191): Implement BufferHubProducer::detachBuffer - ASSERT_EQ(NO_INIT, mProducer->detachNextBuffer(&buffer, &fence)); - } + ASSERT_EQ(NO_INIT, mProducer->detachNextBuffer(&buffer, &fence)); } TEST_P(IGraphicBufferProducerTest, @@ -735,10 +732,7 @@ ASSERT_OK(mProducer->disconnect(TEST_API)); - if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) { - // TODO(b/38137191): Implement BufferHubProducer::detachBuffer - ASSERT_EQ(NO_INIT, mProducer->detachBuffer(slot)); - } + ASSERT_EQ(NO_INIT, mProducer->detachBuffer(slot)); } TEST_P(IGraphicBufferProducerTest, @@ -778,18 +772,29 @@ sp<GraphicBuffer> buffer; setupDequeueRequestBuffer(&slot, &fence, &buffer); + ASSERT_TRUE(buffer != nullptr); - if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) { - // TODO(b/38137191): Implement BufferHubProducer::detachBuffer - ASSERT_OK(mProducer->detachBuffer(slot)); - } + ASSERT_OK(mProducer->detachBuffer(slot)); + EXPECT_OK(buffer->initCheck()); ASSERT_OK(mProducer->disconnect(TEST_API)); - if (GetParam() == USE_BUFFER_QUEUE_PRODUCER) { - // TODO(b/69981968): Implement BufferHubProducer::attachBuffer - ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer)); - } + ASSERT_EQ(NO_INIT, mProducer->attachBuffer(&slot, buffer)); +} + +TEST_P(IGraphicBufferProducerTest, DetachThenAttach_Succeeds) { + int slot = -1; + sp<Fence> fence; + sp<GraphicBuffer> buffer; + + setupDequeueRequestBuffer(&slot, &fence, &buffer); + ASSERT_TRUE(buffer != nullptr); + + ASSERT_OK(mProducer->detachBuffer(slot)); + EXPECT_OK(buffer->initCheck()); + + EXPECT_OK(mProducer->attachBuffer(&slot, buffer)); + EXPECT_OK(buffer->initCheck()); } #if USE_BUFFER_HUB_AS_BUFFER_QUEUE
diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp index bb6b8a5..acd4297 100644 --- a/libs/gui/tests/Malicious.cpp +++ b/libs/gui/tests/Malicious.cpp
@@ -27,7 +27,7 @@ class ProxyBQP : public BnGraphicBufferProducer { public: - ProxyBQP(const sp<IGraphicBufferProducer>& producer) : mProducer(producer) {} + explicit ProxyBQP(const sp<IGraphicBufferProducer>& producer) : mProducer(producer) {} // Pass through calls to mProducer status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override { @@ -102,7 +102,7 @@ class MaliciousBQP : public ProxyBQP { public: - MaliciousBQP(const sp<IGraphicBufferProducer>& producer) : ProxyBQP(producer) {} + explicit MaliciousBQP(const sp<IGraphicBufferProducer>& producer) : ProxyBQP(producer) {} void beMalicious(int32_t value) { mMaliciousValue = value; }
diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp index 3a25ac5..7d3d4aa 100644 --- a/libs/gui/tests/MultiTextureConsumer_test.cpp +++ b/libs/gui/tests/MultiTextureConsumer_test.cpp
@@ -47,7 +47,7 @@ GLTest::TearDown(); } virtual EGLint const* getContextAttribs() { - return NULL; + return nullptr; } virtual EGLint const* getConfigAttribs() { static EGLint sDefaultConfigAttribs[] = { @@ -105,7 +105,7 @@ glClear(GL_COLOR_BUFFER_BIT); for (int i=0 ; i<8 ; i++) { - mSurface->lock(&buffer, NULL); + mSurface->lock(&buffer, nullptr); memset(buffer.bits, (i&7) * 0x20, buffer.stride * buffer.height * 4); mSurface->unlockAndPost();
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp new file mode 100644 index 0000000..d33ecfb --- /dev/null +++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -0,0 +1,300 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <thread> + +#include <binder/ProcessState.h> +#include <gui/DisplayEventReceiver.h> +#include <gui/IRegionSamplingListener.h> +#include <gui/ISurfaceComposer.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <private/gui/ComposerService.h> +#include <utils/Looper.h> + +using namespace std::chrono_literals; + +namespace android::test { + +struct ChoreographerSync { + ChoreographerSync(DisplayEventReceiver& receiver) : receiver_(receiver) {} + ~ChoreographerSync() = default; + + void notify() const { + std::unique_lock<decltype(mutex_)> lk(mutex_); + + auto check_event = [](auto const& ev) -> bool { + return ev.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC; + }; + DisplayEventReceiver::Event ev_; + int evs = receiver_.getEvents(&ev_, 1); + auto vsync_event_found = check_event(ev_); + while (evs) { + evs = receiver_.getEvents(&ev_, 1); + vsync_event_found |= check_event(ev_); + } + + if (vsync_event_found) { + notification_arrived_ = true; + cv_.notify_all(); + } + } + + void wait_vsync_notify() const { + std::unique_lock<decltype(mutex_)> lk(mutex_); + cv_.wait(lk, [this] { return notification_arrived_; }); + notification_arrived_ = false; + } + +private: + ChoreographerSync(ChoreographerSync const&) = delete; + ChoreographerSync& operator=(ChoreographerSync const&) = delete; + + std::mutex mutable mutex_; + std::condition_variable mutable cv_; + bool mutable notification_arrived_ = false; + DisplayEventReceiver& receiver_; +}; + +struct ChoreographerSim { + static std::unique_ptr<ChoreographerSim> make() { + auto receiver = std::make_unique<DisplayEventReceiver>(); + if (!receiver || receiver->initCheck() == NO_INIT) { + ALOGE("No display reciever"); + return nullptr; + } + return std::unique_ptr<ChoreographerSim>(new ChoreographerSim(std::move(receiver))); + } + + ~ChoreographerSim() { + poll_ = false; + looper->wake(); + choreographer_thread_.join(); + } + + void request_render_wait(std::function<void()> const& render_fn) { + display_event_receiver_->requestNextVsync(); + choreographer_.wait_vsync_notify(); + render_fn(); + + // Purpose is to make sure that the content is latched by the time we sample. + // Waiting one vsync after queueing could still race with vsync, so wait for two, after + // which the content is pretty reliably on screen. + display_event_receiver_->requestNextVsync(); + choreographer_.wait_vsync_notify(); + display_event_receiver_->requestNextVsync(); + choreographer_.wait_vsync_notify(); + } + +private: + ChoreographerSim(std::unique_ptr<DisplayEventReceiver> receiver) + : display_event_receiver_{std::move(receiver)}, + choreographer_{*display_event_receiver_}, + looper{new Looper(false)} { + choreographer_thread_ = std::thread([this] { + auto vsync_notify_fd = display_event_receiver_->getFd(); + looper->addFd(vsync_notify_fd, 0, Looper::EVENT_INPUT, + [](int /*fd*/, int /*events*/, void* data) -> int { + if (!data) return 0; + reinterpret_cast<ChoreographerSync*>(data)->notify(); + return 1; + }, + const_cast<void*>(reinterpret_cast<void const*>(&choreographer_))); + + while (poll_) { + auto const poll_interval = + std::chrono::duration_cast<std::chrono::milliseconds>(1s).count(); + auto rc = looper->pollOnce(poll_interval); + if ((rc != Looper::POLL_CALLBACK) && (rc != Looper::POLL_WAKE)) + ALOGW("Vsync Looper returned: %i\n", rc); + } + }); + } + + ChoreographerSim(ChoreographerSim const&) = delete; + ChoreographerSim& operator=(ChoreographerSim const&) = delete; + + std::unique_ptr<DisplayEventReceiver> const display_event_receiver_; + ChoreographerSync const choreographer_; + sp<Looper> looper; + std::thread choreographer_thread_; + std::atomic<bool> poll_{true}; +}; + +struct Listener : BnRegionSamplingListener { + void onSampleCollected(float medianLuma) override { + std::unique_lock<decltype(mutex)> lk(mutex); + received = true; + mLuma = medianLuma; + cv.notify_all(); + }; + bool wait_event(std::chrono::milliseconds timeout) { + std::unique_lock<decltype(mutex)> lk(mutex); + return cv.wait_for(lk, timeout, [this] { return received; }); + } + + float luma() { + std::unique_lock<decltype(mutex)> lk(mutex); + return mLuma; + } + + void reset() { + std::unique_lock<decltype(mutex)> lk(mutex); + received = false; + } + +private: + std::condition_variable cv; + std::mutex mutex; + bool received = false; + float mLuma = -0.0f; +}; + +// Hoisted to TestSuite setup to avoid flake in test (b/124675919) +std::unique_ptr<ChoreographerSim> gChoreographerSim = nullptr; + +struct RegionSamplingTest : ::testing::Test { +protected: + RegionSamplingTest() { ProcessState::self()->startThreadPool(); } + + static void SetUpTestSuite() { + gChoreographerSim = ChoreographerSim::make(); + ASSERT_NE(gChoreographerSim, nullptr); + } + + void SetUp() override { + mSurfaceComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mSurfaceComposerClient->initCheck()); + + mBackgroundLayer = + mSurfaceComposerClient->createSurface(String8("Background RegionSamplingTest"), 0, + 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor); + uint32_t layerPositionBottom = 0x7E000000; + SurfaceComposerClient::Transaction{} + .setLayer(mBackgroundLayer, layerPositionBottom) + .setPosition(mBackgroundLayer, 100, 100) + .setColor(mBackgroundLayer, half3{0.5, 0.5, 0.5}) + .show(mBackgroundLayer) + .apply(); + + mContentLayer = mSurfaceComposerClient->createSurface(String8("Content RegionSamplingTest"), + 300, 300, PIXEL_FORMAT_RGBA_8888, 0); + + SurfaceComposerClient::Transaction{} + .setLayer(mContentLayer, layerPositionBottom + 1) + .setPosition(mContentLayer, 100, 100) + .setColor(mContentLayer, half3{0.5, 0.5, 0.5}) + .show(mContentLayer) + .apply(); + + mTopLayer = mSurfaceComposerClient->createSurface(String8("TopLayer RegionSamplingTest"), 0, + 0, PIXEL_FORMAT_RGBA_8888, 0); + SurfaceComposerClient::Transaction{} + .setLayer(mTopLayer, layerPositionBottom + 2) + .setPosition(mTopLayer, 0, 0) + .show(mBackgroundLayer) + .apply(); + } + + void fill_render(uint32_t rgba_value) { + auto surface = mContentLayer->getSurface(); + ANativeWindow_Buffer outBuffer; + status_t status = surface->lock(&outBuffer, NULL); + ASSERT_EQ(status, android::OK); + auto b = reinterpret_cast<uint32_t*>(outBuffer.bits); + for (auto i = 0; i < outBuffer.height; i++) { + for (auto j = 0; j < outBuffer.width; j++) { + b[j] = rgba_value; + } + b += outBuffer.stride; + } + + gChoreographerSim->request_render_wait([&surface] { surface->unlockAndPost(); }); + } + + sp<SurfaceComposerClient> mSurfaceComposerClient; + sp<SurfaceControl> mBackgroundLayer; + sp<SurfaceControl> mContentLayer; + sp<SurfaceControl> mTopLayer; + + uint32_t const rgba_green = 0xFF00FF00; + float const luma_green = 0.7152; + uint32_t const rgba_blue = 0xFFFF0000; + float const luma_blue = 0.0722; + float const error_margin = 0.01; + float const luma_gray = 0.50; +}; + +TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) { + fill_render(rgba_green); + + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<Listener> listener = new Listener(); + const Rect sampleArea{100, 100, 200, 200}; + composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); + + EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; + EXPECT_NEAR(listener->luma(), luma_green, error_margin); + + composer->removeRegionSamplingListener(listener); +} + +TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) { + fill_render(rgba_green); + + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<Listener> listener = new Listener(); + const Rect sampleArea{100, 100, 200, 200}; + composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); + + EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; + EXPECT_NEAR(listener->luma(), luma_green, error_margin); + + listener->reset(); + + fill_render(rgba_blue); + EXPECT_TRUE(listener->wait_event(300ms)) + << "timed out waiting for 2nd luma event to be received"; + EXPECT_NEAR(listener->luma(), luma_blue, error_margin); + + composer->removeRegionSamplingListener(listener); +} + +TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) { + fill_render(rgba_green); + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<Listener> greenListener = new Listener(); + const Rect greenSampleArea{100, 100, 200, 200}; + composer->addRegionSamplingListener(greenSampleArea, mTopLayer->getHandle(), greenListener); + + sp<Listener> grayListener = new Listener(); + const Rect graySampleArea{500, 100, 600, 200}; + composer->addRegionSamplingListener(graySampleArea, mTopLayer->getHandle(), grayListener); + + EXPECT_TRUE(grayListener->wait_event(300ms)) + << "timed out waiting for luma event to be received"; + EXPECT_NEAR(grayListener->luma(), luma_gray, error_margin); + EXPECT_TRUE(greenListener->wait_event(300ms)) + << "timed out waiting for luma event to be received"; + EXPECT_NEAR(greenListener->luma(), luma_green, error_margin); + + composer->removeRegionSamplingListener(greenListener); + composer->removeRegionSamplingListener(grayListener); +} + +} // namespace android::test
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp new file mode 100644 index 0000000..9891587 --- /dev/null +++ b/libs/gui/tests/SamplingDemo.cpp
@@ -0,0 +1,135 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#define LOG_TAG "SamplingTest" + +#include <chrono> +#include <thread> + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <gui/IRegionSamplingListener.h> +#include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/SurfaceControl.h> +#include <private/gui/ComposerService.h> +#include <utils/Trace.h> + +using namespace std::chrono_literals; + +namespace android { + +class Button : public BnRegionSamplingListener { +public: + Button(const char* name, const Rect& samplingArea) { + sp<SurfaceComposerClient> client = new SurfaceComposerClient; + + mButton = client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor); + + const int32_t width = samplingArea.getWidth(); + const int32_t height = samplingArea.getHeight(); + + SurfaceComposerClient::Transaction{} + .setLayer(mButton, 0x7fffffff) + .setCrop_legacy(mButton, + {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING}) + .setPosition(mButton, samplingArea.left + BUTTON_PADDING, + samplingArea.top + BUTTON_PADDING) + .setColor(mButton, half3{1, 1, 1}) + .show(mButton) + .apply(); + + mButtonBlend = client->createSurface(String8(name) + "Blend", 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor); + + SurfaceComposerClient::Transaction{} + .setLayer(mButtonBlend, 0x7ffffffe) + .setCrop_legacy(mButtonBlend, + {0, 0, width - 2 * SAMPLE_AREA_PADDING, + height - 2 * SAMPLE_AREA_PADDING}) + .setPosition(mButtonBlend, samplingArea.left + SAMPLE_AREA_PADDING, + samplingArea.top + SAMPLE_AREA_PADDING) + .setColor(mButtonBlend, half3{1, 1, 1}) + .setAlpha(mButtonBlend, 0.2) + .show(mButtonBlend) + .apply(true); + + const bool HIGHLIGHT_SAMPLING_AREA = false; + if (HIGHLIGHT_SAMPLING_AREA) { + mSamplingArea = + client->createSurface(String8("SamplingArea"), 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor); + + SurfaceComposerClient::Transaction{} + .setLayer(mSamplingArea, 0x7ffffffd) + .setCrop_legacy(mSamplingArea, {0, 0, 100, 32}) + .setPosition(mSamplingArea, 490, 1606) + .setColor(mSamplingArea, half3{0, 1, 0}) + .setAlpha(mSamplingArea, 0.1) + .show(mSamplingArea) + .apply(); + } + } + + sp<IBinder> getStopLayerHandle() { return mButtonBlend->getHandle(); } + +private: + static const int32_t BLEND_WIDTH = 2; + static const int32_t SAMPLE_AREA_PADDING = 8; + static const int32_t BUTTON_PADDING = BLEND_WIDTH + SAMPLE_AREA_PADDING; + + void setColor(float color) { + const float complement = std::fmod(color + 0.5f, 1.0f); + SurfaceComposerClient::Transaction{} + .setColor(mButton, half3{complement, complement, complement}) + .setColor(mButtonBlend, half3{color, color, color}) + .apply(); + } + + void onSampleCollected(float medianLuma) override { + ATRACE_CALL(); + setColor(medianLuma); + } + + sp<SurfaceComposerClient> mClient; + sp<SurfaceControl> mButton; + sp<SurfaceControl> mButtonBlend; + sp<SurfaceControl> mSamplingArea; +}; + +} // namespace android + +using namespace android; + +int main(int, const char**) { + const Rect homeButtonArea{490, 1606, 590, 1654}; + sp<android::Button> homeButton = new android::Button("HomeButton", homeButtonArea); + const Rect backButtonArea{200, 1606, 248, 1654}; + sp<android::Button> backButton = new android::Button("BackButton", backButtonArea); + + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + composer->addRegionSamplingListener(homeButtonArea, homeButton->getStopLayerHandle(), + homeButton); + composer->addRegionSamplingListener(backButtonArea, backButton->getStopLayerHandle(), + backButton); + + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); + + return 0; +}
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index d5b2f00..65e09f2 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -29,7 +29,6 @@ #include <utils/Thread.h> extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); -#define CROP_EXT_STR "EGL_ANDROID_image_crop" namespace android { @@ -39,7 +38,7 @@ mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT), - mEglConfig(NULL) { + mEglConfig(nullptr) { } virtual void SetUp() { @@ -82,7 +81,7 @@ ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mEglSurface); - mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, 0); + mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mEglContext); @@ -127,7 +126,7 @@ TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) { sp<IGraphicBufferProducer> ist(mSTC->getIGraphicBufferProducer()); - ASSERT_TRUE(ist != NULL); + ASSERT_TRUE(ist != nullptr); } TEST_F(SurfaceTextureClientTest, QueuesToWindowCompositorIsFalse) { @@ -155,7 +154,7 @@ EXPECT_TRUE(eglInitialize(dpy, &majorVersion, &minorVersion)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); - EGLConfig myConfig = {0}; + EGLConfig myConfig = {nullptr}; EGLint numConfigs = 0; EGLint configAttribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, @@ -172,7 +171,7 @@ ASSERT_EQ(EGL_SUCCESS, eglGetError()); EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, mANW.get(), - NULL); + nullptr); EXPECT_NE(EGL_NO_SURFACE, eglSurface); EXPECT_EQ(EGL_SUCCESS, eglGetError()); @@ -185,7 +184,7 @@ TEST_F(SurfaceTextureClientTest, EglSwapBuffersAbandonErrorIsEglBadSurface) { - EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mANW.get(), NULL); + EGLSurface eglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, mANW.get(), nullptr); EXPECT_NE(EGL_NO_SURFACE, eglSurface); EXPECT_EQ(EGL_SUCCESS, eglGetError()); @@ -638,18 +637,6 @@ } TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) { - // Query to see if the image crop extension exists - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); - const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); - size_t cropExtLen = strlen(CROP_EXT_STR); - size_t extsLen = strlen(exts); - bool equal = !strcmp(CROP_EXT_STR, exts); - bool atStart = !strncmp(CROP_EXT_STR " ", exts, cropExtLen+1); - bool atEnd = (cropExtLen+1) < extsLen && - !strcmp(" " CROP_EXT_STR, exts + extsLen - (cropExtLen+1)); - bool inMiddle = strstr(exts, " " CROP_EXT_STR " "); - bool hasEglAndroidImageCrop = equal || atStart || atEnd || inMiddle; - android_native_buffer_t* buf[3]; float mtx[16] = {}; android_native_rect_t crop; @@ -669,17 +656,15 @@ ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 6)); // frees buffers mST->getTransformMatrix(mtx); - // If the egl image crop extension is not present, this accounts for the - // .5 texel shrink for each edge that's included in the transform matrix - // to avoid texturing outside the crop region. Otherwise the crop is not - // included in the transform matrix. - EXPECT_EQ(hasEglAndroidImageCrop ? 1 : 0.5, mtx[0]); + // This accounts for the .5 texel shrink for each edge that's included in + // the transform matrix to avoid texturing outside the crop region. + EXPECT_EQ(0.5f, mtx[0]); EXPECT_EQ(0.f, mtx[1]); EXPECT_EQ(0.f, mtx[2]); EXPECT_EQ(0.f, mtx[3]); EXPECT_EQ(0.f, mtx[4]); - EXPECT_EQ(hasEglAndroidImageCrop ? -1 : -0.5, mtx[5]); + EXPECT_EQ(-0.5f, mtx[5]); EXPECT_EQ(0.f, mtx[6]); EXPECT_EQ(0.f, mtx[7]); @@ -688,8 +673,8 @@ EXPECT_EQ(1.f, mtx[10]); EXPECT_EQ(0.f, mtx[11]); - EXPECT_EQ(hasEglAndroidImageCrop ? 0 : 0.0625f, mtx[12]); - EXPECT_EQ(hasEglAndroidImageCrop ? 1 : 0.5625f, mtx[13]); + EXPECT_EQ(0.0625f, mtx[12]); + EXPECT_EQ(0.5625f, mtx[13]); EXPECT_EQ(0.f, mtx[14]); EXPECT_EQ(1.f, mtx[15]); } @@ -753,7 +738,7 @@ ASSERT_EQ(EGL_SUCCESS, eglGetError()); mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, - 0); + nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mEglContext); @@ -765,7 +750,7 @@ GLConsumer::TEXTURE_EXTERNAL, true, false)); sp<Surface> stc(new Surface(producer)); mEglSurfaces[i] = eglCreateWindowSurface(mEglDisplay, myConfig, - static_cast<ANativeWindow*>(stc.get()), NULL); + static_cast<ANativeWindow*>(stc.get()), nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mEglSurfaces[i]); }
diff --git a/libs/gui/tests/SurfaceTextureFBO.h b/libs/gui/tests/SurfaceTextureFBO.h index 7f1ae84..70f988d 100644 --- a/libs/gui/tests/SurfaceTextureFBO.h +++ b/libs/gui/tests/SurfaceTextureFBO.h
@@ -34,7 +34,7 @@ glGenTextures(1, &mFboTex); glBindTexture(GL_TEXTURE_2D, mFboTex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSurfaceWidth(), - getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glBindTexture(GL_TEXTURE_2D, 0); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp index 0134273..f34561f 100644 --- a/libs/gui/tests/SurfaceTextureFBO_test.cpp +++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp
@@ -39,12 +39,12 @@ android_native_buffer_t* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); // Fill the buffer with green - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255, 0, 255); @@ -63,7 +63,7 @@ ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); buf = GraphicBuffer::from(anb);
diff --git a/libs/gui/tests/SurfaceTextureGLThreadToGL.h b/libs/gui/tests/SurfaceTextureGLThreadToGL.h index 2ce20eb..03975b1 100644 --- a/libs/gui/tests/SurfaceTextureGLThreadToGL.h +++ b/libs/gui/tests/SurfaceTextureGLThreadToGL.h
@@ -158,7 +158,7 @@ } virtual void TearDown() { - if (mProducerThread != NULL) { + if (mProducerThread != nullptr) { mProducerThread->requestExitAndWait(); } mProducerThread.clear(); @@ -167,7 +167,7 @@ } void runProducerThread(const sp<ProducerThread> producerThread) { - ASSERT_TRUE(mProducerThread == NULL); + ASSERT_TRUE(mProducerThread == nullptr); mProducerThread = producerThread; producerThread->setEglObjects(mEglDisplay, mProducerEglSurface, mProducerEglContext);
diff --git a/libs/gui/tests/SurfaceTextureGLToGL.h b/libs/gui/tests/SurfaceTextureGLToGL.h index 5d43a48..3a87c12 100644 --- a/libs/gui/tests/SurfaceTextureGLToGL.h +++ b/libs/gui/tests/SurfaceTextureGLToGL.h
@@ -38,7 +38,7 @@ void SetUpWindowAndContext() { mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, - mANW.get(), NULL); + mANW.get(), nullptr); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);
diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp index 5639286..e2b4f3d 100644 --- a/libs/gui/tests/SurfaceTextureGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp
@@ -40,12 +40,12 @@ ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); // Fill the buffer with the a checkerboard pattern - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); buf->unlock(); @@ -90,12 +90,12 @@ ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); // Fill the buffer with the a checkerboard pattern - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); buf->unlock(); @@ -155,11 +155,11 @@ ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - ASSERT_TRUE(anb != NULL); + ASSERT_TRUE(anb != nullptr); sp<GraphicBuffer> buf(GraphicBuffer::from(anb)); - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop); buf->unlock(); @@ -234,7 +234,7 @@ &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } @@ -248,7 +248,7 @@ int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * texHeight/2; int yuvTexStrideU = yuvTexStrideV; - uint8_t* img = NULL; + uint8_t* img = nullptr; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); // Gray out all the test pixels first, so we're more likely to @@ -457,7 +457,7 @@ &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) @@ -641,7 +641,7 @@ &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) @@ -654,7 +654,7 @@ &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1)
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 04686e5..960cf18 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp
@@ -72,7 +72,7 @@ mSurfaceControl = mComposerClient->createSurface( String8("Test Surface"), 32, 32, PIXEL_FORMAT_RGBA_8888, 0); - ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl != nullptr); ASSERT_TRUE(mSurfaceControl->isValid()); Transaction t; @@ -81,7 +81,7 @@ .apply()); mSurface = mSurfaceControl->getSurface(); - ASSERT_TRUE(mSurface != NULL); + ASSERT_TRUE(mSurface != nullptr); } virtual void TearDown() { @@ -131,12 +131,15 @@ // Verify the screenshot works with no protected buffers. sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - sp<IBinder> display(sf->getBuiltInDisplay( - ISurfaceComposer::eDisplayIdMain)); + + const sp<IBinder> display = sf->getInternalDisplayToken(); + ASSERT_FALSE(display == nullptr); + sp<GraphicBuffer> outBuffer; bool ignored; - ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, ignored, Rect(), - 64, 64, 0, 0x7fffffff, false)); + ASSERT_EQ(NO_ERROR, + sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false)); ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); @@ -146,7 +149,7 @@ ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), GRALLOC_USAGE_PROTECTED)); ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3)); - ANativeWindowBuffer* buf = 0; + ANativeWindowBuffer* buf = nullptr; status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf); if (err) { @@ -166,8 +169,9 @@ &buf)); ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1)); } - ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, ignored, Rect(), - 64, 64, 0, 0x7fffffff, false)); + ASSERT_EQ(NO_ERROR, + sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false)); } TEST_F(SurfaceTest, ConcreteTypeIsSurface) { @@ -206,7 +210,7 @@ } TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) { - const android_dataspace TEST_DATASPACE = HAL_DATASPACE_SRGB; + const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB; sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); @@ -365,10 +369,17 @@ 78.0, 62.0, }; + + std::vector<uint8_t> hdr10plus; + hdr10plus.push_back(0xff); + int error = native_window_set_buffers_smpte2086_metadata(window.get(), &smpte2086); ASSERT_EQ(error, NO_ERROR); error = native_window_set_buffers_cta861_3_metadata(window.get(), &cta861_3); ASSERT_EQ(error, NO_ERROR); + error = native_window_set_buffers_hdr10_plus_metadata(window.get(), hdr10plus.size(), + hdr10plus.data()); + ASSERT_EQ(error, NO_ERROR); } TEST_F(SurfaceTest, DynamicSetBufferCount) { @@ -537,10 +548,6 @@ } sp<ISurfaceComposerClient> createConnection() override { return nullptr; } - sp<ISurfaceComposerClient> createScopedConnection( - const sp<IGraphicBufferProducer>& /* parent */) override { - return nullptr; - } sp<IDisplayEventConnection> createDisplayEventConnection(ISurfaceComposer::VsyncSource) override { return nullptr; @@ -548,10 +555,16 @@ sp<IBinder> createDisplay(const String8& /*displayName*/, bool /*secure*/) override { return nullptr; } void destroyDisplay(const sp<IBinder>& /*display */) override {} - sp<IBinder> getBuiltInDisplay(int32_t /*id*/) override { return nullptr; } + std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; } + sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; } void setTransactionState(const Vector<ComposerState>& /*state*/, - const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/) - override {} + const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/, + const sp<IBinder>& /*applyToken*/, + const InputWindowCommands& /*inputWindowCommands*/, + int64_t /*desiredPresentTime*/, const client_cache_t& /*cachedBuffer*/, + const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override { + } + void bootFinished() override {} bool authenticateSurfaceTexture( const sp<IGraphicBufferProducer>& /*surface*/) const override { @@ -582,9 +595,6 @@ Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; } status_t getDisplayStats(const sp<IBinder>& /*display*/, DisplayStatInfo* /*stats*/) override { return NO_ERROR; } - status_t getDisplayViewport(const sp<IBinder>& /*display*/, Rect* /*outViewport*/) override { - return NO_ERROR; - } int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; } status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/) override { @@ -594,23 +604,36 @@ Vector<ColorMode>* /*outColorModes*/) override { return NO_ERROR; } + status_t getDisplayNativePrimaries(const sp<IBinder>& /*display*/, + ui::DisplayPrimaries& /*primaries*/) override { + return NO_ERROR; + } ColorMode getActiveColorMode(const sp<IBinder>& /*display*/) override { return ColorMode::NATIVE; } status_t setActiveColorMode(const sp<IBinder>& /*display*/, ColorMode /*colorMode*/) override { return NO_ERROR; } - status_t captureScreen(const sp<IBinder>& /*display*/, - sp<GraphicBuffer>* /*outBuffer*/, - bool& /* outCapturedSecureLayers */, - Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, - int32_t /*minLayerZ*/, int32_t /*maxLayerZ*/, - bool /*useIdentityTransform*/, - Rotation /*rotation*/, - bool /*captureSecureLayers*/) override { return NO_ERROR; } - virtual status_t captureLayers(const sp<IBinder>& /*parentHandle*/, - sp<GraphicBuffer>* /*outBuffer*/, const Rect& /*sourceCrop*/, - float /*frameScale*/, bool /*childrenOnly*/) override { + status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/, + bool& /* outCapturedSecureLayers */, + const ui::Dataspace /*reqDataspace*/, + const ui::PixelFormat /*reqPixelFormat*/, Rect /*sourceCrop*/, + uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, + bool /*useIdentityTransform*/, Rotation /*rotation*/, + bool /*captureSecureLayers*/) override { + return NO_ERROR; + } + status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/, + sp<GraphicBuffer>* /*outBuffer*/) override { + return NO_ERROR; + } + virtual status_t captureLayers( + const sp<IBinder>& /*parentHandle*/, sp<GraphicBuffer>* /*outBuffer*/, + const ui::Dataspace /*reqDataspace*/, const ui::PixelFormat /*reqPixelFormat*/, + const Rect& /*sourceCrop*/, + const std::unordered_set<sp<IBinder>, + ISurfaceComposer::SpHash<IBinder>>& /*excludeHandles*/, + float /*frameScale*/, bool /*childrenOnly*/) override { return NO_ERROR; } status_t clearAnimationFrameStats() override { return NO_ERROR; } @@ -628,6 +651,60 @@ status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override { return NO_ERROR; } + status_t getCompositionPreference( + ui::Dataspace* /*outDefaultDataspace*/, ui::PixelFormat* /*outDefaultPixelFormat*/, + ui::Dataspace* /*outWideColorGamutDataspace*/, + ui::PixelFormat* /*outWideColorGamutPixelFormat*/) const override { + return NO_ERROR; + } + status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& /*display*/, + ui::PixelFormat* /*outFormat*/, + ui::Dataspace* /*outDataspace*/, + uint8_t* /*outComponentMask*/) const override { + return NO_ERROR; + } + status_t setDisplayContentSamplingEnabled(const sp<IBinder>& /*display*/, bool /*enable*/, + uint8_t /*componentMask*/, + uint64_t /*maxFrames*/) const override { + return NO_ERROR; + } + status_t getDisplayedContentSample(const sp<IBinder>& /*display*/, uint64_t /*maxFrames*/, + uint64_t /*timestamp*/, + DisplayedFrameStats* /*outStats*/) const override { + return NO_ERROR; + } + + status_t getColorManagement(bool* /*outGetColorManagement*/) const override { return NO_ERROR; } + status_t getProtectedContentSupport(bool* /*outSupported*/) const override { return NO_ERROR; } + + status_t isWideColorDisplay(const sp<IBinder>&, bool*) const override { return NO_ERROR; } + status_t getDisplayBrightnessSupport(const sp<IBinder>& /*displayToken*/, + bool* /*outSupport*/) const override { + return NO_ERROR; + } + status_t setDisplayBrightness(const sp<IBinder>& /*displayToken*/, + float /*brightness*/) const override { + return NO_ERROR; + } + + status_t addRegionSamplingListener(const Rect& /*samplingArea*/, + const sp<IBinder>& /*stopLayerHandle*/, + const sp<IRegionSamplingListener>& /*listener*/) override { + return NO_ERROR; + } + status_t removeRegionSamplingListener( + const sp<IRegionSamplingListener>& /*listener*/) override { + return NO_ERROR; + } + status_t setAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/, + const std::vector<int32_t>& /*allowedConfigs*/) override { + return NO_ERROR; + } + status_t getAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/, + std::vector<int32_t>* /*outAllowedConfigs*/) override { + return NO_ERROR; + } + status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; } protected: IBinder* onAsBinder() override { return nullptr; } @@ -638,8 +715,7 @@ class FakeProducerFrameEventHistory : public ProducerFrameEventHistory { public: - FakeProducerFrameEventHistory(FenceToFenceTimeMap* fenceMap) - : mFenceMap(fenceMap) {} + explicit FakeProducerFrameEventHistory(FenceToFenceTimeMap* fenceMap) : mFenceMap(fenceMap) {} ~FakeProducerFrameEventHistory() {}
diff --git a/libs/incidentcompanion/Android.bp b/libs/incidentcompanion/Android.bp new file mode 100644 index 0000000..63411b9 --- /dev/null +++ b/libs/incidentcompanion/Android.bp
@@ -0,0 +1,52 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +filegroup { + name: "incidentcompanion_aidl", + srcs: [ + "binder/android/os/IIncidentAuthListener.aidl", + "binder/android/os/IIncidentCompanion.aidl", + ], + path: "binder", +} + +cc_library_static { + name: "libincidentcompanion", + shared_libs: [ + "libbinder", + "libutils", + ], + aidl: { + local_include_dirs: ["binder"], + export_aidl_headers: true, + }, + srcs: [ + ":incidentcompanion_aidl", + "src/IncidentManager.cpp", + ], + export_include_dirs: [ + "binder", + "include", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-unused-variable", + "-Wunused-parameter", + ], +} +
diff --git a/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl b/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl new file mode 100644 index 0000000..5484be8 --- /dev/null +++ b/libs/incidentcompanion/binder/android/os/IIncidentAuthListener.aidl
@@ -0,0 +1,34 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * Callback for IIncidentCompanion. + * + * @hide + */ +oneway interface IIncidentAuthListener { + /** + * The user approved the incident or bug report to be sent. + */ + void onReportApproved(); + + /** + * The user did not approve the incident or bug report to be sent. + */ + void onReportDenied(); +}
diff --git a/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl new file mode 100644 index 0000000..98c2814 --- /dev/null +++ b/libs/incidentcompanion/binder/android/os/IIncidentCompanion.aidl
@@ -0,0 +1,105 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.os.IIncidentAuthListener; +import android.os.IncidentManager; + +/** + * Helper service for incidentd and dumpstated to provide user feedback + * and authorization for bug and inicdent reports to be taken. + * + * @hide + */ +interface IIncidentCompanion { + /** + * Request an authorization for an incident or bug report. + * // TODO(b/111441001): Add the permission + * <p> + * This function requires the ___ permission. + * + * @param callingUid The original application that requested the report. This function + * returns via the callback whether the application should be trusted. It is up + * to the caller to actually implement the restriction to take or not take + * the incident or bug report. + * @param receiverClass The class that will be the eventual broacast receiver for the + * INCIDENT_REPORT_READY message. Used as part of the id in incidentd. + * @param reportId The incident report ID. Incidentd should call with this parameter, but + * everyone else should pass null or empty string. + * @param flags FLAG_CONFIRMATION_DIALOG (0x1) - to show this as a dialog. Otherwise + * a dialog will be shown as a notification. + * @param callback Interface to receive results. The results may not come back for + * a long (user's choice) time, or ever (if they never respond to the notification). + * Authorization requests are not persisted across reboot. It is up to the calling + * service to request another authorization after reboot if they still would like + * to send their report. + */ + oneway void authorizeReport(int callingUid, String callingPackage, + String receiverClass, String reportId, + int flags, IIncidentAuthListener callback); + + /** + * Cancel an authorization. + */ + oneway void cancelAuthorization(IIncidentAuthListener callback); + + /** + * Send the report ready broadcast on behalf of incidentd. + */ + oneway void sendReportReadyBroadcast(String pkg, String cls); + + /** + * Return the list of pending approvals. + */ + List<String> getPendingReports(); + + /** + * The user has authorized the report to be shared. + * + * @param uri the report. + */ + void approveReport(String uri); + + /** + * The user has denied the report from being shared. + * + * @param uri the report. + */ + void denyReport(String uri); + + /** + * List the incident reports for the given ComponentName. The receiver + * must be for a package inside the caller. + */ + List<String> getIncidentReportList(String pkg, String cls); + + /** + * Get the IncidentReport object. + */ + IncidentManager.IncidentReport getIncidentReport(String pkg, String cls, String id); + + /** + * Signal that the client is done with this incident report and it can be deleted. + */ + void deleteIncidentReports(String pkg, String cls, String id); + + /** + * Signal that the client is done with all incident reports from this package. + * Especially useful for testing. + */ + void deleteAllIncidentReports(String pkg); +}
diff --git a/libs/incidentcompanion/binder/android/os/IncidentManager.aidl b/libs/incidentcompanion/binder/android/os/IncidentManager.aidl new file mode 100644 index 0000000..d17823e --- /dev/null +++ b/libs/incidentcompanion/binder/android/os/IncidentManager.aidl
@@ -0,0 +1,20 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +parcelable IncidentManager.IncidentReport cpp_header "android/os/IncidentManager.h"; +
diff --git a/libs/incidentcompanion/include/android/os/IncidentManager.h b/libs/incidentcompanion/include/android/os/IncidentManager.h new file mode 100644 index 0000000..07b6d82 --- /dev/null +++ b/libs/incidentcompanion/include/android/os/IncidentManager.h
@@ -0,0 +1,71 @@ +/** + * Copyright (c) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <utils/String16.h> + +#include <set> +#include <vector> + +namespace android { +namespace os { + +class IncidentManager : public virtual RefBase { +public: + class IncidentReport : public Parcelable { + public: + IncidentReport(); + virtual ~IncidentReport(); + + virtual status_t writeToParcel(Parcel* out) const; + virtual status_t readFromParcel(const Parcel* in); + + void setTimestampNs(int64_t val) { mTimestampNs = val; } + int64_t getTimestampNs() const { return mTimestampNs; } + int64_t getTimestampMs() const { return mTimestampNs / 1000000; } + + void setPrivacyPolicy(int32_t val) { mPrivacyPolicy = val; } + // This was accidentally published as a long in the java api. + int64_t getPrivacyPolicy() const { return mPrivacyPolicy; } + // Dups the fd, so you retain ownership of the original one. If there is a + // previously set fd, closes that, since this object owns its own fd. + status_t setFileDescriptor(int fd); + + // Does not dup the fd, so ownership is passed to this object. If there is a + // previously set fd, closes that, since this object owns its own fd. + void takeFileDescriptor(int fd); + + // Returns the fd, which you don't own. Call dup if you need a copy. + int getFileDescriptor() const { return mFileDescriptor; } + + private: + int64_t mTimestampNs; + int32_t mPrivacyPolicy; + int mFileDescriptor; + }; + + +private: + // Not implemented for now. + IncidentManager(); + virtual ~IncidentManager(); +}; +} +} +
diff --git a/libs/incidentcompanion/src/IncidentManager.cpp b/libs/incidentcompanion/src/IncidentManager.cpp new file mode 100644 index 0000000..f7c8a5e --- /dev/null +++ b/libs/incidentcompanion/src/IncidentManager.cpp
@@ -0,0 +1,135 @@ +/** + * Copyright (c) 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/os/IncidentManager.h> + +namespace android { +namespace os { + +// ============================================================ +IncidentManager::IncidentReport::IncidentReport() + :mTimestampNs(0), + mPrivacyPolicy(0), + mFileDescriptor(-1) { +} + +IncidentManager::IncidentReport::~IncidentReport() { + if (mFileDescriptor >= 0) { + close(mFileDescriptor); + } +} + +status_t IncidentManager::IncidentReport::writeToParcel(Parcel* out) const { + status_t err; + + err = out->writeInt64(mTimestampNs); + if (err != NO_ERROR) { + return err; + } + + + err = out->writeInt32(mPrivacyPolicy); + if (err != NO_ERROR) { + return err; + } + + if (mFileDescriptor >= 0) { + err = out->writeInt32(1); + if (err != NO_ERROR) { + return err; + } + + err = out->writeDupParcelFileDescriptor(mFileDescriptor); + if (err != NO_ERROR) { + return err; + } + + } else { + err = out->writeInt32(0); + if (err != NO_ERROR) { + return err; + } + } + + return NO_ERROR; +} + +status_t IncidentManager::IncidentReport::readFromParcel(const Parcel* in) { + status_t err; + int32_t hasField; + + err = in->readInt64(&mTimestampNs); + if (err != NO_ERROR) { + return err; + } + + err = in->readInt32(&mPrivacyPolicy); + if (err != NO_ERROR) { + return err; + } + + err = in->readInt32(&hasField); + if (err != NO_ERROR) { + return err; + } + + if (hasField) { + int fd = in->readParcelFileDescriptor(); + if (fd >= 0) { + mFileDescriptor = dup(fd); + if (mFileDescriptor < 0) { + return -errno; + } + } + } + + return NO_ERROR; +} + +status_t IncidentManager::IncidentReport::setFileDescriptor(int fd) { + if (mFileDescriptor >= 0) { + close(mFileDescriptor); + } + if (fd < 0) { + mFileDescriptor = -1; + } else { + mFileDescriptor = dup(fd); + if (mFileDescriptor < 0) { + return -errno; + } + } + return NO_ERROR; +} + +void IncidentManager::IncidentReport::takeFileDescriptor(int fd) { + if (mFileDescriptor >= 0) { + close(mFileDescriptor); + } + if (fd < 0) { + mFileDescriptor = -1; + } else { + mFileDescriptor = fd; + } +} + +// ============================================================ +IncidentManager::~IncidentManager() { +} + +} +} + +
diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 2f39976..2d78811 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp
@@ -28,6 +28,7 @@ "Keyboard.cpp", "KeyCharacterMap.cpp", "KeyLayoutMap.cpp", + "TouchVideoFrame.cpp", "VirtualKeyMap.cpp", ], @@ -43,7 +44,10 @@ android: { srcs: [ "IInputFlinger.cpp", + "InputApplication.cpp", "InputTransport.cpp", + "InputWindow.cpp", + "ISetInputWindowsListener.cpp", "VelocityControl.cpp", "VelocityTracker.cpp", ], @@ -51,6 +55,7 @@ shared_libs: [ "libutils", "libbinder", + "libui" ], sanitize: {
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp index 003e73d..d6a73bf 100644 --- a/libs/input/IInputFlinger.cpp +++ b/libs/input/IInputFlinger.cpp
@@ -23,7 +23,6 @@ #include <input/IInputFlinger.h> - namespace android { class BpInputFlinger : public BpInterface<IInputFlinger> { @@ -31,23 +30,85 @@ explicit BpInputFlinger(const sp<IBinder>& impl) : BpInterface<IInputFlinger>(impl) { } - virtual status_t doSomething() { + virtual void setInputWindows(const std::vector<InputWindowInfo>& inputInfo, + const sp<ISetInputWindowsListener>& setInputWindowsListener) { Parcel data, reply; data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); - remote()->transact(BnInputFlinger::DO_SOMETHING_TRANSACTION, data, &reply); - return reply.readInt32(); + + data.writeUint32(static_cast<uint32_t>(inputInfo.size())); + for (const auto& info : inputInfo) { + info.write(data); + } + data.writeStrongBinder(IInterface::asBinder(setInputWindowsListener)); + + remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply, + IBinder::FLAG_ONEWAY); + } + + virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) { + Parcel data, reply; + data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); + + data.writeStrongBinder(fromToken); + data.writeStrongBinder(toToken); + remote()->transact(BnInputFlinger::TRANSFER_TOUCH_FOCUS, data, &reply, + IBinder::FLAG_ONEWAY); + } + + virtual void registerInputChannel(const sp<InputChannel>& channel) { + Parcel data, reply; + data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); + channel->write(data); + remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply); + } + + virtual void unregisterInputChannel(const sp<InputChannel>& channel) { + Parcel data, reply; + data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); + channel->write(data); + remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply); } }; IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger"); - status_t BnInputFlinger::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { - case DO_SOMETHING_TRANSACTION: { + case SET_INPUT_WINDOWS_TRANSACTION: { CHECK_INTERFACE(IInputFlinger, data, reply); - reply->writeInt32(0); + size_t count = data.readUint32(); + if (count > data.dataSize()) { + return BAD_VALUE; + } + std::vector<InputWindowInfo> handles; + for (size_t i = 0; i < count; i++) { + handles.push_back(InputWindowInfo::read(data)); + } + const sp<ISetInputWindowsListener> setInputWindowsListener = + ISetInputWindowsListener::asInterface(data.readStrongBinder()); + setInputWindows(handles, setInputWindowsListener); + break; + } + case REGISTER_INPUT_CHANNEL_TRANSACTION: { + CHECK_INTERFACE(IInputFlinger, data, reply); + sp<InputChannel> channel = new InputChannel(); + channel->read(data); + registerInputChannel(channel); + break; + } + case UNREGISTER_INPUT_CHANNEL_TRANSACTION: { + CHECK_INTERFACE(IInputFlinger, data, reply); + sp<InputChannel> channel = new InputChannel(); + channel->read(data); + unregisterInputChannel(channel); + break; + } + case TRANSFER_TOUCH_FOCUS: { + CHECK_INTERFACE(IInputFlinger, data, reply); + sp<IBinder> fromToken = data.readStrongBinder(); + sp<IBinder> toToken = data.readStrongBinder(); + transferTouchFocus(fromToken, toToken); break; } default:
diff --git a/libs/input/ISetInputWindowsListener.cpp b/libs/input/ISetInputWindowsListener.cpp new file mode 100644 index 0000000..a0330da --- /dev/null +++ b/libs/input/ISetInputWindowsListener.cpp
@@ -0,0 +1,53 @@ +/* + * 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. + */ + +#include <input/ISetInputWindowsListener.h> + +namespace android { + +class BpSetInputWindowsListener : public BpInterface<ISetInputWindowsListener> { +public: + explicit BpSetInputWindowsListener(const sp<IBinder>& impl) + : BpInterface<ISetInputWindowsListener>(impl) { + } + + virtual ~BpSetInputWindowsListener() = default; + + virtual void onSetInputWindowsFinished() { + Parcel data, reply; + data.writeInterfaceToken(ISetInputWindowsListener::getInterfaceDescriptor()); + remote()->transact(BnSetInputWindowsListener::ON_SET_INPUT_WINDOWS_FINISHED, data, &reply, + IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(SetInputWindowsListener, "android.input.ISetInputWindowsListener"); + +status_t BnSetInputWindowsListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + switch(code) { + case ON_SET_INPUT_WINDOWS_FINISHED: { + CHECK_INTERFACE(ISetInputWindowsListener, data, reply); + onSetInputWindowsFinished(); + return NO_ERROR; + } + default: { + return BBinder::onTransact(code, data, reply, flags); + } + } +} + +} // namespace android
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index a624663..9fd25f9 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp
@@ -29,16 +29,29 @@ namespace android { +const char* motionClassificationToString(MotionClassification classification) { + switch (classification) { + case MotionClassification::NONE: + return "NONE"; + case MotionClassification::AMBIGUOUS_GESTURE: + return "AMBIGUOUS_GESTURE"; + case MotionClassification::DEEP_PRESS: + return "DEEP_PRESS"; + } +} + // --- InputEvent --- -void InputEvent::initialize(int32_t deviceId, int32_t source) { +void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) { mDeviceId = deviceId; mSource = source; + mDisplayId = displayId; } void InputEvent::initialize(const InputEvent& from) { mDeviceId = from.mDeviceId; mSource = from.mSource; + mDisplayId = from.mDisplayId; } // --- KeyEvent --- @@ -54,6 +67,7 @@ void KeyEvent::initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -62,7 +76,7 @@ int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { - InputEvent::initialize(deviceId, source); + InputEvent::initialize(deviceId, source, displayId); mAction = action; mFlags = flags; mKeyCode = keyCode; @@ -128,15 +142,24 @@ } } -void PointerCoords::scale(float scaleFactor) { +void PointerCoords::scale(float globalScaleFactor, float windowXScale, float windowYScale) { // No need to scale pressure or size since they are normalized. // No need to scale orientation since it is meaningless to do so. - scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); - scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); + + // If there is a global scale factor, it is included in the windowX/YScale + // so we don't need to apply it twice to the X/Y axes. + // However we don't want to apply any windowXYScale not included in the global scale + // to the TOUCH_MAJOR/MINOR coordinates. + scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, windowXScale); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, windowYScale); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, globalScaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, globalScaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, globalScaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, globalScaleFactor); +} + +void PointerCoords::scale(float globalScaleFactor) { + scale(globalScaleFactor, globalScaleFactor, globalScaleFactor); } void PointerCoords::applyOffset(float xOffset, float yOffset) { @@ -215,12 +238,14 @@ void MotionEvent::initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xOffset, float yOffset, float xPrecision, @@ -230,13 +255,14 @@ size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { - InputEvent::initialize(deviceId, source); + InputEvent::initialize(deviceId, source, displayId); mAction = action; mActionButton = actionButton; mFlags = flags; mEdgeFlags = edgeFlags; mMetaState = metaState; mButtonState = buttonState; + mClassification = classification; mXOffset = xOffset; mYOffset = yOffset; mXPrecision = xPrecision; @@ -250,13 +276,14 @@ } void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { - InputEvent::initialize(other->mDeviceId, other->mSource); + InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId); mAction = other->mAction; mActionButton = other->mActionButton; mFlags = other->mFlags; mEdgeFlags = other->mEdgeFlags; mMetaState = other->mMetaState; mButtonState = other->mButtonState; + mClassification = other->mClassification; mXOffset = other->mXOffset; mYOffset = other->mYOffset; mXPrecision = other->mXPrecision; @@ -341,15 +368,15 @@ mYOffset += yOffset; } -void MotionEvent::scale(float scaleFactor) { - mXOffset *= scaleFactor; - mYOffset *= scaleFactor; - mXPrecision *= scaleFactor; - mYPrecision *= scaleFactor; +void MotionEvent::scale(float globalScaleFactor) { + mXOffset *= globalScaleFactor; + mYOffset *= globalScaleFactor; + mXPrecision *= globalScaleFactor; + mYPrecision *= globalScaleFactor; size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { - mSamplePointerCoords.editItemAt(i).scale(scaleFactor); + mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor); } } @@ -431,12 +458,14 @@ mDeviceId = parcel->readInt32(); mSource = parcel->readInt32(); + mDisplayId = parcel->readInt32(); mAction = parcel->readInt32(); mActionButton = parcel->readInt32(); mFlags = parcel->readInt32(); mEdgeFlags = parcel->readInt32(); mMetaState = parcel->readInt32(); mButtonState = parcel->readInt32(); + mClassification = static_cast<MotionClassification>(parcel->readByte()); mXOffset = parcel->readFloat(); mYOffset = parcel->readFloat(); mXPrecision = parcel->readFloat(); @@ -480,12 +509,14 @@ parcel->writeInt32(mDeviceId); parcel->writeInt32(mSource); + parcel->writeInt32(mDisplayId); parcel->writeInt32(mAction); parcel->writeInt32(mActionButton); parcel->writeInt32(mFlags); parcel->writeInt32(mEdgeFlags); parcel->writeInt32(mMetaState); parcel->writeInt32(mButtonState); + parcel->writeByte(static_cast<int8_t>(mClassification)); parcel->writeFloat(mXOffset); parcel->writeFloat(mYOffset); parcel->writeFloat(mXPrecision);
diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp new file mode 100644 index 0000000..1d9f8a7 --- /dev/null +++ b/libs/input/InputApplication.cpp
@@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputApplication" + +#include <input/InputApplication.h> + +#include <android/log.h> + +namespace android { + +// --- InputApplicationHandle --- + +InputApplicationHandle::InputApplicationHandle() { +} + +InputApplicationHandle::~InputApplicationHandle() { +} + +InputApplicationInfo InputApplicationInfo::read(const Parcel& from) { + InputApplicationInfo ret; + ret.token = from.readStrongBinder(); + ret.name = from.readString8().c_str(); + ret.dispatchingTimeout = from.readInt64(); + + return ret; +} + +status_t InputApplicationInfo::write(Parcel& output) const { + output.writeStrongBinder(token); + output.writeString8(String8(name.c_str())); + output.writeInt64(dispatchingTimeout); + + return OK; +} + +} // namespace android
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 4287abe..4db9e06 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp
@@ -20,9 +20,12 @@ #include <unistd.h> #include <ctype.h> +#include <android-base/stringprintf.h> #include <input/InputDevice.h> #include <input/InputEventLabels.h> +using android::base::StringPrintf; + namespace android { static const char* CONFIGURATION_FILE_DIR[] = { @@ -41,65 +44,62 @@ return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); } -static void appendInputDeviceConfigurationFileRelativePath(String8& path, - const String8& name, InputDeviceConfigurationFileType type) { - path.append(CONFIGURATION_FILE_DIR[type]); - for (size_t i = 0; i < name.length(); i++) { - char ch = name[i]; - if (!isValidNameChar(ch)) { - ch = '_'; - } - path.append(&ch, 1); - } - path.append(CONFIGURATION_FILE_EXTENSION[type]); +static void appendInputDeviceConfigurationFileRelativePath(std::string& path, + const std::string& name, InputDeviceConfigurationFileType type) { + path += CONFIGURATION_FILE_DIR[type]; + path += name; + path += CONFIGURATION_FILE_EXTENSION[type]; } -String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( +std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type) { if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { if (deviceIdentifier.version != 0) { // Try vendor product version. - String8 versionPath(getInputDeviceConfigurationFilePathByName( - String8::format("Vendor_%04x_Product_%04x_Version_%04x", + std::string versionPath = getInputDeviceConfigurationFilePathByName( + StringPrintf("Vendor_%04x_Product_%04x_Version_%04x", deviceIdentifier.vendor, deviceIdentifier.product, deviceIdentifier.version), - type)); - if (!versionPath.isEmpty()) { + type); + if (!versionPath.empty()) { return versionPath; } } // Try vendor product. - String8 productPath(getInputDeviceConfigurationFilePathByName( - String8::format("Vendor_%04x_Product_%04x", + std::string productPath = getInputDeviceConfigurationFilePathByName( + StringPrintf("Vendor_%04x_Product_%04x", deviceIdentifier.vendor, deviceIdentifier.product), - type)); - if (!productPath.isEmpty()) { + type); + if (!productPath.empty()) { return productPath; } } // Try device name. - return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); + return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type); } -String8 getInputDeviceConfigurationFilePathByName( - const String8& name, InputDeviceConfigurationFileType type) { +std::string getInputDeviceConfigurationFilePathByName( + const std::string& name, InputDeviceConfigurationFileType type) { // Search system repository. - String8 path; + std::string path; // Treblized input device config files will be located /odm/usr or /vendor/usr. const char *rootsForPartition[] {"/odm", "/vendor", getenv("ANDROID_ROOT")}; for (size_t i = 0; i < size(rootsForPartition); i++) { - path.setTo(rootsForPartition[i]); - path.append("/usr/"); + if (rootsForPartition[i] == nullptr) { + continue; + } + path = rootsForPartition[i]; + path += "/usr/"; appendInputDeviceConfigurationFileRelativePath(path, name, type); #if DEBUG_PROBE ALOGD("Probing for system provided input device configuration file: path='%s'", - path.string()); + path.c_str()); #endif - if (!access(path.string(), R_OK)) { + if (!access(path.c_str(), R_OK)) { #if DEBUG_PROBE ALOGD("Found"); #endif @@ -109,13 +109,17 @@ // Search user repository. // TODO Should only look here if not in safe mode. - path.setTo(getenv("ANDROID_DATA")); - path.append("/system/devices/"); + path = ""; + char *androidData = getenv("ANDROID_DATA"); + if (androidData != nullptr) { + path += androidData; + } + path += "/system/devices/"; appendInputDeviceConfigurationFileRelativePath(path, name, type); #if DEBUG_PROBE - ALOGD("Probing for system user input device configuration file: path='%s'", path.string()); + ALOGD("Probing for system user input device configuration file: path='%s'", path.c_str()); #endif - if (!access(path.string(), R_OK)) { + if (!access(path.c_str(), R_OK)) { #if DEBUG_PROBE ALOGD("Found"); #endif @@ -125,16 +129,28 @@ // Not found. #if DEBUG_PROBE ALOGD("Probe failed to find input device configuration file: name='%s', type=%d", - name.string(), type); + name.c_str(), type); #endif - return String8(); + return ""; +} + +// --- InputDeviceIdentifier + +std::string InputDeviceIdentifier::getCanonicalName() const { + std::string replacedName = name; + for (char& ch : replacedName) { + if (!isValidNameChar(ch)) { + ch = '_'; + } + } + return replacedName; } // --- InputDeviceInfo --- InputDeviceInfo::InputDeviceInfo() { - initialize(-1, 0, -1, InputDeviceIdentifier(), String8(), false, false); + initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false); } InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : @@ -150,7 +166,7 @@ } void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal, + const InputDeviceIdentifier& identifier, const std::string& alias, bool isExternal, bool hasMic) { mId = id; mGeneration = generation; @@ -170,12 +186,12 @@ int32_t axis, uint32_t source) const { size_t numRanges = mMotionRanges.size(); for (size_t i = 0; i < numRanges; i++) { - const MotionRange& range = mMotionRanges.itemAt(i); + const MotionRange& range = mMotionRanges[i]; if (range.axis == axis && range.source == source) { return ⦥ } } - return NULL; + return nullptr; } void InputDeviceInfo::addSource(uint32_t source) { @@ -185,11 +201,11 @@ void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max, float flat, float fuzz, float resolution) { MotionRange range = { axis, source, min, max, flat, fuzz, resolution }; - mMotionRanges.add(range); + mMotionRanges.push_back(range); } void InputDeviceInfo::addMotionRange(const MotionRange& range) { - mMotionRanges.add(range); + mMotionRanges.push_back(range); } } // namespace android
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 03f593f..d02cb8e 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp
@@ -27,11 +27,16 @@ #include <sys/types.h> #include <unistd.h> +#include <android-base/stringprintf.h> +#include <binder/Parcel.h> #include <cutils/properties.h> #include <log/log.h> +#include <utils/Trace.h> #include <input/InputTransport.h> +using android::base::StringPrintf; + namespace android { // Socket buffer size. The default is typically about 128KB, which is much larger than @@ -58,6 +63,18 @@ // far into the future. This time is further bounded by 50% of the last time delta. static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; +/** + * System property for enabling / disabling touch resampling. + * Resampling extrapolates / interpolates the reported touch event coordinates to better + * align them to the VSYNC signal, thus resulting in smoother scrolling performance. + * Resampling is not needed (and should be disabled) on hardware that already + * has touch events triggered by VSYNC. + * Set to "1" to enable resampling (default). + * Set to "0" to disable resampling. + * Resampling is enabled by default. + */ +static const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling"; + template<typename T> inline static T min(const T& a, const T& b) { return a < b ? a : b; @@ -67,6 +84,10 @@ return a + alpha * (b - a); } +inline static bool isPointerEvent(int32_t source) { + return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; +} + // --- InputMessage --- bool InputMessage::isValid(size_t actualSize) const { @@ -156,6 +177,8 @@ msg->body.motion.metaState = body.motion.metaState; // int32_t buttonState msg->body.motion.buttonState = body.motion.buttonState; + // MotionClassification classification + msg->body.motion.classification = body.motion.classification; // int32_t edgeFlags msg->body.motion.edgeFlags = body.motion.edgeFlags; // nsecs_t downTime @@ -200,15 +223,13 @@ // --- InputChannel --- InputChannel::InputChannel(const std::string& name, int fd) : - mName(name), mFd(fd) { + mName(name) { #if DEBUG_CHANNEL_LIFECYCLE ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), fd); #endif - int result = fcntl(mFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " - "non-blocking. errno=%d", mName.c_str(), errno); + setFd(fd); } InputChannel::~InputChannel() { @@ -220,6 +241,18 @@ ::close(mFd); } +void InputChannel::setFd(int fd) { + if (mFd > 0) { + ::close(mFd); + } + mFd = fd; + if (mFd > 0) { + int result = fcntl(mFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " + "non-blocking. errno=%d", mName.c_str(), errno); + } +} + status_t InputChannel::openInputChannelPair(const std::string& name, sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { int sockets[2]; @@ -328,10 +361,51 @@ sp<InputChannel> InputChannel::dup() const { int fd = ::dup(getFd()); - return fd >= 0 ? new InputChannel(getName(), fd) : NULL; + return fd >= 0 ? new InputChannel(getName(), fd) : nullptr; } +status_t InputChannel::write(Parcel& out) const { + status_t s = out.writeString8(String8(getName().c_str())); + + if (s != OK) { + return s; + } + s = out.writeStrongBinder(mToken); + if (s != OK) { + return s; + } + + s = out.writeDupFileDescriptor(getFd()); + + return s; +} + +status_t InputChannel::read(const Parcel& from) { + mName = from.readString8(); + mToken = from.readStrongBinder(); + + int rawFd = from.readFileDescriptor(); + setFd(::dup(rawFd)); + + if (mFd < 0) { + return BAD_VALUE; + } + + return OK; +} + +sp<IBinder> InputChannel::getToken() const { + return mToken; +} + +void InputChannel::setToken(const sp<IBinder>& token) { + if (mToken != nullptr) { + ALOGE("Assigning InputChannel (%s) a second handle?", mName.c_str()); + } + mToken = token; +} + // --- InputPublisher --- InputPublisher::InputPublisher(const sp<InputChannel>& channel) : @@ -345,6 +419,7 @@ uint32_t seq, int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -353,6 +428,11 @@ int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { + if (ATRACE_ENABLED()) { + std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")", + mChannel->getName().c_str(), keyCode); + ATRACE_NAME(message.c_str()); + } #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, " "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d," @@ -372,6 +452,7 @@ msg.body.key.seq = seq; msg.body.key.deviceId = deviceId; msg.body.key.source = source; + msg.body.key.displayId = displayId; msg.body.key.action = action; msg.body.key.flags = flags; msg.body.key.keyCode = keyCode; @@ -394,6 +475,7 @@ int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xOffset, float yOffset, float xPrecision, @@ -403,14 +485,22 @@ uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { + if (ATRACE_ENABLED()) { + std::string message = StringPrintf( + "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")", + mChannel->getName().c_str(), action); + ATRACE_NAME(message.c_str()); + } #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " + "displayId=%" PRId32 ", " "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " - "metaState=0x%x, buttonState=0x%x, xOffset=%f, yOffset=%f, " + "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, " "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " "pointerCount=%" PRIu32, mChannel->getName().c_str(), seq, - deviceId, source, action, actionButton, flags, edgeFlags, metaState, buttonState, + deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState, + buttonState, motionClassificationToString(classification), xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); #endif @@ -437,6 +527,7 @@ msg.body.motion.edgeFlags = edgeFlags; msg.body.motion.metaState = metaState; msg.body.motion.buttonState = buttonState; + msg.body.motion.classification = classification; msg.body.motion.xOffset = xOffset; msg.body.motion.yOffset = yOffset; msg.body.motion.xPrecision = xPrecision; @@ -485,31 +576,18 @@ } bool InputConsumer::isTouchResamplingEnabled() { - char value[PROPERTY_VALUE_MAX]; - int length = property_get("ro.input.noresample", value, NULL); - if (length > 0) { - if (!strcmp("1", value)) { - return false; - } - if (strcmp("0", value)) { - ALOGD("Unrecognized property value for 'ro.input.noresample'. " - "Use '1' or '0'."); - } - } - return true; + return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true); } status_t InputConsumer::consume(InputEventFactoryInterface* factory, - bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, - int32_t* displayId) { + bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, mChannel->getName().c_str(), consumeBatches ? "true" : "false", frameTime); #endif *outSeq = 0; - *outEvent = NULL; - *displayId = -1; // Invalid display. + *outEvent = nullptr; // Fetch the next input message. // Loop until an event can be returned or no additional events are received. @@ -524,7 +602,7 @@ if (result) { // Consume the next batched event unless batches are being held for later. if (consumeBatches || result != WOULD_BLOCK) { - result = consumeBatch(factory, frameTime, outSeq, outEvent, displayId); + result = consumeBatch(factory, frameTime, outSeq, outEvent); if (*outEvent) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", @@ -563,12 +641,22 @@ mChannel->getName().c_str()); #endif break; + } else if (isPointerEvent(mMsg.body.motion.source) && + mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) { + // No need to process events that we are going to cancel anyways + const size_t count = batch.samples.size(); + for (size_t i = 0; i < count; i++) { + const InputMessage& msg = batch.samples.itemAt(i); + sendFinishedSignal(msg.body.motion.seq, false); + } + batch.samples.removeItemsAt(0, count); + mBatches.removeAt(batchIndex); } else { // We cannot append to the batch in progress, so we need to consume // the previous batch right now and defer the new message until later. mMsgDeferred = true; status_t result = consumeSamples(factory, - batch, batch.samples.size(), outSeq, outEvent, displayId); + batch, batch.samples.size(), outSeq, outEvent); mBatches.removeAt(batchIndex); if (result) { return result; @@ -602,7 +690,7 @@ initializeMotionEvent(motionEvent, &mMsg); *outSeq = mMsg.body.motion.seq; *outEvent = motionEvent; - *displayId = mMsg.body.motion.displayId; + #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", mChannel->getName().c_str(), *outSeq); @@ -620,14 +708,13 @@ } status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, - nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId) { + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { status_t result; for (size_t i = mBatches.size(); i > 0; ) { i--; Batch& batch = mBatches.editItemAt(i); if (frameTime < 0) { - result = consumeSamples(factory, batch, batch.samples.size(), - outSeq, outEvent, displayId); + result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); mBatches.removeAt(i); return result; } @@ -641,11 +728,11 @@ continue; } - result = consumeSamples(factory, batch, split + 1, outSeq, outEvent, displayId); + result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); const InputMessage* next; if (batch.samples.isEmpty()) { mBatches.removeAt(i); - next = NULL; + next = nullptr; } else { next = &batch.samples.itemAt(0); } @@ -659,7 +746,7 @@ } status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, - Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent, int32_t* displayId) { + Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) { MotionEvent* motionEvent = factory->createMotionEvent(); if (! motionEvent) return NO_MEMORY; @@ -674,7 +761,6 @@ mSeqChains.push(seqChain); addSample(motionEvent, &msg); } else { - *displayId = msg.body.motion.displayId; initializeMotionEvent(motionEvent, &msg); } chain = msg.body.motion.seq; @@ -687,8 +773,7 @@ } void InputConsumer::updateTouchState(InputMessage& msg) { - if (!mResampleTouch || - !(msg.body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) { + if (!mResampleTouch || !isPointerEvent(msg.body.motion.source)) { return; } @@ -800,7 +885,7 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, const InputMessage* next) { if (!mResampleTouch - || !(event->getSource() & AINPUT_SOURCE_CLASS_POINTER) + || !(isPointerEvent(event->getSource())) || event->getAction() != AMOTION_EVENT_ACTION_MOVE) { return; } @@ -1030,6 +1115,7 @@ event->initialize( msg->body.key.deviceId, msg->body.key.source, + msg->body.key.displayId, msg->body.key.action, msg->body.key.flags, msg->body.key.keyCode, @@ -1052,12 +1138,14 @@ event->initialize( msg->body.motion.deviceId, msg->body.motion.source, + msg->body.motion.displayId, msg->body.motion.action, msg->body.motion.actionButton, msg->body.motion.flags, msg->body.motion.edgeFlags, msg->body.motion.metaState, msg->body.motion.buttonState, + msg->body.motion.classification, msg->body.motion.xOffset, msg->body.motion.yOffset, msg->body.motion.xPrecision,
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp new file mode 100644 index 0000000..5a60347 --- /dev/null +++ b/libs/input/InputWindow.cpp
@@ -0,0 +1,174 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputWindow" +#define LOG_NDEBUG 0 + +#include <binder/Parcel.h> +#include <input/InputWindow.h> +#include <input/InputTransport.h> + +#include <log/log.h> + +#include <ui/Rect.h> +#include <ui/Region.h> + +namespace android { + +// --- InputWindowInfo --- +void InputWindowInfo::addTouchableRegion(const Rect& region) { + touchableRegion.orSelf(region); +} + +bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const { + return touchableRegion.contains(x,y); +} + +bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { + return x >= frameLeft && x < frameRight + && y >= frameTop && y < frameBottom; +} + +bool InputWindowInfo::isTrustedOverlay() const { + return layoutParamsType == TYPE_INPUT_METHOD + || layoutParamsType == TYPE_INPUT_METHOD_DIALOG + || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY + || layoutParamsType == TYPE_STATUS_BAR + || layoutParamsType == TYPE_NAVIGATION_BAR + || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL + || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY + || layoutParamsType == TYPE_DOCK_DIVIDER + || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY + || layoutParamsType == TYPE_INPUT_CONSUMER; +} + +bool InputWindowInfo::supportsSplitTouch() const { + return layoutParamsFlags & FLAG_SPLIT_TOUCH; +} + +bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { + return frameLeft < other->frameRight && frameRight > other->frameLeft + && frameTop < other->frameBottom && frameBottom > other->frameTop; +} + +status_t InputWindowInfo::write(Parcel& output) const { + if (token == nullptr) { + output.writeInt32(0); + return OK; + } + output.writeInt32(1); + status_t s = output.writeStrongBinder(token); + if (s != OK) return s; + + output.writeString8(String8(name.c_str())); + output.writeInt32(layoutParamsFlags); + output.writeInt32(layoutParamsType); + output.writeInt64(dispatchingTimeout); + output.writeInt32(frameLeft); + output.writeInt32(frameTop); + output.writeInt32(frameRight); + output.writeInt32(frameBottom); + output.writeInt32(surfaceInset); + output.writeFloat(globalScaleFactor); + output.writeFloat(windowXScale); + output.writeFloat(windowYScale); + output.writeBool(visible); + output.writeBool(canReceiveKeys); + output.writeBool(hasFocus); + output.writeBool(hasWallpaper); + output.writeBool(paused); + output.writeInt32(layer); + output.writeInt32(ownerPid); + output.writeInt32(ownerUid); + output.writeInt32(inputFeatures); + output.writeInt32(displayId); + output.writeInt32(portalToDisplayId); + applicationInfo.write(output); + output.write(touchableRegion); + output.writeBool(replaceTouchableRegionWithCrop); + output.writeWeakBinder(touchableRegionCropHandle); + return OK; +} + +InputWindowInfo InputWindowInfo::read(const Parcel& from) { + InputWindowInfo ret; + + if (from.readInt32() == 0) { + return ret; + } + + sp<IBinder> token = from.readStrongBinder(); + if (token == nullptr) { + return ret; + } + + ret.token = token; + ret.name = from.readString8().c_str(); + ret.layoutParamsFlags = from.readInt32(); + ret.layoutParamsType = from.readInt32(); + ret.dispatchingTimeout = from.readInt64(); + ret.frameLeft = from.readInt32(); + ret.frameTop = from.readInt32(); + ret.frameRight = from.readInt32(); + ret.frameBottom = from.readInt32(); + ret.surfaceInset = from.readInt32(); + ret.globalScaleFactor = from.readFloat(); + ret.windowXScale = from.readFloat(); + ret.windowYScale = from.readFloat(); + ret.visible = from.readBool(); + ret.canReceiveKeys = from.readBool(); + ret.hasFocus = from.readBool(); + ret.hasWallpaper = from.readBool(); + ret.paused = from.readBool(); + ret.layer = from.readInt32(); + ret.ownerPid = from.readInt32(); + ret.ownerUid = from.readInt32(); + ret.inputFeatures = from.readInt32(); + ret.displayId = from.readInt32(); + ret.portalToDisplayId = from.readInt32(); + ret.applicationInfo = InputApplicationInfo::read(from); + from.read(ret.touchableRegion); + ret.replaceTouchableRegionWithCrop = from.readBool(); + ret.touchableRegionCropHandle = from.readWeakBinder(); + + return ret; +} + +InputWindowInfo::InputWindowInfo(const Parcel& from) { + *this = read(from); +} + +// --- InputWindowHandle --- + +InputWindowHandle::InputWindowHandle() { +} + +InputWindowHandle::~InputWindowHandle() { +} + +void InputWindowHandle::releaseChannel() { + mInfo.token.clear(); +} + +sp<IBinder> InputWindowHandle::getToken() const { + return mInfo.token; +} + +void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) { + mInfo = handle->mInfo; +} + +} // namespace android
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index cba1111..e189d20 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp
@@ -106,14 +106,14 @@ } } -status_t KeyCharacterMap::load(const String8& filename, +status_t KeyCharacterMap::load(const std::string& filename, Format format, sp<KeyCharacterMap>* outMap) { outMap->clear(); Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); + status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { - ALOGE("Error %d opening key character map file %s.", status, filename.string()); + ALOGE("Error %d opening key character map file %s.", status, filename.c_str()); } else { status = load(tokenizer, format, outMap); delete tokenizer; @@ -121,12 +121,12 @@ return status; } -status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents, +status_t KeyCharacterMap::loadContents(const std::string& filename, const char* contents, Format format, sp<KeyCharacterMap>* outMap) { outMap->clear(); Tokenizer* tokenizer; - status_t status = Tokenizer::fromContents(filename, contents, &tokenizer); + status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); if (status) { ALOGE("Error %d opening key character map.", status); } else { @@ -164,10 +164,10 @@ sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base, const sp<KeyCharacterMap>& overlay) { - if (overlay == NULL) { + if (overlay == nullptr) { return base; } - if (base == NULL) { + if (base == nullptr) { return overlay; } @@ -468,7 +468,7 @@ // Try to find the most general behavior that maps to this character. // For example, the base key behavior will usually be last in the list. - const Behavior* found = NULL; + const Behavior* found = nullptr; for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { if (behavior->character == ch) { found = behavior; @@ -487,7 +487,7 @@ int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { outEvents.push(); KeyEvent& event = outEvents.editTop(); - event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, + event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState, 0, time, time); } @@ -605,11 +605,11 @@ map->mType = parcel->readInt32(); size_t numKeys = parcel->readInt32(); if (parcel->errorCheck()) { - return NULL; + return nullptr; } if (numKeys > MAX_KEYS) { ALOGE("Too many keys in KeyCharacterMap (%zu > %d)", numKeys, MAX_KEYS); - return NULL; + return nullptr; } for (size_t i = 0; i < numKeys; i++) { @@ -617,7 +617,7 @@ char16_t label = parcel->readInt32(); char16_t number = parcel->readInt32(); if (parcel->errorCheck()) { - return NULL; + return nullptr; } Key* key = new Key(); @@ -625,14 +625,14 @@ key->number = number; map->mKeys.add(keyCode, key); - Behavior* lastBehavior = NULL; + Behavior* lastBehavior = nullptr; while (parcel->readInt32()) { int32_t metaState = parcel->readInt32(); char16_t character = parcel->readInt32(); int32_t fallbackKeyCode = parcel->readInt32(); int32_t replacementKeyCode = parcel->readInt32(); if (parcel->errorCheck()) { - return NULL; + return nullptr; } Behavior* behavior = new Behavior(); @@ -649,7 +649,7 @@ } if (parcel->errorCheck()) { - return NULL; + return nullptr; } } return map; @@ -666,7 +666,7 @@ parcel->writeInt32(keyCode); parcel->writeInt32(key->label); parcel->writeInt32(key->number); - for (const Behavior* behavior = key->firstBehavior; behavior != NULL; + for (const Behavior* behavior = key->firstBehavior; behavior != nullptr; behavior = behavior->next) { parcel->writeInt32(1); parcel->writeInt32(behavior->metaState); @@ -683,12 +683,12 @@ // --- KeyCharacterMap::Key --- KeyCharacterMap::Key::Key() : - label(0), number(0), firstBehavior(NULL) { + label(0), number(0), firstBehavior(nullptr) { } KeyCharacterMap::Key::Key(const Key& other) : label(other.label), number(other.number), - firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) { + firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : nullptr) { } KeyCharacterMap::Key::~Key() { @@ -704,11 +704,11 @@ // --- KeyCharacterMap::Behavior --- KeyCharacterMap::Behavior::Behavior() : - next(NULL), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) { + next(nullptr), metaState(0), character(0), fallbackKeyCode(0), replacementKeyCode(0) { } KeyCharacterMap::Behavior::Behavior(const Behavior& other) : - next(other.next ? new Behavior(*other.next) : NULL), + next(other.next ? new Behavior(*other.next) : nullptr), metaState(other.metaState), character(other.character), fallbackKeyCode(other.fallbackKeyCode), replacementKeyCode(other.replacementKeyCode) { @@ -944,7 +944,7 @@ properties.add(Property(PROPERTY_NUMBER)); } else { int32_t metaState; - status_t status = parseModifier(token, &metaState); + status_t status = parseModifier(token.string(), &metaState); if (status) { ALOGE("%s: Expected a property name or modifier, got '%s'.", mTokenizer->getLocation().string(), token.string()); @@ -1137,7 +1137,7 @@ return NO_ERROR; } -status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) { +status_t KeyCharacterMap::Parser::parseModifier(const std::string& token, int32_t* outMetaState) { if (token == "base") { *outMetaState = 0; return NO_ERROR; @@ -1145,7 +1145,7 @@ int32_t combinedMeta = 0; - const char* str = token.string(); + const char* str = token.c_str(); const char* start = str; for (const char* cur = str; ; cur++) { char ch = *cur; @@ -1164,7 +1164,7 @@ } if (combinedMeta & metaState) { ALOGE("%s: Duplicate modifier combination '%s'.", - mTokenizer->getLocation().string(), token.string()); + mTokenizer->getLocation().string(), token.c_str()); return BAD_VALUE; }
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index 2b2f13e..efca68d 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp
@@ -49,13 +49,13 @@ KeyLayoutMap::~KeyLayoutMap() { } -status_t KeyLayoutMap::load(const String8& filename, sp<KeyLayoutMap>* outMap) { +status_t KeyLayoutMap::load(const std::string& filename, sp<KeyLayoutMap>* outMap) { outMap->clear(); Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); + status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { - ALOGE("Error %d opening key layout map file %s.", status, filename.string()); + ALOGE("Error %d opening key layout map file %s.", status, filename.c_str()); } else { sp<KeyLayoutMap> map = new KeyLayoutMap(); if (!map.get()) { @@ -117,14 +117,15 @@ return &mKeysByScanCode.valueAt(index); } } - return NULL; + return nullptr; } -status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const { +status_t KeyLayoutMap::findScanCodesForKey( + int32_t keyCode, std::vector<int32_t>* outScanCodes) const { const size_t N = mKeysByScanCode.size(); for (size_t i=0; i<N; i++) { if (mKeysByScanCode.valueAt(i).keyCode == keyCode) { - outScanCodes->add(mKeysByScanCode.keyAt(i)); + outScanCodes->push_back(mKeysByScanCode.keyAt(i)); } } return NO_ERROR;
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index 11842ee..0c22bfe 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp
@@ -45,22 +45,22 @@ String8 keyLayoutName; if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"), keyLayoutName)) { - status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName); + status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName.c_str()); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but " "it was not found.", - deviceIdenfifier.name.string(), keyLayoutName.string()); + deviceIdenfifier.name.c_str(), keyLayoutName.string()); } } String8 keyCharacterMapName; if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), keyCharacterMapName)) { - status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName); + status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName.c_str()); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard character " "map '%s' but it was not found.", - deviceIdenfifier.name.string(), keyLayoutName.string()); + deviceIdenfifier.name.c_str(), keyLayoutName.string()); } } @@ -70,30 +70,30 @@ } // Try searching by device identifier. - if (probeKeyMap(deviceIdenfifier, String8::empty())) { + if (probeKeyMap(deviceIdenfifier, "")) { return OK; } // Fall back on the Generic key map. // TODO Apply some additional heuristics here to figure out what kind of // generic key map to use (US English, etc.) for typical external keyboards. - if (probeKeyMap(deviceIdenfifier, String8("Generic"))) { + if (probeKeyMap(deviceIdenfifier, "Generic")) { return OK; } // Try the Virtual key map as a last resort. - if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) { + if (probeKeyMap(deviceIdenfifier, "Virtual")) { return OK; } // Give up! ALOGE("Could not determine key map for device '%s' and no default key maps were found!", - deviceIdenfifier.name.string()); + deviceIdenfifier.name.c_str()); return NAME_NOT_FOUND; } bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, - const String8& keyMapName) { + const std::string& keyMapName) { if (!haveKeyLayout()) { loadKeyLayout(deviceIdentifier, keyMapName); } @@ -104,10 +104,10 @@ } status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, - const String8& name) { - String8 path(getPath(deviceIdentifier, name, + const std::string& name) { + std::string path(getPath(deviceIdentifier, name, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT)); - if (path.isEmpty()) { + if (path.empty()) { return NAME_NOT_FOUND; } @@ -116,15 +116,15 @@ return status; } - keyLayoutFile.setTo(path); + keyLayoutFile = path; return OK; } status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, - const String8& name) { - String8 path(getPath(deviceIdentifier, name, - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); - if (path.isEmpty()) { + const std::string& name) { + std::string path = getPath(deviceIdentifier, name, + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP); + if (path.empty()) { return NAME_NOT_FOUND; } @@ -134,13 +134,13 @@ return status; } - keyCharacterMapFile.setTo(path); + keyCharacterMapFile = path; return OK; } -String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, - const String8& name, InputDeviceConfigurationFileType type) { - return name.isEmpty() +std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, + const std::string& name, InputDeviceConfigurationFileType type) { + return name.empty() ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) : getInputDeviceConfigurationFilePathByName(name, type); } @@ -174,7 +174,7 @@ } } - return strstr(deviceIdentifier.name.string(), "-keypad"); + return strstr(deviceIdentifier.name.c_str(), "-keypad"); } static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) {
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp new file mode 100644 index 0000000..8a4298a --- /dev/null +++ b/libs/input/TouchVideoFrame.cpp
@@ -0,0 +1,100 @@ +/* + * 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. + */ + +#include <input/TouchVideoFrame.h> + +namespace android { + +TouchVideoFrame::TouchVideoFrame(uint32_t height, uint32_t width, std::vector<int16_t> data, + const struct timeval& timestamp) : + mHeight(height), mWidth(width),mData(std::move(data)), mTimestamp(timestamp) { +} + +bool TouchVideoFrame::operator==(const TouchVideoFrame& rhs) const { + return mHeight == rhs.mHeight + && mWidth == rhs.mWidth + && mData == rhs.mData + && mTimestamp.tv_sec == rhs.mTimestamp.tv_sec + && mTimestamp.tv_usec == rhs.mTimestamp.tv_usec; +} + +uint32_t TouchVideoFrame::getHeight() const { return mHeight; } + +uint32_t TouchVideoFrame::getWidth() const { return mWidth; } + +const std::vector<int16_t>& TouchVideoFrame::getData() const { return mData; } + +const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp; } + +void TouchVideoFrame::rotate(int32_t orientation) { + switch (orientation) { + case DISPLAY_ORIENTATION_90: + rotateQuarterTurn(true /*clockwise*/); + break; + case DISPLAY_ORIENTATION_180: + rotate180(); + break; + case DISPLAY_ORIENTATION_270: + rotateQuarterTurn(false /*clockwise*/); + break; + } +} + +/** + * Rotate once clockwise by a quarter turn === rotate 90 degrees + * Rotate once counterclockwise by a quarter turn === rotate 270 degrees + * For a clockwise rotation: + * An element at position (i, j) is rotated to (j, height - i - 1) + * For a counterclockwise rotation: + * An element at position (i, j) is rotated to (width - j - 1, i) + */ +void TouchVideoFrame::rotateQuarterTurn(bool clockwise) { + std::vector<int16_t> rotated(mData.size()); + for (size_t i = 0; i < mHeight; i++) { + for (size_t j = 0; j < mWidth; j++) { + size_t iRotated, jRotated; + if (clockwise) { + iRotated = j; + jRotated = mHeight - i - 1; + } else { + iRotated = mWidth - j - 1; + jRotated = i; + } + size_t indexRotated = iRotated * mHeight + jRotated; + rotated[indexRotated] = mData[i * mWidth + j]; + } + } + mData = std::move(rotated); + std::swap(mHeight, mWidth); +} + +/** + * An element at position (i, j) is rotated to (height - i - 1, width - j - 1) + * This is equivalent to moving element [i] to position [height * width - i - 1] + * Since element at [height * width - i - 1] would move to position [i], + * we can just swap elements [i] and [height * width - i - 1]. + */ +void TouchVideoFrame::rotate180() { + if (mData.size() == 0) { + return; + } + // Just need to swap elements i and (height * width - 1 - i) + for (size_t i = 0; i < mData.size() / 2; i++) { + std::swap(mData[i], mData[mHeight * mWidth - 1 - i]); + } +} + +} // namespace android
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index c70ace0..c6cc4fc 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp
@@ -23,9 +23,11 @@ // Log debug messages about the progress of the algorithm itself. #define DEBUG_STRATEGY 0 +#include <array> #include <inttypes.h> #include <limits.h> #include <math.h> +#include <optional> #include <android-base/stringprintf.h> #include <cutils/properties.h> @@ -115,7 +117,7 @@ // Allow the default strategy to be overridden using a system property for debugging. if (!strategy) { - int length = property_get("debug.velocitytracker.strategy", value, NULL); + int length = property_get("persist.input.velocitytracker.strategy", value, nullptr); if (length > 0) { strategy = value; } else { @@ -139,7 +141,7 @@ bool VelocityTracker::configureStrategy(const char* strategy) { mStrategy = createStrategy(strategy); - return mStrategy != NULL; + return mStrategy != nullptr; } VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { @@ -204,7 +206,7 @@ // time to adjust to changes in direction. return new LegacyVelocityTrackerStrategy(); } - return NULL; + return nullptr; } void VelocityTracker::clear() { @@ -380,7 +382,16 @@ void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, const VelocityTracker::Position* positions) { - if (++mIndex == HISTORY_SIZE) { + if (mMovements[mIndex].eventTime != eventTime) { + // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates + // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include + // the new pointer. If the eventtimes for both events are identical, just update the data + // for this time. + // We only compare against the last value, as it is likely that addMovement is called + // in chronological order as events occur. + mIndex++; + } + if (mIndex == HISTORY_SIZE) { mIndex = 0; } @@ -564,7 +575,9 @@ * Optimized unweighted second-order least squares fit. About 2x speed improvement compared to * the default implementation */ -static float solveUnweightedLeastSquaresDeg2(const float* x, const float* y, size_t count) { +static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2( + const float* x, const float* y, size_t count) { + // Solving y = a*x^2 + b*x + c float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0; for (size_t i = 0; i < count; i++) { @@ -573,8 +586,8 @@ float xi2 = xi*xi; float xi3 = xi2*xi; float xi4 = xi3*xi; - float xi2yi = xi2*yi; float xiyi = xi*yi; + float xi2yi = xi2*yi; sxi += xi; sxi2 += xi2; @@ -591,13 +604,23 @@ float Sx2y = sxi2yi - sxi2*syi / count; float Sx2x2 = sxi4 - sxi2*sxi2 / count; - float numerator = Sxy*Sx2x2 - Sx2y*Sxx2; float denominator = Sxx*Sx2x2 - Sxx2*Sxx2; if (denominator == 0) { ALOGW("division by 0 when computing velocity, Sxx=%f, Sx2x2=%f, Sxx2=%f", Sxx, Sx2x2, Sxx2); - return 0; + return std::nullopt; } - return numerator/denominator; + // Compute a + float numerator = Sx2y*Sxx - Sxy*Sxx2; + float a = numerator / denominator; + + // Compute b + numerator = Sxy*Sx2x2 - Sx2y*Sxx2; + float b = numerator / denominator; + + // Compute c + float c = syi/count - b * sxi/count - a * sxi2/count; + + return std::make_optional(std::array<float, 3>({c, b, a})); } bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, @@ -640,20 +663,23 @@ if (degree > m - 1) { degree = m - 1; } - if (degree >= 1) { - if (degree == 2 && mWeighting == WEIGHTING_NONE) { // optimize unweighted, degree=2 fit + + if (degree == 2 && mWeighting == WEIGHTING_NONE) { + // Optimize unweighted, quadratic polynomial fit + std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x, m); + std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y, m); + if (xCoeff && yCoeff) { outEstimator->time = newestMovement.eventTime; outEstimator->degree = 2; outEstimator->confidence = 1; - outEstimator->xCoeff[0] = 0; // only slope is calculated, set rest of coefficients = 0 - outEstimator->yCoeff[0] = 0; - outEstimator->xCoeff[1] = solveUnweightedLeastSquaresDeg2(time, x, m); - outEstimator->yCoeff[1] = solveUnweightedLeastSquaresDeg2(time, y, m); - outEstimator->xCoeff[2] = 0; - outEstimator->yCoeff[2] = 0; + for (size_t i = 0; i <= outEstimator->degree; i++) { + outEstimator->xCoeff[i] = (*xCoeff)[i]; + outEstimator->yCoeff[i] = (*yCoeff)[i]; + } return true; } - + } else if (degree >= 1) { + // General case for an Nth degree polynomial fit float xdet, ydet; uint32_t n = degree + 1; if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet) @@ -1000,7 +1026,16 @@ void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, const VelocityTracker::Position* positions) { - if (++mIndex == HISTORY_SIZE) { + if (mMovements[mIndex].eventTime != eventTime) { + // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates + // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include + // the new pointer. If the eventtimes for both events are identical, just update the data + // for this time. + // We only compare against the last value, as it is likely that addMovement is called + // in chronological order as events occur. + mIndex++; + } + if (mIndex == HISTORY_SIZE) { mIndex = 0; }
diff --git a/libs/input/VirtualKeyMap.cpp b/libs/input/VirtualKeyMap.cpp index 28ea717..865366b 100644 --- a/libs/input/VirtualKeyMap.cpp +++ b/libs/input/VirtualKeyMap.cpp
@@ -28,10 +28,6 @@ // Enables debug output for the parser. #define DEBUG_PARSER 0 -// Enables debug output for parser performance. -#define DEBUG_PARSER_PERFORMANCE 0 - - namespace android { static const char* WHITESPACE = " \t\r"; @@ -46,39 +42,28 @@ VirtualKeyMap::~VirtualKeyMap() { } -status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) { - *outMap = NULL; - - Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); - if (status) { - ALOGE("Error %d opening virtual key map file %s.", status, filename.string()); - } else { - VirtualKeyMap* map = new VirtualKeyMap(); - if (!map) { - ALOGE("Error allocating virtual key map."); - status = NO_MEMORY; - } else { -#if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); -#endif - Parser parser(map, tokenizer); - status = parser.parse(); -#if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); -#endif - if (status) { - delete map; - } else { - *outMap = map; - } - } - delete tokenizer; +std::unique_ptr<VirtualKeyMap> VirtualKeyMap::load(const std::string& filename) { + Tokenizer* t; + status_t status = Tokenizer::open(String8(filename.c_str()), &t); + if (status != OK) { + ALOGE("Error %d opening virtual key map file %s.", status, filename.c_str()); + return nullptr; } - return status; + std::unique_ptr<Tokenizer> tokenizer(t); + // Using 'new' to access a non-public constructor + std::unique_ptr<VirtualKeyMap> map(new VirtualKeyMap()); + if (!map) { + ALOGE("Error allocating virtual key map."); + return nullptr; + } + + Parser parser(map.get(), tokenizer.get()); + status = parser.parse(); + if (status != OK) { + return nullptr; + } + + return map; } @@ -127,7 +112,7 @@ "width=%d, height=%d", defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height); #endif - mMap->mVirtualKeys.push(defn); + mMap->mVirtualKeys.push_back(defn); } while (consumeFieldDelimiterAndSkipWhitespace()); if (!mTokenizer->isEol()) {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index aca9521..ade931e 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp
@@ -1,11 +1,13 @@ // Build the unit tests. cc_test { name: "libinput_tests", - test_per_src: true, srcs: [ "InputChannel_test.cpp", + "InputDevice_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", + "InputWindow_test.cpp", + "TouchVideoFrame_test.cpp", "VelocityTracker_test.cpp", ], cflags: [ @@ -34,5 +36,14 @@ "-O0", "-Wall", "-Werror", + "-Wextra", ], + shared_libs: [ + "libinput", + "libcutils", + "libutils", + "libbinder", + "libui", + "libbase", + ] }
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index 96c165c..f1675c0 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp
@@ -14,6 +14,8 @@ * limitations under the License. */ +#include <array> + #include "TestHelpers.h" #include <unistd.h> @@ -155,5 +157,37 @@ << "sendMessage should have returned DEAD_OBJECT"; } +TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { + sp<InputChannel> serverChannel, clientChannel; + status_t result = InputChannel::openInputChannelPair("channel name", + serverChannel, clientChannel); + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + std::array<MotionClassification, 3> classifications = { + MotionClassification::NONE, + MotionClassification::AMBIGUOUS_GESTURE, + MotionClassification::DEEP_PRESS, + }; + + InputMessage serverMsg = {}, clientMsg; + serverMsg.header.type = InputMessage::TYPE_MOTION; + serverMsg.body.motion.seq = 1; + serverMsg.body.motion.pointerCount = 1; + + for (MotionClassification classification : classifications) { + // Send and receive a message with classification + serverMsg.body.motion.classification = classification; + EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg)) + << "server channel should be able to send message to client channel"; + + EXPECT_EQ(OK, clientChannel->receiveMessage(&clientMsg)) + << "client channel should be able to receive message from server channel"; + EXPECT_EQ(serverMsg.header.type, clientMsg.header.type); + EXPECT_EQ(classification, clientMsg.body.motion.classification) << + "Expected to receive " << motionClassificationToString(classification); + } +} + } // namespace android
diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp new file mode 100644 index 0000000..c174ae9 --- /dev/null +++ b/libs/input/tests/InputDevice_test.cpp
@@ -0,0 +1,34 @@ +/* + * 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. + */ + + +#include <gtest/gtest.h> +#include <input/InputDevice.h> + +namespace android { + +// --- InputDeviceIdentifierTest --- + +TEST(InputDeviceIdentifierTest, getCanonicalName) { + InputDeviceIdentifier identifier; + identifier.name = "test device"; + ASSERT_EQ(std::string("test_device"), identifier.getCanonicalName()); + + identifier.name = "deviceName-123 version_C!"; + ASSERT_EQ(std::string("deviceName-123_version_C_"), identifier.getCanonicalName()); +} + +} // namespace android \ No newline at end of file
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index fd3b7c8..2b75c82 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp
@@ -14,6 +14,7 @@ * limitations under the License. */ +#include <array> #include <math.h> #include <binder/Parcel.h> @@ -22,11 +23,10 @@ namespace android { -class BaseTest : public testing::Test { -protected: - virtual void SetUp() { } - virtual void TearDown() { } -}; +// Default display id. +static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; + +class BaseTest : public testing::Test { }; // --- PointerCoordsTest --- @@ -178,13 +178,14 @@ // Initialize and get properties. const nsecs_t ARBITRARY_DOWN_TIME = 1; const nsecs_t ARBITRARY_EVENT_TIME = 2; - event.initialize(2, AINPUT_SOURCE_GAMEPAD, AKEY_EVENT_ACTION_DOWN, + event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); ASSERT_EQ(2, event.getDeviceId()); ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_GAMEPAD), event.getSource()); + ASSERT_EQ(DISPLAY_ID, event.getDisplayId()); ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags()); ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode()); @@ -197,6 +198,11 @@ // Set source. event.setSource(AINPUT_SOURCE_JOYSTICK); ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource()); + + // Set display id. + constexpr int32_t newDisplayId = 2; + event.setDisplayId(newDisplayId); + ASSERT_EQ(newDisplayId, event.getDisplayId()); } @@ -248,10 +254,10 @@ pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); - event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, 0, + event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, - X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + MotionClassification::NONE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); @@ -301,11 +307,13 @@ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); ASSERT_EQ(2, event->getDeviceId()); ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_TOUCHSCREEN), event->getSource()); + ASSERT_EQ(DISPLAY_ID, event->getDisplayId()); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); + ASSERT_EQ(MotionClassification::NONE, event->getClassification()); ASSERT_EQ(X_OFFSET, event->getXOffset()); ASSERT_EQ(Y_OFFSET, event->getYOffset()); ASSERT_EQ(2.0f, event->getXPrecision()); @@ -434,6 +442,11 @@ event.setSource(AINPUT_SOURCE_JOYSTICK); ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource()); + // Set displayId. + constexpr int32_t newDisplayId = 2; + event.setDisplayId(newDisplayId); + ASSERT_EQ(newDisplayId, event.getDisplayId()); + // Set action. event.setAction(AMOTION_EVENT_ACTION_CANCEL); ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction()); @@ -557,8 +570,11 @@ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; - event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, + 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, + AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, + 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, + 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; @@ -591,4 +607,30 @@ ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); } +TEST_F(MotionEventTest, Initialize_SetsClassification) { + std::array<MotionClassification, 3> classifications = { + MotionClassification::NONE, + MotionClassification::AMBIGUOUS_GESTURE, + MotionClassification::DEEP_PRESS, + }; + + MotionEvent event; + constexpr size_t pointerCount = 1; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerCoords[i].clear(); + } + + for (MotionClassification classification : classifications) { + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, + AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, + classification, 0, 0, 0, 0, 0 /*downTime*/, 0 /*eventTime*/, + pointerCount, pointerProperties, pointerCoords); + ASSERT_EQ(classification, event.getClassification()); + } +} + } // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index c532241..f2cd1be 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -46,12 +46,12 @@ virtual void TearDown() { if (mPublisher) { delete mPublisher; - mPublisher = NULL; + mPublisher = nullptr; } if (mConsumer) { delete mConsumer; - mConsumer = NULL; + mConsumer = nullptr; } serverChannel.clear(); @@ -70,32 +70,31 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { status_t status; - const uint32_t seq = 15; - const int32_t deviceId = 1; - const int32_t source = AINPUT_SOURCE_KEYBOARD; - const int32_t action = AKEY_EVENT_ACTION_DOWN; - const int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; - const int32_t keyCode = AKEYCODE_ENTER; - const int32_t scanCode = 13; - const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; - const int32_t repeatCount = 1; - const nsecs_t downTime = 3; - const nsecs_t eventTime = 4; + constexpr uint32_t seq = 15; + constexpr int32_t deviceId = 1; + constexpr int32_t source = AINPUT_SOURCE_KEYBOARD; + constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr int32_t action = AKEY_EVENT_ACTION_DOWN; + constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM; + constexpr int32_t keyCode = AKEYCODE_ENTER; + constexpr int32_t scanCode = 13; + constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + constexpr int32_t repeatCount = 1; + constexpr nsecs_t downTime = 3; + constexpr nsecs_t eventTime = 4; - status = mPublisher->publishKeyEvent(seq, deviceId, source, action, flags, + status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; uint32_t consumeSeq; InputEvent* event; - int32_t displayId; - status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event, - &displayId); + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; - ASSERT_TRUE(event != NULL) + ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event->getType()) << "consumer should have returned a key event"; @@ -104,6 +103,7 @@ EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(deviceId, keyEvent->getDeviceId()); EXPECT_EQ(source, keyEvent->getSource()); + EXPECT_EQ(displayId, keyEvent->getDisplayId()); EXPECT_EQ(action, keyEvent->getAction()); EXPECT_EQ(flags, keyEvent->getFlags()); EXPECT_EQ(keyCode, keyEvent->getKeyCode()); @@ -131,23 +131,24 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { status_t status; - const uint32_t seq = 15; - const int32_t deviceId = 1; - const int32_t source = AINPUT_SOURCE_TOUCHSCREEN; - int32_t displayId = 0; - const int32_t action = AMOTION_EVENT_ACTION_MOVE; - const int32_t actionButton = 0; - const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; - const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; - const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; - const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; - const float xOffset = -10; - const float yOffset = -20; - const float xPrecision = 0.25; - const float yPrecision = 0.5; - const nsecs_t downTime = 3; - const size_t pointerCount = 3; - const nsecs_t eventTime = 4; + constexpr uint32_t seq = 15; + constexpr int32_t deviceId = 1; + constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN; + constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; + constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE; + constexpr int32_t actionButton = 0; + constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; + constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; + constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE; + constexpr float xOffset = -10; + constexpr float yOffset = -20; + constexpr float xPrecision = 0.25; + constexpr float yPrecision = 0.5; + constexpr nsecs_t downTime = 3; + constexpr size_t pointerCount = 3; + constexpr nsecs_t eventTime = 4; PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (size_t i = 0; i < pointerCount; i++) { @@ -168,20 +169,19 @@ } status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton, - flags, edgeFlags, metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision, - downTime, eventTime, pointerCount, + flags, edgeFlags, metaState, buttonState, classification, + xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; uint32_t consumeSeq; InputEvent* event; - status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event, - &displayId); + status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; - ASSERT_TRUE(event != NULL) + ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()) << "consumer should have returned a motion event"; @@ -190,11 +190,13 @@ EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(deviceId, motionEvent->getDeviceId()); EXPECT_EQ(source, motionEvent->getSource()); + EXPECT_EQ(displayId, motionEvent->getDisplayId()); EXPECT_EQ(action, motionEvent->getAction()); EXPECT_EQ(flags, motionEvent->getFlags()); EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); EXPECT_EQ(metaState, motionEvent->getMetaState()); EXPECT_EQ(buttonState, motionEvent->getButtonState()); + EXPECT_EQ(classification, motionEvent->getClassification()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); EXPECT_EQ(downTime, motionEvent->getDownTime()); @@ -264,7 +266,8 @@ pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + MotionClassification::NONE, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; @@ -276,7 +279,8 @@ PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + MotionClassification::NONE, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; @@ -293,7 +297,8 @@ pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + MotionClassification::NONE, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE";
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp new file mode 100644 index 0000000..6db18ab --- /dev/null +++ b/libs/input/tests/InputWindow_test.cpp
@@ -0,0 +1,103 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <binder/Binder.h> +#include <binder/Parcel.h> + +#include <input/InputWindow.h> +#include <input/InputTransport.h> + +namespace android { +namespace test { + +TEST(InputWindowInfo, ParcellingWithoutToken) { + InputWindowInfo i; + i.token = nullptr; + + Parcel p; + ASSERT_EQ(OK, i.write(p)); + p.setDataPosition(0); + InputWindowInfo i2 = InputWindowInfo::read(p); + ASSERT_TRUE(i2.token == nullptr); +} + +TEST(InputWindowInfo, Parcelling) { + sp<IBinder> touchableRegionCropHandle = new BBinder(); + InputWindowInfo i; + i.token = new BBinder(); + i.name = "Foobar"; + i.layoutParamsFlags = 7; + i.layoutParamsType = 39; + i.dispatchingTimeout = 12; + i.frameLeft = 93; + i.frameTop = 34; + i.frameRight = 16; + i.frameBottom = 19; + i.surfaceInset = 17; + i.globalScaleFactor = 0.3; + i.windowXScale = 0.4; + i.windowYScale = 0.5; + i.visible = false; + i.canReceiveKeys = false; + i.hasFocus = false; + i.hasWallpaper = false; + i.paused = false; + i.layer = 7; + i.ownerPid = 19; + i.ownerUid = 24; + i.inputFeatures = 29; + i.displayId = 34; + i.portalToDisplayId = 2; + i.replaceTouchableRegionWithCrop = true; + i.touchableRegionCropHandle = touchableRegionCropHandle; + + Parcel p; + i.write(p); + + p.setDataPosition(0); + InputWindowInfo i2 = InputWindowInfo::read(p); + ASSERT_EQ(i.token, i2.token); + ASSERT_EQ(i.name, i2.name); + ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags); + ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType); + ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout); + ASSERT_EQ(i.frameLeft, i2.frameLeft); + ASSERT_EQ(i.frameTop, i2.frameTop); + ASSERT_EQ(i.frameRight, i2.frameRight); + ASSERT_EQ(i.frameBottom, i2.frameBottom); + ASSERT_EQ(i.surfaceInset, i2.surfaceInset); + ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor); + ASSERT_EQ(i.windowXScale, i2.windowXScale); + ASSERT_EQ(i.windowYScale, i2.windowYScale); + ASSERT_EQ(i.visible, i2.visible); + ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys); + ASSERT_EQ(i.hasFocus, i2.hasFocus); + ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper); + ASSERT_EQ(i.paused, i2.paused); + ASSERT_EQ(i.layer, i2.layer); + ASSERT_EQ(i.ownerPid, i2.ownerPid); + ASSERT_EQ(i.ownerUid, i2.ownerUid); + ASSERT_EQ(i.inputFeatures, i2.inputFeatures); + ASSERT_EQ(i.displayId, i2.displayId); + ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId); + ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop); + ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle); +} + +} // namespace test +} // namespace android
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 12a6782..62023fb 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp
@@ -57,7 +57,8 @@ CHECK_OFFSET(InputMessage::Body::Motion, flags, 36); CHECK_OFFSET(InputMessage::Body::Motion, metaState, 40); CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 44); - CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 48); + CHECK_OFFSET(InputMessage::Body::Motion, classification, 48); + CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 52); CHECK_OFFSET(InputMessage::Body::Motion, downTime, 56); CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 64); CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68);
diff --git a/libs/input/tests/TestHelpers.h b/libs/input/tests/TestHelpers.h index fe87bb9..343d81f 100644 --- a/libs/input/tests/TestHelpers.h +++ b/libs/input/tests/TestHelpers.h
@@ -62,7 +62,7 @@ int mDelayMillis; public: - DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { } + explicit DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { } protected: virtual ~DelayedTask() { }
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp new file mode 100644 index 0000000..815424e --- /dev/null +++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -0,0 +1,196 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <input/TouchVideoFrame.h> + +namespace android { +namespace test { + +static const struct timeval TIMESTAMP = {1, 2}; + +TEST(TouchVideoFrame, Constructor) { + const std::vector<int16_t> data = {1, 2, 3, 4, 5, 6}; + constexpr uint32_t height = 3; + constexpr uint32_t width = 2; + + TouchVideoFrame frame(height, width, data, TIMESTAMP); + + ASSERT_EQ(data, frame.getData()); + ASSERT_EQ(height, frame.getHeight()); + ASSERT_EQ(width, frame.getWidth()); + ASSERT_EQ(TIMESTAMP.tv_sec, frame.getTimestamp().tv_sec); + ASSERT_EQ(TIMESTAMP.tv_usec, frame.getTimestamp().tv_usec); +} + +TEST(TouchVideoFrame, Equality) { + const std::vector<int16_t> data = {1, 2, 3, 4, 5, 6}; + constexpr uint32_t height = 3; + constexpr uint32_t width = 2; + TouchVideoFrame frame(height, width, data, TIMESTAMP); + + TouchVideoFrame identicalFrame(height, width, data, TIMESTAMP); + ASSERT_EQ(frame, identicalFrame); + + // The two cases below create an invalid frame, but it is OK for comparison purposes. + // There aren't any checks currently enforced on the frame dimensions and data + // Change height + TouchVideoFrame changedHeightFrame(height + 1, width, data, TIMESTAMP); + ASSERT_FALSE(frame == changedHeightFrame); + + // Change width + TouchVideoFrame changedWidthFrame(height, width + 1, data, TIMESTAMP); + ASSERT_FALSE(frame == changedWidthFrame); + + // Change data + const std::vector<int16_t> differentData = {1, 2, 3, 3, 5, 6}; + TouchVideoFrame changedDataFrame(height, width, differentData, TIMESTAMP); + ASSERT_FALSE(frame == changedDataFrame); + + // Change timestamp + const struct timeval differentTimestamp = {TIMESTAMP.tv_sec + 1, TIMESTAMP.tv_usec + 1}; + TouchVideoFrame changedTimestampFrame(height, width, data, differentTimestamp); + ASSERT_FALSE(frame == changedTimestampFrame); +} + +// --- Rotate 90 degrees --- + +TEST(TouchVideoFrame, Rotate90_0x0) { + TouchVideoFrame frame(0, 0, {}, TIMESTAMP); + TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_90); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate90_1x1) { + TouchVideoFrame frame(1, 1, {1}, TIMESTAMP); + TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_90); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate90_2x2) { + TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_90); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate90_3x2) { + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_90); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate90_3x2_4times) { + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_90); + frame.rotate(DISPLAY_ORIENTATION_90); + frame.rotate(DISPLAY_ORIENTATION_90); + frame.rotate(DISPLAY_ORIENTATION_90); + ASSERT_EQ(frame, frameOriginal); +} + +// --- Rotate 180 degrees --- + +TEST(TouchVideoFrame, Rotate180_0x0) { + TouchVideoFrame frame(0, 0, {}, TIMESTAMP); + TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_180); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate180_1x1) { + TouchVideoFrame frame(1, 1, {1}, TIMESTAMP); + TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_180); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate180_2x2) { + TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 2, {4, 3, 2, 1}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_180); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate180_3x2) { + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + TouchVideoFrame frameRotated(3, 2, {6, 5, 4, 3, 2, 1}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_180); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate180_3x2_2times) { + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_180); + frame.rotate(DISPLAY_ORIENTATION_180); + ASSERT_EQ(frame, frameOriginal); +} + +TEST(TouchVideoFrame, Rotate180_3x3) { + TouchVideoFrame frame(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9}, TIMESTAMP); + TouchVideoFrame frameRotated(3, 3, {9, 8, 7, 6, 5, 4, 3, 2, 1}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_180); + ASSERT_EQ(frame, frameRotated); +} + +// --- Rotate 270 degrees --- + +TEST(TouchVideoFrame, Rotate270_0x0) { + TouchVideoFrame frame(0, 0, {}, TIMESTAMP); + TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_270); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate270_1x1) { + TouchVideoFrame frame(1, 1, {1}, TIMESTAMP); + TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_270); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate270_2x2) { + TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_270); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate270_3x2) { + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_270); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate270_3x2_4times) { + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_270); + frame.rotate(DISPLAY_ORIENTATION_270); + frame.rotate(DISPLAY_ORIENTATION_270); + frame.rotate(DISPLAY_ORIENTATION_270); + ASSERT_EQ(frame, frameOriginal); +} + +} // namespace test +} // namespace android
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 43b6012..368446f 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -16,97 +16,188 @@ #define LOG_TAG "VelocityTracker_test" +#include <array> +#include <chrono> #include <math.h> #include <android-base/stringprintf.h> #include <gtest/gtest.h> #include <input/VelocityTracker.h> +using namespace std::chrono_literals; using android::base::StringPrintf; namespace android { +constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; // default display id + constexpr int32_t DEFAULT_POINTER_ID = 0; // pointer ID used for manually defined tests // velocity must be in the range (1-tol)*EV <= velocity <= (1+tol)*EV // here EV = expected value, tol = VELOCITY_TOLERANCE constexpr float VELOCITY_TOLERANCE = 0.2; +// estimate coefficients must be within 0.001% of the target value +constexpr float COEFFICIENT_TOLERANCE = 0.00001; + // --- VelocityTrackerTest --- class VelocityTrackerTest : public testing::Test { }; -static void checkVelocity(float Vactual, float Vtarget) { - // Compare directions - if ((Vactual > 0 && Vtarget <= 0) || (Vactual < 0 && Vtarget >= 0)) { - FAIL() << StringPrintf("Velocity %f does not have the same direction" - " as the target velocity %f", Vactual, Vtarget); - } +/* + * Similar to EXPECT_NEAR, but ensures that the difference between the two float values + * is at most a certain fraction of the target value. + * If fraction is zero, require exact match. + */ +static void EXPECT_NEAR_BY_FRACTION(float actual, float target, float fraction) { + float tolerance = fabsf(target * fraction); - // Compare magnitudes - const float Vlower = fabsf(Vtarget * (1 - VELOCITY_TOLERANCE)); - const float Vupper = fabsf(Vtarget * (1 + VELOCITY_TOLERANCE)); - if (fabsf(Vactual) < Vlower) { - FAIL() << StringPrintf("Velocity %f is more than %.0f%% below target velocity %f", - Vactual, VELOCITY_TOLERANCE * 100, Vtarget); + if (target == 0 && fraction != 0) { + // If target is zero, this would force actual == target, which is too harsh. + // Relax this requirement a little. The value is determined empirically from the + // coefficients computed by the quadratic least squares algorithms. + tolerance = 1E-6; } - if (fabsf(Vactual) > Vupper) { - FAIL() << StringPrintf("Velocity %f is more than %.0f%% above target velocity %f", - Vactual, VELOCITY_TOLERANCE * 100, Vtarget); - } - SUCCEED() << StringPrintf("Velocity %f within %.0f%% of target %f)", - Vactual, VELOCITY_TOLERANCE * 100, Vtarget); + EXPECT_NEAR(actual, target, tolerance); } -void failWithMessage(std::string message) { - FAIL() << message; // cannot do this directly from a non-void function +static void checkVelocity(float Vactual, float Vtarget) { + EXPECT_NEAR_BY_FRACTION(Vactual, Vtarget, VELOCITY_TOLERANCE); +} + +static void checkCoefficient(float actual, float target) { + EXPECT_NEAR_BY_FRACTION(actual, target, COEFFICIENT_TOLERANCE); } struct Position { - nsecs_t time; - float x; - float y; + float x; + float y; + + /** + * If both values are NAN, then this is considered to be an empty entry (no pointer data). + * If only one of the values is NAN, this is still a valid entry, + * because we may only care about a single axis. + */ + bool isValid() const { + return !(isnan(x) && isnan(y)); + } }; +struct MotionEventEntry { + std::chrono::nanoseconds eventTime; + std::vector<Position> positions; +}; -MotionEvent* createSimpleMotionEvent(const Position* positions, size_t numSamples) { - /** - * Only populate the basic fields of a MotionEvent, such as time and a single axis - * Designed for use with manually-defined tests. - * Create a new MotionEvent on the heap, caller responsible for destroying the object. - */ - if (numSamples < 1) { - failWithMessage(StringPrintf("Need at least 1 sample to create a MotionEvent." - " Received numSamples=%zu", numSamples)); +static BitSet32 getValidPointers(const std::vector<Position>& positions) { + BitSet32 pointers; + for (size_t i = 0; i < positions.size(); i++) { + if (positions[i].isValid()) { + pointers.markBit(i); + } } - - MotionEvent* event = new MotionEvent(); - PointerCoords coords; - PointerProperties properties[1]; - - properties[0].id = DEFAULT_POINTER_ID; - properties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - - // First sample added separately with initialize - coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x); - coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y); - event->initialize(0, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, positions[0].time, 1, properties, &coords); - - for (size_t i = 1; i < numSamples; i++) { - coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[i].x); - coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[i].y); - event->addSample(positions[i].time, &coords); - } - return event; + return pointers; } -static void computeAndCheckVelocity(const Position* positions, size_t numSamples, - int32_t axis, float targetVelocity) { - VelocityTracker vt(nullptr); +static uint32_t getChangingPointerId(BitSet32 pointers, BitSet32 otherPointers) { + BitSet32 difference(pointers.value ^ otherPointers.value); + uint32_t pointerId = difference.clearFirstMarkedBit(); + EXPECT_EQ(0U, difference.value) << "Only 1 pointer can enter or leave at a time"; + return pointerId; +} + +static int32_t resolveAction(const std::vector<Position>& lastPositions, + const std::vector<Position>& currentPositions, + const std::vector<Position>& nextPositions) { + BitSet32 pointers = getValidPointers(currentPositions); + const uint32_t pointerCount = pointers.count(); + + BitSet32 lastPointers = getValidPointers(lastPositions); + const uint32_t lastPointerCount = lastPointers.count(); + if (lastPointerCount < pointerCount) { + // A new pointer is down + uint32_t pointerId = getChangingPointerId(pointers, lastPointers); + return AMOTION_EVENT_ACTION_POINTER_DOWN | + (pointerId << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + } + + BitSet32 nextPointers = getValidPointers(nextPositions); + const uint32_t nextPointerCount = nextPointers.count(); + if (pointerCount > nextPointerCount) { + // An existing pointer is leaving + uint32_t pointerId = getChangingPointerId(pointers, nextPointers); + return AMOTION_EVENT_ACTION_POINTER_UP | + (pointerId << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + } + + return AMOTION_EVENT_ACTION_MOVE; +} + +static std::vector<MotionEvent> createMotionEventStream( + const std::vector<MotionEventEntry>& motions) { + if (motions.empty()) { + ADD_FAILURE() << "Need at least 1 sample to create a MotionEvent. Received empty vector."; + } + + std::vector<MotionEvent> events; + for (size_t i = 0; i < motions.size(); i++) { + const MotionEventEntry& entry = motions[i]; + BitSet32 pointers = getValidPointers(entry.positions); + const uint32_t pointerCount = pointers.count(); + + int32_t action; + if (i == 0) { + action = AMOTION_EVENT_ACTION_DOWN; + EXPECT_EQ(1U, pointerCount) << "First event should only have 1 pointer"; + } else if (i == motions.size() - 1) { + EXPECT_EQ(1U, pointerCount) << "Last event should only have 1 pointer"; + action = AMOTION_EVENT_ACTION_UP; + } else { + const MotionEventEntry& previousEntry = motions[i-1]; + const MotionEventEntry& nextEntry = motions[i+1]; + action = resolveAction(previousEntry.positions, entry.positions, nextEntry.positions); + } + + PointerCoords coords[pointerCount]; + PointerProperties properties[pointerCount]; + uint32_t pointerIndex = 0; + while(!pointers.isEmpty()) { + uint32_t pointerId = pointers.clearFirstMarkedBit(); + + coords[pointerIndex].clear(); + // We are treating column positions as pointerId + EXPECT_TRUE(entry.positions[pointerId].isValid()) << + "The entry at pointerId must be valid"; + coords[pointerIndex].setAxisValue(AMOTION_EVENT_AXIS_X, entry.positions[pointerId].x); + coords[pointerIndex].setAxisValue(AMOTION_EVENT_AXIS_Y, entry.positions[pointerId].y); + + properties[pointerIndex].id = pointerId; + properties[pointerIndex].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + pointerIndex++; + } + EXPECT_EQ(pointerIndex, pointerCount); + + MotionEvent event; + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, + action, 0 /*actionButton*/, 0 /*flags*/, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, + MotionClassification::NONE, + 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, + 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); + + events.emplace_back(event); + } + + return events; +} + +static void computeAndCheckVelocity(const char* strategy, + const std::vector<MotionEventEntry>& motions, int32_t axis, float targetVelocity) { + VelocityTracker vt(strategy); float Vx, Vy; - MotionEvent* event = createSimpleMotionEvent(positions, numSamples); - vt.addMovement(event); + std::vector<MotionEvent> events = createMotionEventStream(motions); + for (MotionEvent event : events) { + vt.addMovement(&event); + } vt.getVelocity(DEFAULT_POINTER_ID, &Vx, &Vy); @@ -120,46 +211,61 @@ default: FAIL() << "Axis must be either AMOTION_EVENT_AXIS_X or AMOTION_EVENT_AXIS_Y"; } - delete event; +} + +static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions, + const std::array<float, 3>& coefficients) { + VelocityTracker vt("lsq2"); + std::vector<MotionEvent> events = createMotionEventStream(motions); + for (MotionEvent event : events) { + vt.addMovement(&event); + } + VelocityTracker::Estimator estimator; + EXPECT_TRUE(vt.getEstimator(0, &estimator)); + for (size_t i = 0; i< coefficients.size(); i++) { + checkCoefficient(estimator.xCoeff[i], coefficients[i]); + checkCoefficient(estimator.yCoeff[i], coefficients[i]); + } } /* * ================== VelocityTracker tests generated manually ===================================== */ - // @todo Currently disabled, enable when switching away from lsq2 VelocityTrackerStrategy -TEST_F(VelocityTrackerTest, DISABLED_ThreePointsPositiveVelocityTest) { +TEST_F(VelocityTrackerTest, ThreePointsPositiveVelocityTest) { // Same coordinate is reported 2 times in a row // It is difficult to determine the correct answer here, but at least the direction // of the reported velocity should be positive. - Position values[] = { - { 0, 273, NAN }, - { 12585000, 293, NAN }, - { 14730000, 293, NAN }, + std::vector<MotionEventEntry> motions = { + {0ms, {{ 273, NAN}}}, + {12585us, {{293, NAN}}}, + {14730us, {{293, NAN}}}, + {14730us, {{293, NAN}}}, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1600); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1600); } TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) { // Same coordinate is reported 3 times in a row - Position values[] = { - { 0, 293, NAN }, - { 6132000, 293, NAN }, - { 11283000, 293, NAN }, + std::vector<MotionEventEntry> motions = { + { 0ms, {{293, NAN}} }, + { 6132us, {{293, NAN}} }, + { 11283us, {{293, NAN}} }, + { 11283us, {{293, NAN}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 0); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 0); } TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { // Fixed velocity at 5 points per 10 milliseconds - Position values[] = { - { 0, 0, NAN }, - { 10000000, 5, NAN }, - { 20000000, 10, NAN }, + std::vector<MotionEventEntry> motions = { + { 0ms, {{0, NAN}} }, + { 10ms, {{5, NAN}} }, + { 20ms, {{10, NAN}} }, + { 20ms, {{10, NAN}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 500); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 500); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 500); } @@ -171,24 +277,26 @@ * Also record all calls to VelocityTracker::clear(). * Finally, record the output of VelocityTracker::getVelocity(...) * This will give you the necessary data to create a new test. + * + * Another good way to generate this data is to use 'dumpsys input' just after the event has + * occurred. */ // --------------- Recorded by hand on swordfish --------------------------------------------------- -// @todo Currently disabled, enable when switching away from lsq2 VelocityTrackerStrategy -TEST_F(VelocityTrackerTest, DISABLED_SwordfishFlingDown) { +TEST_F(VelocityTrackerTest, SwordfishFlingDown) { // Recording of a fling on Swordfish that could cause a fling in the wrong direction - Position values[] = { - { 0, 271, 96 }, - { 16071042, 269.786346, 106.922775 }, - { 35648403, 267.983063, 156.660034 }, - { 52313925, 262.638397, 220.339081 }, - { 68976522, 266.138824, 331.581116 }, - { 85639375, 274.79245, 428.113159 }, - { 96948871, 274.79245, 428.113159 }, + std::vector<MotionEventEntry> motions = { + { 0ms, {{271, 96}} }, + { 16071042ns, {{269.786346, 106.922775}} }, + { 35648403ns, {{267.983063, 156.660034}} }, + { 52313925ns, {{262.638397, 220.339081}} }, + { 68976522ns, {{266.138824, 331.581116}} }, + { 85639375ns, {{274.79245, 428.113159}} }, + { 96948871ns, {{274.79245, 428.113159}} }, + { 96948871ns, {{274.79245, 428.113159}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 623.577637); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 8523.348633); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 623.577637); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 5970.7309); } // --------------- Recorded by hand on sailfish, generated by a script ----------------------------- @@ -210,455 +318,591 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) { // Sailfish - fling up - slow - 1 - Position values[] = { - { 235089067457000, 528.00, 983.00 }, - { 235089084684000, 527.00, 981.00 }, - { 235089093349000, 527.00, 977.00 }, - { 235089095677625, 527.00, 975.93 }, - { 235089101859000, 527.00, 970.00 }, - { 235089110378000, 528.00, 960.00 }, - { 235089112497111, 528.25, 957.51 }, - { 235089118760000, 531.00, 946.00 }, - { 235089126686000, 535.00, 931.00 }, - { 235089129316820, 536.33, 926.02 }, - { 235089135199000, 540.00, 914.00 }, - { 235089144297000, 546.00, 896.00 }, - { 235089146136443, 547.21, 892.36 }, - { 235089152923000, 553.00, 877.00 }, - { 235089160784000, 559.00, 851.00 }, - { 235089162955851, 560.66, 843.82 }, + std::vector<MotionEventEntry> motions = { + { 235089067457000ns, {{528.00, 983.00}} }, + { 235089084684000ns, {{527.00, 981.00}} }, + { 235089093349000ns, {{527.00, 977.00}} }, + { 235089095677625ns, {{527.00, 975.93}} }, + { 235089101859000ns, {{527.00, 970.00}} }, + { 235089110378000ns, {{528.00, 960.00}} }, + { 235089112497111ns, {{528.25, 957.51}} }, + { 235089118760000ns, {{531.00, 946.00}} }, + { 235089126686000ns, {{535.00, 931.00}} }, + { 235089129316820ns, {{536.33, 926.02}} }, + { 235089135199000ns, {{540.00, 914.00}} }, + { 235089144297000ns, {{546.00, 896.00}} }, + { 235089146136443ns, {{547.21, 892.36}} }, + { 235089152923000ns, {{553.00, 877.00}} }, + { 235089160784000ns, {{559.00, 851.00}} }, + { 235089162955851ns, {{560.66, 843.82}} }, + { 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 872.794617); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 951.698181); // lsq2 - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3604.819336); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3044.966064); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 872.794617); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 951.698181); + computeAndCheckVelocity("impulse",motions, AMOTION_EVENT_AXIS_Y, -3604.819336); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3044.966064); } TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) { // Sailfish - fling up - slow - 2 - Position values[] = { - { 235110560704000, 522.00, 1107.00 }, - { 235110575764000, 522.00, 1107.00 }, - { 235110584385000, 522.00, 1107.00 }, - { 235110588421179, 521.52, 1106.52 }, - { 235110592830000, 521.00, 1106.00 }, - { 235110601385000, 520.00, 1104.00 }, - { 235110605088160, 519.14, 1102.27 }, - { 235110609952000, 518.00, 1100.00 }, - { 235110618353000, 517.00, 1093.00 }, - { 235110621755146, 516.60, 1090.17 }, - { 235110627010000, 517.00, 1081.00 }, - { 235110634785000, 518.00, 1063.00 }, - { 235110638422450, 518.87, 1052.58 }, - { 235110643161000, 520.00, 1039.00 }, - { 235110651767000, 524.00, 1011.00 }, - { 235110655089581, 525.54, 1000.19 }, - { 235110660368000, 530.00, 980.00 }, + std::vector<MotionEventEntry> motions = { + { 235110560704000ns, {{522.00, 1107.00}} }, + { 235110575764000ns, {{522.00, 1107.00}} }, + { 235110584385000ns, {{522.00, 1107.00}} }, + { 235110588421179ns, {{521.52, 1106.52}} }, + { 235110592830000ns, {{521.00, 1106.00}} }, + { 235110601385000ns, {{520.00, 1104.00}} }, + { 235110605088160ns, {{519.14, 1102.27}} }, + { 235110609952000ns, {{518.00, 1100.00}} }, + { 235110618353000ns, {{517.00, 1093.00}} }, + { 235110621755146ns, {{516.60, 1090.17}} }, + { 235110627010000ns, {{517.00, 1081.00}} }, + { 235110634785000ns, {{518.00, 1063.00}} }, + { 235110638422450ns, {{518.87, 1052.58}} }, + { 235110643161000ns, {{520.00, 1039.00}} }, + { 235110651767000ns, {{524.00, 1011.00}} }, + { 235110655089581ns, {{525.54, 1000.19}} }, + { 235110660368000ns, {{530.00, 980.00}} }, + { 235110660368000ns, {{530.00, 980.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4096.583008); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3455.094238); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4096.583008); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3455.094238); } TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) { // Sailfish - fling up - slow - 3 - Position values[] = { - { 792536237000, 580.00, 1317.00 }, - { 792541538987, 580.63, 1311.94 }, - { 792544613000, 581.00, 1309.00 }, - { 792552301000, 583.00, 1295.00 }, - { 792558362309, 585.13, 1282.92 }, - { 792560828000, 586.00, 1278.00 }, - { 792569446000, 589.00, 1256.00 }, - { 792575185095, 591.54, 1241.41 }, - { 792578491000, 593.00, 1233.00 }, - { 792587044000, 597.00, 1211.00 }, - { 792592008172, 600.28, 1195.92 }, - { 792594616000, 602.00, 1188.00 }, - { 792603129000, 607.00, 1167.00 }, - { 792608831290, 609.48, 1155.83 }, - { 792612321000, 611.00, 1149.00 }, - { 792620768000, 615.00, 1131.00 }, - { 792625653873, 617.32, 1121.73 }, - { 792629200000, 619.00, 1115.00 }, + std::vector<MotionEventEntry> motions = { + { 792536237000ns, {{580.00, 1317.00}} }, + { 792541538987ns, {{580.63, 1311.94}} }, + { 792544613000ns, {{581.00, 1309.00}} }, + { 792552301000ns, {{583.00, 1295.00}} }, + { 792558362309ns, {{585.13, 1282.92}} }, + { 792560828000ns, {{586.00, 1278.00}} }, + { 792569446000ns, {{589.00, 1256.00}} }, + { 792575185095ns, {{591.54, 1241.41}} }, + { 792578491000ns, {{593.00, 1233.00}} }, + { 792587044000ns, {{597.00, 1211.00}} }, + { 792592008172ns, {{600.28, 1195.92}} }, + { 792594616000ns, {{602.00, 1188.00}} }, + { 792603129000ns, {{607.00, 1167.00}} }, + { 792608831290ns, {{609.48, 1155.83}} }, + { 792612321000ns, {{611.00, 1149.00}} }, + { 792620768000ns, {{615.00, 1131.00}} }, + { 792625653873ns, {{617.32, 1121.73}} }, + { 792629200000ns, {{619.00, 1115.00}} }, + { 792629200000ns, {{619.00, 1115.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 574.33429); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 617.40564); // lsq2 - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -2361.982666); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -2500.055664); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 574.33429); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 617.40564); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -2361.982666); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -2500.055664); } TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) { // Sailfish - fling up - faster - 1 - Position values[] = { - { 235160420675000, 610.00, 1042.00 }, - { 235160428220000, 609.00, 1026.00 }, - { 235160436544000, 609.00, 1024.00 }, - { 235160441852394, 609.64, 1020.82 }, - { 235160444878000, 610.00, 1019.00 }, - { 235160452673000, 613.00, 1006.00 }, - { 235160458519743, 617.18, 992.06 }, - { 235160461061000, 619.00, 986.00 }, - { 235160469798000, 627.00, 960.00 }, - { 235160475186713, 632.22, 943.02 }, - { 235160478051000, 635.00, 934.00 }, - { 235160486489000, 644.00, 906.00 }, - { 235160491853697, 649.56, 890.56 }, - { 235160495177000, 653.00, 881.00 }, - { 235160504148000, 662.00, 858.00 }, - { 235160509231495, 666.81, 845.37 }, - { 235160512603000, 670.00, 837.00 }, - { 235160520366000, 679.00, 814.00 }, + std::vector<MotionEventEntry> motions = { + { 235160420675000ns, {{610.00, 1042.00}} }, + { 235160428220000ns, {{609.00, 1026.00}} }, + { 235160436544000ns, {{609.00, 1024.00}} }, + { 235160441852394ns, {{609.64, 1020.82}} }, + { 235160444878000ns, {{610.00, 1019.00}} }, + { 235160452673000ns, {{613.00, 1006.00}} }, + { 235160458519743ns, {{617.18, 992.06}} }, + { 235160461061000ns, {{619.00, 986.00}} }, + { 235160469798000ns, {{627.00, 960.00}} }, + { 235160475186713ns, {{632.22, 943.02}} }, + { 235160478051000ns, {{635.00, 934.00}} }, + { 235160486489000ns, {{644.00, 906.00}} }, + { 235160491853697ns, {{649.56, 890.56}} }, + { 235160495177000ns, {{653.00, 881.00}} }, + { 235160504148000ns, {{662.00, 858.00}} }, + { 235160509231495ns, {{666.81, 845.37}} }, + { 235160512603000ns, {{670.00, 837.00}} }, + { 235160520366000ns, {{679.00, 814.00}} }, + { 235160520366000ns, {{679.00, 814.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1274.141724); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1438.53186); // lsq2 - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3877.35498); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3695.859619); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1274.141724); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 1438.53186); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -3001.4348); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3695.859619); } TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) { // Sailfish - fling up - faster - 2 - Position values[] = { - { 847153808000, 576.00, 1264.00 }, - { 847171174000, 576.00, 1262.00 }, - { 847179640000, 576.00, 1257.00 }, - { 847185187540, 577.41, 1249.22 }, - { 847187487000, 578.00, 1246.00 }, - { 847195710000, 581.00, 1227.00 }, - { 847202027059, 583.93, 1209.40 }, - { 847204324000, 585.00, 1203.00 }, - { 847212672000, 590.00, 1176.00 }, - { 847218861395, 594.36, 1157.11 }, - { 847221190000, 596.00, 1150.00 }, - { 847230484000, 602.00, 1124.00 }, - { 847235701400, 607.56, 1103.83 }, - { 847237986000, 610.00, 1095.00 }, + std::vector<MotionEventEntry> motions = { + { 847153808000ns, {{576.00, 1264.00}} }, + { 847171174000ns, {{576.00, 1262.00}} }, + { 847179640000ns, {{576.00, 1257.00}} }, + { 847185187540ns, {{577.41, 1249.22}} }, + { 847187487000ns, {{578.00, 1246.00}} }, + { 847195710000ns, {{581.00, 1227.00}} }, + { 847202027059ns, {{583.93, 1209.40}} }, + { 847204324000ns, {{585.00, 1203.00}} }, + { 847212672000ns, {{590.00, 1176.00}} }, + { 847218861395ns, {{594.36, 1157.11}} }, + { 847221190000ns, {{596.00, 1150.00}} }, + { 847230484000ns, {{602.00, 1124.00}} }, + { 847235701400ns, {{607.56, 1103.83}} }, + { 847237986000ns, {{610.00, 1095.00}} }, + { 847237986000ns, {{610.00, 1095.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4280.07959); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4241.004395); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4280.07959); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -4241.004395); } TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) { // Sailfish - fling up - faster - 3 - Position values[] = { - { 235200532789000, 507.00, 1084.00 }, - { 235200549221000, 507.00, 1083.00 }, - { 235200557841000, 507.00, 1081.00 }, - { 235200558051189, 507.00, 1080.95 }, - { 235200566314000, 507.00, 1078.00 }, - { 235200574876586, 508.97, 1070.12 }, - { 235200575006000, 509.00, 1070.00 }, - { 235200582900000, 514.00, 1054.00 }, - { 235200591276000, 525.00, 1023.00 }, - { 235200591701829, 525.56, 1021.42 }, - { 235200600064000, 542.00, 976.00 }, - { 235200608519000, 563.00, 911.00 }, - { 235200608527086, 563.02, 910.94 }, - { 235200616933000, 590.00, 844.00 }, + std::vector<MotionEventEntry> motions = { + { 235200532789000ns, {{507.00, 1084.00}} }, + { 235200549221000ns, {{507.00, 1083.00}} }, + { 235200557841000ns, {{507.00, 1081.00}} }, + { 235200558051189ns, {{507.00, 1080.95}} }, + { 235200566314000ns, {{507.00, 1078.00}} }, + { 235200574876586ns, {{508.97, 1070.12}} }, + { 235200575006000ns, {{509.00, 1070.00}} }, + { 235200582900000ns, {{514.00, 1054.00}} }, + { 235200591276000ns, {{525.00, 1023.00}} }, + { 235200591701829ns, {{525.56, 1021.42}} }, + { 235200600064000ns, {{542.00, 976.00}} }, + { 235200608519000ns, {{563.00, 911.00}} }, + { 235200608527086ns, {{563.02, 910.94}} }, + { 235200616933000ns, {{590.00, 844.00}} }, + { 235200616933000ns, {{590.00, 844.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -8715.686523); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -7639.026367); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -8715.686523); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -7639.026367); } TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) { // Sailfish - fling up - fast - 1 - Position values[] = { - { 920922149000, 561.00, 1412.00 }, - { 920930185000, 559.00, 1377.00 }, - { 920930262463, 558.98, 1376.66 }, - { 920938547000, 559.00, 1371.00 }, - { 920947096857, 562.91, 1342.68 }, - { 920947302000, 563.00, 1342.00 }, - { 920955502000, 577.00, 1272.00 }, - { 920963931021, 596.87, 1190.54 }, - { 920963987000, 597.00, 1190.00 }, - { 920972530000, 631.00, 1093.00 }, - { 920980765511, 671.31, 994.68 }, - { 920980906000, 672.00, 993.00 }, - { 920989261000, 715.00, 903.00 }, + std::vector<MotionEventEntry> motions = { + { 920922149000ns, {{561.00, 1412.00}} }, + { 920930185000ns, {{559.00, 1377.00}} }, + { 920930262463ns, {{558.98, 1376.66}} }, + { 920938547000ns, {{559.00, 1371.00}} }, + { 920947096857ns, {{562.91, 1342.68}} }, + { 920947302000ns, {{563.00, 1342.00}} }, + { 920955502000ns, {{577.00, 1272.00}} }, + { 920963931021ns, {{596.87, 1190.54}} }, + { 920963987000ns, {{597.00, 1190.00}} }, + { 920972530000ns, {{631.00, 1093.00}} }, + { 920980765511ns, {{671.31, 994.68}} }, + { 920980906000ns, {{672.00, 993.00}} }, + { 920989261000ns, {{715.00, 903.00}} }, + { 920989261000ns, {{715.00, 903.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 5670.329102); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 5991.866699); // lsq2 - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -13021.101562); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -15093.995117); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 5670.329102); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 5991.866699); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -13021.101562); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -15093.995117); } TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) { // Sailfish - fling up - fast - 2 - Position values[] = { - { 235247153233000, 518.00, 1168.00 }, - { 235247170452000, 517.00, 1167.00 }, - { 235247178908000, 515.00, 1159.00 }, - { 235247179556213, 514.85, 1158.39 }, - { 235247186821000, 515.00, 1125.00 }, - { 235247195265000, 521.00, 1051.00 }, - { 235247196389476, 521.80, 1041.15 }, - { 235247203649000, 538.00, 932.00 }, - { 235247212253000, 571.00, 794.00 }, - { 235247213222491, 574.72, 778.45 }, - { 235247220736000, 620.00, 641.00 }, + std::vector<MotionEventEntry> motions = { + { 235247153233000ns, {{518.00, 1168.00}} }, + { 235247170452000ns, {{517.00, 1167.00}} }, + { 235247178908000ns, {{515.00, 1159.00}} }, + { 235247179556213ns, {{514.85, 1158.39}} }, + { 235247186821000ns, {{515.00, 1125.00}} }, + { 235247195265000ns, {{521.00, 1051.00}} }, + { 235247196389476ns, {{521.80, 1041.15}} }, + { 235247203649000ns, {{538.00, 932.00}} }, + { 235247212253000ns, {{571.00, 794.00}} }, + { 235247213222491ns, {{574.72, 778.45}} }, + { 235247220736000ns, {{620.00, 641.00}} }, + { 235247220736000ns, {{620.00, 641.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -20286.958984); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -20494.587891); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -20286.958984); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -20494.587891); } TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) { // Sailfish - fling up - fast - 3 - Position values[] = { - { 235302568736000, 529.00, 1167.00 }, - { 235302576644000, 523.00, 1140.00 }, - { 235302579395063, 520.91, 1130.61 }, - { 235302585140000, 522.00, 1130.00 }, - { 235302593615000, 527.00, 1065.00 }, - { 235302596207444, 528.53, 1045.12 }, - { 235302602102000, 559.00, 872.00 }, - { 235302610545000, 652.00, 605.00 }, - { 235302613019881, 679.26, 526.73 }, + std::vector<MotionEventEntry> motions = { + { 235302568736000ns, {{529.00, 1167.00}} }, + { 235302576644000ns, {{523.00, 1140.00}} }, + { 235302579395063ns, {{520.91, 1130.61}} }, + { 235302585140000ns, {{522.00, 1130.00}} }, + { 235302593615000ns, {{527.00, 1065.00}} }, + { 235302596207444ns, {{528.53, 1045.12}} }, + { 235302602102000ns, {{559.00, 872.00}} }, + { 235302610545000ns, {{652.00, 605.00}} }, + { 235302613019881ns, {{679.26, 526.73}} }, + { 235302613019881ns, {{679.26, 526.73}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -39295.941406); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -36461.421875); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -39295.941406); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -36461.421875); } TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) { // Sailfish - fling down - slow - 1 - Position values[] = { - { 235655749552755, 582.00, 432.49 }, - { 235655750638000, 582.00, 433.00 }, - { 235655758865000, 582.00, 440.00 }, - { 235655766221523, 581.16, 448.43 }, - { 235655767594000, 581.00, 450.00 }, - { 235655776044000, 580.00, 462.00 }, - { 235655782890696, 579.18, 474.35 }, - { 235655784360000, 579.00, 477.00 }, - { 235655792795000, 578.00, 496.00 }, - { 235655799559531, 576.27, 515.04 }, - { 235655800612000, 576.00, 518.00 }, - { 235655809535000, 574.00, 542.00 }, - { 235655816988015, 572.17, 564.86 }, - { 235655817685000, 572.00, 567.00 }, - { 235655825981000, 569.00, 595.00 }, - { 235655833808653, 566.26, 620.60 }, - { 235655834541000, 566.00, 623.00 }, - { 235655842893000, 563.00, 649.00 }, + std::vector<MotionEventEntry> motions = { + { 235655749552755ns, {{582.00, 432.49}} }, + { 235655750638000ns, {{582.00, 433.00}} }, + { 235655758865000ns, {{582.00, 440.00}} }, + { 235655766221523ns, {{581.16, 448.43}} }, + { 235655767594000ns, {{581.00, 450.00}} }, + { 235655776044000ns, {{580.00, 462.00}} }, + { 235655782890696ns, {{579.18, 474.35}} }, + { 235655784360000ns, {{579.00, 477.00}} }, + { 235655792795000ns, {{578.00, 496.00}} }, + { 235655799559531ns, {{576.27, 515.04}} }, + { 235655800612000ns, {{576.00, 518.00}} }, + { 235655809535000ns, {{574.00, 542.00}} }, + { 235655816988015ns, {{572.17, 564.86}} }, + { 235655817685000ns, {{572.00, 567.00}} }, + { 235655825981000ns, {{569.00, 595.00}} }, + { 235655833808653ns, {{566.26, 620.60}} }, + { 235655834541000ns, {{566.00, 623.00}} }, + { 235655842893000ns, {{563.00, 649.00}} }, + { 235655842893000ns, {{563.00, 649.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -419.749695); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -398.303894); // lsq2 - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3309.016357); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3969.099854); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -419.749695); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -398.303894); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3309.016357); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 3969.099854); } TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) { // Sailfish - fling down - slow - 2 - Position values[] = { - { 235671152083370, 485.24, 558.28 }, - { 235671154126000, 485.00, 559.00 }, - { 235671162497000, 484.00, 566.00 }, - { 235671168750511, 483.27, 573.29 }, - { 235671171071000, 483.00, 576.00 }, - { 235671179390000, 482.00, 588.00 }, - { 235671185417210, 481.31, 598.98 }, - { 235671188173000, 481.00, 604.00 }, - { 235671196371000, 480.00, 624.00 }, - { 235671202084196, 479.27, 639.98 }, - { 235671204235000, 479.00, 646.00 }, - { 235671212554000, 478.00, 673.00 }, - { 235671219471011, 476.39, 697.12 }, - { 235671221159000, 476.00, 703.00 }, - { 235671229592000, 474.00, 734.00 }, - { 235671236281462, 472.43, 758.38 }, - { 235671238098000, 472.00, 765.00 }, - { 235671246532000, 470.00, 799.00 }, + std::vector<MotionEventEntry> motions = { + { 235671152083370ns, {{485.24, 558.28}} }, + { 235671154126000ns, {{485.00, 559.00}} }, + { 235671162497000ns, {{484.00, 566.00}} }, + { 235671168750511ns, {{483.27, 573.29}} }, + { 235671171071000ns, {{483.00, 576.00}} }, + { 235671179390000ns, {{482.00, 588.00}} }, + { 235671185417210ns, {{481.31, 598.98}} }, + { 235671188173000ns, {{481.00, 604.00}} }, + { 235671196371000ns, {{480.00, 624.00}} }, + { 235671202084196ns, {{479.27, 639.98}} }, + { 235671204235000ns, {{479.00, 646.00}} }, + { 235671212554000ns, {{478.00, 673.00}} }, + { 235671219471011ns, {{476.39, 697.12}} }, + { 235671221159000ns, {{476.00, 703.00}} }, + { 235671229592000ns, {{474.00, 734.00}} }, + { 235671236281462ns, {{472.43, 758.38}} }, + { 235671238098000ns, {{472.00, 765.00}} }, + { 235671246532000ns, {{470.00, 799.00}} }, + { 235671246532000ns, {{470.00, 799.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -262.80426); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -243.665344); // lsq2 - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4215.682129); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4587.986816); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -262.80426); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -243.665344); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4215.682129); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4587.986816); } TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) { // Sailfish - fling down - slow - 3 - Position values[] = { - { 170983201000, 557.00, 533.00 }, - { 171000668000, 556.00, 534.00 }, - { 171007359750, 554.73, 535.27 }, - { 171011197000, 554.00, 536.00 }, - { 171017660000, 552.00, 540.00 }, - { 171024201831, 549.97, 544.73 }, - { 171027333000, 549.00, 547.00 }, - { 171034603000, 545.00, 557.00 }, - { 171041043371, 541.98, 567.55 }, - { 171043147000, 541.00, 571.00 }, - { 171051052000, 536.00, 586.00 }, + std::vector<MotionEventEntry> motions = { + { 170983201000ns, {{557.00, 533.00}} }, + { 171000668000ns, {{556.00, 534.00}} }, + { 171007359750ns, {{554.73, 535.27}} }, + { 171011197000ns, {{554.00, 536.00}} }, + { 171017660000ns, {{552.00, 540.00}} }, + { 171024201831ns, {{549.97, 544.73}} }, + { 171027333000ns, {{549.00, 547.00}} }, + { 171034603000ns, {{545.00, 557.00}} }, + { 171041043371ns, {{541.98, 567.55}} }, + { 171043147000ns, {{541.00, 571.00}} }, + { 171051052000ns, {{536.00, 586.00}} }, + { 171051052000ns, {{536.00, 586.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -723.413513); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -651.038452); // lsq2 - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 2091.502441); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 1934.517456); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -723.413513); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -651.038452); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 2091.502441); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 1934.517456); } TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) { // Sailfish - fling down - faster - 1 - Position values[] = { - { 235695280333000, 558.00, 451.00 }, - { 235695283971237, 558.43, 454.45 }, - { 235695289038000, 559.00, 462.00 }, - { 235695297388000, 561.00, 478.00 }, - { 235695300638465, 561.83, 486.25 }, - { 235695305265000, 563.00, 498.00 }, - { 235695313591000, 564.00, 521.00 }, - { 235695317305492, 564.43, 532.68 }, - { 235695322181000, 565.00, 548.00 }, - { 235695330709000, 565.00, 577.00 }, - { 235695333972227, 565.00, 588.10 }, - { 235695339250000, 565.00, 609.00 }, - { 235695347839000, 565.00, 642.00 }, - { 235695351313257, 565.00, 656.18 }, - { 235695356412000, 565.00, 677.00 }, - { 235695364899000, 563.00, 710.00 }, - { 235695368118682, 562.24, 722.52 }, - { 235695373403000, 564.00, 744.00 }, + std::vector<MotionEventEntry> motions = { + { 235695280333000ns, {{558.00, 451.00}} }, + { 235695283971237ns, {{558.43, 454.45}} }, + { 235695289038000ns, {{559.00, 462.00}} }, + { 235695297388000ns, {{561.00, 478.00}} }, + { 235695300638465ns, {{561.83, 486.25}} }, + { 235695305265000ns, {{563.00, 498.00}} }, + { 235695313591000ns, {{564.00, 521.00}} }, + { 235695317305492ns, {{564.43, 532.68}} }, + { 235695322181000ns, {{565.00, 548.00}} }, + { 235695330709000ns, {{565.00, 577.00}} }, + { 235695333972227ns, {{565.00, 588.10}} }, + { 235695339250000ns, {{565.00, 609.00}} }, + { 235695347839000ns, {{565.00, 642.00}} }, + { 235695351313257ns, {{565.00, 656.18}} }, + { 235695356412000ns, {{565.00, 677.00}} }, + { 235695364899000ns, {{563.00, 710.00}} }, + { 235695368118682ns, {{562.24, 722.52}} }, + { 235695373403000ns, {{564.00, 744.00}} }, + { 235695373403000ns, {{564.00, 744.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4254.639648); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4698.415039); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4254.639648); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4698.415039); } TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) { // Sailfish - fling down - faster - 2 - Position values[] = { - { 235709624766000, 535.00, 579.00 }, - { 235709642256000, 534.00, 580.00 }, - { 235709643350278, 533.94, 580.06 }, - { 235709650760000, 532.00, 584.00 }, - { 235709658615000, 530.00, 593.00 }, - { 235709660170495, 529.60, 594.78 }, - { 235709667095000, 527.00, 606.00 }, - { 235709675616000, 524.00, 628.00 }, - { 235709676983261, 523.52, 631.53 }, - { 235709684289000, 521.00, 652.00 }, - { 235709692763000, 518.00, 682.00 }, - { 235709693804993, 517.63, 685.69 }, - { 235709701438000, 515.00, 709.00 }, - { 235709709830000, 512.00, 739.00 }, - { 235709710626776, 511.72, 741.85 }, + std::vector<MotionEventEntry> motions = { + { 235709624766000ns, {{535.00, 579.00}} }, + { 235709642256000ns, {{534.00, 580.00}} }, + { 235709643350278ns, {{533.94, 580.06}} }, + { 235709650760000ns, {{532.00, 584.00}} }, + { 235709658615000ns, {{530.00, 593.00}} }, + { 235709660170495ns, {{529.60, 594.78}} }, + { 235709667095000ns, {{527.00, 606.00}} }, + { 235709675616000ns, {{524.00, 628.00}} }, + { 235709676983261ns, {{523.52, 631.53}} }, + { 235709684289000ns, {{521.00, 652.00}} }, + { 235709692763000ns, {{518.00, 682.00}} }, + { 235709693804993ns, {{517.63, 685.69}} }, + { 235709701438000ns, {{515.00, 709.00}} }, + { 235709709830000ns, {{512.00, 739.00}} }, + { 235709710626776ns, {{511.72, 741.85}} }, + { 235709710626776ns, {{511.72, 741.85}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -430.440247); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -447.600311); // lsq2 - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3953.859375); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4316.155273); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -430.440247); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -447.600311); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3953.859375); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4316.155273); } TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) { // Sailfish - fling down - faster - 3 - Position values[] = { - { 235727628927000, 540.00, 440.00 }, - { 235727636810000, 537.00, 454.00 }, - { 235727646176000, 536.00, 454.00 }, - { 235727653586628, 535.12, 456.65 }, - { 235727654557000, 535.00, 457.00 }, - { 235727663024000, 534.00, 465.00 }, - { 235727670410103, 533.04, 479.45 }, - { 235727670691000, 533.00, 480.00 }, - { 235727679255000, 531.00, 501.00 }, - { 235727687233704, 529.09, 526.73 }, - { 235727687628000, 529.00, 528.00 }, - { 235727696113000, 526.00, 558.00 }, - { 235727704057546, 523.18, 588.98 }, - { 235727704576000, 523.00, 591.00 }, - { 235727713099000, 520.00, 626.00 }, - { 235727720880776, 516.33, 655.36 }, - { 235727721580000, 516.00, 658.00 }, + std::vector<MotionEventEntry> motions = { + { 235727628927000ns, {{540.00, 440.00}} }, + { 235727636810000ns, {{537.00, 454.00}} }, + { 235727646176000ns, {{536.00, 454.00}} }, + { 235727653586628ns, {{535.12, 456.65}} }, + { 235727654557000ns, {{535.00, 457.00}} }, + { 235727663024000ns, {{534.00, 465.00}} }, + { 235727670410103ns, {{533.04, 479.45}} }, + { 235727670691000ns, {{533.00, 480.00}} }, + { 235727679255000ns, {{531.00, 501.00}} }, + { 235727687233704ns, {{529.09, 526.73}} }, + { 235727687628000ns, {{529.00, 528.00}} }, + { 235727696113000ns, {{526.00, 558.00}} }, + { 235727704057546ns, {{523.18, 588.98}} }, + { 235727704576000ns, {{523.00, 591.00}} }, + { 235727713099000ns, {{520.00, 626.00}} }, + { 235727720880776ns, {{516.33, 655.36}} }, + { 235727721580000ns, {{516.00, 658.00}} }, + { 235727721580000ns, {{516.00, 658.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4484.617676); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4927.92627); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4484.617676); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4927.92627); } TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) { // Sailfish - fling down - fast - 1 - Position values[] = { - { 235762352849000, 467.00, 286.00 }, - { 235762360250000, 443.00, 344.00 }, - { 235762362787412, 434.77, 363.89 }, - { 235762368807000, 438.00, 359.00 }, - { 235762377220000, 425.00, 423.00 }, - { 235762379608561, 421.31, 441.17 }, - { 235762385698000, 412.00, 528.00 }, - { 235762394133000, 406.00, 648.00 }, - { 235762396429369, 404.37, 680.67 }, + std::vector<MotionEventEntry> motions = { + { 235762352849000ns, {{467.00, 286.00}} }, + { 235762360250000ns, {{443.00, 344.00}} }, + { 235762362787412ns, {{434.77, 363.89}} }, + { 235762368807000ns, {{438.00, 359.00}} }, + { 235762377220000ns, {{425.00, 423.00}} }, + { 235762379608561ns, {{421.31, 441.17}} }, + { 235762385698000ns, {{412.00, 528.00}} }, + { 235762394133000ns, {{406.00, 648.00}} }, + { 235762396429369ns, {{404.37, 680.67}} }, + { 235762396429369ns, {{404.37, 680.67}} }, //ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 19084.931641); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 16064.685547); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 14227.0224); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16064.685547); } TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) { // Sailfish - fling down - fast - 2 - Position values[] = { - { 235772487188000, 576.00, 204.00 }, - { 235772495159000, 553.00, 236.00 }, - { 235772503568000, 551.00, 240.00 }, - { 235772508192247, 545.55, 254.17 }, - { 235772512051000, 541.00, 266.00 }, - { 235772520794000, 520.00, 337.00 }, - { 235772525015263, 508.92, 394.43 }, - { 235772529174000, 498.00, 451.00 }, - { 235772537635000, 484.00, 589.00 }, + std::vector<MotionEventEntry> motions = { + { 235772487188000ns, {{576.00, 204.00}} }, + { 235772495159000ns, {{553.00, 236.00}} }, + { 235772503568000ns, {{551.00, 240.00}} }, + { 235772508192247ns, {{545.55, 254.17}} }, + { 235772512051000ns, {{541.00, 266.00}} }, + { 235772520794000ns, {{520.00, 337.00}} }, + { 235772525015263ns, {{508.92, 394.43}} }, + { 235772529174000ns, {{498.00, 451.00}} }, + { 235772537635000ns, {{484.00, 589.00}} }, + { 235772537635000ns, {{484.00, 589.00}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 18660.048828); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 16918.439453); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 18660.048828); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16918.439453); } TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) { // Sailfish - fling down - fast - 3 - Position values[] = { - { 507650295000, 628.00, 233.00 }, - { 507658234000, 605.00, 269.00 }, - { 507666784000, 601.00, 274.00 }, - { 507669660483, 599.65, 275.68 }, - { 507675427000, 582.00, 308.00 }, - { 507683740000, 541.00, 404.00 }, - { 507686506238, 527.36, 435.95 }, - { 507692220000, 487.00, 581.00 }, - { 507700707000, 454.00, 792.00 }, - { 507703352649, 443.71, 857.77 }, + std::vector<MotionEventEntry> motions = { + { 507650295000ns, {{628.00, 233.00}} }, + { 507658234000ns, {{605.00, 269.00}} }, + { 507666784000ns, {{601.00, 274.00}} }, + { 507669660483ns, {{599.65, 275.68}} }, + { 507675427000ns, {{582.00, 308.00}} }, + { 507683740000ns, {{541.00, 404.00}} }, + { 507686506238ns, {{527.36, 435.95}} }, + { 507692220000ns, {{487.00, 581.00}} }, + { 507700707000ns, {{454.00, 792.00}} }, + { 507703352649ns, {{443.71, 857.77}} }, + { 507703352649ns, {{443.71, 857.77}} }, // ACTION_UP }; - size_t count = sizeof(values) / sizeof(Position); - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -6772.508301); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -6388.48877); // lsq2 - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 29765.908203); // impulse - computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 28354.796875); // lsq2 + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -4111.8173); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -6388.48877); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 29765.908203); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 28354.796875); } +/** + * ================== Multiple pointers ============================================================ + * + * Three fingers quickly tap the screen. Since this is a tap, the velocities should be zero. + * If the events with POINTER_UP or POINTER_DOWN are not handled correctly (these should not be + * part of the fitted data), this can cause large velocity values to be reported instead. + */ +TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFingerTap) { + std::vector<MotionEventEntry> motions = { + { 0us, {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} }, + { 10800us, {{1063, 1128}, {682, 1318}, {NAN, NAN}} }, // POINTER_DOWN + { 10800us, {{1063, 1128}, {682, 1318}, {397, 1747}} }, // POINTER_DOWN + { 267300us, {{1063, 1128}, {682, 1318}, {397, 1747}} }, // POINTER_UP + { 267300us, {{1063, 1128}, {NAN, NAN}, {397, 1747}} }, // POINTER_UP + { 272700us, {{1063, 1128}, {NAN, NAN}, {NAN, NAN}} }, + }; + + // Velocity should actually be zero, but we expect 0.016 here instead. + // This is close enough to zero, and is likely caused by division by a very small number. + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -0.016); + computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -0.016); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0); + computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 0); +} + +/** + * ================== Tests for least squares fitting ============================================== + * + * Special care must be taken when constructing tests for LeastSquaresVelocityTrackerStrategy + * getEstimator function. In particular: + * - inside the function, time gets converted from nanoseconds to seconds + * before being used in the fit. + * - any values that are older than 100 ms are being discarded. + * - the newest time gets subtracted from all of the other times before being used in the fit. + * So these tests have to be designed with those limitations in mind. + * + * General approach for the tests below: + * We only used timestamps in milliseconds, 0 ms, 1 ms, and 2 ms, to be sure that + * we are well within the HORIZON range. + * When specifying the expected values of the coefficients, we treat the x values as if + * they were in ms. Then, to adjust for the time units, the coefficients get progressively + * multiplied by powers of 1E3. + * For example: + * data: t(ms), x + * 1 ms, 1 + * 2 ms, 4 + * 3 ms, 9 + * The coefficients are (0, 0, 1). + * In the test, we would convert these coefficients to (0*(1E3)^0, 0*(1E3)^1, 1*(1E3)^2). + */ +TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constant) { + std::vector<MotionEventEntry> motions = { + { 0ms, {{1, 1}} }, // 0 s + { 1ms, {{1, 1}} }, // 0.001 s + { 2ms, {{1, 1}} }, // 0.002 s + { 2ms, {{1, 1}} }, // ACTION_UP + }; + // The data used for the fit will be as follows: + // time(s), position + // -0.002, 1 + // -0.001, 1 + // -0.ms, 1 + computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({1, 0, 0})); +} + +/* + * Straight line y = x :: the constant and quadratic coefficients are zero. + */ +TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) { + std::vector<MotionEventEntry> motions = { + { 0ms, {{-2, -2}} }, + { 1ms, {{-1, -1}} }, + { 2ms, {{-0, -0}} }, + { 2ms, {{-0, -0}} }, // ACTION_UP + }; + // The data used for the fit will be as follows: + // time(s), position + // -0.002, -2 + // -0.001, -1 + // -0.000, 0 + computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 1E3, 0})); +} + +/* + * Parabola + */ +TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic) { + std::vector<MotionEventEntry> motions = { + { 0ms, {{1, 1}} }, + { 1ms, {{4, 4}} }, + { 2ms, {{8, 8}} }, + { 2ms, {{8, 8}} }, // ACTION_UP + }; + // The data used for the fit will be as follows: + // time(s), position + // -0.002, 1 + // -0.001, 4 + // -0.000, 8 + computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({8, 4.5E3, 0.5E6})); +} + +/* + * Parabola + */ +TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic2) { + std::vector<MotionEventEntry> motions = { + { 0ms, {{1, 1}} }, + { 1ms, {{4, 4}} }, + { 2ms, {{9, 9}} }, + { 2ms, {{9, 9}} }, // ACTION_UP + }; + // The data used for the fit will be as follows: + // time(s), position + // -0.002, 1 + // -0.001, 4 + // -0.000, 9 + computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({9, 6E3, 1E6})); +} + +/* + * Parabola :: y = x^2 :: the constant and linear coefficients are zero. + */ +TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic3) { + std::vector<MotionEventEntry> motions = { + { 0ms, {{4, 4}} }, + { 1ms, {{1, 1}} }, + { 2ms, {{0, 0}} }, + { 2ms, {{0, 0}} }, // ACTION_UP + }; + // The data used for the fit will be as follows: + // time(s), position + // -0.002, 4 + // -0.001, 1 + // -0.000, 0 + computeAndCheckQuadraticEstimate(motions, std::array<float, 3>({0, 0E3, 1E6})); +} } // namespace android
diff --git a/libs/math/OWNERS b/libs/math/OWNERS new file mode 100644 index 0000000..6fb149a --- /dev/null +++ b/libs/math/OWNERS
@@ -0,0 +1,6 @@ +jaesoo@google.com +jiyong@google.com +mathias@google.com +pawin@google.com +randolphs@google.com +romainguy@google.com
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index 49ffc8f..9bd3095 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -41,33 +41,11 @@ // ---------------------------------------------------------------------------- int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) { - if (!outBuffer || !desc) - return BAD_VALUE; - - if (!AHardwareBuffer_isValidPixelFormat(desc->format)) { - ALOGE("Invalid AHardwareBuffer pixel format %u (%#x))", desc->format, desc->format); - return BAD_VALUE; - } + if (!outBuffer || !desc) return BAD_VALUE; + if (!AHardwareBuffer_isValidDescription(desc, /*log=*/true)) return BAD_VALUE; int format = AHardwareBuffer_convertToPixelFormat(desc->format); - if (desc->rfu0 != 0 || desc->rfu1 != 0) { - ALOGE("AHardwareBuffer_Desc::rfu fields must be 0"); - return BAD_VALUE; - } - - if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) { - ALOGE("Height must be 1 when using the AHARDWAREBUFFER_FORMAT_BLOB format"); - return BAD_VALUE; - } - - if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) && - (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) { - ALOGE("AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER " - "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER"); - return BAD_VALUE; - } - - uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage); + uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage); sp<GraphicBuffer> gbuffer(new GraphicBuffer( desc->width, desc->height, format, desc->layers, usage, std::string("AHardwareBuffer pid [") + std::to_string(getpid()) + "]")); @@ -115,13 +93,100 @@ outDesc->rfu1 = 0; } +int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage, + int32_t fence, const ARect* rect, void** outVirtualAddress, + int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { + if (outBytesPerPixel) *outBytesPerPixel = -1; + if (outBytesPerStride) *outBytesPerStride = -1; + + if (!buffer) { + return BAD_VALUE; + } + + if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | + AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) { + ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only " + "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); + return BAD_VALUE; + } + + usage = AHardwareBuffer_convertToGrallocUsageBits(usage); + GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + + //Mapper implementations before 3.0 will not return bytes per pixel or + //bytes per stride information. + if (gbuffer->getBufferMapperVersion() == GraphicBufferMapper::Version::GRALLOC_2) { + ALOGE("Mapper versions before 3.0 cannot retrieve bytes per pixel and bytes per stride info"); + return INVALID_OPERATION; + } + + if (gbuffer->getLayerCount() > 1) { + ALOGE("Buffer with multiple layers passed to AHardwareBuffer_lock; " + "only buffers with one layer are allowed"); + return INVALID_OPERATION; + } + + Rect bounds; + if (!rect) { + bounds.set(Rect(gbuffer->getWidth(), gbuffer->getHeight())); + } else { + bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom)); + } + int32_t bytesPerPixel; + int32_t bytesPerStride; + int result = gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, &bytesPerPixel, &bytesPerStride); + + // if hardware returns -1 for bytes per pixel or bytes per stride, we fail + // and unlock the buffer + if (bytesPerPixel == -1 || bytesPerStride == -1) { + gbuffer->unlock(); + return INVALID_OPERATION; + } + + if (outBytesPerPixel) *outBytesPerPixel = bytesPerPixel; + if (outBytesPerStride) *outBytesPerStride = bytesPerStride; + return result; +} + int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, - int32_t fence, const ARect* rect, void** outVirtualAddress) { + int32_t fence, const ARect* rect, void** outVirtualAddress) { + int32_t bytesPerPixel; + int32_t bytesPerStride; + if (!buffer) return BAD_VALUE; if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) { ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only " + "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); + return BAD_VALUE; + } + + usage = AHardwareBuffer_convertToGrallocUsageBits(usage); + GraphicBuffer* gbuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + + if (gbuffer->getLayerCount() > 1) { + ALOGE("Buffer with multiple layers passed to AHardwareBuffer_lock; " + "only buffers with one layer are allowed"); + return INVALID_OPERATION; + } + + Rect bounds; + if (!rect) { + bounds.set(Rect(gbuffer->getWidth(), gbuffer->getHeight())); + } else { + bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom)); + } + return gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence, &bytesPerPixel, &bytesPerStride); +} + +int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage, + int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) { + if (!buffer || !outPlanes) return BAD_VALUE; + + if (usage & ~(AHARDWAREBUFFER_USAGE_CPU_READ_MASK | + AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) { + ALOGE("Invalid usage flags passed to AHardwareBuffer_lock; only " " AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); return BAD_VALUE; } @@ -134,7 +199,33 @@ } else { bounds.set(Rect(rect->left, rect->top, rect->right, rect->bottom)); } - return gBuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence); + int format = AHardwareBuffer_convertFromPixelFormat(uint32_t(gBuffer->getPixelFormat())); + memset(outPlanes->planes, 0, sizeof(outPlanes->planes)); + if (AHardwareBuffer_formatIsYuv(format)) { + android_ycbcr yuvData; + int result = gBuffer->lockAsyncYCbCr(usage, bounds, &yuvData, fence); + if (result == 0) { + outPlanes->planeCount = 3; + outPlanes->planes[0].data = yuvData.y; + outPlanes->planes[0].pixelStride = 1; + outPlanes->planes[0].rowStride = yuvData.ystride; + outPlanes->planes[1].data = yuvData.cb; + outPlanes->planes[1].pixelStride = yuvData.chroma_step; + outPlanes->planes[1].rowStride = yuvData.cstride; + outPlanes->planes[2].data = yuvData.cr; + outPlanes->planes[2].pixelStride = yuvData.chroma_step; + outPlanes->planes[2].rowStride = yuvData.cstride; + } else { + outPlanes->planeCount = 0; + } + return result; + } else { + const uint32_t pixelStride = AHardwareBuffer_bytesPerPixel(format); + outPlanes->planeCount = 1; + outPlanes->planes[0].pixelStride = pixelStride; + outPlanes->planes[0].rowStride = gBuffer->getStride() * pixelStride; + return gBuffer->lockAsync(usage, usage, bounds, &outPlanes->planes[0].data, fence); + } } int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) { @@ -274,6 +365,38 @@ return NO_ERROR; } +int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) { + if (!desc) return 0; + if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0; + + bool supported = false; + GraphicBuffer* gBuffer = new GraphicBuffer(); + status_t err = gBuffer->isSupported(desc->width, desc->height, desc->format, desc->layers, + desc->usage, &supported); + + if (err == NO_ERROR) { + return supported; + } + + // function isSupported is not implemented on device or an error occurred during HAL + // query. Make a trial allocation. + AHardwareBuffer_Desc trialDesc = *desc; + trialDesc.width = 4; + trialDesc.height = desc->format == AHARDWAREBUFFER_FORMAT_BLOB ? 1 : 4; + if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) { + trialDesc.layers = desc->layers == 6 ? 6 : 12; + } else { + trialDesc.layers = desc->layers == 1 ? 1 : 2; + } + AHardwareBuffer* trialBuffer = nullptr; + int result = AHardwareBuffer_allocate(&trialDesc, &trialBuffer); + if (result == NO_ERROR) { + AHardwareBuffer_release(trialBuffer); + return 1; + } + return 0; +} + // ---------------------------------------------------------------------------- // VNDK functions @@ -286,6 +409,35 @@ return gbuffer->handle; } +int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc, + const native_handle_t* handle, int32_t method, + AHardwareBuffer** outBuffer) { + static_assert(static_cast<int32_t>(AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_REGISTER) == + static_cast<int32_t>(GraphicBuffer::TAKE_UNREGISTERED_HANDLE)); + static_assert(static_cast<int32_t>(AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE) == + static_cast<int32_t>(GraphicBuffer::CLONE_HANDLE)); + + if (!desc || !handle || !outBuffer) return BAD_VALUE; + if (!(method == AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_REGISTER || + method == AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE)) + return BAD_VALUE; + if (desc->rfu0 != 0 || desc->rfu1 != 0) return BAD_VALUE; + if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB && desc->height != 1) return BAD_VALUE; + + const int format = AHardwareBuffer_convertToPixelFormat(desc->format); + const uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage); + const auto wrapMethod = static_cast<GraphicBuffer::HandleWrapMethod>(method); + sp<GraphicBuffer> gbuffer(new GraphicBuffer(handle, wrapMethod, desc->width, desc->height, + format, desc->layers, usage, desc->stride)); + status_t err = gbuffer->initCheck(); + if (err != 0 || gbuffer->handle == 0) return err; + + *outBuffer = AHardwareBuffer_from_GraphicBuffer(gbuffer.get()); + // Ensure the buffer doesn't get destroyed when the sp<> goes away. + AHardwareBuffer_acquire(*outBuffer); + + return NO_ERROR; +} // ---------------------------------------------------------------------------- // Helpers implementation @@ -293,14 +445,84 @@ namespace android { -// A 1:1 mapping of AHardwaqreBuffer bitmasks to gralloc1 bitmasks. -struct UsageMaskMapping { - uint64_t hardwareBufferMask; - uint64_t grallocMask; -}; +bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log) { + if (desc->width == 0 || desc->height == 0 || desc->layers == 0) { + ALOGE_IF(log, "Width, height and layers must all be nonzero"); + return false; + } -static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) { - return (mask & bitsToCheck) == bitsToCheck && bitsToCheck; + if (!AHardwareBuffer_isValidPixelFormat(desc->format)) { + ALOGE_IF(log, "Invalid AHardwareBuffer pixel format %u (%#x))", + desc->format, desc->format); + return false; + } + + if (desc->rfu0 != 0 || desc->rfu1 != 0) { + ALOGE_IF(log, "AHardwareBuffer_Desc::rfu fields must be 0"); + return false; + } + + if (desc->format == AHARDWAREBUFFER_FORMAT_BLOB) { + if (desc->height != 1 || desc->layers != 1) { + ALOGE_IF(log, "Height and layers must be 1 for AHARDWAREBUFFER_FORMAT_BLOB"); + return false; + } + const uint64_t blobInvalidGpuMask = + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | + AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER | + AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE | + AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP; + if (desc->usage & blobInvalidGpuMask) { + ALOGE_IF(log, "Invalid GPU usage flag for AHARDWAREBUFFER_FORMAT_BLOB; " + "only AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER is allowed"); + return false; + } + if (desc->usage & AHARDWAREBUFFER_USAGE_VIDEO_ENCODE) { + ALOGE_IF(log, "AHARDWAREBUFFER_FORMAT_BLOB cannot be encoded as video"); + return false; + } + } else if (AHardwareBuffer_formatIsYuv(desc->format)) { + if (desc->layers != 1) { + ALOGE_IF(log, "Layers must be 1 for YUV formats."); + return false; + } + const uint64_t yuvInvalidGpuMask = + AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE | + AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP; + if (desc->usage & yuvInvalidGpuMask) { + ALOGE_IF(log, "Invalid usage flags specified for YUV format; " + "mip-mapping and cube-mapping are not allowed."); + return false; + } + } else { + if (desc->usage & AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA) { + ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA requires AHARDWAREBUFFER_FORMAT_BLOB"); + return false; + } + if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) { + ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER requires AHARDWAREBUFFER_FORMAT_BLOB"); + return false; + } + } + + if ((desc->usage & (AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK)) && + (desc->usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT)) { + ALOGE_IF(log, "AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT requires AHARDWAREBUFFER_USAGE_CPU_READ_NEVER " + "and AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER"); + return false; + } + + if (desc->usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) { + if (desc->width != desc->height) { + ALOGE_IF(log, "Cube maps must be square"); + return false; + } + if (desc->layers % 6 != 0) { + ALOGE_IF(log, "Cube map layers must be a multiple of 6"); + return false; + } + } + return true; } bool AHardwareBuffer_isValidPixelFormat(uint32_t format) { @@ -371,6 +593,7 @@ case AHARDWAREBUFFER_FORMAT_D32_FLOAT: case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT: case AHARDWAREBUFFER_FORMAT_S8_UINT: + case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420: // VNDK formats only -- unfortunately we can't differentiate from where we're called case AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM: case AHARDWAREBUFFER_FORMAT_YV12: @@ -381,7 +604,6 @@ case AHARDWAREBUFFER_FORMAT_RAW12: case AHARDWAREBUFFER_FORMAT_RAW_OPAQUE: case AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED: - case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420: case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP: case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP: case AHARDWAREBUFFER_FORMAT_YCbCr_422_I: @@ -392,6 +614,40 @@ } } +bool AHardwareBuffer_formatIsYuv(uint32_t format) { + switch (format) { + case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420: + case AHARDWAREBUFFER_FORMAT_YV12: + case AHARDWAREBUFFER_FORMAT_Y8: + case AHARDWAREBUFFER_FORMAT_Y16: + case AHARDWAREBUFFER_FORMAT_YCbCr_422_SP: + case AHARDWAREBUFFER_FORMAT_YCrCb_420_SP: + case AHARDWAREBUFFER_FORMAT_YCbCr_422_I: + return true; + default: + return false; + } +} + +uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) { + switch (format) { + case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: + case AHARDWAREBUFFER_FORMAT_D16_UNORM: + return 2; + case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM: + case AHARDWAREBUFFER_FORMAT_D24_UNORM: + return 3; + case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: + case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM: + case AHARDWAREBUFFER_FORMAT_D32_FLOAT: + case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: + case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT: + return 4; + default: + return 0; + } +} + uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t hal_format) { return hal_format; } @@ -416,7 +672,7 @@ "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE == (uint64_t)BufferUsage::GPU_TEXTURE, "gralloc and AHardwareBuffer flags don't match"); - static_assert(AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT == (uint64_t)BufferUsage::GPU_RENDER_TARGET, + static_assert(AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER == (uint64_t)BufferUsage::GPU_RENDER_TARGET, "gralloc and AHardwareBuffer flags don't match"); static_assert(AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT == (uint64_t)BufferUsage::PROTECTED, "gralloc and AHardwareBuffer flags don't match"); @@ -438,11 +694,11 @@ } const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer) { - return reinterpret_cast<const GraphicBuffer*>(buffer); + return GraphicBuffer::fromAHardwareBuffer(buffer); } GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer) { - return reinterpret_cast<GraphicBuffer*>(buffer); + return GraphicBuffer::fromAHardwareBuffer(buffer); } const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer) { @@ -454,7 +710,7 @@ } AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer) { - return reinterpret_cast<AHardwareBuffer*>(buffer); + return buffer->toAHardwareBuffer(); } } // namespace android
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index 765dcd9..8435dac 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp
@@ -126,12 +126,12 @@ } int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) { - static_assert(ADATASPACE_UNKNOWN == HAL_DATASPACE_UNKNOWN); - static_assert(ADATASPACE_SCRGB_LINEAR == HAL_DATASPACE_V0_SCRGB_LINEAR); - static_assert(ADATASPACE_SRGB == HAL_DATASPACE_V0_SRGB); - static_assert(ADATASPACE_SCRGB == HAL_DATASPACE_V0_SCRGB); - static_assert(ADATASPACE_DISPLAY_P3 == HAL_DATASPACE_DISPLAY_P3); - static_assert(ADATASPACE_BT2020_PQ == HAL_DATASPACE_BT2020_PQ); + static_assert(static_cast<int>(ADATASPACE_UNKNOWN) == static_cast<int>(HAL_DATASPACE_UNKNOWN)); + static_assert(static_cast<int>(ADATASPACE_SCRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SCRGB_LINEAR)); + static_assert(static_cast<int>(ADATASPACE_SRGB) == static_cast<int>(HAL_DATASPACE_V0_SRGB)); + static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB)); + static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3)); + static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ)); if (!window || !query(window, NATIVE_WINDOW_IS_VALID) || !isDataSpaceValid(window, dataSpace)) {
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index 5fbb3b2..27ab482 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp
@@ -13,13 +13,20 @@ // limitations under the License. ndk_headers { - name: "libnativewindow_headers", + name: "libnativewindow_ndk_headers", from: "include/android", to: "android", srcs: ["include/android/*.h"], license: "NOTICE", } +// TODO(b/118715870): cleanup header files +cc_library_headers { + name: "libnativewindow_headers", + export_include_dirs: ["include"], + vendor_available: true, +} + ndk_library { name: "libnativewindow", symbol_file: "libnativewindow.map.txt", @@ -40,13 +47,10 @@ cflags: [ "-Wall", "-Werror", + "-Wno-enum-compare", "-Wno-unused-function", ], - cppflags: [ - "-std=c++1z" - ], - version_script: "libnativewindow.map.txt", srcs: [ @@ -70,6 +74,7 @@ header_libs: [ "libnativebase_headers", + "libnativewindow_headers", ], // headers we include in our public headers
diff --git a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h index 71f5634..ddfd1d1 100644 --- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h +++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h
@@ -28,13 +28,24 @@ #include <stdint.h> struct AHardwareBuffer; +struct AHardwareBuffer_Desc; struct ANativeWindowBuffer; namespace android { +// Validates whether the passed description does not have conflicting +// parameters. Note: this does not verify any platform-specific contraints. +bool AHardwareBuffer_isValidDescription(const AHardwareBuffer_Desc* desc, bool log); + // whether this AHardwareBuffer format is valid bool AHardwareBuffer_isValidPixelFormat(uint32_t ahardwarebuffer_format); +// whether this is a YUV type format +bool AHardwareBuffer_formatIsYuv(uint32_t format); + +// number of bytes per pixel or 0 if unknown or multi-planar +uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format); + // convert AHardwareBuffer format to HAL format (note: this is a no-op) uint32_t AHardwareBuffer_convertFromPixelFormat(uint32_t format);
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h index 3ac1c58..2899bcf 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h
@@ -75,7 +75,7 @@ * scRGB: * * The red, green, and blue components are stored in extended sRGB space, - * but are linear, not gamma-encoded. + * and gamma-encoded using the SRGB transfer function. * The RGB primaries and the white point are the same as BT.709. * * The values are floating point.
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index 78cec41..da959e3 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -16,6 +16,30 @@ /** * @file hardware_buffer.h + * @brief API for native hardware buffers. + */ +/** + * @defgroup AHardwareBuffer Native Hardware Buffer + * + * AHardwareBuffer objects represent chunks of memory that can be + * accessed by various hardware components in the system. It can be + * easily converted to the Java counterpart + * android.hardware.HardwareBuffer and passed between processes using + * Binder. All operations involving AHardwareBuffer and HardwareBuffer + * are zero-copy, i.e., passing AHardwareBuffer to another process + * creates a shared view of the same region of memory. + * + * AHardwareBuffers can be bound to EGL/OpenGL and Vulkan primitives. + * For EGL, use the extension function eglGetNativeClientBufferANDROID + * to obtain an EGLClientBuffer and pass it directly to + * eglCreateImageKHR. Refer to the EGL extensions + * EGL_ANDROID_get_native_client_buffer and + * EGL_ANDROID_image_native_buffer for more information. In Vulkan, + * the contents of the AHardwareBuffer can be accessed as external + * memory. See the VK_ANDROID_external_memory_android_hardware_buffer + * extension for details. + * + * @{ */ #ifndef ANDROID_HARDWARE_BUFFER_H @@ -32,7 +56,7 @@ /** * Buffer pixel formats. */ -enum { +enum AHardwareBuffer_Format { /** * Corresponding formats: * Vulkan: VK_FORMAT_R8G8B8A8_UNORM @@ -78,8 +102,10 @@ AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM = 0x2b, /** - * An opaque binary blob format that must have height 1, with width equal to - * the buffer size in bytes. + * Opaque binary blob format. + * Must have height 1 and one layer, with width equal to the buffer + * size in bytes. Corresponds to Vulkan buffers and OpenGL buffer + * objects. Can be bound to the latter using GL_EXT_external_buffer. */ AHARDWAREBUFFER_FORMAT_BLOB = 0x21, @@ -124,51 +150,118 @@ * OpenGL ES: GL_STENCIL_INDEX8 */ AHARDWAREBUFFER_FORMAT_S8_UINT = 0x35, + + /** + * YUV 420 888 format. + * Must have an even width and height. Can be accessed in OpenGL + * shaders through an external sampler. Does not support mip-maps + * cube-maps or multi-layered textures. + */ + AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23, }; /** * Buffer usage flags, specifying how the buffer will be accessed. */ -enum { - /// The buffer will never be read by the CPU. +enum AHardwareBuffer_UsageFlags { + /// The buffer will never be locked for direct CPU reads using the + /// AHardwareBuffer_lock() function. Note that reading the buffer + /// using OpenGL or Vulkan functions or memory mappings is still + /// allowed. AHARDWAREBUFFER_USAGE_CPU_READ_NEVER = 0UL, - /// The buffer will sometimes be read by the CPU. + /// The buffer will sometimes be locked for direct CPU reads using + /// the AHardwareBuffer_lock() function. Note that reading the + /// buffer using OpenGL or Vulkan functions or memory mappings + /// does not require the presence of this flag. AHARDWAREBUFFER_USAGE_CPU_READ_RARELY = 2UL, - /// The buffer will often be read by the CPU. + /// The buffer will often be locked for direct CPU reads using + /// the AHardwareBuffer_lock() function. Note that reading the + /// buffer using OpenGL or Vulkan functions or memory mappings + /// does not require the presence of this flag. AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN = 3UL, /// CPU read value mask. AHARDWAREBUFFER_USAGE_CPU_READ_MASK = 0xFUL, - /// The buffer will never be written by the CPU. + /// The buffer will never be locked for direct CPU writes using the + /// AHardwareBuffer_lock() function. Note that writing the buffer + /// using OpenGL or Vulkan functions or memory mappings is still + /// allowed. AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER = 0UL << 4, - /// The buffer will sometimes be written to by the CPU. + /// The buffer will sometimes be locked for direct CPU writes using + /// the AHardwareBuffer_lock() function. Note that writing the + /// buffer using OpenGL or Vulkan functions or memory mappings + /// does not require the presence of this flag. AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY = 2UL << 4, - /// The buffer will often be written to by the CPU. + /// The buffer will often be locked for direct CPU writes using + /// the AHardwareBuffer_lock() function. Note that writing the + /// buffer using OpenGL or Vulkan functions or memory mappings + /// does not require the presence of this flag. AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN = 3UL << 4, /// CPU write value mask. AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK = 0xFUL << 4, /// The buffer will be read from by the GPU as a texture. AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE = 1UL << 8, + /// The buffer will be written to by the GPU as a framebuffer attachment. + AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER = 1UL << 9, /** - * The buffer will be written to by the GPU as a framebuffer attachment. - * Note that the name of this flag is somewhat misleading: it does not imply - * that the buffer contains a color format. A buffer with depth or stencil - * format that will be used as a framebuffer attachment should also have - * this flag. + * The buffer will be written to by the GPU as a framebuffer + * attachment. + * + * Note that the name of this flag is somewhat misleading: it does + * not imply that the buffer contains a color format. A buffer with + * depth or stencil format that will be used as a framebuffer + * attachment should also have this flag. Use the equivalent flag + * AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER to avoid this confusion. */ - AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = 1UL << 9, - /// The buffer must not be used outside of a protected hardware path. + AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, + /** + * The buffer will be used as a composer HAL overlay layer. + * + * This flag is currently only needed when using ASurfaceTransaction_setBuffer + * to set a buffer. In all other cases, the framework adds this flag + * internally to buffers that could be presented in a composer overlay. + * ASurfaceTransaction_setBuffer is special because it uses buffers allocated + * directly through AHardwareBuffer_allocate instead of buffers allocated + * by the framework. + */ + AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY = 1ULL << 11, + /** + * The buffer is protected from direct CPU access or being read by + * non-secure hardware, such as video encoders. + * + * This flag is incompatible with CPU read and write flags. It is + * mainly used when handling DRM video. Refer to the EGL extension + * EGL_EXT_protected_content and GL extension + * GL_EXT_protected_textures for more information on how these + * buffers are expected to behave. + */ AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT = 1UL << 14, /// The buffer will be read by a hardware video encoder. AHARDWAREBUFFER_USAGE_VIDEO_ENCODE = 1UL << 16, - /// The buffer will be used for direct writes from sensors. + /** + * The buffer will be used for direct writes from sensors. + * When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB. + */ AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA = 1UL << 23, - /// The buffer will be used as a shader storage or uniform buffer object. + /** + * The buffer will be used as a shader storage or uniform buffer object. + * When this flag is present, the format must be AHARDWAREBUFFER_FORMAT_BLOB. + */ AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER = 1UL << 24, - /// The buffer will be used as a cube map texture. + /** + * The buffer will be used as a cube map texture. + * When this flag is present, the buffer must have a layer count + * that is a multiple of 6. Note that buffers with this flag must be + * bound to OpenGL textures using the extension + * GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image. + */ AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP = 1UL << 25, - /// The buffer contains a complete mipmap hierarchy. + /** + * The buffer contains a complete mipmap hierarchy. + * Note that buffers with this flag must be bound to OpenGL textures using + * the extension GL_EXT_EGL_image_storage instead of GL_KHR_EGL_image. + */ AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE = 1UL << 26, AHARDWAREBUFFER_USAGE_VENDOR_0 = 1ULL << 28, @@ -194,97 +287,185 @@ }; /** - * Buffer description. Used for allocating new buffers and querying parameters - * of existing ones. + * Buffer description. Used for allocating new buffers and querying + * parameters of existing ones. */ typedef struct AHardwareBuffer_Desc { uint32_t width; ///< Width in pixels. uint32_t height; ///< Height in pixels. - uint32_t layers; ///< Number of images in an image array. - uint32_t format; ///< One of AHARDWAREBUFFER_FORMAT_* - uint64_t usage; ///< Combination of AHARDWAREBUFFER_USAGE_* + /** + * Number of images in an image array. AHardwareBuffers with one + * layer correspond to regular 2D textures. AHardwareBuffers with + * more than layer correspond to texture arrays. If the layer count + * is a multiple of 6 and the usage flag + * AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP is present, the buffer is + * a cube map or a cube map array. + */ + uint32_t layers; + uint32_t format; ///< One of AHardwareBuffer_Format. + uint64_t usage; ///< Combination of AHardwareBuffer_UsageFlags. uint32_t stride; ///< Row stride in pixels, ignored for AHardwareBuffer_allocate() uint32_t rfu0; ///< Initialize to zero, reserved for future use. uint64_t rfu1; ///< Initialize to zero, reserved for future use. } AHardwareBuffer_Desc; -typedef struct AHardwareBuffer AHardwareBuffer; +/** + * Holds data for a single image plane. + */ +typedef struct AHardwareBuffer_Plane { + void* data; ///< Points to first byte in plane + uint32_t pixelStride; ///< Distance in bytes from the color channel of one pixel to the next + uint32_t rowStride; ///< Distance in bytes from the first value of one row of the image to + /// the first value of the next row. +} AHardwareBuffer_Plane; /** - * Allocates a buffer that backs an AHardwareBuffer using the passed - * AHardwareBuffer_Desc. + * Holds all image planes that contain the pixel data. + */ +typedef struct AHardwareBuffer_Planes { + uint32_t planeCount; ///< Number of distinct planes + AHardwareBuffer_Plane planes[4]; ///< Array of image planes +} AHardwareBuffer_Planes; + +/** + * Opaque handle for a native hardware buffer. + */ +typedef struct AHardwareBuffer AHardwareBuffer; + +#if __ANDROID_API__ >= 26 + +/** + * Allocates a buffer that matches the passed AHardwareBuffer_Desc. + * + * If allocation succeeds, the buffer can be used according to the + * usage flags specified in its description. If a buffer is used in ways + * not compatible with its usage flags, the results are undefined and + * may include program termination. * * \return 0 on success, or an error number of the allocation fails for * any reason. The returned buffer has a reference count of 1. */ int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, - AHardwareBuffer** outBuffer); + AHardwareBuffer** outBuffer) __INTRODUCED_IN(26); /** - * Acquire a reference on the given AHardwareBuffer object. This prevents the - * object from being deleted until the last reference is removed. + * Acquire a reference on the given AHardwareBuffer object. + * + * This prevents the object from being deleted until the last reference + * is removed. */ -void AHardwareBuffer_acquire(AHardwareBuffer* buffer); +void AHardwareBuffer_acquire(AHardwareBuffer* buffer) __INTRODUCED_IN(26); /** * Remove a reference that was previously acquired with - * AHardwareBuffer_acquire(). + * AHardwareBuffer_acquire() or AHardwareBuffer_allocate(). */ -void AHardwareBuffer_release(AHardwareBuffer* buffer); +void AHardwareBuffer_release(AHardwareBuffer* buffer) __INTRODUCED_IN(26); /** * Return a description of the AHardwareBuffer in the passed * AHardwareBuffer_Desc struct. */ void AHardwareBuffer_describe(const AHardwareBuffer* buffer, - AHardwareBuffer_Desc* outDesc); + AHardwareBuffer_Desc* outDesc) __INTRODUCED_IN(26); /** - * Lock the AHardwareBuffer for reading or writing, depending on the usage flags - * passed. This call may block if the hardware needs to finish rendering or if - * CPU caches need to be synchronized, or possibly for other implementation- - * specific reasons. If fence is not negative, then it specifies a fence file - * descriptor that will be signaled when the buffer is locked, otherwise the - * caller will block until the buffer is available. + * Lock the AHardwareBuffer for direct CPU access. * - * If \a rect is not NULL, the caller promises to modify only data in the area - * specified by rect. If rect is NULL, the caller may modify the contents of the - * entire buffer. + * This function can lock the buffer for either reading or writing. + * It may block if the hardware needs to finish rendering, if CPU caches + * need to be synchronized, or possibly for other implementation- + * specific reasons. * - * The content of the buffer outside of the specified rect is NOT modified - * by this call. + * The passed AHardwareBuffer must have one layer, otherwise the call + * will fail. * - * The \a usage parameter may only specify AHARDWAREBUFFER_USAGE_CPU_*. If set, - * then outVirtualAddress is filled with the address of the buffer in virtual + * If \a fence is not negative, it specifies a fence file descriptor on + * which to wait before locking the buffer. If it's negative, the caller + * is responsible for ensuring that writes to the buffer have completed + * before calling this function. Using this parameter is more efficient + * than waiting on the fence and then calling this function. + * + * The \a usage parameter may only specify AHARDWAREBUFFER_USAGE_CPU_*. + * If set, then outVirtualAddress is filled with the address of the + * buffer in virtual memory. The flags must also be compatible with + * usage flags specified at buffer creation: if a read flag is passed, + * the buffer must have been created with + * AHARDWAREBUFFER_USAGE_CPU_READ_RARELY or + * AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN. If a write flag is passed, it + * must have been created with AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY or + * AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN. + * + * If \a rect is not NULL, the caller promises to modify only data in + * the area specified by rect. If rect is NULL, the caller may modify + * the contents of the entire buffer. The content of the buffer outside + * of the specified rect is NOT modified by this call. + * + * It is legal for several different threads to lock a buffer for read + * access; none of the threads are blocked. + * + * Locking a buffer simultaneously for write or read/write is undefined, + * but will neither terminate the process nor block the caller. + * AHardwareBuffer_lock may return an error or leave the buffer's + * content in an indeterminate state. + * + * If the buffer has AHARDWAREBUFFER_FORMAT_BLOB, it is legal lock it + * for reading and writing in multiple threads and/or processes + * simultaneously, and the contents of the buffer behave like shared * memory. * - * THREADING CONSIDERATIONS: - * - * It is legal for several different threads to lock a buffer for read access; - * none of the threads are blocked. - * - * Locking a buffer simultaneously for write or read/write is undefined, but - * will neither terminate the process nor block the caller; AHardwareBuffer_lock - * may return an error or leave the buffer's content into an indeterminate - * state. - * - * \return 0 on success, -EINVAL if \a buffer is NULL or if the usage - * flags are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or an error - * number of the lock fails for any reason. + * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags + * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer + * has more than one layer. Error number if the lock fails for any other + * reason. */ int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, - int32_t fence, const ARect* rect, void** outVirtualAddress); + int32_t fence, const ARect* rect, void** outVirtualAddress) __INTRODUCED_IN(26); /** - * Unlock the AHardwareBuffer; must be called after all changes to the buffer - * are completed by the caller. If fence is not NULL then it will be set to a - * file descriptor that is signaled when all pending work on the buffer is - * completed. The caller is responsible for closing the fence when it is no - * longer needed. + * Lock a potentially multi-planar AHardwareBuffer for direct CPU access. * - * \return 0 on success, -EINVAL if \a buffer is NULL, or an error - * number if the unlock fails for any reason. + * This function is similar to AHardwareBuffer_lock, but can lock multi-planar + * formats. The locked planes are returned in the \a outPlanes argument. Note, + * that multi-planar should not be confused with multi-layer images, which this + * locking function does not support. + * + * YUV formats are always represented by three separate planes of data, one for + * each color plane. The order of planes in the array is guaranteed such that + * plane #0 is always Y, plane #1 is always U (Cb), and plane #2 is always V + * (Cr). All other formats are represented by a single plane. + * + * Additional information always accompanies the buffers, describing the row + * stride and the pixel stride for each plane. + * + * In case the buffer cannot be locked, \a outPlanes will contain zero planes. + * + * See the AHardwareBuffer_lock documentation for all other locking semantics. + * + * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags + * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer + * has more than one layer. Error number if the lock fails for any other + * reason. */ -int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence); +int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage, + int32_t fence, const ARect* rect, AHardwareBuffer_Planes* outPlanes) __INTRODUCED_IN(29); + +/** + * Unlock the AHardwareBuffer from direct CPU access. + * + * Must be called after all changes to the buffer are completed by the + * caller. If \a fence is NULL, the function will block until all work + * is completed. Otherwise, \a fence will be set either to a valid file + * descriptor or to -1. The file descriptor will become signaled once + * the unlocking is complete and buffer contents are updated. + * The caller is responsible for closing the file descriptor once it's + * no longer needed. The value -1 indicates that unlocking has already + * completed before the function returned and no further operations are + * necessary. + * + * \return 0 on success. -EINVAL if \a buffer is NULL. Error number if + * the unlock fails for any reason. + */ +int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) __INTRODUCED_IN(26); /** * Send the AHardwareBuffer to an AF_UNIX socket. @@ -292,16 +473,55 @@ * \return 0 on success, -EINVAL if \a buffer is NULL, or an error * number if the operation fails for any reason. */ -int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd); +int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int socketFd) __INTRODUCED_IN(26); /** - * Receive the AHardwareBuffer from an AF_UNIX socket. + * Receive an AHardwareBuffer from an AF_UNIX socket. * * \return 0 on success, -EINVAL if \a outBuffer is NULL, or an error * number if the operation fails for any reason. */ -int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer); +int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** outBuffer) __INTRODUCED_IN(26); + +#endif // __ANDROID_API__ >= 26 + +#if __ANDROID_API__ >= 29 + +/** + * Test whether the given format and usage flag combination is + * allocatable. + * + * If this function returns true, it means that a buffer with the given + * description can be allocated on this implementation, unless resource + * exhaustion occurs. If this function returns false, it means that the + * allocation of the given description will never succeed. + * + * The return value of this function may depend on all fields in the + * description, except stride, which is always ignored. For example, + * some implementations have implementation-defined limits on texture + * size and layer count. + * + * \return 1 if the format and usage flag combination is allocatable, + * 0 otherwise. + */ +int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_IN(29); + +/** + * Lock an AHardwareBuffer for direct CPU access. + * + * This function is the same as the above lock function, but passes back + * additional information about the bytes per pixel and the bytes per stride + * of the locked buffer. If the bytes per pixel or bytes per stride are unknown + * or variable, or if the underlying mapper implementation does not support returning + * additional information, then this call will fail with INVALID_OPERATION + */ +int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage, + int32_t fence, const ARect* rect, void** outVirtualAddress, + int32_t* outBytesPerPixel, int32_t* outBytesPerStride) __INTRODUCED_IN(29); +#endif // __ANDROID_API__ >= 29 __END_DECLS #endif // ANDROID_HARDWARE_BUFFER_H + +/** @} */
diff --git a/libs/nativewindow/include/android/hdr_metadata.h b/libs/nativewindow/include/android/hdr_metadata.h new file mode 100644 index 0000000..88772a9 --- /dev/null +++ b/libs/nativewindow/include/android/hdr_metadata.h
@@ -0,0 +1,74 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file hdr_metadata.h + */ + +#ifndef ANDROID_HDR_METADATA_H +#define ANDROID_HDR_METADATA_H + +#include <inttypes.h> + +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * These structures are used to define the display's capabilities for HDR content. + * They can be used to better tone map content to user's display. + */ + +/** + * HDR metadata standards that are supported by Android. + */ +enum AHdrMetadataType : uint32_t { + HDR10_SMPTE2086 = 1, + HDR10_CTA861_3 = 2, + HDR10PLUS_SEI = 3, +}; + +/** + * Color is defined in CIE XYZ coordinates. + */ +struct AColor_xy { + float x; + float y; +}; + +/** + * SMPTE ST 2086 "Mastering Display Color Volume" static metadata + */ +struct AHdrMetadata_smpte2086 { + struct AColor_xy displayPrimaryRed; + struct AColor_xy displayPrimaryGreen; + struct AColor_xy displayPrimaryBlue; + struct AColor_xy whitePoint; + float maxLuminance; + float minLuminance; +}; + +/** + * CTA 861.3 "HDR Static Metadata Extension" static metadata + */ +struct AHdrMetadata_cta861_3 { + float maxContentLightLevel; + float maxFrameAverageLightLevel; +}; + +__END_DECLS + +#endif // ANDROID_HDR_METADATA_H
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index d5e5e9d..6730596 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h
@@ -15,7 +15,13 @@ */ /** - * @addtogroup NativeActivity Native Activity + * @defgroup ANativeWindow Native Window + * + * ANativeWindow represents the producer end of an image queue. + * It is the C counterpart of the android.view.Surface object in Java, + * and can be converted both ways. Depending on the consumer, images + * submitted to ANativeWindow can be shown on the display or sent to + * other consumers, such as video encoders. * @{ */ @@ -41,7 +47,7 @@ * Legacy window pixel format names, kept for backwards compatibility. * New code and APIs should use AHARDWAREBUFFER_FORMAT_*. */ -enum { +enum ANativeWindow_LegacyFormat { // NOTE: these values must match the values from graphics/common/x.x/types.hal /** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/ @@ -95,7 +101,7 @@ /// memory. This may be >= width. int32_t stride; - /// The format of the buffer. One of AHARDWAREBUFFER_FORMAT_* + /// The format of the buffer. One of AHardwareBuffer_Format. int32_t format; /// The actual bits. @@ -151,7 +157,7 @@ * * \param width width of the buffers in pixels. * \param height height of the buffers in pixels. - * \param format one of AHARDWAREBUFFER_FORMAT_* constants. + * \param format one of the AHardwareBuffer_Format constants. * \return 0 for success, or a negative value on error. */ int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, @@ -178,7 +184,7 @@ */ int32_t ANativeWindow_unlockAndPost(ANativeWindow* window); -#if __ANDROID_API__ >= __ANDROID_API_O__ +#if __ANDROID_API__ >= 26 /** * Set a transform that will be applied to future buffers posted to the window. @@ -186,11 +192,11 @@ * \param transform combination of {@link ANativeWindowTransform} flags * \return 0 for success, or -EINVAL if \p transform is invalid */ -int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transform); +int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transform) __INTRODUCED_IN(26); -#endif // __ANDROID_API__ >= __ANDROID_API_O__ +#endif // __ANDROID_API__ >= 26 -#if __ANDROID_API__ >= __ANDROID_API_P__ +#if __ANDROID_API__ >= 28 /** * All buffers queued after this call will be associated with the dataSpace @@ -206,16 +212,16 @@ * \return 0 for success, -EINVAL if window is invalid or the dataspace is not * supported. */ -int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace); +int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpace) __INTRODUCED_IN(28); /** * Get the dataspace of the buffers in window. * \return the dataspace of buffers in window, ADATASPACE_UNKNOWN is returned if * dataspace is unknown, or -EINVAL if window is invalid. */ -int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window); +int32_t ANativeWindow_getBuffersDataSpace(ANativeWindow* window) __INTRODUCED_IN(28); -#endif // __ANDROID_API__ >= __ANDROID_API_P__ +#endif // __ANDROID_API__ >= 28 #ifdef __cplusplus };
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index 197f73f..61590e0 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h
@@ -202,7 +202,7 @@ * ANativeWindow. */ enum { -// clang-format off + // clang-format off NATIVE_WINDOW_SET_USAGE = 0, /* deprecated */ NATIVE_WINDOW_CONNECT = 1, /* deprecated */ NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */ @@ -237,7 +237,8 @@ NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31, NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32, NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33, -// clang-format on + NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34, + // clang-format on }; /* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */ @@ -748,6 +749,27 @@ } /* + * native_window_set_buffers_hdr10_plus_metadata(..., metadata) + * All buffers queued after this call will be associated with the + * HDR10+ dynamic metadata specified. + * + * metadata specifies additional dynamic information about the + * contents of the buffer that may affect how it is displayed. When + * it is nullptr, it means no such information is available. No + * HDR10+ dynamic emtadata is associated with the buffers by default. + * + * Parameter "size" refers to the length of the metadata blob pointed to + * by parameter "data". The metadata blob will adhere to the HDR10+ SEI + * message standard. + */ +static inline int native_window_set_buffers_hdr10_plus_metadata(struct ANativeWindow* window, + const size_t size, + const uint8_t* metadata) { + return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA, size, + metadata); +} + +/* * native_window_set_buffers_transform(..., int transform) * All buffers queued after this call will be displayed transformed according * to the transform parameter specified.
diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h index 7a4b31f..3392d7f 100644 --- a/libs/nativewindow/include/vndk/hardware_buffer.h +++ b/libs/nativewindow/include/vndk/hardware_buffer.h
@@ -26,6 +26,28 @@ const native_handle_t* AHardwareBuffer_getNativeHandle(const AHardwareBuffer* buffer); +enum CreateFromHandleMethod { + // enum values chosen to match internal GraphicBuffer::HandleWrapMethod + AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_REGISTER = 2, + AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE = 3, +}; + +/** + * Create a AHardwareBuffer from a native handle. + * + * This function wraps a native handle in a AHardwareBuffer suitable for use by applications or + * other parts of the system. The contents of desc will be returned by AHardwareBuffer_describe(). + * + * If method is AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_REGISTER, the handle is assumed to be + * unregistered, and it will be registered/imported before being wrapped in the AHardwareBuffer. + * If successful, the AHardwareBuffer will own the handle. + * + * If method is AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, the handle will be cloned and the + * clone registered. The AHardwareBuffer will own the cloned handle but not the original. + */ +int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc, + const native_handle_t* handle, int32_t method, + AHardwareBuffer** outBuffer); /** * Buffer pixel formats. @@ -51,8 +73,6 @@ AHARDWAREBUFFER_FORMAT_RAW_OPAQUE = 0x24, /* same as HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED */ AHARDWAREBUFFER_FORMAT_IMPLEMENTATION_DEFINED = 0x22, - /* same as HAL_PIXEL_FORMAT_YCBCR_420_888 */ - AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23, /* same as HAL_PIXEL_FORMAT_YCBCR_422_SP */ AHARDWAREBUFFER_FORMAT_YCbCr_422_SP = 0x10, /* same as HAL_PIXEL_FORMAT_YCRCB_420_SP */
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index d2ba971..bad8b11 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt
@@ -2,9 +2,13 @@ global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; + AHardwareBuffer_createFromHandle; # vndk AHardwareBuffer_describe; AHardwareBuffer_getNativeHandle; # vndk + AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; + AHardwareBuffer_lockAndGetInfo; # introduced=29 + AHardwareBuffer_lockPlanes; # introduced=29 AHardwareBuffer_recvHandleFromUnixSocket; AHardwareBuffer_release; AHardwareBuffer_sendHandleToUnixSocket;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp new file mode 100644 index 0000000..36211ca --- /dev/null +++ b/libs/renderengine/Android.bp
@@ -0,0 +1,98 @@ +cc_defaults { + name: "renderengine_defaults", + cflags: [ + "-DLOG_TAG=\"RenderEngine\"", + "-Wall", + "-Werror", + "-Wthread-safety", + "-Wunused", + "-Wunreachable-code", + ], +} + +cc_defaults { + name: "librenderengine_defaults", + defaults: ["renderengine_defaults"], + cflags: [ + "-DGL_GLEXT_PROTOTYPES", + "-DEGL_EGLEXT_PROTOTYPES", + ], + shared_libs: [ + "libbase", + "libcutils", + "libEGL", + "libGLESv1_CM", + "libGLESv2", + "libgui", + "liblog", + "libnativewindow", + "libsync", + "libui", + "libutils", + ], + local_include_dirs: ["include"], + export_include_dirs: ["include"], +} + +filegroup { + name: "librenderengine_sources", + srcs: [ + "Description.cpp", + "Mesh.cpp", + "RenderEngine.cpp", + "Texture.cpp", + ], +} + +filegroup { + name: "librenderengine_gl_sources", + srcs: [ + "gl/GLESRenderEngine.cpp", + "gl/GLExtensions.cpp", + "gl/GLFramebuffer.cpp", + "gl/GLImage.cpp", + "gl/Program.cpp", + "gl/ProgramCache.cpp", + ], +} + +cc_library_static { + name: "librenderengine", + defaults: ["librenderengine_defaults"], + vendor_available: true, + vndk: { + enabled: true, + }, + double_loadable: true, + clang: true, + cflags: [ + "-fvisibility=hidden", + "-Werror=format", + ], + cppflags: [ + "-fwhole-program-vtables", // requires ThinLTO + ], + srcs: [ + ":librenderengine_sources", + ":librenderengine_gl_sources", + ], + lto: { + thin: true, + }, +} + +cc_library_static { + name: "librenderengine_mocks", + defaults: ["librenderengine_defaults"], + srcs: [ + "mock/Framebuffer.cpp", + "mock/Image.cpp", + "mock/RenderEngine.cpp", + ], + static_libs: [ + "libgtest", + "libgmock", + ], + local_include_dirs: ["include"], + export_include_dirs: ["include"], +}
diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp new file mode 100644 index 0000000..b9cea10 --- /dev/null +++ b/libs/renderengine/Description.cpp
@@ -0,0 +1,56 @@ +/* + * Copyright 2013 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 <renderengine/private/Description.h> + +#include <stdint.h> + +#include <utils/TypeHelpers.h> + +namespace android { +namespace renderengine { + +Description::TransferFunction Description::dataSpaceToTransferFunction(ui::Dataspace dataSpace) { + ui::Dataspace transfer = static_cast<ui::Dataspace>(dataSpace & ui::Dataspace::TRANSFER_MASK); + switch (transfer) { + case ui::Dataspace::TRANSFER_ST2084: + return Description::TransferFunction::ST2084; + case ui::Dataspace::TRANSFER_HLG: + return Description::TransferFunction::HLG; + case ui::Dataspace::TRANSFER_LINEAR: + return Description::TransferFunction::LINEAR; + default: + return Description::TransferFunction::SRGB; + } +} + +bool Description::hasInputTransformMatrix() const { + const mat4 identity; + return inputTransformMatrix != identity; +} + +bool Description::hasOutputTransformMatrix() const { + const mat4 identity; + return outputTransformMatrix != identity; +} + +bool Description::hasColorMatrix() const { + const mat4 identity; + return colorMatrix != identity; +} + +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/Mesh.cpp b/libs/renderengine/Mesh.cpp new file mode 100644 index 0000000..f5387f2 --- /dev/null +++ b/libs/renderengine/Mesh.cpp
@@ -0,0 +1,104 @@ +/* + * Copyright 2013 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 <renderengine/Mesh.h> + +#include <utils/Log.h> + +namespace android { +namespace renderengine { + +Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize) + : mVertexCount(vertexCount), + mVertexSize(vertexSize), + mTexCoordsSize(texCoordSize), + mPrimitive(primitive) { + if (vertexCount == 0) { + mVertices.resize(1); + mVertices[0] = 0.0f; + mStride = 0; + return; + } + + const size_t CROP_COORD_SIZE = 2; + size_t stride = vertexSize + texCoordSize + CROP_COORD_SIZE; + size_t remainder = (stride * vertexCount) / vertexCount; + // Since all of the input parameters are unsigned, if stride is less than + // either vertexSize or texCoordSize, it must have overflowed. remainder + // will be equal to stride as long as stride * vertexCount doesn't overflow. + if ((stride < vertexSize) || (remainder != stride)) { + ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize, + CROP_COORD_SIZE); + mVertices.resize(1); + mVertices[0] = 0.0f; + mVertexCount = 0; + mVertexSize = 0; + mTexCoordsSize = 0; + mStride = 0; + return; + } + + mVertices.resize(stride * vertexCount); + mStride = stride; +} + +Mesh::Primitive Mesh::getPrimitive() const { + return mPrimitive; +} + +float const* Mesh::getPositions() const { + return mVertices.data(); +} +float* Mesh::getPositions() { + return mVertices.data(); +} + +float const* Mesh::getTexCoords() const { + return mVertices.data() + mVertexSize; +} +float* Mesh::getTexCoords() { + return mVertices.data() + mVertexSize; +} + +float const* Mesh::getCropCoords() const { + return mVertices.data() + mVertexSize + mTexCoordsSize; +} +float* Mesh::getCropCoords() { + return mVertices.data() + mVertexSize + mTexCoordsSize; +} + +size_t Mesh::getVertexCount() const { + return mVertexCount; +} + +size_t Mesh::getVertexSize() const { + return mVertexSize; +} + +size_t Mesh::getTexCoordsSize() const { + return mTexCoordsSize; +} + +size_t Mesh::getByteStride() const { + return mStride * sizeof(float); +} + +size_t Mesh::getStride() const { + return mStride; +} + +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS new file mode 100644 index 0000000..c00fbba --- /dev/null +++ b/libs/renderengine/OWNERS
@@ -0,0 +1,2 @@ +lpy@google.com +stoza@google.com
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp new file mode 100644 index 0000000..166c267 --- /dev/null +++ b/libs/renderengine/RenderEngine.cpp
@@ -0,0 +1,57 @@ +/* + * Copyright 2013 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 <renderengine/RenderEngine.h> + +#include <cutils/properties.h> +#include <log/log.h> +#include <private/gui/SyncFeatures.h> +#include "gl/GLESRenderEngine.h" + +namespace android { +namespace renderengine { + +std::unique_ptr<impl::RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags, + uint32_t imageCacheSize) { + char prop[PROPERTY_VALUE_MAX]; + property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles"); + if (strcmp(prop, "gles") == 0) { + ALOGD("RenderEngine GLES Backend"); + return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize); + } + ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop); + return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize); +} + +RenderEngine::~RenderEngine() = default; + +namespace impl { + +RenderEngine::RenderEngine(uint32_t featureFlags) : mFeatureFlags(featureFlags) {} + +RenderEngine::~RenderEngine() = default; + +bool RenderEngine::useNativeFenceSync() const { + return SyncFeatures::getInstance().useNativeFenceSync(); +} + +bool RenderEngine::useWaitSync() const { + return SyncFeatures::getInstance().useWaitSync(); +} + +} // namespace impl +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/TEST_MAPPING b/libs/renderengine/TEST_MAPPING new file mode 100644 index 0000000..995dba1 --- /dev/null +++ b/libs/renderengine/TEST_MAPPING
@@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "librenderengine_test" + } + ] +}
diff --git a/libs/renderengine/Texture.cpp b/libs/renderengine/Texture.cpp new file mode 100644 index 0000000..154cde8 --- /dev/null +++ b/libs/renderengine/Texture.cpp
@@ -0,0 +1,77 @@ +/* + * Copyright 2013 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 <renderengine/Texture.h> + +namespace android { +namespace renderengine { + +Texture::Texture() + : mTextureName(0), mTextureTarget(TEXTURE_2D), mWidth(0), mHeight(0), mFiltering(false) {} + +Texture::Texture(Target textureTarget, uint32_t textureName) + : mTextureName(textureName), + mTextureTarget(textureTarget), + mWidth(0), + mHeight(0), + mFiltering(false) {} + +void Texture::init(Target textureTarget, uint32_t textureName) { + mTextureName = textureName; + mTextureTarget = textureTarget; +} + +Texture::~Texture() {} + +void Texture::setMatrix(float const* matrix) { + mTextureMatrix = mat4(matrix); +} + +void Texture::setFiltering(bool enabled) { + mFiltering = enabled; +} + +void Texture::setDimensions(size_t width, size_t height) { + mWidth = width; + mHeight = height; +} + +uint32_t Texture::getTextureName() const { + return mTextureName; +} + +uint32_t Texture::getTextureTarget() const { + return mTextureTarget; +} + +const mat4& Texture::getMatrix() const { + return mTextureMatrix; +} + +bool Texture::getFiltering() const { + return mFiltering; +} + +size_t Texture::getWidth() const { + return mWidth; +} + +size_t Texture::getHeight() const { + return mHeight; +} + +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp new file mode 100644 index 0000000..46a8e9e --- /dev/null +++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -0,0 +1,1499 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "GLESRenderEngine.h" + +#include <math.h> +#include <fstream> +#include <sstream> +#include <unordered_set> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <android-base/stringprintf.h> +#include <cutils/compiler.h> +#include <cutils/properties.h> +#include <renderengine/Mesh.h> +#include <renderengine/Texture.h> +#include <renderengine/private/Description.h> +#include <sync/sync.h> +#include <ui/ColorSpace.h> +#include <ui/DebugUtils.h> +#include <ui/GraphicBuffer.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <utils/KeyedVector.h> +#include <utils/Trace.h> +#include "GLExtensions.h" +#include "GLFramebuffer.h" +#include "GLImage.h" +#include "Program.h" +#include "ProgramCache.h" + +extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); + +bool checkGlError(const char* op, int lineNumber) { + bool errorFound = false; + GLint error = glGetError(); + while (error != GL_NO_ERROR) { + errorFound = true; + error = glGetError(); + ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error); + } + return errorFound; +} + +static constexpr bool outputDebugPPMs = false; + +void writePPM(const char* basename, GLuint width, GLuint height) { + ALOGV("writePPM #%s: %d x %d", basename, width, height); + + std::vector<GLubyte> pixels(width * height * 4); + std::vector<GLubyte> outBuffer(width * height * 3); + + // TODO(courtneygo): We can now have float formats, need + // to remove this code or update to support. + // Make returned pixels fit in uint32_t, one byte per component + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); + if (checkGlError(__FUNCTION__, __LINE__)) { + return; + } + + std::string filename(basename); + filename.append(".ppm"); + std::ofstream file(filename.c_str(), std::ios::binary); + if (!file.is_open()) { + ALOGE("Unable to open file: %s", filename.c_str()); + ALOGE("You may need to do: \"adb shell setenforce 0\" to enable " + "surfaceflinger to write debug images"); + return; + } + + file << "P6\n"; + file << width << "\n"; + file << height << "\n"; + file << 255 << "\n"; + + auto ptr = reinterpret_cast<char*>(pixels.data()); + auto outPtr = reinterpret_cast<char*>(outBuffer.data()); + for (int y = height - 1; y >= 0; y--) { + char* data = ptr + y * width * sizeof(uint32_t); + + for (GLuint x = 0; x < width; x++) { + // Only copy R, G and B components + outPtr[0] = data[0]; + outPtr[1] = data[1]; + outPtr[2] = data[2]; + data += sizeof(uint32_t); + outPtr += 3; + } + } + file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size()); +} + +namespace android { +namespace renderengine { +namespace gl { + +using base::StringAppendF; +using ui::Dataspace; + +static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute, + EGLint wanted, EGLConfig* outConfig) { + EGLint numConfigs = -1, n = 0; + eglGetConfigs(dpy, nullptr, 0, &numConfigs); + std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR); + eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n); + configs.resize(n); + + if (!configs.empty()) { + if (attribute != EGL_NONE) { + for (EGLConfig config : configs) { + EGLint value = 0; + eglGetConfigAttrib(dpy, config, attribute, &value); + if (wanted == value) { + *outConfig = config; + return NO_ERROR; + } + } + } else { + // just pick the first one + *outConfig = configs[0]; + return NO_ERROR; + } + } + + return NAME_NOT_FOUND; +} + +class EGLAttributeVector { + struct Attribute; + class Adder; + friend class Adder; + KeyedVector<Attribute, EGLint> mList; + struct Attribute { + Attribute() : v(0){}; + explicit Attribute(EGLint v) : v(v) {} + EGLint v; + bool operator<(const Attribute& other) const { + // this places EGL_NONE at the end + EGLint lhs(v); + EGLint rhs(other.v); + if (lhs == EGL_NONE) lhs = 0x7FFFFFFF; + if (rhs == EGL_NONE) rhs = 0x7FFFFFFF; + return lhs < rhs; + } + }; + class Adder { + friend class EGLAttributeVector; + EGLAttributeVector& v; + EGLint attribute; + Adder(EGLAttributeVector& v, EGLint attribute) : v(v), attribute(attribute) {} + + public: + void operator=(EGLint value) { + if (attribute != EGL_NONE) { + v.mList.add(Attribute(attribute), value); + } + } + operator EGLint() const { return v.mList[attribute]; } + }; + +public: + EGLAttributeVector() { mList.add(Attribute(EGL_NONE), EGL_NONE); } + void remove(EGLint attribute) { + if (attribute != EGL_NONE) { + mList.removeItem(Attribute(attribute)); + } + } + Adder operator[](EGLint attribute) { return Adder(*this, attribute); } + EGLint operator[](EGLint attribute) const { return mList[attribute]; } + // cast-operator to (EGLint const*) + operator EGLint const*() const { return &mList.keyAt(0).v; } +}; + +static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType, + EGLConfig* config) { + // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if + // it is to be used with WIFI displays + status_t err; + EGLint wantedAttribute; + EGLint wantedAttributeValue; + + EGLAttributeVector attribs; + if (renderableType) { + attribs[EGL_RENDERABLE_TYPE] = renderableType; + attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE; + attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT; + attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE; + attribs[EGL_RED_SIZE] = 8; + attribs[EGL_GREEN_SIZE] = 8; + attribs[EGL_BLUE_SIZE] = 8; + attribs[EGL_ALPHA_SIZE] = 8; + wantedAttribute = EGL_NONE; + wantedAttributeValue = EGL_NONE; + } else { + // if no renderable type specified, fallback to a simplified query + wantedAttribute = EGL_NATIVE_VISUAL_ID; + wantedAttributeValue = format; + } + + err = selectConfigForAttribute(display, attribs, wantedAttribute, wantedAttributeValue, config); + if (err == NO_ERROR) { + EGLint caveat; + if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat)) + ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!"); + } + + return err; +} + +std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(int hwcFormat, uint32_t featureFlags, + uint32_t imageCacheSize) { + // initialize EGL for the default display + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!eglInitialize(display, nullptr, nullptr)) { + LOG_ALWAYS_FATAL("failed to initialize EGL"); + } + + GLExtensions& extensions = GLExtensions::getInstance(); + extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION), + eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS)); + + // The code assumes that ES2 or later is available if this extension is + // supported. + EGLConfig config = EGL_NO_CONFIG; + if (!extensions.hasNoConfigContext()) { + config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); + } + + bool useContextPriority = extensions.hasContextPriority() && + (featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT); + EGLContext protectedContext = EGL_NO_CONTEXT; + if ((featureFlags & RenderEngine::ENABLE_PROTECTED_CONTEXT) && + extensions.hasProtectedContent()) { + protectedContext = createEglContext(display, config, nullptr, useContextPriority, + Protection::PROTECTED); + ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context"); + } + + EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority, + Protection::UNPROTECTED); + + // if can't create a GL context, we can only abort. + LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed"); + + EGLSurface dummy = EGL_NO_SURFACE; + if (!extensions.hasSurfacelessContext()) { + dummy = createDummyEglPbufferSurface(display, config, hwcFormat, Protection::UNPROTECTED); + LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer"); + } + EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt); + LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current"); + extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER), + glGetString(GL_VERSION), glGetString(GL_EXTENSIONS)); + + EGLSurface protectedDummy = EGL_NO_SURFACE; + if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) { + protectedDummy = + createDummyEglPbufferSurface(display, config, hwcFormat, Protection::PROTECTED); + ALOGE_IF(protectedDummy == EGL_NO_SURFACE, "can't create protected dummy pbuffer"); + } + + // now figure out what version of GL did we actually get + GlesVersion version = parseGlesVersion(extensions.getVersion()); + + // initialize the renderer while GL is current + std::unique_ptr<GLESRenderEngine> engine; + switch (version) { + case GLES_VERSION_1_0: + case GLES_VERSION_1_1: + LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run."); + break; + case GLES_VERSION_2_0: + case GLES_VERSION_3_0: + engine = std::make_unique<GLESRenderEngine>(featureFlags, display, config, ctxt, dummy, + protectedContext, protectedDummy, + imageCacheSize); + break; + } + + ALOGI("OpenGL ES informations:"); + ALOGI("vendor : %s", extensions.getVendor()); + ALOGI("renderer : %s", extensions.getRenderer()); + ALOGI("version : %s", extensions.getVersion()); + ALOGI("extensions: %s", extensions.getExtensions()); + ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize()); + ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims()); + + return engine; +} + +EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) { + status_t err; + EGLConfig config; + + // First try to get an ES3 config + err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config); + if (err != NO_ERROR) { + // If ES3 fails, try to get an ES2 config + err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config); + if (err != NO_ERROR) { + // If ES2 still doesn't work, probably because we're on the emulator. + // try a simplified query + ALOGW("no suitable EGLConfig found, trying a simpler query"); + err = selectEGLConfig(display, format, 0, &config); + if (err != NO_ERROR) { + // this EGL is too lame for android + LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); + } + } + } + + if (logConfig) { + // print some debugging info + EGLint r, g, b, a; + eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); + eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); + ALOGI("EGL information:"); + ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); + ALOGI("version : %s", eglQueryString(display, EGL_VERSION)); + ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS)); + ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported"); + ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); + } + + return config; +} + +GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EGLConfig config, + EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext, + EGLSurface protectedDummy, uint32_t imageCacheSize) + : renderengine::impl::RenderEngine(featureFlags), + mEGLDisplay(display), + mEGLConfig(config), + mEGLContext(ctxt), + mDummySurface(dummy), + mProtectedEGLContext(protectedContext), + mProtectedDummySurface(protectedDummy), + mVpWidth(0), + mVpHeight(0), + mFramebufferImageCacheSize(imageCacheSize), + mUseColorManagement(featureFlags & USE_COLOR_MANAGEMENT) { + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + + // Initialize protected EGL Context. + if (mProtectedEGLContext != EGL_NO_CONTEXT) { + EGLBoolean success = eglMakeCurrent(display, mProtectedDummySurface, mProtectedDummySurface, + mProtectedEGLContext); + ALOGE_IF(!success, "can't make protected context current"); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + success = eglMakeCurrent(display, mDummySurface, mDummySurface, mEGLContext); + LOG_ALWAYS_FATAL_IF(!success, "can't make default context current"); + } + + const uint16_t protTexData[] = {0}; + glGenTextures(1, &mProtectedTexName); + glBindTexture(GL_TEXTURE_2D, mProtectedTexName); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData); + + // mColorBlindnessCorrection = M; + + if (mUseColorManagement) { + const ColorSpace srgb(ColorSpace::sRGB()); + const ColorSpace displayP3(ColorSpace::DisplayP3()); + const ColorSpace bt2020(ColorSpace::BT2020()); + + // no chromatic adaptation needed since all color spaces use D65 for their white points. + mSrgbToXyz = mat4(srgb.getRGBtoXYZ()); + mDisplayP3ToXyz = mat4(displayP3.getRGBtoXYZ()); + mBt2020ToXyz = mat4(bt2020.getRGBtoXYZ()); + mXyzToSrgb = mat4(srgb.getXYZtoRGB()); + mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB()); + mXyzToBt2020 = mat4(bt2020.getXYZtoRGB()); + + // Compute sRGB to Display P3 and BT2020 transform matrix. + // NOTE: For now, we are limiting output wide color space support to + // Display-P3 and BT2020 only. + mSrgbToDisplayP3 = mXyzToDisplayP3 * mSrgbToXyz; + mSrgbToBt2020 = mXyzToBt2020 * mSrgbToXyz; + + // Compute Display P3 to sRGB and BT2020 transform matrix. + mDisplayP3ToSrgb = mXyzToSrgb * mDisplayP3ToXyz; + mDisplayP3ToBt2020 = mXyzToBt2020 * mDisplayP3ToXyz; + + // Compute BT2020 to sRGB and Display P3 transform matrix + mBt2020ToSrgb = mXyzToSrgb * mBt2020ToXyz; + mBt2020ToDisplayP3 = mXyzToDisplayP3 * mBt2020ToXyz; + } + + char value[PROPERTY_VALUE_MAX]; + property_get("debug.egl.traceGpuCompletion", value, "0"); + if (atoi(value)) { + mTraceGpuCompletion = true; + mFlushTracer = std::make_unique<FlushTracer>(this); + } + mDrawingBuffer = createFramebuffer(); +} + +GLESRenderEngine::~GLESRenderEngine() { + std::lock_guard<std::mutex> lock(mRenderingMutex); + unbindFrameBuffer(mDrawingBuffer.get()); + mDrawingBuffer = nullptr; + while (!mFramebufferImageCache.empty()) { + EGLImageKHR expired = mFramebufferImageCache.front().second; + mFramebufferImageCache.pop_front(); + eglDestroyImageKHR(mEGLDisplay, expired); + } + mImageCache.clear(); + eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(mEGLDisplay); +} + +std::unique_ptr<Framebuffer> GLESRenderEngine::createFramebuffer() { + return std::make_unique<GLFramebuffer>(*this); +} + +std::unique_ptr<Image> GLESRenderEngine::createImage() { + return std::make_unique<GLImage>(*this); +} + +Framebuffer* GLESRenderEngine::getFramebufferForDrawing() { + return mDrawingBuffer.get(); +} + +void GLESRenderEngine::primeCache() const { + ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext, + mFeatureFlags & USE_COLOR_MANAGEMENT); +} + +bool GLESRenderEngine::isCurrent() const { + return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext(); +} + +base::unique_fd GLESRenderEngine::flush() { + ATRACE_CALL(); + if (!GLExtensions::getInstance().hasNativeFenceSync()) { + return base::unique_fd(); + } + + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGW("failed to create EGL native fence sync: %#x", eglGetError()); + return base::unique_fd(); + } + + // native fence fd will not be populated until flush() is done. + glFlush(); + + // get the fence fd + base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync)); + eglDestroySyncKHR(mEGLDisplay, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ALOGW("failed to dup EGL native fence sync: %#x", eglGetError()); + } + + // Only trace if we have a valid fence, as current usage falls back to + // calling finish() if the fence fd is invalid. + if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer) && fenceFd.get() >= 0) { + mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr)); + } + + return fenceFd; +} + +bool GLESRenderEngine::finish() { + ATRACE_CALL(); + if (!GLExtensions::getInstance().hasFenceSync()) { + ALOGW("no synchronization support"); + return false; + } + + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGW("failed to create EGL fence sync: %#x", eglGetError()); + return false; + } + + if (CC_UNLIKELY(mTraceGpuCompletion && mFlushTracer)) { + mFlushTracer->queueSync(eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr)); + } + + return waitSync(sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR); +} + +bool GLESRenderEngine::waitSync(EGLSyncKHR sync, EGLint flags) { + EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, flags, 2000000000 /*2 sec*/); + EGLint error = eglGetError(); + eglDestroySyncKHR(mEGLDisplay, sync); + if (result != EGL_CONDITION_SATISFIED_KHR) { + if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGW("fence wait timed out"); + } else { + ALOGW("error waiting on EGL fence: %#x", error); + } + return false; + } + + return true; +} + +bool GLESRenderEngine::waitFence(base::unique_fd fenceFd) { + if (!GLExtensions::getInstance().hasNativeFenceSync() || + !GLExtensions::getInstance().hasWaitSync()) { + return false; + } + + // release the fd and transfer the ownership to EGLSync + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE}; + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + ALOGE("failed to create EGL native fence sync: %#x", eglGetError()); + return false; + } + + // XXX: The spec draft is inconsistent as to whether this should return an + // EGLint or void. Ignore the return value for now, as it's not strictly + // needed. + eglWaitSyncKHR(mEGLDisplay, sync, 0); + EGLint error = eglGetError(); + eglDestroySyncKHR(mEGLDisplay, sync); + if (error != EGL_SUCCESS) { + ALOGE("failed to wait for EGL native fence sync: %#x", error); + return false; + } + + return true; +} + +void GLESRenderEngine::clearWithColor(float red, float green, float blue, float alpha) { + ATRACE_CALL(); + glDisable(GL_BLEND); + glClearColor(red, green, blue, alpha); + glClear(GL_COLOR_BUFFER_BIT); +} + +void GLESRenderEngine::fillRegionWithColor(const Region& region, float red, float green, float blue, + float alpha) { + size_t c; + Rect const* r = region.getArray(&c); + Mesh mesh(Mesh::TRIANGLES, c * 6, 2); + Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + for (size_t i = 0; i < c; i++, r++) { + position[i * 6 + 0].x = r->left; + position[i * 6 + 0].y = r->top; + position[i * 6 + 1].x = r->left; + position[i * 6 + 1].y = r->bottom; + position[i * 6 + 2].x = r->right; + position[i * 6 + 2].y = r->bottom; + position[i * 6 + 3].x = r->left; + position[i * 6 + 3].y = r->top; + position[i * 6 + 4].x = r->right; + position[i * 6 + 4].y = r->bottom; + position[i * 6 + 5].x = r->right; + position[i * 6 + 5].y = r->top; + } + setupFillWithColor(red, green, blue, alpha); + drawMesh(mesh); +} + +void GLESRenderEngine::setScissor(const Rect& region) { + glScissor(region.left, region.top, region.getWidth(), region.getHeight()); + glEnable(GL_SCISSOR_TEST); +} + +void GLESRenderEngine::disableScissor() { + glDisable(GL_SCISSOR_TEST); +} + +void GLESRenderEngine::genTextures(size_t count, uint32_t* names) { + glGenTextures(count, names); +} + +void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) { + glDeleteTextures(count, names); +} + +void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) { + ATRACE_CALL(); + const GLImage& glImage = static_cast<const GLImage&>(image); + const GLenum target = GL_TEXTURE_EXTERNAL_OES; + + glBindTexture(target, texName); + if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) { + glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage())); + } +} + +status_t GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { + std::lock_guard<std::mutex> lock(mRenderingMutex); + return cacheExternalTextureBufferLocked(buffer); +} + +status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, + const sp<GraphicBuffer>& buffer, + const sp<Fence>& bufferFence) { + std::lock_guard<std::mutex> lock(mRenderingMutex); + return bindExternalTextureBufferLocked(texName, buffer, bufferFence); +} + +status_t GLESRenderEngine::cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer) { + if (buffer == nullptr) { + return BAD_VALUE; + } + + ATRACE_CALL(); + + if (mImageCache.count(buffer->getId()) > 0) { + return NO_ERROR; + } + + std::unique_ptr<Image> newImage = createImage(); + + 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()); + return NO_INIT; + } + mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage))); + + return NO_ERROR; +} + +status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName, + const sp<GraphicBuffer>& buffer, + const sp<Fence>& bufferFence) { + ATRACE_CALL(); + status_t cacheResult = cacheExternalTextureBufferLocked(buffer); + + if (cacheResult != NO_ERROR) { + return cacheResult; + } + + auto cachedImage = mImageCache.find(buffer->getId()); + + if (cachedImage == mImageCache.end()) { + // We failed creating the image if we got here, so bail out. + bindExternalTextureImage(texName, *createImage()); + return NO_INIT; + } + + bindExternalTextureImage(texName, *cachedImage->second); + + // Wait for the new buffer to be ready. + if (bufferFence != nullptr && bufferFence->isValid()) { + if (GLExtensions::getInstance().hasWaitSync()) { + base::unique_fd fenceFd(bufferFence->dup()); + if (fenceFd == -1) { + ALOGE("error dup'ing fence fd: %d", errno); + return -errno; + } + if (!waitFence(std::move(fenceFd))) { + ALOGE("failed to wait on fence fd"); + return UNKNOWN_ERROR; + } + } else { + status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer"); + if (err != NO_ERROR) { + ALOGE("error waiting for fence: %d", err); + return err; + } + } + } + + return NO_ERROR; +} + +void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) { + std::lock_guard<std::mutex> lock(mRenderingMutex); + const auto& cachedImage = mImageCache.find(bufferId); + if (cachedImage != mImageCache.end()) { + ALOGV("Destroying image for buffer: %" PRIu64, bufferId); + mImageCache.erase(bufferId); + return; + } + ALOGV("Failed to find image for buffer: %" PRIu64, bufferId); +} + +FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) { + // Translate win by the rounded corners rect coordinates, to have all values in + // layer coordinate space. + FloatRect cropWin = layer.geometry.boundaries; + const FloatRect& roundedCornersCrop = layer.geometry.roundedCornersCrop; + cropWin.left -= roundedCornersCrop.left; + cropWin.right -= roundedCornersCrop.left; + cropWin.top -= roundedCornersCrop.top; + cropWin.bottom -= roundedCornersCrop.top; + Mesh::VertexArray<vec2> cropCoords(mesh.getCropCoordArray<vec2>()); + cropCoords[0] = vec2(cropWin.left, cropWin.top); + cropCoords[1] = vec2(cropWin.left, cropWin.top + cropWin.getHeight()); + cropCoords[2] = vec2(cropWin.right, cropWin.top + cropWin.getHeight()); + cropCoords[3] = vec2(cropWin.right, cropWin.top); + + setupCornerRadiusCropSize(roundedCornersCrop.getWidth(), roundedCornersCrop.getHeight()); + return cropWin; +} + +void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display, + const LayerSettings& layer, const Mesh& mesh) { + // We separate the layer into 3 parts essentially, such that we only turn on blending for the + // top rectangle and the bottom rectangle, and turn off blending for the middle rectangle. + FloatRect bounds = layer.geometry.roundedCornersCrop; + + // Firstly, we need to convert the coordination from layer native coordination space to + // device coordination space. + const auto transformMatrix = display.globalTransform * layer.geometry.positionTransform; + const vec4 leftTopCoordinate(bounds.left, bounds.top, 1.0, 1.0); + const vec4 rightBottomCoordinate(bounds.right, bounds.bottom, 1.0, 1.0); + const vec4 leftTopCoordinateInBuffer = transformMatrix * leftTopCoordinate; + const vec4 rightBottomCoordinateInBuffer = transformMatrix * rightBottomCoordinate; + bounds = FloatRect(leftTopCoordinateInBuffer[0], leftTopCoordinateInBuffer[1], + rightBottomCoordinateInBuffer[0], rightBottomCoordinateInBuffer[1]); + + // Secondly, if the display is rotated, we need to undo the rotation on coordination and + // align the (left, top) and (right, bottom) coordination with the device coordination + // space. + switch (display.orientation) { + case ui::Transform::ROT_90: + std::swap(bounds.left, bounds.right); + break; + case ui::Transform::ROT_180: + std::swap(bounds.left, bounds.right); + std::swap(bounds.top, bounds.bottom); + break; + case ui::Transform::ROT_270: + std::swap(bounds.top, bounds.bottom); + break; + default: + break; + } + + // Finally, we cut the layer into 3 parts, with top and bottom parts having rounded corners + // and the middle part without rounded corners. + const int32_t radius = ceil(layer.geometry.roundedCornersRadius); + const Rect topRect(bounds.left, bounds.top, bounds.right, bounds.top + radius); + setScissor(topRect); + drawMesh(mesh); + const Rect bottomRect(bounds.left, bounds.bottom - radius, bounds.right, bounds.bottom); + setScissor(bottomRect); + drawMesh(mesh); + + // The middle part of the layer can turn off blending. + const Rect middleRect(bounds.left, bounds.top + radius, bounds.right, bounds.bottom - radius); + setScissor(middleRect); + mState.cornerRadius = 0.0; + disableBlending(); + drawMesh(mesh); + disableScissor(); +} + +status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) { + ATRACE_CALL(); + GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer); + EGLImageKHR eglImage = glFramebuffer->getEGLImage(); + uint32_t textureName = glFramebuffer->getTextureName(); + uint32_t framebufferName = glFramebuffer->getFramebufferName(); + + // Bind the texture and turn our EGLImage into a texture + glBindTexture(GL_TEXTURE_2D, textureName); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage); + + // Bind the Framebuffer to render into + glBindFramebuffer(GL_FRAMEBUFFER, framebufferName); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0); + + uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d", + glStatus); + + return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE; +} + +void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) { + ATRACE_CALL(); + + // back to main framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void GLESRenderEngine::checkErrors() const { + do { + // there could be more than one error flag + GLenum error = glGetError(); + if (error == GL_NO_ERROR) break; + ALOGE("GL error 0x%04x", int(error)); + } while (true); +} + +bool GLESRenderEngine::supportsProtectedContent() const { + return mProtectedEGLContext != EGL_NO_CONTEXT; +} + +bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) { + if (useProtectedContext == mInProtectedContext) { + return true; + } + if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) { + return false; + } + const EGLSurface surface = useProtectedContext ? mProtectedDummySurface : mDummySurface; + const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext; + const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE; + if (success) { + mInProtectedContext = useProtectedContext; + } + return success; +} +EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, + bool isProtected, + bool useFramebufferCache) { + sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer); + if (useFramebufferCache) { + for (const auto& image : mFramebufferImageCache) { + if (image.first == graphicBuffer->getId()) { + return image.second; + } + } + } + EGLint attributes[] = { + isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, + isProtected ? EGL_TRUE : EGL_NONE, + EGL_NONE, + }; + EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + nativeBuffer, attributes); + if (useFramebufferCache) { + if (image != EGL_NO_IMAGE_KHR) { + if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) { + EGLImageKHR expired = mFramebufferImageCache.front().second; + mFramebufferImageCache.pop_front(); + eglDestroyImageKHR(mEGLDisplay, expired); + } + mFramebufferImageCache.push_back({graphicBuffer->getId(), image}); + } + } + return image; +} + +status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, + const std::vector<LayerSettings>& layers, + ANativeWindowBuffer* const buffer, + const bool useFramebufferCache, base::unique_fd&& bufferFence, + base::unique_fd* drawFence) { + ATRACE_CALL(); + if (layers.empty()) { + ALOGV("Drawing empty layer stack"); + return NO_ERROR; + } + + if (bufferFence.get() >= 0 && !waitFence(std::move(bufferFence))) { + ATRACE_NAME("Waiting before draw"); + sync_wait(bufferFence.get(), -1); + } + + if (buffer == nullptr) { + ALOGE("No output buffer provided. Aborting GPU composition."); + return BAD_VALUE; + } + + { + std::lock_guard<std::mutex> lock(mRenderingMutex); + + BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache); + + if (fbo.getStatus() != NO_ERROR) { + ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", + buffer->handle); + checkErrors(); + return fbo.getStatus(); + } + + // clear the entire buffer, sometimes when we reuse buffers we'd persist + // ghost images otherwise. + // we also require a full transparent framebuffer for overlays. This is + // probably not quite efficient on all GPUs, since we could filter out + // opaque layers. + clearWithColor(0.0, 0.0, 0.0, 0.0); + + setViewportAndProjection(display.physicalDisplay, display.clip); + + setOutputDataSpace(display.outputDataspace); + setDisplayMaxLuminance(display.maxLuminance); + + mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform; + mState.projectionMatrix = projectionMatrix; + if (!display.clearRegion.isEmpty()) { + glDisable(GL_BLEND); + fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0); + } + + Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2); + for (auto layer : layers) { + mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform; + + const FloatRect bounds = layer.geometry.boundaries; + Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + position[0] = vec2(bounds.left, bounds.top); + position[1] = vec2(bounds.left, bounds.bottom); + position[2] = vec2(bounds.right, bounds.bottom); + position[3] = vec2(bounds.right, bounds.top); + + setupLayerCropping(layer, mesh); + setColorTransform(display.colorTransform * layer.colorTransform); + + bool usePremultipliedAlpha = true; + bool disableTexture = true; + bool isOpaque = false; + + if (layer.source.buffer.buffer != nullptr) { + disableTexture = false; + isOpaque = layer.source.buffer.isOpaque; + + sp<GraphicBuffer> gBuf = layer.source.buffer.buffer; + bindExternalTextureBufferLocked(layer.source.buffer.textureName, gBuf, + layer.source.buffer.fence); + + usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha; + Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName); + mat4 texMatrix = layer.source.buffer.textureTransform; + + texture.setMatrix(texMatrix.asArray()); + texture.setFiltering(layer.source.buffer.useTextureFiltering); + + texture.setDimensions(gBuf->getWidth(), gBuf->getHeight()); + setSourceY410BT2020(layer.source.buffer.isY410BT2020); + + renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>()); + texCoords[0] = vec2(0.0, 0.0); + texCoords[1] = vec2(0.0, 1.0); + texCoords[2] = vec2(1.0, 1.0); + texCoords[3] = vec2(1.0, 0.0); + setupLayerTexturing(texture); + } + + const half3 solidColor = layer.source.solidColor; + const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha); + // Buffer sources will have a black solid color ignored in the shader, + // so in that scenario the solid color passed here is arbitrary. + setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, + layer.geometry.roundedCornersRadius); + if (layer.disableBlending) { + glDisable(GL_BLEND); + } + setSourceDataSpace(layer.sourceDataspace); + + // We only want to do a special handling for rounded corners when having rounded corners + // is the only reason it needs to turn on blending, otherwise, we handle it like the + // usual way since it needs to turn on blending anyway. + if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) { + handleRoundedCorners(display, layer, mesh); + } else { + drawMesh(mesh); + } + + // Cleanup if there's a buffer source + if (layer.source.buffer.buffer != nullptr) { + disableBlending(); + setSourceY410BT2020(false); + disableTexturing(); + } + } + + if (drawFence != nullptr) { + *drawFence = flush(); + } + // If flush failed or we don't support native fences, we need to force the + // gl command stream to be executed. + if (drawFence == nullptr || drawFence->get() < 0) { + bool success = finish(); + if (!success) { + ALOGE("Failed to flush RenderEngine commands"); + checkErrors(); + // Chances are, something illegal happened (either the caller passed + // us bad parameters, or we messed up our shader generation). + return INVALID_OPERATION; + } + } + + checkErrors(); + } + return NO_ERROR; +} + +void GLESRenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, + ui::Transform::orientation_flags rotation) { + setViewportAndProjection(Rect(vpw, vph), sourceCrop); + + if (rotation == ui::Transform::ROT_0) { + return; + } + + // Apply custom rotation to the projection. + float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f; + mat4 m = mState.projectionMatrix; + switch (rotation) { + case ui::Transform::ROT_90: + m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m; + break; + case ui::Transform::ROT_180: + m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m; + break; + case ui::Transform::ROT_270: + m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m; + break; + default: + break; + } + mState.projectionMatrix = m; +} + +void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) { + ATRACE_CALL(); + mVpWidth = viewport.getWidth(); + mVpHeight = viewport.getHeight(); + + // We pass the the top left corner instead of the bottom left corner, + // because since we're rendering off-screen first. + glViewport(viewport.left, viewport.top, mVpWidth, mVpHeight); + + mState.projectionMatrix = mat4::ortho(clip.left, clip.right, clip.top, clip.bottom, 0, 1); +} + +void GLESRenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, + const half4& color, float cornerRadius) { + mState.isPremultipliedAlpha = premultipliedAlpha; + mState.isOpaque = opaque; + mState.color = color; + mState.cornerRadius = cornerRadius; + + if (disableTexture) { + mState.textureEnabled = false; + } + + if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) { + glEnable(GL_BLEND); + glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } +} + +void GLESRenderEngine::setSourceY410BT2020(bool enable) { + mState.isY410BT2020 = enable; +} + +void GLESRenderEngine::setSourceDataSpace(Dataspace source) { + mDataSpace = source; +} + +void GLESRenderEngine::setOutputDataSpace(Dataspace dataspace) { + mOutputDataSpace = dataspace; +} + +void GLESRenderEngine::setDisplayMaxLuminance(const float maxLuminance) { + mState.displayMaxLuminance = maxLuminance; +} + +void GLESRenderEngine::setupLayerTexturing(const Texture& texture) { + GLuint target = texture.getTextureTarget(); + glBindTexture(target, texture.getTextureName()); + GLenum filter = GL_NEAREST; + if (texture.getFiltering()) { + filter = GL_LINEAR; + } + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); + + mState.texture = texture; + mState.textureEnabled = true; +} + +void GLESRenderEngine::setupLayerBlackedOut() { + glBindTexture(GL_TEXTURE_2D, mProtectedTexName); + Texture texture(Texture::TEXTURE_2D, mProtectedTexName); + texture.setDimensions(1, 1); // FIXME: we should get that from somewhere + mState.texture = texture; + mState.textureEnabled = true; +} + +void GLESRenderEngine::setColorTransform(const mat4& colorTransform) { + mState.colorMatrix = colorTransform; +} + +void GLESRenderEngine::disableTexturing() { + mState.textureEnabled = false; +} + +void GLESRenderEngine::disableBlending() { + glDisable(GL_BLEND); +} + +void GLESRenderEngine::setupFillWithColor(float r, float g, float b, float a) { + mState.isPremultipliedAlpha = true; + mState.isOpaque = false; + mState.color = half4(r, g, b, a); + mState.textureEnabled = false; + glDisable(GL_BLEND); +} + +void GLESRenderEngine::setupCornerRadiusCropSize(float width, float height) { + mState.cropSize = half2(width, height); +} + +void GLESRenderEngine::drawMesh(const Mesh& mesh) { + ATRACE_CALL(); + if (mesh.getTexCoordsSize()) { + glEnableVertexAttribArray(Program::texCoords); + glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE, + mesh.getByteStride(), mesh.getTexCoords()); + } + + glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE, + mesh.getByteStride(), mesh.getPositions()); + + if (mState.cornerRadius > 0.0f) { + glEnableVertexAttribArray(Program::cropCoords); + glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE, + mesh.getByteStride(), mesh.getCropCoords()); + } + + // By default, DISPLAY_P3 is the only supported wide color output. However, + // when HDR content is present, hardware composer may be able to handle + // BT2020 data space, in that case, the output data space is set to be + // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need + // to respect this and convert non-HDR content to HDR format. + if (mUseColorManagement) { + Description managedState = mState; + Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK); + Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK); + Dataspace outputStandard = + static_cast<Dataspace>(mOutputDataSpace & Dataspace::STANDARD_MASK); + Dataspace outputTransfer = + static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK); + bool needsXYZConversion = needsXYZTransformMatrix(); + + // NOTE: if the input standard of the input dataspace is not STANDARD_DCI_P3 or + // STANDARD_BT2020, it will be treated as STANDARD_BT709 + if (inputStandard != Dataspace::STANDARD_DCI_P3 && + inputStandard != Dataspace::STANDARD_BT2020) { + inputStandard = Dataspace::STANDARD_BT709; + } + + if (needsXYZConversion) { + // The supported input color spaces are standard RGB, Display P3 and BT2020. + switch (inputStandard) { + case Dataspace::STANDARD_DCI_P3: + managedState.inputTransformMatrix = mDisplayP3ToXyz; + break; + case Dataspace::STANDARD_BT2020: + managedState.inputTransformMatrix = mBt2020ToXyz; + break; + default: + managedState.inputTransformMatrix = mSrgbToXyz; + break; + } + + // The supported output color spaces are BT2020, Display P3 and standard RGB. + switch (outputStandard) { + case Dataspace::STANDARD_BT2020: + managedState.outputTransformMatrix = mXyzToBt2020; + break; + case Dataspace::STANDARD_DCI_P3: + managedState.outputTransformMatrix = mXyzToDisplayP3; + break; + default: + managedState.outputTransformMatrix = mXyzToSrgb; + break; + } + } else if (inputStandard != outputStandard) { + // At this point, the input data space and output data space could be both + // HDR data spaces, but they match each other, we do nothing in this case. + // In addition to the case above, the input data space could be + // - scRGB linear + // - scRGB non-linear + // - sRGB + // - Display P3 + // - BT2020 + // The output data spaces could be + // - sRGB + // - Display P3 + // - BT2020 + switch (outputStandard) { + case Dataspace::STANDARD_BT2020: + if (inputStandard == Dataspace::STANDARD_BT709) { + managedState.outputTransformMatrix = mSrgbToBt2020; + } else if (inputStandard == Dataspace::STANDARD_DCI_P3) { + managedState.outputTransformMatrix = mDisplayP3ToBt2020; + } + break; + case Dataspace::STANDARD_DCI_P3: + if (inputStandard == Dataspace::STANDARD_BT709) { + managedState.outputTransformMatrix = mSrgbToDisplayP3; + } else if (inputStandard == Dataspace::STANDARD_BT2020) { + managedState.outputTransformMatrix = mBt2020ToDisplayP3; + } + break; + default: + if (inputStandard == Dataspace::STANDARD_DCI_P3) { + managedState.outputTransformMatrix = mDisplayP3ToSrgb; + } else if (inputStandard == Dataspace::STANDARD_BT2020) { + managedState.outputTransformMatrix = mBt2020ToSrgb; + } + break; + } + } + + // we need to convert the RGB value to linear space and convert it back when: + // - there is a color matrix that is not an identity matrix, or + // - there is an output transform matrix that is not an identity matrix, or + // - the input transfer function doesn't match the output transfer function. + if (managedState.hasColorMatrix() || managedState.hasOutputTransformMatrix() || + inputTransfer != outputTransfer) { + managedState.inputTransferFunction = + Description::dataSpaceToTransferFunction(inputTransfer); + managedState.outputTransferFunction = + Description::dataSpaceToTransferFunction(outputTransfer); + } + + ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext + : mEGLContext, + managedState); + + glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); + + if (outputDebugPPMs) { + static uint64_t managedColorFrameCount = 0; + std::ostringstream out; + out << "/data/texture_out" << managedColorFrameCount++; + writePPM(out.str().c_str(), mVpWidth, mVpHeight); + } + } else { + ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext + : mEGLContext, + mState); + + glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); + } + + if (mesh.getTexCoordsSize()) { + glDisableVertexAttribArray(Program::texCoords); + } + + if (mState.cornerRadius > 0.0f) { + glDisableVertexAttribArray(Program::cropCoords); + } +} + +size_t GLESRenderEngine::getMaxTextureSize() const { + return mMaxTextureSize; +} + +size_t GLESRenderEngine::getMaxViewportDims() const { + return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1]; +} + +void GLESRenderEngine::dump(std::string& result) { + const GLExtensions& extensions = GLExtensions::getInstance(); + ProgramCache& cache = ProgramCache::getInstance(); + + StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion()); + StringAppendF(&result, "%s\n", extensions.getEGLExtensions()); + StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(), + extensions.getVersion()); + StringAppendF(&result, "%s\n", extensions.getExtensions()); + StringAppendF(&result, "RenderEngine supports protected context: %d\n", + supportsProtectedContent()); + StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext); + StringAppendF(&result, "RenderEngine program cache size for unprotected context: %zu\n", + cache.getSize(mEGLContext)); + StringAppendF(&result, "RenderEngine program cache size for protected context: %zu\n", + cache.getSize(mProtectedEGLContext)); + StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n", + dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(), + dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str()); +} + +GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) { + int major, minor; + if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) { + if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) { + ALOGW("Unable to parse GL_VERSION string: \"%s\"", str); + return GLES_VERSION_1_0; + } + } + + if (major == 1 && minor == 0) return GLES_VERSION_1_0; + if (major == 1 && minor >= 1) return GLES_VERSION_1_1; + if (major == 2 && minor >= 0) return GLES_VERSION_2_0; + if (major == 3 && minor >= 0) return GLES_VERSION_3_0; + + ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor); + return GLES_VERSION_1_0; +} + +EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config, + EGLContext shareContext, bool useContextPriority, + Protection protection) { + EGLint renderableType = 0; + if (config == EGL_NO_CONFIG) { + renderableType = EGL_OPENGL_ES3_BIT; + } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) { + LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE"); + } + EGLint contextClientVersion = 0; + if (renderableType & EGL_OPENGL_ES3_BIT) { + contextClientVersion = 3; + } else if (renderableType & EGL_OPENGL_ES2_BIT) { + contextClientVersion = 2; + } else if (renderableType & EGL_OPENGL_ES_BIT) { + contextClientVersion = 1; + } else { + LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs"); + } + + std::vector<EGLint> contextAttributes; + contextAttributes.reserve(7); + contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION); + contextAttributes.push_back(contextClientVersion); + if (useContextPriority) { + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); + } + if (protection == Protection::PROTECTED) { + contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT); + contextAttributes.push_back(EGL_TRUE); + } + contextAttributes.push_back(EGL_NONE); + + EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data()); + + if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) { + // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus + // EGL_NO_CONTEXT so that we can abort. + if (config != EGL_NO_CONFIG) { + return context; + } + // If |config| is EGL_NO_CONFIG, we speculatively try to create GLES 3 context, so we should + // try to fall back to GLES 2. + contextAttributes[1] = 2; + context = eglCreateContext(display, config, shareContext, contextAttributes.data()); + } + + return context; +} + +EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config, + int hwcFormat, Protection protection) { + EGLConfig dummyConfig = config; + if (dummyConfig == EGL_NO_CONFIG) { + dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); + } + std::vector<EGLint> attributes; + attributes.reserve(7); + attributes.push_back(EGL_WIDTH); + attributes.push_back(1); + attributes.push_back(EGL_HEIGHT); + attributes.push_back(1); + if (protection == Protection::PROTECTED) { + attributes.push_back(EGL_PROTECTED_CONTENT_EXT); + attributes.push_back(EGL_TRUE); + } + attributes.push_back(EGL_NONE); + + return eglCreatePbufferSurface(display, dummyConfig, attributes.data()); +} + +bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const { + const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK); + const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK); + return standard == Dataspace::STANDARD_BT2020 && + (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG); +} + +// For convenience, we want to convert the input color space to XYZ color space first, +// and then convert from XYZ color space to output color space when +// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or +// HDR content will be tone-mapped to SDR; Or, +// - there are HDR PQ and HLG contents presented at the same time, where we want to convert +// HLG content to PQ content. +// In either case above, we need to operate the Y value in XYZ color space. Thus, when either +// input data space or output data space is HDR data space, and the input transfer function +// doesn't match the output transfer function, we would enable an intermediate transfrom to +// XYZ color space. +bool GLESRenderEngine::needsXYZTransformMatrix() const { + const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace); + const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace); + const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK); + const Dataspace outputTransfer = + static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK); + + return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer; +} + +bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) { + std::lock_guard<std::mutex> lock(mRenderingMutex); + const auto& cachedImage = mImageCache.find(bufferId); + return cachedImage != mImageCache.end(); +} + +bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) { + std::lock_guard<std::mutex> lock(mRenderingMutex); + return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(), + [=](std::pair<uint64_t, EGLImageKHR> image) { + return image.first == bufferId; + }); +} + +// FlushTracer implementation +GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) { + mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this); +} + +GLESRenderEngine::FlushTracer::~FlushTracer() { + { + std::lock_guard<std::mutex> lock(mMutex); + mRunning = false; + } + mCondition.notify_all(); + if (mThread.joinable()) { + mThread.join(); + } +} + +void GLESRenderEngine::FlushTracer::queueSync(EGLSyncKHR sync) { + std::lock_guard<std::mutex> lock(mMutex); + char name[64]; + const uint64_t frameNum = mFramesQueued++; + snprintf(name, sizeof(name), "Queueing sync for frame: %lu", + static_cast<unsigned long>(frameNum)); + ATRACE_NAME(name); + mQueue.push({sync, frameNum}); + ATRACE_INT("GPU Frames Outstanding", mQueue.size()); + mCondition.notify_one(); +} + +void GLESRenderEngine::FlushTracer::loop() { + while (mRunning) { + QueueEntry entry; + { + std::lock_guard<std::mutex> lock(mMutex); + + mCondition.wait(mMutex, + [&]() REQUIRES(mMutex) { return !mQueue.empty() || !mRunning; }); + + if (!mRunning) { + // if mRunning is false, then FlushTracer is being destroyed, so + // bail out now. + break; + } + entry = mQueue.front(); + mQueue.pop(); + } + { + char name[64]; + snprintf(name, sizeof(name), "waiting for frame %lu", + static_cast<unsigned long>(entry.mFrameNum)); + ATRACE_NAME(name); + mEngine->waitSync(entry.mSync, 0); + } + } +} + +} // namespace gl +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h new file mode 100644 index 0000000..de793c2 --- /dev/null +++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -0,0 +1,263 @@ +/* + * Copyright 2013 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 SF_GLESRENDERENGINE_H_ +#define SF_GLESRENDERENGINE_H_ + +#include <android-base/thread_annotations.h> +#include <stdint.h> +#include <sys/types.h> +#include <condition_variable> +#include <deque> +#include <mutex> +#include <queue> +#include <thread> +#include <unordered_map> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <renderengine/RenderEngine.h> +#include <renderengine/private/Description.h> + +#define EGL_NO_CONFIG ((EGLConfig)0) + +namespace android { + +namespace renderengine { + +class Mesh; +class Texture; + +namespace gl { + +class GLImage; + +class GLESRenderEngine : public impl::RenderEngine { +public: + static std::unique_ptr<GLESRenderEngine> create(int hwcFormat, uint32_t featureFlags, + uint32_t imageCacheSize); + static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); + + GLESRenderEngine(uint32_t featureFlags, // See RenderEngine::FeatureFlag + EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy, + EGLContext protectedContext, EGLSurface protectedDummy, + uint32_t imageCacheSize); + ~GLESRenderEngine() override EXCLUDES(mRenderingMutex); + + std::unique_ptr<Framebuffer> createFramebuffer() override; + std::unique_ptr<Image> createImage() override; + + void primeCache() const override; + bool isCurrent() const override; + base::unique_fd flush() override; + bool finish() override; + bool waitFence(base::unique_fd fenceFd) override; + void clearWithColor(float red, float green, float blue, float alpha) override; + void fillRegionWithColor(const Region& region, float red, float green, float blue, + float alpha) override; + void genTextures(size_t count, uint32_t* names) override; + void deleteTextures(size_t count, uint32_t const* names) override; + void bindExternalTextureImage(uint32_t texName, const Image& image) override; + status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, + const sp<Fence>& fence) EXCLUDES(mRenderingMutex); + status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex); + void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex); + status_t bindFrameBuffer(Framebuffer* framebuffer) override; + void unbindFrameBuffer(Framebuffer* framebuffer) override; + void checkErrors() const override; + + bool isProtected() const override { return mInProtectedContext; } + bool supportsProtectedContent() const override; + bool useProtectedContext(bool useProtectedContext) override; + status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers, + ANativeWindowBuffer* buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence, base::unique_fd* drawFence) + EXCLUDES(mRenderingMutex) override; + + // internal to RenderEngine + EGLDisplay getEGLDisplay() const { return mEGLDisplay; } + EGLConfig getEGLConfig() const { return mEGLConfig; } + // Creates an output image for rendering to + EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected, + bool useFramebufferCache); + + // Test-only methods + // Returns true iff mImageCache contains an image keyed by bufferId + bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex); + // Returns true iff mFramebufferImageCache contains an image keyed by bufferId + bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex); + +protected: + Framebuffer* getFramebufferForDrawing() override; + void dump(std::string& result) override; + void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, + ui::Transform::orientation_flags rotation) override; + void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, + const half4& color, float cornerRadius) override; + void setupLayerTexturing(const Texture& texture) override; + void setupLayerBlackedOut() override; + void setupFillWithColor(float r, float g, float b, float a) override; + void setColorTransform(const mat4& colorTransform) override; + void disableTexturing() override; + void disableBlending() override; + void setupCornerRadiusCropSize(float width, float height) override; + + // HDR and color management related functions and state + void setSourceY410BT2020(bool enable) override; + void setSourceDataSpace(ui::Dataspace source) override; + void setOutputDataSpace(ui::Dataspace dataspace) override; + void setDisplayMaxLuminance(const float maxLuminance) override; + + // drawing + void drawMesh(const Mesh& mesh) override; + + size_t getMaxTextureSize() const override; + size_t getMaxViewportDims() const override; + +private: + enum GlesVersion { + GLES_VERSION_1_0 = 0x10000, + GLES_VERSION_1_1 = 0x10001, + GLES_VERSION_2_0 = 0x20000, + GLES_VERSION_3_0 = 0x30000, + }; + + static GlesVersion parseGlesVersion(const char* str); + static EGLContext createEglContext(EGLDisplay display, EGLConfig config, + EGLContext shareContext, bool useContextPriority, + Protection protection); + static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config, + int hwcFormat, Protection protection); + void setScissor(const Rect& region); + void disableScissor(); + bool waitSync(EGLSyncKHR sync, EGLint flags); + + // A data space is considered HDR data space if it has BT2020 color space + // with PQ or HLG transfer function. + bool isHdrDataSpace(const ui::Dataspace dataSpace) const; + bool needsXYZTransformMatrix() const; + // Defines the viewport, and sets the projection matrix to the projection + // defined by the clip. + void setViewportAndProjection(Rect viewport, Rect clip); + // Evicts stale images from the buffer cache. + void evictImages(const std::vector<LayerSettings>& layers); + // Computes the cropping window for the layer and sets up cropping + // coordinates for the mesh. + FloatRect setupLayerCropping(const LayerSettings& layer, Mesh& mesh); + + // We do a special handling for rounded corners when it's possible to turn off blending + // for the majority of the layer. The rounded corners needs to turn on blending such that + // we can set the alpha value correctly, however, only the corners need this, and since + // blending is an expensive operation, we want to turn off blending when it's not necessary. + void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer, + const Mesh& mesh); + + EGLDisplay mEGLDisplay; + EGLConfig mEGLConfig; + EGLContext mEGLContext; + EGLSurface mDummySurface; + EGLContext mProtectedEGLContext; + EGLSurface mProtectedDummySurface; + GLuint mProtectedTexName; + GLint mMaxViewportDims[2]; + GLint mMaxTextureSize; + GLuint mVpWidth; + GLuint mVpHeight; + Description mState; + + mat4 mSrgbToXyz; + mat4 mDisplayP3ToXyz; + mat4 mBt2020ToXyz; + mat4 mXyzToSrgb; + mat4 mXyzToDisplayP3; + mat4 mXyzToBt2020; + mat4 mSrgbToDisplayP3; + mat4 mSrgbToBt2020; + mat4 mDisplayP3ToSrgb; + mat4 mDisplayP3ToBt2020; + mat4 mBt2020ToSrgb; + mat4 mBt2020ToDisplayP3; + + bool mInProtectedContext = false; + // If set to true, then enables tracing flush() and finish() to systrace. + bool mTraceGpuCompletion = false; + // Maximum size of mFramebufferImageCache. If more images would be cached, then (approximately) + // the last recently used buffer should be kicked out. + uint32_t mFramebufferImageCacheSize = 0; + + // Cache of output images, keyed by corresponding GraphicBuffer ID. + std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache; + + // Current dataspace of layer being rendered + ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN; + + // Current output dataspace of the render engine + ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN; + + // Whether device supports color management, currently color management + // supports sRGB, DisplayP3 color spaces. + const bool mUseColorManagement = false; + + // Cache of GL images that we'll store per GraphicBuffer ID + std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex); + // Mutex guarding rendering operations, so that: + // 1. GL operations aren't interleaved, and + // 2. Internal state related to rendering that is potentially modified by + // multiple threads is guaranteed thread-safe. + std::mutex mRenderingMutex; + + // See bindExternalTextureBuffer above, but requiring that mRenderingMutex + // is held. + status_t bindExternalTextureBufferLocked(uint32_t texName, const sp<GraphicBuffer>& buffer, + const sp<Fence>& fence) REQUIRES(mRenderingMutex); + // See cacheExternalTextureBuffer above, but requiring that mRenderingMutex + // is held. + status_t cacheExternalTextureBufferLocked(const sp<GraphicBuffer>& buffer) + REQUIRES(mRenderingMutex); + + std::unique_ptr<Framebuffer> mDrawingBuffer; + + class FlushTracer { + public: + FlushTracer(GLESRenderEngine* engine); + ~FlushTracer(); + void queueSync(EGLSyncKHR sync) EXCLUDES(mMutex); + + struct QueueEntry { + EGLSyncKHR mSync = nullptr; + uint64_t mFrameNum = 0; + }; + + private: + void loop(); + GLESRenderEngine* const mEngine; + std::thread mThread; + std::condition_variable_any mCondition; + std::mutex mMutex; + std::queue<QueueEntry> mQueue GUARDED_BY(mMutex); + uint64_t mFramesQueued GUARDED_BY(mMutex) = 0; + bool mRunning = true; + }; + friend class FlushTracer; + std::unique_ptr<FlushTracer> mFlushTracer; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android + +#endif /* SF_GLESRENDERENGINE_H_ */
diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp new file mode 100644 index 0000000..2924b0e --- /dev/null +++ b/libs/renderengine/gl/GLExtensions.cpp
@@ -0,0 +1,135 @@ +/* + * Copyright (C) 2010 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 "GLExtensions.h" + +#include <string> +#include <unordered_set> + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::GLExtensions) + +namespace android { +namespace renderengine { +namespace gl { + +namespace { + +class ExtensionSet { +public: + ExtensionSet(const char* extensions) { + char const* curr = extensions; + char const* head = curr; + do { + head = strchr(curr, ' '); + size_t len = head ? head - curr : strlen(curr); + if (len > 0) { + mExtensions.emplace(curr, len); + } + curr = head + 1; + } while (head); + } + + bool hasExtension(const char* extension) const { return mExtensions.count(extension) > 0; } + +private: + std::unordered_set<std::string> mExtensions; +}; + +} // anonymous namespace + +void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, + GLubyte const* version, GLubyte const* extensions) { + mVendor = (char const*)vendor; + mRenderer = (char const*)renderer; + mVersion = (char const*)version; + mExtensions = (char const*)extensions; + + ExtensionSet extensionSet(mExtensions.c_str()); + if (extensionSet.hasExtension("GL_EXT_protected_textures")) { + mHasProtectedTexture = true; + } +} + +char const* GLExtensions::getVendor() const { + return mVendor.string(); +} + +char const* GLExtensions::getRenderer() const { + return mRenderer.string(); +} + +char const* GLExtensions::getVersion() const { + return mVersion.string(); +} + +char const* GLExtensions::getExtensions() const { + return mExtensions.string(); +} + +void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) { + mEGLVersion = eglVersion; + mEGLExtensions = eglExtensions; + + ExtensionSet extensionSet(eglExtensions); + + // EGL_ANDROIDX_no_config_context is an experimental extension with no + // written specification. It will be replaced by something more formal. + // SurfaceFlinger is using it to allow a single EGLContext to render to + // both a 16-bit primary display framebuffer and a 32-bit virtual display + // framebuffer. + // + // EGL_KHR_no_config_context is official extension to allow creating a + // context that works with any surface of a display. + if (extensionSet.hasExtension("EGL_ANDROIDX_no_config_context") || + extensionSet.hasExtension("EGL_KHR_no_config_context")) { + mHasNoConfigContext = true; + } + + if (extensionSet.hasExtension("EGL_ANDROID_native_fence_sync")) { + mHasNativeFenceSync = true; + } + if (extensionSet.hasExtension("EGL_KHR_fence_sync")) { + mHasFenceSync = true; + } + if (extensionSet.hasExtension("EGL_KHR_wait_sync")) { + mHasWaitSync = true; + } + if (extensionSet.hasExtension("EGL_EXT_protected_content")) { + mHasProtectedContent = true; + } + if (extensionSet.hasExtension("EGL_IMG_context_priority")) { + mHasContextPriority = true; + } + if (extensionSet.hasExtension("EGL_KHR_surfaceless_context")) { + mHasSurfacelessContext = true; + } +} + +char const* GLExtensions::getEGLVersion() const { + return mEGLVersion.string(); +} + +char const* GLExtensions::getEGLExtensions() const { + return mEGLExtensions.string(); +} + +} // namespace gl +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h new file mode 100644 index 0000000..ef00009 --- /dev/null +++ b/libs/renderengine/gl/GLExtensions.h
@@ -0,0 +1,86 @@ +/* + * Copyright (C) 2010 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_SF_GLEXTENSION_H +#define ANDROID_SF_GLEXTENSION_H + +#include <stdint.h> +#include <sys/types.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <utils/Singleton.h> +#include <utils/String8.h> + +namespace android { +namespace renderengine { +namespace gl { + +class GLExtensions : public Singleton<GLExtensions> { +public: + bool hasNoConfigContext() const { return mHasNoConfigContext; } + bool hasNativeFenceSync() const { return mHasNativeFenceSync; } + bool hasFenceSync() const { return mHasFenceSync; } + bool hasWaitSync() const { return mHasWaitSync; } + bool hasProtectedContent() const { return mHasProtectedContent; } + bool hasContextPriority() const { return mHasContextPriority; } + bool hasSurfacelessContext() const { return mHasSurfacelessContext; } + bool hasProtectedTexture() const { return mHasProtectedTexture; } + + void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version, + GLubyte const* extensions); + char const* getVendor() const; + char const* getRenderer() const; + char const* getVersion() const; + char const* getExtensions() const; + + void initWithEGLStrings(char const* eglVersion, char const* eglExtensions); + char const* getEGLVersion() const; + char const* getEGLExtensions() const; + +protected: + GLExtensions() = default; + +private: + friend class Singleton<GLExtensions>; + + bool mHasNoConfigContext = false; + bool mHasNativeFenceSync = false; + bool mHasFenceSync = false; + bool mHasWaitSync = false; + bool mHasProtectedContent = false; + bool mHasContextPriority = false; + bool mHasSurfacelessContext = false; + bool mHasProtectedTexture = false; + + String8 mVendor; + String8 mRenderer; + String8 mVersion; + String8 mExtensions; + String8 mEGLVersion; + String8 mEGLExtensions; + + GLExtensions(const GLExtensions&); + GLExtensions& operator=(const GLExtensions&); +}; + +} // namespace gl +} // namespace renderengine +} // namespace android + +#endif // ANDROID_SF_GLEXTENSION_H
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp new file mode 100644 index 0000000..dacf8d3 --- /dev/null +++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -0,0 +1,71 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "GLFramebuffer.h" + +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <nativebase/nativebase.h> +#include <utils/Trace.h> +#include "GLESRenderEngine.h" + +namespace android { +namespace renderengine { +namespace gl { + +GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine) + : mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) { + glGenTextures(1, &mTextureName); + glGenFramebuffers(1, &mFramebufferName); +} + +GLFramebuffer::~GLFramebuffer() { + glDeleteFramebuffers(1, &mFramebufferName); + glDeleteTextures(1, &mTextureName); +} + +bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected, + const bool useFramebufferCache) { + ATRACE_CALL(); + if (mEGLImage != EGL_NO_IMAGE_KHR) { + if (!usingFramebufferCache) { + eglDestroyImageKHR(mEGLDisplay, mEGLImage); + } + mEGLImage = EGL_NO_IMAGE_KHR; + mBufferWidth = 0; + mBufferHeight = 0; + } + + if (nativeBuffer) { + mEGLImage = mEngine.createFramebufferImageIfNeeded(nativeBuffer, isProtected, + useFramebufferCache); + if (mEGLImage == EGL_NO_IMAGE_KHR) { + return false; + } + usingFramebufferCache = useFramebufferCache; + mBufferWidth = nativeBuffer->width; + mBufferHeight = nativeBuffer->height; + } + return true; +} + +} // namespace gl +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h new file mode 100644 index 0000000..b7650bb --- /dev/null +++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -0,0 +1,59 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <renderengine/Framebuffer.h> + +struct ANativeWindowBuffer; + +namespace android { +namespace renderengine { +namespace gl { + +class GLESRenderEngine; + +class GLFramebuffer : public renderengine::Framebuffer { +public: + explicit GLFramebuffer(GLESRenderEngine& engine); + ~GLFramebuffer() override; + + bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected, + const bool useFramebufferCache) override; + EGLImageKHR getEGLImage() const { return mEGLImage; } + uint32_t getTextureName() const { return mTextureName; } + uint32_t getFramebufferName() const { return mFramebufferName; } + int32_t getBufferHeight() const { return mBufferHeight; } + int32_t getBufferWidth() const { return mBufferWidth; } + +private: + GLESRenderEngine& mEngine; + EGLDisplay mEGLDisplay; + EGLImageKHR mEGLImage; + bool usingFramebufferCache = false; + uint32_t mTextureName, mFramebufferName; + + int32_t mBufferHeight = 0; + int32_t mBufferWidth = 0; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp new file mode 100644 index 0000000..77e648e --- /dev/null +++ b/libs/renderengine/gl/GLImage.cpp
@@ -0,0 +1,80 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "GLImage.h" + +#include <vector> + +#include <log/log.h> +#include <utils/Trace.h> +#include "GLESRenderEngine.h" +#include "GLExtensions.h" + +namespace android { +namespace renderengine { +namespace gl { + +static std::vector<EGLint> buildAttributeList(bool isProtected) { + std::vector<EGLint> attrs; + attrs.reserve(16); + + attrs.push_back(EGL_IMAGE_PRESERVED_KHR); + attrs.push_back(EGL_TRUE); + + if (isProtected && GLExtensions::getInstance().hasProtectedContent()) { + attrs.push_back(EGL_PROTECTED_CONTENT_EXT); + attrs.push_back(EGL_TRUE); + } + + attrs.push_back(EGL_NONE); + + return attrs; +} + +GLImage::GLImage(const GLESRenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {} + +GLImage::~GLImage() { + setNativeWindowBuffer(nullptr, false); +} + +bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) { + ATRACE_CALL(); + if (mEGLImage != EGL_NO_IMAGE_KHR) { + if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) { + ALOGE("failed to destroy image: %#x", eglGetError()); + } + mEGLImage = EGL_NO_IMAGE_KHR; + } + + if (buffer) { + std::vector<EGLint> attrs = buildAttributeList(isProtected); + mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + static_cast<EGLClientBuffer>(buffer), attrs.data()); + if (mEGLImage == EGL_NO_IMAGE_KHR) { + ALOGE("failed to create EGLImage: %#x", eglGetError()); + return false; + } + mProtected = isProtected; + } + + return true; +} + +} // namespace gl +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/gl/GLImage.h b/libs/renderengine/gl/GLImage.h new file mode 100644 index 0000000..59d6ce3 --- /dev/null +++ b/libs/renderengine/gl/GLImage.h
@@ -0,0 +1,54 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <android-base/macros.h> +#include <renderengine/Image.h> + +struct ANativeWindowBuffer; + +namespace android { +namespace renderengine { +namespace gl { + +class GLESRenderEngine; + +class GLImage : public renderengine::Image { +public: + explicit GLImage(const GLESRenderEngine& engine); + ~GLImage() override; + + bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override; + + EGLImageKHR getEGLImage() const { return mEGLImage; } + bool isProtected() const { return mProtected; } + +private: + EGLDisplay mEGLDisplay; + EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR; + bool mProtected = false; + + DISALLOW_COPY_AND_ASSIGN(GLImage); +}; + +} // namespace gl +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp new file mode 100644 index 0000000..fe9d909 --- /dev/null +++ b/libs/renderengine/gl/Program.cpp
@@ -0,0 +1,153 @@ +/*Gluint + * Copyright 2013 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 "Program.h" + +#include <stdint.h> + +#include <log/log.h> +#include <math/mat4.h> +#include <utils/String8.h> +#include "ProgramCache.h" + +namespace android { +namespace renderengine { +namespace gl { + +Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const char* fragment) + : mInitialized(false) { + GLuint vertexId = buildShader(vertex, GL_VERTEX_SHADER); + GLuint fragmentId = buildShader(fragment, GL_FRAGMENT_SHADER); + GLuint programId = glCreateProgram(); + glAttachShader(programId, vertexId); + glAttachShader(programId, fragmentId); + glBindAttribLocation(programId, position, "position"); + glBindAttribLocation(programId, texCoords, "texCoords"); + glBindAttribLocation(programId, cropCoords, "cropCoords"); + glLinkProgram(programId); + + GLint status; + glGetProgramiv(programId, GL_LINK_STATUS, &status); + if (status != GL_TRUE) { + ALOGE("Error while linking shaders:"); + GLint infoLen = 0; + glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen > 1) { + GLchar log[infoLen]; + glGetProgramInfoLog(programId, infoLen, 0, &log[0]); + ALOGE("%s", log); + } + glDetachShader(programId, vertexId); + glDetachShader(programId, fragmentId); + glDeleteShader(vertexId); + glDeleteShader(fragmentId); + glDeleteProgram(programId); + } else { + mProgram = programId; + mVertexShader = vertexId; + mFragmentShader = fragmentId; + mInitialized = true; + mProjectionMatrixLoc = glGetUniformLocation(programId, "projection"); + mTextureMatrixLoc = glGetUniformLocation(programId, "texture"); + mSamplerLoc = glGetUniformLocation(programId, "sampler"); + mColorLoc = glGetUniformLocation(programId, "color"); + mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance"); + mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix"); + mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix"); + mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius"); + mCropCenterLoc = glGetUniformLocation(programId, "cropCenter"); + + // set-up the default values for our uniforms + glUseProgram(programId); + glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, mat4().asArray()); + glEnableVertexAttribArray(0); + } +} + +bool Program::isValid() const { + return mInitialized; +} + +void Program::use() { + glUseProgram(mProgram); +} + +GLuint Program::getAttrib(const char* name) const { + // TODO: maybe use a local cache + return glGetAttribLocation(mProgram, name); +} + +GLint Program::getUniform(const char* name) const { + // TODO: maybe use a local cache + return glGetUniformLocation(mProgram, name); +} + +GLuint Program::buildShader(const char* source, GLenum type) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &source, 0); + glCompileShader(shader); + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) { + // Some drivers return wrong values for GL_INFO_LOG_LENGTH + // use a fixed size instead + GLchar log[512]; + glGetShaderInfoLog(shader, sizeof(log), 0, log); + ALOGE("Error while compiling shader: \n%s\n%s", source, log); + glDeleteShader(shader); + return 0; + } + return shader; +} + +void Program::setUniforms(const Description& desc) { + // TODO: we should have a mechanism here to not always reset uniforms that + // didn't change for this program. + + if (mSamplerLoc >= 0) { + glUniform1i(mSamplerLoc, 0); + glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.texture.getMatrix().asArray()); + } + if (mColorLoc >= 0) { + const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a}; + glUniform4fv(mColorLoc, 1, color); + } + if (mInputTransformMatrixLoc >= 0) { + mat4 inputTransformMatrix = desc.inputTransformMatrix; + glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray()); + } + if (mOutputTransformMatrixLoc >= 0) { + // The output transform matrix and color matrix can be combined as one matrix + // that is applied right before applying OETF. + mat4 outputTransformMatrix = desc.colorMatrix * desc.outputTransformMatrix; + glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE, outputTransformMatrix.asArray()); + } + if (mDisplayMaxLuminanceLoc >= 0) { + glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance); + } + if (mCornerRadiusLoc >= 0) { + glUniform1f(mCornerRadiusLoc, desc.cornerRadius); + } + if (mCropCenterLoc >= 0) { + glUniform2f(mCropCenterLoc, desc.cropSize.x / 2.0f, desc.cropSize.y / 2.0f); + } + // these uniforms are always present + glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray()); +} + +} // namespace gl +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h new file mode 100644 index 0000000..bc9cf08 --- /dev/null +++ b/libs/renderengine/gl/Program.h
@@ -0,0 +1,109 @@ +/* + * Copyright 2013 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 SF_RENDER_ENGINE_PROGRAM_H +#define SF_RENDER_ENGINE_PROGRAM_H + +#include <stdint.h> + +#include <GLES2/gl2.h> +#include <renderengine/private/Description.h> +#include "ProgramCache.h" + +namespace android { + +class String8; + +namespace renderengine { +namespace gl { + +/* + * Abstracts a GLSL program comprising a vertex and fragment shader + */ +class Program { +public: + // known locations for position and texture coordinates + enum { + /* position of each vertex for vertex shader */ + position = 0, + + /* UV coordinates for texture mapping */ + texCoords = 1, + + /* Crop coordinates, in pixels */ + cropCoords = 2 + }; + + Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment); + ~Program() = default; + + /* whether this object is usable */ + bool isValid() const; + + /* Binds this program to the GLES context */ + void use(); + + /* Returns the location of the specified attribute */ + GLuint getAttrib(const char* name) const; + + /* Returns the location of the specified uniform */ + GLint getUniform(const char* name) const; + + /* set-up uniforms from the description */ + void setUniforms(const Description& desc); + +private: + GLuint buildShader(const char* source, GLenum type); + + // whether the initialization succeeded + bool mInitialized; + + // Name of the OpenGL program and shaders + GLuint mProgram; + GLuint mVertexShader; + GLuint mFragmentShader; + + /* location of the projection matrix uniform */ + GLint mProjectionMatrixLoc; + + /* location of the texture matrix uniform */ + GLint mTextureMatrixLoc; + + /* location of the sampler uniform */ + GLint mSamplerLoc; + + /* location of the color uniform */ + GLint mColorLoc; + + /* location of display luminance uniform */ + GLint mDisplayMaxLuminanceLoc; + + /* location of transform matrix */ + GLint mInputTransformMatrixLoc; + GLint mOutputTransformMatrixLoc; + + /* location of corner radius uniform */ + GLint mCornerRadiusLoc; + + /* location of surface crop origin uniform, for rounded corner clipping */ + GLint mCropCenterLoc; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android + +#endif /* SF_RENDER_ENGINE_PROGRAM_H */
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp new file mode 100644 index 0000000..d242677 --- /dev/null +++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -0,0 +1,737 @@ +/* + * Copyright 2013 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 ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "ProgramCache.h" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <log/log.h> +#include <renderengine/private/Description.h> +#include <utils/String8.h> +#include <utils/Trace.h> +#include "Program.h" + +ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::ProgramCache) + +namespace android { +namespace renderengine { +namespace gl { + +/* + * A simple formatter class to automatically add the endl and + * manage the indentation. + */ + +class Formatter; +static Formatter& indent(Formatter& f); +static Formatter& dedent(Formatter& f); + +class Formatter { + String8 mString; + int mIndent; + typedef Formatter& (*FormaterManipFunc)(Formatter&); + friend Formatter& indent(Formatter& f); + friend Formatter& dedent(Formatter& f); + +public: + Formatter() : mIndent(0) {} + + String8 getString() const { return mString; } + + friend Formatter& operator<<(Formatter& out, const char* in) { + for (int i = 0; i < out.mIndent; i++) { + out.mString.append(" "); + } + out.mString.append(in); + out.mString.append("\n"); + return out; + } + friend inline Formatter& operator<<(Formatter& out, const String8& in) { + return operator<<(out, in.string()); + } + friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) { + return (*func)(to); + } +}; +Formatter& indent(Formatter& f) { + f.mIndent++; + return f; +} +Formatter& dedent(Formatter& f) { + f.mIndent--; + return f; +} + +void ProgramCache::primeCache(EGLContext context, bool useColorManagement) { + auto& cache = mCaches[context]; + uint32_t shaderCount = 0; + uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK + | Key::ROUNDED_CORNERS_MASK; + // Prime the cache for all combinations of the above masks, + // leaving off the experimental color matrix mask options. + + nsecs_t timeBefore = systemTime(); + for (uint32_t keyVal = 0; keyVal <= keyMask; keyVal++) { + Key shaderKey; + shaderKey.set(keyMask, keyVal); + uint32_t tex = shaderKey.getTextureTarget(); + if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) { + continue; + } + if (cache.count(shaderKey) == 0) { + cache.emplace(shaderKey, generateProgram(shaderKey)); + shaderCount++; + } + } + + // Prime for sRGB->P3 conversion + if (useColorManagement) { + Key shaderKey; + shaderKey.set(Key::BLEND_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::INPUT_TF_MASK | + Key::OUTPUT_TF_MASK, + Key::BLEND_PREMULT | Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::INPUT_TF_SRGB | + Key::OUTPUT_TF_SRGB); + for (int i = 0; i < 16; i++) { + shaderKey.set(Key::OPACITY_MASK, + (i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT); + shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE); + + // Cache rounded corners + shaderKey.set(Key::ROUNDED_CORNERS_MASK, + (i & 4) ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF); + + // Cache texture off option for window transition + shaderKey.set(Key::TEXTURE_MASK, (i & 8) ? Key::TEXTURE_EXT : Key::TEXTURE_OFF); + if (cache.count(shaderKey) == 0) { + cache.emplace(shaderKey, generateProgram(shaderKey)); + shaderCount++; + } + } + } + + nsecs_t timeAfter = systemTime(); + float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6; + ALOGD("shader cache generated - %u shaders in %f ms\n", shaderCount, compileTimeMs); +} + +ProgramCache::Key ProgramCache::computeKey(const Description& description) { + Key needs; + needs.set(Key::TEXTURE_MASK, + !description.textureEnabled + ? Key::TEXTURE_OFF + : description.texture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES + ? Key::TEXTURE_EXT + : description.texture.getTextureTarget() == GL_TEXTURE_2D + ? Key::TEXTURE_2D + : Key::TEXTURE_OFF) + .set(Key::ALPHA_MASK, (description.color.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE) + .set(Key::BLEND_MASK, + description.isPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL) + .set(Key::OPACITY_MASK, + description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT) + .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK, + description.hasInputTransformMatrix() + ? Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF) + .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK, + description.hasOutputTransformMatrix() || description.hasColorMatrix() + ? Key::OUTPUT_TRANSFORM_MATRIX_ON + : Key::OUTPUT_TRANSFORM_MATRIX_OFF) + .set(Key::ROUNDED_CORNERS_MASK, + description.cornerRadius > 0 + ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF); + + needs.set(Key::Y410_BT2020_MASK, + description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF); + + if (needs.hasTransformMatrix() || + (description.inputTransferFunction != description.outputTransferFunction)) { + switch (description.inputTransferFunction) { + case Description::TransferFunction::LINEAR: + default: + needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR); + break; + case Description::TransferFunction::SRGB: + needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_SRGB); + break; + case Description::TransferFunction::ST2084: + needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_ST2084); + break; + case Description::TransferFunction::HLG: + needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_HLG); + break; + } + + switch (description.outputTransferFunction) { + case Description::TransferFunction::LINEAR: + default: + needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR); + break; + case Description::TransferFunction::SRGB: + needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_SRGB); + break; + case Description::TransferFunction::ST2084: + needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_ST2084); + break; + case Description::TransferFunction::HLG: + needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_HLG); + break; + } + } + + return needs; +} + +// Generate EOTF that converts signal values to relative display light, +// both normalized to [0, 1]. +void ProgramCache::generateEOTF(Formatter& fs, const Key& needs) { + switch (needs.getInputTF()) { + case Key::INPUT_TF_SRGB: + fs << R"__SHADER__( + float EOTF_sRGB(float srgb) { + return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4); + } + + vec3 EOTF_sRGB(const vec3 srgb) { + return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b)); + } + + vec3 EOTF(const vec3 srgb) { + return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb)); + } + )__SHADER__"; + break; + case Key::INPUT_TF_ST2084: + fs << R"__SHADER__( + vec3 EOTF(const highp vec3 color) { + const highp float m1 = (2610.0 / 4096.0) / 4.0; + const highp float m2 = (2523.0 / 4096.0) * 128.0; + const highp float c1 = (3424.0 / 4096.0); + const highp float c2 = (2413.0 / 4096.0) * 32.0; + const highp float c3 = (2392.0 / 4096.0) * 32.0; + + highp vec3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / vec3(m2)); + tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp); + return pow(tmp, 1.0 / vec3(m1)); + } + )__SHADER__"; + break; + case Key::INPUT_TF_HLG: + fs << R"__SHADER__( + highp float EOTF_channel(const highp float channel) { + const highp float a = 0.17883277; + const highp float b = 0.28466892; + const highp float c = 0.55991073; + return channel <= 0.5 ? channel * channel / 3.0 : + (exp((channel - c) / a) + b) / 12.0; + } + + vec3 EOTF(const highp vec3 color) { + return vec3(EOTF_channel(color.r), EOTF_channel(color.g), + EOTF_channel(color.b)); + } + )__SHADER__"; + break; + default: + fs << R"__SHADER__( + vec3 EOTF(const vec3 linear) { + return linear; + } + )__SHADER__"; + break; + } +} + +void ProgramCache::generateToneMappingProcess(Formatter& fs, const Key& needs) { + // Convert relative light to absolute light. + switch (needs.getInputTF()) { + case Key::INPUT_TF_ST2084: + fs << R"__SHADER__( + highp vec3 ScaleLuminance(highp vec3 color) { + return color * 10000.0; + } + )__SHADER__"; + break; + case Key::INPUT_TF_HLG: + fs << R"__SHADER__( + highp vec3 ScaleLuminance(highp vec3 color) { + // The formula is: + // alpha * pow(Y, gamma - 1.0) * color + beta; + // where alpha is 1000.0, gamma is 1.2, beta is 0.0. + return color * 1000.0 * pow(color.y, 0.2); + } + )__SHADER__"; + break; + default: + fs << R"__SHADER__( + highp vec3 ScaleLuminance(highp vec3 color) { + return color * displayMaxLuminance; + } + )__SHADER__"; + break; + } + + // Tone map absolute light to display luminance range. + switch (needs.getInputTF()) { + case Key::INPUT_TF_ST2084: + case Key::INPUT_TF_HLG: + switch (needs.getOutputTF()) { + case Key::OUTPUT_TF_HLG: + // Right now when mixed PQ and HLG contents are presented, + // HLG content will always be converted to PQ. However, for + // completeness, we simply clamp the value to [0.0, 1000.0]. + fs << R"__SHADER__( + highp vec3 ToneMap(highp vec3 color) { + return clamp(color, 0.0, 1000.0); + } + )__SHADER__"; + break; + case Key::OUTPUT_TF_ST2084: + fs << R"__SHADER__( + highp vec3 ToneMap(highp vec3 color) { + return color; + } + )__SHADER__"; + break; + default: + fs << R"__SHADER__( + highp vec3 ToneMap(highp vec3 color) { + const float maxMasteringLumi = 1000.0; + const float maxContentLumi = 1000.0; + const float maxInLumi = min(maxMasteringLumi, maxContentLumi); + float maxOutLumi = displayMaxLuminance; + + float nits = color.y; + + // clamp to max input luminance + nits = clamp(nits, 0.0, maxInLumi); + + // scale [0.0, maxInLumi] to [0.0, maxOutLumi] + if (maxInLumi <= maxOutLumi) { + return color * (maxOutLumi / maxInLumi); + } else { + // three control points + const float x0 = 10.0; + const float y0 = 17.0; + float x1 = maxOutLumi * 0.75; + float y1 = x1; + float x2 = x1 + (maxInLumi - x1) / 2.0; + float y2 = y1 + (maxOutLumi - y1) * 0.75; + + // horizontal distances between the last three control points + float h12 = x2 - x1; + float h23 = maxInLumi - x2; + // tangents at the last three control points + float m1 = (y2 - y1) / h12; + float m3 = (maxOutLumi - y2) / h23; + float m2 = (m1 + m3) / 2.0; + + if (nits < x0) { + // scale [0.0, x0] to [0.0, y0] linearly + float slope = y0 / x0; + return color * slope; + } else if (nits < x1) { + // scale [x0, x1] to [y0, y1] linearly + float slope = (y1 - y0) / (x1 - x0); + nits = y0 + (nits - x0) * slope; + } else if (nits < x2) { + // scale [x1, x2] to [y1, y2] using Hermite interp + float t = (nits - x1) / h12; + nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) + + (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t; + } else { + // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp + float t = (nits - x2) / h23; + nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) + + (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t; + } + } + + // color.y is greater than x0 and is thus non-zero + return color * (nits / color.y); + } + )__SHADER__"; + break; + } + break; + default: + // inverse tone map; the output luminance can be up to maxOutLumi. + fs << R"__SHADER__( + highp vec3 ToneMap(highp vec3 color) { + const float maxOutLumi = 3000.0; + + const float x0 = 5.0; + const float y0 = 2.5; + float x1 = displayMaxLuminance * 0.7; + float y1 = maxOutLumi * 0.15; + float x2 = displayMaxLuminance * 0.9; + float y2 = maxOutLumi * 0.45; + float x3 = displayMaxLuminance; + float y3 = maxOutLumi; + + float c1 = y1 / 3.0; + float c2 = y2 / 2.0; + float c3 = y3 / 1.5; + + float nits = color.y; + + float scale; + if (nits <= x0) { + // scale [0.0, x0] to [0.0, y0] linearly + const float slope = y0 / x0; + return color * slope; + } else if (nits <= x1) { + // scale [x0, x1] to [y0, y1] using a curve + float t = (nits - x0) / (x1 - x0); + nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1; + } else if (nits <= x2) { + // scale [x1, x2] to [y1, y2] using a curve + float t = (nits - x1) / (x2 - x1); + nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2; + } else { + // scale [x2, x3] to [y2, y3] using a curve + float t = (nits - x2) / (x3 - x2); + nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3; + } + + // color.y is greater than x0 and is thus non-zero + return color * (nits / color.y); + } + )__SHADER__"; + break; + } + + // convert absolute light to relative light. + switch (needs.getOutputTF()) { + case Key::OUTPUT_TF_ST2084: + fs << R"__SHADER__( + highp vec3 NormalizeLuminance(highp vec3 color) { + return color / 10000.0; + } + )__SHADER__"; + break; + case Key::OUTPUT_TF_HLG: + fs << R"__SHADER__( + highp vec3 NormalizeLuminance(highp vec3 color) { + return color / 1000.0 * pow(color.y / 1000.0, -0.2 / 1.2); + } + )__SHADER__"; + break; + default: + fs << R"__SHADER__( + highp vec3 NormalizeLuminance(highp vec3 color) { + return color / displayMaxLuminance; + } + )__SHADER__"; + break; + } +} + +// Generate OOTF that modifies the relative scence light to relative display light. +void ProgramCache::generateOOTF(Formatter& fs, const ProgramCache::Key& needs) { + if (!needs.needsToneMapping()) { + fs << R"__SHADER__( + highp vec3 OOTF(const highp vec3 color) { + return color; + } + )__SHADER__"; + } else { + generateToneMappingProcess(fs, needs); + fs << R"__SHADER__( + highp vec3 OOTF(const highp vec3 color) { + return NormalizeLuminance(ToneMap(ScaleLuminance(color))); + } + )__SHADER__"; + } +} + +// Generate OETF that converts relative display light to signal values, +// both normalized to [0, 1] +void ProgramCache::generateOETF(Formatter& fs, const Key& needs) { + switch (needs.getOutputTF()) { + case Key::OUTPUT_TF_SRGB: + fs << R"__SHADER__( + float OETF_sRGB(const float linear) { + return linear <= 0.0031308 ? + linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055; + } + + vec3 OETF_sRGB(const vec3 linear) { + return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b)); + } + + vec3 OETF(const vec3 linear) { + return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)); + } + )__SHADER__"; + break; + case Key::OUTPUT_TF_ST2084: + fs << R"__SHADER__( + vec3 OETF(const vec3 linear) { + const highp float m1 = (2610.0 / 4096.0) / 4.0; + const highp float m2 = (2523.0 / 4096.0) * 128.0; + const highp float c1 = (3424.0 / 4096.0); + const highp float c2 = (2413.0 / 4096.0) * 32.0; + const highp float c3 = (2392.0 / 4096.0) * 32.0; + + highp vec3 tmp = pow(linear, vec3(m1)); + tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); + return pow(tmp, vec3(m2)); + } + )__SHADER__"; + break; + case Key::OUTPUT_TF_HLG: + fs << R"__SHADER__( + highp float OETF_channel(const highp float channel) { + const highp float a = 0.17883277; + const highp float b = 0.28466892; + const highp float c = 0.55991073; + return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) : + a * log(12.0 * channel - b) + c; + } + + vec3 OETF(const highp vec3 color) { + return vec3(OETF_channel(color.r), OETF_channel(color.g), + OETF_channel(color.b)); + } + )__SHADER__"; + break; + default: + fs << R"__SHADER__( + vec3 OETF(const vec3 linear) { + return linear; + } + )__SHADER__"; + break; + } +} + +String8 ProgramCache::generateVertexShader(const Key& needs) { + Formatter vs; + if (needs.isTexturing()) { + vs << "attribute vec4 texCoords;" + << "varying vec2 outTexCoords;"; + } + if (needs.hasRoundedCorners()) { + vs << "attribute lowp vec4 cropCoords;"; + vs << "varying lowp vec2 outCropCoords;"; + } + vs << "attribute vec4 position;" + << "uniform mat4 projection;" + << "uniform mat4 texture;" + << "void main(void) {" << indent << "gl_Position = projection * position;"; + if (needs.isTexturing()) { + vs << "outTexCoords = (texture * texCoords).st;"; + } + if (needs.hasRoundedCorners()) { + vs << "outCropCoords = cropCoords.st;"; + } + vs << dedent << "}"; + return vs.getString(); +} + +String8 ProgramCache::generateFragmentShader(const Key& needs) { + Formatter fs; + if (needs.getTextureTarget() == Key::TEXTURE_EXT) { + fs << "#extension GL_OES_EGL_image_external : require"; + } + + // default precision is required-ish in fragment shaders + fs << "precision mediump float;"; + + if (needs.getTextureTarget() == Key::TEXTURE_EXT) { + fs << "uniform samplerExternalOES sampler;" + << "varying vec2 outTexCoords;"; + } else if (needs.getTextureTarget() == Key::TEXTURE_2D) { + fs << "uniform sampler2D sampler;" + << "varying vec2 outTexCoords;"; + } + + if (needs.hasRoundedCorners()) { + // Rounded corners implementation using a signed distance function. + fs << R"__SHADER__( + uniform float cornerRadius; + uniform vec2 cropCenter; + varying vec2 outCropCoords; + + /** + * This function takes the current crop coordinates and calculates an alpha value based + * on the corner radius and distance from the crop center. + */ + float applyCornerRadius(vec2 cropCoords) + { + vec2 position = cropCoords - cropCenter; + // Scale down the dist vector here, as otherwise large corner + // radii can cause floating point issues when computing the norm + vec2 dist = (abs(position) - cropCenter + vec2(cornerRadius)) / 16.0; + // Once we've found the norm, then scale back up. + float plane = length(max(dist, vec2(0.0))) * 16.0; + return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0); + } + )__SHADER__"; + } + + if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) { + fs << "uniform vec4 color;"; + } + + if (needs.isY410BT2020()) { + fs << R"__SHADER__( + vec3 convertY410BT2020(const vec3 color) { + const vec3 offset = vec3(0.0625, 0.5, 0.5); + const mat3 transform = mat3( + vec3(1.1678, 1.1678, 1.1678), + vec3( 0.0, -0.1878, 2.1481), + vec3(1.6836, -0.6523, 0.0)); + // Y is in G, U is in R, and V is in B + return clamp(transform * (color.grb - offset), 0.0, 1.0); + } + )__SHADER__"; + } + + if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) { + // Currently, display maximum luminance is needed when doing tone mapping. + if (needs.needsToneMapping()) { + fs << "uniform float displayMaxLuminance;"; + } + + if (needs.hasInputTransformMatrix()) { + fs << "uniform mat4 inputTransformMatrix;"; + fs << R"__SHADER__( + highp vec3 InputTransform(const highp vec3 color) { + return clamp(vec3(inputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0); + } + )__SHADER__"; + } else { + fs << R"__SHADER__( + highp vec3 InputTransform(const highp vec3 color) { + return color; + } + )__SHADER__"; + } + + // the transformation from a wider colorspace to a narrower one can + // result in >1.0 or <0.0 pixel values + if (needs.hasOutputTransformMatrix()) { + fs << "uniform mat4 outputTransformMatrix;"; + fs << R"__SHADER__( + highp vec3 OutputTransform(const highp vec3 color) { + return clamp(vec3(outputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0); + } + )__SHADER__"; + } else { + fs << R"__SHADER__( + highp vec3 OutputTransform(const highp vec3 color) { + return clamp(color, 0.0, 1.0); + } + )__SHADER__"; + } + + generateEOTF(fs, needs); + generateOOTF(fs, needs); + generateOETF(fs, needs); + } + + fs << "void main(void) {" << indent; + if (needs.isTexturing()) { + fs << "gl_FragColor = texture2D(sampler, outTexCoords);"; + if (needs.isY410BT2020()) { + fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);"; + } + } else { + fs << "gl_FragColor.rgb = color.rgb;"; + fs << "gl_FragColor.a = 1.0;"; + } + if (needs.isOpaque()) { + fs << "gl_FragColor.a = 1.0;"; + } + if (needs.hasAlpha()) { + // modulate the current alpha value with alpha set + if (needs.isPremultiplied()) { + // ... and the color too if we're premultiplied + fs << "gl_FragColor *= color.a;"; + } else { + fs << "gl_FragColor.a *= color.a;"; + } + } + + if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) { + 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)))));"; + 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);"; + } + } + + if (needs.hasRoundedCorners()) { + if (needs.isPremultiplied()) { + fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));"; + } else { + fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);"; + } + } + + fs << dedent << "}"; + return fs.getString(); +} + +std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) { + ATRACE_CALL(); + + // vertex shader + String8 vs = generateVertexShader(needs); + + // fragment shader + String8 fs = generateFragmentShader(needs); + + return std::make_unique<Program>(needs, vs.string(), fs.string()); +} + +void ProgramCache::useProgram(EGLContext context, const Description& description) { + // generate the key for the shader based on the description + Key needs(computeKey(description)); + + // look-up the program in the cache + auto& cache = mCaches[context]; + auto it = cache.find(needs); + if (it == cache.end()) { + // we didn't find our program, so generate one... + nsecs_t time = systemTime(); + it = cache.emplace(needs, generateProgram(needs)).first; + time = systemTime() - time; + + ALOGV(">>> generated new program for context %p: needs=%08X, time=%u ms (%zu programs)", + context, needs.mKey, uint32_t(ns2ms(time)), cache.size()); + } + + // here we have a suitable program for this description + std::unique_ptr<Program>& program = it->second; + if (program->isValid()) { + program->use(); + program->setUniforms(description); + } +} + +} // namespace gl +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h new file mode 100644 index 0000000..400ad74 --- /dev/null +++ b/libs/renderengine/gl/ProgramCache.h
@@ -0,0 +1,221 @@ +/* + * Copyright 2013 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 SF_RENDER_ENGINE_PROGRAMCACHE_H +#define SF_RENDER_ENGINE_PROGRAMCACHE_H + +#include <memory> +#include <unordered_map> + +#include <EGL/egl.h> +#include <GLES2/gl2.h> +#include <renderengine/private/Description.h> +#include <utils/Singleton.h> +#include <utils/TypeHelpers.h> + +namespace android { + +class String8; + +namespace renderengine { + +struct Description; + +namespace gl { + +class Formatter; +class Program; + +/* + * This class generates GLSL programs suitable to handle a given + * Description. It's responsible for figuring out what to + * generate from a Description. + * It also maintains a cache of these Programs. + */ +class ProgramCache : public Singleton<ProgramCache> { +public: + /* + * Key is used to retrieve a Program in the cache. + * A Key is generated from a Description. + */ + class Key { + friend class ProgramCache; + typedef uint32_t key_t; + key_t mKey; + + public: + enum { + BLEND_SHIFT = 0, + BLEND_MASK = 1 << BLEND_SHIFT, + BLEND_PREMULT = 1 << BLEND_SHIFT, + BLEND_NORMAL = 0 << BLEND_SHIFT, + + OPACITY_SHIFT = 1, + OPACITY_MASK = 1 << OPACITY_SHIFT, + OPACITY_OPAQUE = 1 << OPACITY_SHIFT, + OPACITY_TRANSLUCENT = 0 << OPACITY_SHIFT, + + ALPHA_SHIFT = 2, + ALPHA_MASK = 1 << ALPHA_SHIFT, + ALPHA_LT_ONE = 1 << ALPHA_SHIFT, + ALPHA_EQ_ONE = 0 << ALPHA_SHIFT, + + TEXTURE_SHIFT = 3, + TEXTURE_MASK = 3 << TEXTURE_SHIFT, + TEXTURE_OFF = 0 << TEXTURE_SHIFT, + TEXTURE_EXT = 1 << TEXTURE_SHIFT, + TEXTURE_2D = 2 << TEXTURE_SHIFT, + + ROUNDED_CORNERS_SHIFT = 5, + ROUNDED_CORNERS_MASK = 1 << ROUNDED_CORNERS_SHIFT, + ROUNDED_CORNERS_OFF = 0 << ROUNDED_CORNERS_SHIFT, + ROUNDED_CORNERS_ON = 1 << ROUNDED_CORNERS_SHIFT, + + INPUT_TRANSFORM_MATRIX_SHIFT = 6, + INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT, + INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT, + INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT, + + OUTPUT_TRANSFORM_MATRIX_SHIFT = 7, + OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT, + OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT, + OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT, + + INPUT_TF_SHIFT = 8, + INPUT_TF_MASK = 3 << INPUT_TF_SHIFT, + INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT, + INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT, + INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT, + INPUT_TF_HLG = 3 << INPUT_TF_SHIFT, + + OUTPUT_TF_SHIFT = 10, + OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT, + OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT, + OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT, + OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT, + OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT, + + Y410_BT2020_SHIFT = 12, + Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT, + Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT, + Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT, + }; + + inline Key() : mKey(0) {} + inline Key(const Key& rhs) : mKey(rhs.mKey) {} + + inline Key& set(key_t mask, key_t value) { + mKey = (mKey & ~mask) | value; + return *this; + } + + inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; } + inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); } + inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; } + inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; } + inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; } + inline bool hasRoundedCorners() const { + return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON; + } + inline bool hasInputTransformMatrix() const { + return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON; + } + inline bool hasOutputTransformMatrix() const { + return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON; + } + inline bool hasTransformMatrix() const { + return hasInputTransformMatrix() || hasOutputTransformMatrix(); + } + inline int getInputTF() const { return (mKey & INPUT_TF_MASK); } + inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); } + + // When HDR and non-HDR contents are mixed, or different types of HDR contents are + // mixed, we will do a tone mapping process to tone map the input content to output + // content. Currently, the following conversions handled, they are: + // * SDR -> HLG + // * SDR -> PQ + // * HLG -> PQ + inline bool needsToneMapping() const { + int inputTF = getInputTF(); + int outputTF = getOutputTF(); + + // Return false when converting from SDR to SDR. + if (inputTF == Key::INPUT_TF_SRGB && outputTF == Key::OUTPUT_TF_LINEAR) { + return false; + } + if (inputTF == Key::INPUT_TF_LINEAR && outputTF == Key::OUTPUT_TF_SRGB) { + return false; + } + + inputTF >>= Key::INPUT_TF_SHIFT; + outputTF >>= Key::OUTPUT_TF_SHIFT; + return inputTF != outputTF; + } + inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; } + + // for use by std::unordered_map + + bool operator==(const Key& other) const { return mKey == other.mKey; } + + struct Hash { + size_t operator()(const Key& key) const { return static_cast<size_t>(key.mKey); } + }; + }; + + ProgramCache() = default; + ~ProgramCache() = default; + + // Generate shaders to populate the cache + void primeCache(const EGLContext context, bool useColorManagement); + + size_t getSize(const EGLContext context) { return mCaches[context].size(); } + + // useProgram lookup a suitable program in the cache or generates one + // if none can be found. + void useProgram(const EGLContext context, const Description& description); + +private: + // compute a cache Key from a Description + static Key computeKey(const Description& description); + // Generate EOTF based from Key. + static void generateEOTF(Formatter& fs, const Key& needs); + // Generate necessary tone mapping methods for OOTF. + static void generateToneMappingProcess(Formatter& fs, const Key& needs); + // Generate OOTF based from Key. + static void generateOOTF(Formatter& fs, const Key& needs); + // Generate OETF based from Key. + static void generateOETF(Formatter& fs, const Key& needs); + // generates a program from the Key + static std::unique_ptr<Program> generateProgram(const Key& needs); + // generates the vertex shader from the Key + static String8 generateVertexShader(const Key& needs); + // generates the fragment shader from the Key + static String8 generateFragmentShader(const Key& needs); + + // Key/Value map used for caching Programs. Currently the cache + // is never shrunk (and the GL program objects are never deleted). + std::unordered_map<EGLContext, std::unordered_map<Key, std::unique_ptr<Program>, Key::Hash>> + mCaches; +}; + +} // namespace gl +} // namespace renderengine + +ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key) + +} // namespace android + +#endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h new file mode 100644 index 0000000..9c9884a --- /dev/null +++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -0,0 +1,66 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/mat4.h> +#include <ui/GraphicTypes.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <ui/Transform.h> + +namespace android { +namespace renderengine { + +// DisplaySettings contains the settings that are applicable when drawing all +// layers for a given display. +struct DisplaySettings { + // Rectangle describing the physical display. We will project from the + // logical clip onto this rectangle. + Rect physicalDisplay = Rect::INVALID_RECT; + + // Rectangle bounded by the x,y- clipping planes in the logical display, so + // that the orthographic projection matrix can be computed. When + // constructing this matrix, z-coordinate bound are assumed to be at z=0 and + // z=1. + Rect clip = Rect::INVALID_RECT; + + // Global transform to apply to all layers. + mat4 globalTransform = mat4(); + + // Maximum luminance pulled from the display's HDR capabilities. + float maxLuminance = 1.0f; + + // Output dataspace that will be populated if wide color gamut is used, or + // DataSpace::UNKNOWN otherwise. + ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN; + + // Additional color transform to apply in linear space after transforming + // to the output dataspace. + mat4 colorTransform = mat4(); + + // Region that will be cleared to (0, 0, 0, 1) prior to rendering. + // RenderEngine will transform the clearRegion passed in here, by + // globalTransform, so that it will be in the same coordinate space as the + // rendered layers. + Region clearRegion = Region::INVALID_REGION; + + // The orientation of the physical display. + uint32_t orientation = ui::Transform::ROT_0; +}; + +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/include/renderengine/Framebuffer.h b/libs/renderengine/include/renderengine/Framebuffer.h new file mode 100644 index 0000000..6511127 --- /dev/null +++ b/libs/renderengine/include/renderengine/Framebuffer.h
@@ -0,0 +1,35 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> + +struct ANativeWindowBuffer; + +namespace android { +namespace renderengine { + +class Framebuffer { +public: + virtual ~Framebuffer() = default; + + virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected, + const bool useFramebufferCache) = 0; +}; + +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/include/renderengine/Image.h b/libs/renderengine/include/renderengine/Image.h new file mode 100644 index 0000000..3bb4731 --- /dev/null +++ b/libs/renderengine/include/renderengine/Image.h
@@ -0,0 +1,31 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +struct ANativeWindowBuffer; + +namespace android { +namespace renderengine { + +class Image { +public: + virtual ~Image() = default; + virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0; +}; + +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h new file mode 100644 index 0000000..b8bf801 --- /dev/null +++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -0,0 +1,122 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/mat4.h> +#include <math/vec3.h> +#include <renderengine/Texture.h> +#include <ui/Fence.h> +#include <ui/FloatRect.h> +#include <ui/GraphicBuffer.h> +#include <ui/GraphicTypes.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <ui/Transform.h> + +namespace android { +namespace renderengine { + +// Metadata describing the input buffer to render from. +struct Buffer { + // Buffer containing the image that we will render. + // If buffer == nullptr, then the rest of the fields in this struct will be + // ignored. + sp<GraphicBuffer> buffer = nullptr; + + // Fence that will fire when the buffer is ready to be bound. + sp<Fence> fence = nullptr; + + // Texture identifier to bind the external texture to. + // TODO(alecmouri): This is GL-specific...make the type backend-agnostic. + uint32_t textureName = 0; + + // Whether to use filtering when rendering the texture. + bool useTextureFiltering = false; + + // Transform matrix to apply to texture coordinates. + mat4 textureTransform = mat4(); + + // Wheteher to use pre-multiplied alpha + bool usePremultipliedAlpha = true; + + // Override flag that alpha for each pixel in the buffer *must* be 1.0. + // LayerSettings::alpha is still used if isOpaque==true - this flag only + // overrides the alpha channel of the buffer. + bool isOpaque = false; + + // HDR color-space setting for Y410. + bool isY410BT2020 = false; +}; + +// Metadata describing the layer geometry. +struct Geometry { + // Boundaries of the layer. + FloatRect boundaries = FloatRect(); + + // Transform matrix to apply to mesh coordinates. + mat4 positionTransform = mat4(); + + // Radius of rounded corners, if greater than 0. Otherwise, this layer's + // corners are not rounded. + // Having corner radius will force GPU composition on the layer and its children, drawing it + // with a special shader. The shader will receive the radius and the crop rectangle as input, + // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1. + // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop + // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be + // in local layer coordinate space, so we have to take the layer transform into account when + // walking up the tree. + float roundedCornersRadius = 0.0; + + // Rectangle within which corners will be rounded. + FloatRect roundedCornersCrop = FloatRect(); +}; + +// Descriptor of the source pixels for this layer. +struct PixelSource { + // Source buffer + Buffer buffer = Buffer(); + + // The solid color with which to fill the layer. + // This should only be populated if we don't render from an application + // buffer. + half3 solidColor = half3(0.0f, 0.0f, 0.0f); +}; + +// The settings that RenderEngine requires for correctly rendering a Layer. +struct LayerSettings { + // Geometry information + Geometry geometry = Geometry(); + + // Source pixels for this layer. + PixelSource source = PixelSource(); + + // Alpha option to blend with the source pixels + half alpha = half(0.0); + + // Color space describing how the source pixels should be interpreted. + ui::Dataspace sourceDataspace = ui::Dataspace::UNKNOWN; + + // Additional layer-specific color transform to be applied before the global + // transform. + mat4 colorTransform = mat4(); + + // True if blending will be forced to be disabled. + bool disableBlending = false; +}; + +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h new file mode 100644 index 0000000..7618424 --- /dev/null +++ b/libs/renderengine/include/renderengine/Mesh.h
@@ -0,0 +1,115 @@ +/* + * Copyright 2013 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 SF_RENDER_ENGINE_MESH_H +#define SF_RENDER_ENGINE_MESH_H + +#include <vector> + +#include <stdint.h> + +namespace android { +namespace renderengine { + +class Mesh { +public: + enum Primitive { + TRIANGLES = 0x0004, // GL_TRIANGLES + TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP + TRIANGLE_FAN = 0x0006 // GL_TRIANGLE_FAN + }; + + Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordsSize = 0); + ~Mesh() = default; + + /* + * VertexArray handles the stride automatically. + */ + template <typename TYPE> + class VertexArray { + friend class Mesh; + float* mData; + size_t mStride; + VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {} + + public: + TYPE& operator[](size_t index) { return *reinterpret_cast<TYPE*>(&mData[index * mStride]); } + TYPE const& operator[](size_t index) const { + return *reinterpret_cast<TYPE const*>(&mData[index * mStride]); + } + }; + + template <typename TYPE> + VertexArray<TYPE> getPositionArray() { + return VertexArray<TYPE>(getPositions(), mStride); + } + + template <typename TYPE> + VertexArray<TYPE> getTexCoordArray() { + return VertexArray<TYPE>(getTexCoords(), mStride); + } + + template <typename TYPE> + VertexArray<TYPE> getCropCoordArray() { + return VertexArray<TYPE>(getCropCoords(), mStride); + } + + Primitive getPrimitive() const; + + // returns a pointer to the vertices positions + float const* getPositions() const; + + // returns a pointer to the vertices texture coordinates + float const* getTexCoords() const; + + // returns a pointer to the vertices crop coordinates + float const* getCropCoords() const; + + // number of vertices in this mesh + size_t getVertexCount() const; + + // dimension of vertices + size_t getVertexSize() const; + + // dimension of texture coordinates + size_t getTexCoordsSize() const; + + // return stride in bytes + size_t getByteStride() const; + + // return stride in floats + size_t getStride() const; + +private: + Mesh(const Mesh&); + Mesh& operator=(const Mesh&); + Mesh const& operator=(const Mesh&) const; + + float* getPositions(); + float* getTexCoords(); + float* getCropCoords(); + + std::vector<float> mVertices; + size_t mVertexCount; + size_t mVertexSize; + size_t mTexCoordsSize; + size_t mStride; + Primitive mPrimitive; +}; + +} // namespace renderengine +} // namespace android +#endif /* SF_RENDER_ENGINE_MESH_H */
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h new file mode 100644 index 0000000..e707004 --- /dev/null +++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -0,0 +1,249 @@ +/* + * Copyright 2013 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 SF_RENDERENGINE_H_ +#define SF_RENDERENGINE_H_ + +#include <stdint.h> +#include <sys/types.h> +#include <memory> + +#include <android-base/unique_fd.h> +#include <math/mat4.h> +#include <renderengine/DisplaySettings.h> +#include <renderengine/Framebuffer.h> +#include <renderengine/Image.h> +#include <renderengine/LayerSettings.h> +#include <ui/GraphicTypes.h> +#include <ui/Transform.h> + +/** + * Allows to set RenderEngine backend to GLES (default) or Vulkan (NOT yet supported). + */ +#define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend" + +struct ANativeWindowBuffer; + +namespace android { + +class Rect; +class Region; + +namespace renderengine { + +class BindNativeBufferAsFramebuffer; +class Image; +class Mesh; +class Texture; + +namespace impl { +class RenderEngine; +} + +enum class Protection { + UNPROTECTED = 1, + PROTECTED = 2, +}; + +class RenderEngine { +public: + enum FeatureFlag { + USE_COLOR_MANAGEMENT = 1 << 0, // Device manages color + USE_HIGH_PRIORITY_CONTEXT = 1 << 1, // Use high priority context + + // Create a protected context when if possible + ENABLE_PROTECTED_CONTEXT = 1 << 2, + }; + + static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags, + uint32_t imageCacheSize); + + virtual ~RenderEngine() = 0; + + // ----- BEGIN DEPRECATED INTERFACE ----- + // This interface, while still in use until a suitable replacement is built, + // should be considered deprecated, minus some methods which still may be + // used to support legacy behavior. + + virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0; + virtual std::unique_ptr<Image> createImage() = 0; + + virtual void primeCache() const = 0; + + // dump the extension strings. always call the base class. + virtual void dump(std::string& result) = 0; + + virtual bool useNativeFenceSync() const = 0; + virtual bool useWaitSync() const = 0; + + virtual bool isCurrent() const = 0; + + // helpers + // flush submits RenderEngine command stream for execution and returns a + // native fence fd that is signaled when the execution has completed. It + // returns -1 on errors. + virtual base::unique_fd flush() = 0; + // finish waits until RenderEngine command stream has been executed. It + // returns false on errors. + virtual bool finish() = 0; + // waitFence inserts a wait on an external fence fd to RenderEngine + // command stream. It returns false on errors. + virtual bool waitFence(base::unique_fd fenceFd) = 0; + + virtual void clearWithColor(float red, float green, float blue, float alpha) = 0; + virtual void fillRegionWithColor(const Region& region, float red, float green, float blue, + float alpha) = 0; + virtual void genTextures(size_t count, uint32_t* names) = 0; + virtual void deleteTextures(size_t count, uint32_t const* names) = 0; + virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0; + // Legacy public method used by devices that don't support native fence + // synchronization in their GPU driver, as this method provides implicit + // synchronization for latching buffers. + virtual status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, + const sp<Fence>& fence) = 0; + // Caches Image resources for this buffer, but does not bind the buffer to + // a particular texture. + virtual status_t cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0; + // Removes internal resources referenced by the bufferId. This method should be + // invoked when the caller will no longer hold a reference to a GraphicBuffer + // and needs to clean up its resources. + virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0; + // When binding a native buffer, it must be done before setViewportAndProjection + // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation. + virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0; + virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0; + + // set-up + virtual void checkErrors() const = 0; + virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, + ui::Transform::orientation_flags rotation) = 0; + virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, + const half4& color, float cornerRadius) = 0; + virtual void setupLayerTexturing(const Texture& texture) = 0; + virtual void setupLayerBlackedOut() = 0; + virtual void setupFillWithColor(float r, float g, float b, float a) = 0; + // Sets up the crop size for corner radius clipping. + // + // Having corner radius will force GPU composition on the layer and its children, drawing it + // with a special shader. The shader will receive the radius and the crop rectangle as input, + // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1. + // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop + // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be + // in local layer coordinate space, so we have to take the layer transform into account when + // walking up the tree. + virtual void setupCornerRadiusCropSize(float width, float height) = 0; + + // Set a color transform matrix that is applied in linear space right before OETF. + virtual void setColorTransform(const mat4& /* colorTransform */) = 0; + virtual void disableTexturing() = 0; + virtual void disableBlending() = 0; + + // HDR and color management support + virtual void setSourceY410BT2020(bool enable) = 0; + virtual void setSourceDataSpace(ui::Dataspace source) = 0; + virtual void setOutputDataSpace(ui::Dataspace dataspace) = 0; + virtual void setDisplayMaxLuminance(const float maxLuminance) = 0; + + // drawing + virtual void drawMesh(const Mesh& mesh) = 0; + + // queries + virtual size_t getMaxTextureSize() const = 0; + virtual size_t getMaxViewportDims() const = 0; + + // ----- END DEPRECATED INTERFACE ----- + + // ----- BEGIN NEW INTERFACE ----- + + virtual bool isProtected() const = 0; + virtual bool supportsProtectedContent() const = 0; + virtual bool useProtectedContext(bool useProtectedContext) = 0; + + // Renders layers for a particular display via GPU composition. This method + // should be called for every display that needs to be rendered via the GPU. + // @param display The display-wide settings that should be applied prior to + // drawing any layers. + // @param layers The layers to draw onto the display, in Z-order. + // @param buffer The buffer which will be drawn to. This buffer will be + // ready once drawFence fires. + // @param useFramebufferCache True if the framebuffer cache should be used. + // If an implementation does not cache output framebuffers, then this + // parameter does nothing. + // @param bufferFence Fence signalling that the buffer is ready to be drawn + // to. + // @param drawFence A pointer to a fence, which will fire when the buffer + // has been drawn to and is ready to be examined. The fence will be + // initialized by this method. The caller will be responsible for owning the + // fence. + // @return An error code indicating whether drawing was successful. For + // now, this always returns NO_ERROR. + virtual status_t drawLayers(const DisplaySettings& display, + const std::vector<LayerSettings>& layers, + ANativeWindowBuffer* buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0; + +protected: + // Gets a framebuffer to render to. This framebuffer may or may not be + // cached depending on the implementation. + // + // Note that this method does not transfer ownership, so the caller most not + // live longer than RenderEngine. + virtual Framebuffer* getFramebufferForDrawing() = 0; + friend class BindNativeBufferAsFramebuffer; +}; + +class BindNativeBufferAsFramebuffer { +public: + BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer, + const bool useFramebufferCache) + : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) { + mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(), + useFramebufferCache) + ? mEngine.bindFrameBuffer(mFramebuffer) + : NO_MEMORY; + } + ~BindNativeBufferAsFramebuffer() { + mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true); + mEngine.unbindFrameBuffer(mFramebuffer); + } + status_t getStatus() const { return mStatus; } + +private: + RenderEngine& mEngine; + Framebuffer* mFramebuffer; + status_t mStatus; +}; + +namespace impl { + +// impl::RenderEngine contains common implementation that is graphics back-end agnostic. +class RenderEngine : public renderengine::RenderEngine { +public: + virtual ~RenderEngine() = 0; + + bool useNativeFenceSync() const override; + bool useWaitSync() const override; + +protected: + RenderEngine(uint32_t featureFlags); + const uint32_t mFeatureFlags; +}; + +} // namespace impl +} // namespace renderengine +} // namespace android + +#endif /* SF_RENDERENGINE_H_ */
diff --git a/libs/renderengine/include/renderengine/Texture.h b/libs/renderengine/include/renderengine/Texture.h new file mode 100644 index 0000000..c69ace0 --- /dev/null +++ b/libs/renderengine/include/renderengine/Texture.h
@@ -0,0 +1,60 @@ +/* + * Copyright 2013 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 SF_RENDER_ENGINE_TEXTURE_H +#define SF_RENDER_ENGINE_TEXTURE_H + +#include <stdint.h> + +#include <math/mat4.h> + +namespace android { +namespace renderengine { + +class Texture { +public: + enum Target { TEXTURE_2D = 0x0DE1, TEXTURE_EXTERNAL = 0x8D65 }; + + Texture(); + Texture(Target textureTarget, uint32_t textureName); + ~Texture(); + + void init(Target textureTarget, uint32_t textureName); + + void setMatrix(float const* matrix); + void setFiltering(bool enabled); + void setDimensions(size_t width, size_t height); + + uint32_t getTextureName() const; + uint32_t getTextureTarget() const; + + const mat4& getMatrix() const; + bool getFiltering() const; + size_t getWidth() const; + size_t getHeight() const; + +private: + uint32_t mTextureName; + uint32_t mTextureTarget; + size_t mWidth; + size_t mHeight; + bool mFiltering; + mat4 mTextureMatrix; +}; + +} // namespace renderengine +} // namespace android +#endif /* SF_RENDER_ENGINE_TEXTURE_H */
diff --git a/libs/renderengine/include/renderengine/mock/Framebuffer.h b/libs/renderengine/include/renderengine/mock/Framebuffer.h new file mode 100644 index 0000000..dfb6a4e --- /dev/null +++ b/libs/renderengine/include/renderengine/mock/Framebuffer.h
@@ -0,0 +1,36 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gmock/gmock.h> +#include <renderengine/Framebuffer.h> + +namespace android { +namespace renderengine { +namespace mock { + +class Framebuffer : public renderengine::Framebuffer { +public: + Framebuffer(); + ~Framebuffer() override; + + MOCK_METHOD3(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool, const bool)); +}; + +} // namespace mock +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/include/renderengine/mock/Image.h b/libs/renderengine/include/renderengine/mock/Image.h new file mode 100644 index 0000000..2b0eed1 --- /dev/null +++ b/libs/renderengine/include/renderengine/mock/Image.h
@@ -0,0 +1,36 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gmock/gmock.h> +#include <renderengine/Image.h> + +namespace android { +namespace renderengine { +namespace mock { + +class Image : public renderengine::Image { +public: + Image(); + ~Image() override; + + MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer* buffer, bool isProtected)); +}; + +} // namespace mock +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h new file mode 100644 index 0000000..e33bcfd --- /dev/null +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -0,0 +1,89 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gmock/gmock.h> +#include <renderengine/DisplaySettings.h> +#include <renderengine/LayerSettings.h> +#include <renderengine/Mesh.h> +#include <renderengine/RenderEngine.h> +#include <renderengine/Texture.h> +#include <ui/GraphicBuffer.h> +#include <ui/Region.h> + +namespace android { +namespace renderengine { +namespace mock { + +class RenderEngine : public renderengine::RenderEngine { +public: + RenderEngine(); + ~RenderEngine() override; + + MOCK_METHOD0(createFramebuffer, std::unique_ptr<renderengine::Framebuffer>()); + MOCK_METHOD0(createImage, std::unique_ptr<renderengine::Image>()); + MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*()); + MOCK_CONST_METHOD0(primeCache, void()); + MOCK_METHOD1(dump, void(std::string&)); + MOCK_CONST_METHOD0(useNativeFenceSync, bool()); + MOCK_CONST_METHOD0(useWaitSync, bool()); + MOCK_CONST_METHOD0(isCurrent, bool()); + MOCK_METHOD0(flush, base::unique_fd()); + MOCK_METHOD0(finish, bool()); + MOCK_METHOD1(waitFence, bool(base::unique_fd*)); + bool waitFence(base::unique_fd fd) override { return waitFence(&fd); }; + MOCK_METHOD4(clearWithColor, void(float, float, float, float)); + MOCK_METHOD5(fillRegionWithColor, void(const Region&, float, float, float, float)); + MOCK_METHOD2(genTextures, void(size_t, uint32_t*)); + MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*)); + MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&)); + MOCK_METHOD1(cacheExternalTextureBuffer, status_t(const sp<GraphicBuffer>&)); + MOCK_METHOD3(bindExternalTextureBuffer, + status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&)); + MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t)); + MOCK_CONST_METHOD0(checkErrors, void()); + MOCK_METHOD4(setViewportAndProjection, + void(size_t, size_t, Rect, ui::Transform::orientation_flags)); + MOCK_METHOD5(setupLayerBlending, void(bool, bool, bool, const half4&, float)); + MOCK_METHOD1(setupLayerTexturing, void(const Texture&)); + MOCK_METHOD0(setupLayerBlackedOut, void()); + MOCK_METHOD4(setupFillWithColor, void(float, float, float, float)); + MOCK_METHOD2(setupCornerRadiusCropSize, void(float, float)); + MOCK_METHOD1(setColorTransform, void(const mat4&)); + MOCK_METHOD1(setSaturationMatrix, void(const mat4&)); + MOCK_METHOD0(disableTexturing, void()); + MOCK_METHOD0(disableBlending, void()); + MOCK_METHOD1(setSourceY410BT2020, void(bool)); + MOCK_METHOD1(setSourceDataSpace, void(ui::Dataspace)); + MOCK_METHOD1(setOutputDataSpace, void(ui::Dataspace)); + MOCK_METHOD1(setDisplayMaxLuminance, void(const float)); + MOCK_METHOD1(bindFrameBuffer, status_t(renderengine::Framebuffer*)); + MOCK_METHOD1(unbindFrameBuffer, void(renderengine::Framebuffer*)); + MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&)); + MOCK_CONST_METHOD0(getMaxTextureSize, size_t()); + MOCK_CONST_METHOD0(getMaxViewportDims, size_t()); + MOCK_CONST_METHOD0(isProtected, bool()); + MOCK_CONST_METHOD0(supportsProtectedContent, bool()); + MOCK_METHOD1(useProtectedContext, bool(bool)); + MOCK_METHOD6(drawLayers, + status_t(const DisplaySettings&, const std::vector<LayerSettings>&, + ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*)); +}; + +} // namespace mock +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h new file mode 100644 index 0000000..bd2055f --- /dev/null +++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -0,0 +1,87 @@ +/* + * Copyright 2013 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 SF_RENDER_ENGINE_DESCRIPTION_H_ +#define SF_RENDER_ENGINE_DESCRIPTION_H_ + +#include <renderengine/Texture.h> +#include <ui/GraphicTypes.h> + +namespace android { +namespace renderengine { + +/* + * This is the structure that holds the state of the rendering engine. + * This class is used to generate a corresponding GLSL program and set the + * appropriate uniform. + */ +struct Description { + enum class TransferFunction : int { + LINEAR, + SRGB, + ST2084, + HLG, // Hybrid Log-Gamma for HDR. + }; + + static TransferFunction dataSpaceToTransferFunction(ui::Dataspace dataSpace); + + Description() = default; + ~Description() = default; + + bool hasInputTransformMatrix() const; + bool hasOutputTransformMatrix() const; + bool hasColorMatrix() const; + + // whether textures are premultiplied + bool isPremultipliedAlpha = false; + // whether this layer is marked as opaque + bool isOpaque = true; + + // corner radius of the layer + float cornerRadius = 0; + + // Size of the rounded rectangle we are cropping to + half2 cropSize; + + // Texture this layer uses + Texture texture; + bool textureEnabled = false; + + // color used when texturing is disabled or when setting alpha. + half4 color; + + // true if the sampled pixel values are in Y410/BT2020 rather than RGBA + bool isY410BT2020 = false; + + // transfer functions for the input/output + TransferFunction inputTransferFunction = TransferFunction::LINEAR; + TransferFunction outputTransferFunction = TransferFunction::LINEAR; + + float displayMaxLuminance; + + // projection matrix + mat4 projectionMatrix; + + // The color matrix will be applied in linear space right before OETF. + mat4 colorMatrix; + mat4 inputTransformMatrix; + mat4 outputTransformMatrix; +}; + +} // namespace renderengine +} // namespace android + +#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */
diff --git a/libs/renderengine/mock/Framebuffer.cpp b/libs/renderengine/mock/Framebuffer.cpp new file mode 100644 index 0000000..fbdcaab --- /dev/null +++ b/libs/renderengine/mock/Framebuffer.cpp
@@ -0,0 +1,30 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <renderengine/mock/Framebuffer.h> + +namespace android { +namespace renderengine { +namespace mock { + +// The Google Mock documentation recommends explicit non-header instantiations +// for better compile time performance. +Framebuffer::Framebuffer() = default; +Framebuffer::~Framebuffer() = default; + +} // namespace mock +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/mock/Image.cpp b/libs/renderengine/mock/Image.cpp new file mode 100644 index 0000000..57f4346 --- /dev/null +++ b/libs/renderengine/mock/Image.cpp
@@ -0,0 +1,30 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <renderengine/mock/Image.h> + +namespace android { +namespace renderengine { +namespace mock { + +// The Google Mock documentation recommends explicit non-header instantiations +// for better compile time performance. +Image::Image() = default; +Image::~Image() = default; + +} // namespace mock +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/mock/RenderEngine.cpp b/libs/renderengine/mock/RenderEngine.cpp new file mode 100644 index 0000000..261636d --- /dev/null +++ b/libs/renderengine/mock/RenderEngine.cpp
@@ -0,0 +1,30 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <renderengine/mock/RenderEngine.h> + +namespace android { +namespace renderengine { +namespace mock { + +// The Google Mock documentation recommends explicit non-header instantiations +// for better compile time performance. +RenderEngine::RenderEngine() = default; +RenderEngine::~RenderEngine() = default; + +} // namespace mock +} // namespace renderengine +} // namespace android
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp new file mode 100644 index 0000000..9b483ef --- /dev/null +++ b/libs/renderengine/tests/Android.bp
@@ -0,0 +1,38 @@ +// Copyright 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_test { + name: "librenderengine_test", + defaults: ["surfaceflinger_defaults"], + test_suites: ["device-tests"], + srcs: [ + "RenderEngineTest.cpp", + ], + static_libs: [ + "libgmock", + "librenderengine", + ], + shared_libs: [ + "libbase", + "libcutils", + "libEGL", + "libGLESv2", + "libgui", + "liblog", + "libnativewindow", + "libsync", + "libui", + "libutils", + ], +}
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp new file mode 100644 index 0000000..7acaecf --- /dev/null +++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -0,0 +1,1040 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <renderengine/RenderEngine.h> +#include <sync/sync.h> +#include <ui/PixelFormat.h> +#include "../gl/GLESRenderEngine.h" + +constexpr int DEFAULT_DISPLAY_WIDTH = 128; +constexpr int DEFAULT_DISPLAY_HEIGHT = 256; +constexpr int DEFAULT_DISPLAY_OFFSET = 64; + +namespace android { + +struct RenderEngineTest : public ::testing::Test { + static void SetUpTestSuite() { + sRE = renderengine::gl::GLESRenderEngine::create(static_cast<int32_t>( + ui::PixelFormat::RGBA_8888), + 0, 1); + } + + static void TearDownTestSuite() { + // The ordering here is important - sCurrentBuffer must live longer + // than RenderEngine to avoid a null reference on tear-down. + sRE = nullptr; + sCurrentBuffer = nullptr; + } + + static sp<GraphicBuffer> allocateDefaultBuffer() { + return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, + HAL_PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER, + "output"); + } + + // Allocates a 1x1 buffer to fill with a solid color + static sp<GraphicBuffer> allocateSourceBuffer(uint32_t width, uint32_t height) { + return new GraphicBuffer(width, height, HAL_PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_TEXTURE, + "input"); + } + + RenderEngineTest() { mBuffer = allocateDefaultBuffer(); } + + ~RenderEngineTest() { + for (uint32_t texName : mTexNames) { + sRE->deleteTextures(1, &texName); + } + } + + void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a, + uint8_t tolerance = 0) { + uint8_t* pixels; + mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + + auto colorCompare = [tolerance](uint8_t a, uint8_t b) { + uint8_t tmp = a >= b ? a - b : b - a; + return tmp <= tolerance; + }; + int32_t maxFails = 10; + int32_t fails = 0; + for (int32_t j = 0; j < region.getHeight(); j++) { + const uint8_t* src = + pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4; + for (int32_t i = 0; i < region.getWidth(); i++) { + const uint8_t expected[4] = {r, g, b, a}; + bool equal = std::equal(src, src + 4, expected, colorCompare); + EXPECT_TRUE(equal) + << "pixel @ (" << region.left + i << ", " << region.top + j << "): " + << "expected (" << static_cast<uint32_t>(r) << ", " + << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", " + << static_cast<uint32_t>(a) << "), " + << "got (" << static_cast<uint32_t>(src[0]) << ", " + << static_cast<uint32_t>(src[1]) << ", " << static_cast<uint32_t>(src[2]) + << ", " << static_cast<uint32_t>(src[3]) << ")"; + src += 4; + if (!equal && ++fails >= maxFails) { + break; + } + } + if (fails >= maxFails) { + break; + } + } + mBuffer->unlock(); + } + + static Rect fullscreenRect() { return Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); } + + static Rect offsetRect() { + return Rect(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT); + } + + static Rect offsetRectAtZero() { + return Rect(DEFAULT_DISPLAY_WIDTH - DEFAULT_DISPLAY_OFFSET, + DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET); + } + + void invokeDraw(renderengine::DisplaySettings settings, + std::vector<renderengine::LayerSettings> layers, sp<GraphicBuffer> buffer) { + base::unique_fd fence; + status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true, + base::unique_fd(), &fence); + sCurrentBuffer = buffer; + + int fd = fence.release(); + if (fd >= 0) { + sync_wait(fd, -1); + close(fd); + } + + ASSERT_EQ(NO_ERROR, status); + if (layers.size() > 0) { + ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId())); + } + } + + void drawEmptyLayers() { + renderengine::DisplaySettings settings; + std::vector<renderengine::LayerSettings> layers; + // Meaningless buffer since we don't do any drawing + sp<GraphicBuffer> buffer = new GraphicBuffer(); + invokeDraw(settings, layers, buffer); + } + + template <typename SourceVariant> + void fillBuffer(half r, half g, half b, half a); + + template <typename SourceVariant> + void fillRedBuffer(); + + template <typename SourceVariant> + void fillGreenBuffer(); + + template <typename SourceVariant> + void fillBlueBuffer(); + + template <typename SourceVariant> + void fillRedTransparentBuffer(); + + template <typename SourceVariant> + void fillRedOffsetBuffer(); + + template <typename SourceVariant> + void fillBufferPhysicalOffset(); + + template <typename SourceVariant> + void fillBufferCheckers(mat4 transform); + + template <typename SourceVariant> + void fillBufferCheckersRotate0(); + + template <typename SourceVariant> + void fillBufferCheckersRotate90(); + + template <typename SourceVariant> + void fillBufferCheckersRotate180(); + + template <typename SourceVariant> + void fillBufferCheckersRotate270(); + + template <typename SourceVariant> + void fillBufferWithLayerTransform(); + + template <typename SourceVariant> + void fillBufferLayerTransform(); + + template <typename SourceVariant> + void fillBufferWithColorTransform(); + + template <typename SourceVariant> + void fillBufferColorTransform(); + + template <typename SourceVariant> + void fillRedBufferWithRoundedCorners(); + + template <typename SourceVariant> + void fillBufferWithRoundedCorners(); + + template <typename SourceVariant> + void overlayCorners(); + + void fillRedBufferTextureTransform(); + + void fillBufferTextureTransform(); + + void fillRedBufferWithPremultiplyAlpha(); + + void fillBufferWithPremultiplyAlpha(); + + void fillRedBufferWithoutPremultiplyAlpha(); + + void fillBufferWithoutPremultiplyAlpha(); + + void fillGreenColorBufferThenClearRegion(); + + void clearLeftRegion(); + + void clearRegion(); + + // Keep around the same renderengine object to save on initialization time. + // 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; + // 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. + static sp<GraphicBuffer> sCurrentBuffer; + + sp<GraphicBuffer> mBuffer; + + std::vector<uint32_t> mTexNames; +}; + +std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr; +sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr; + +struct ColorSourceVariant { + static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b, + RenderEngineTest* /*fixture*/) { + layer.source.solidColor = half3(r, g, b); + } +}; + +struct RelaxOpaqueBufferVariant { + static void setOpaqueBit(renderengine::LayerSettings& layer) { + layer.source.buffer.isOpaque = false; + } + + static uint8_t getAlphaChannel() { return 255; } +}; + +struct ForceOpaqueBufferVariant { + static void setOpaqueBit(renderengine::LayerSettings& layer) { + layer.source.buffer.isOpaque = true; + } + + static uint8_t getAlphaChannel() { + // The isOpaque bit will override the alpha channel, so this should be + // arbitrary. + return 10; + } +}; + +template <typename OpaquenessVariant> +struct BufferSourceVariant { + static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b, + RenderEngineTest* fixture) { + sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1); + uint32_t texName; + 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, + reinterpret_cast<void**>(&pixels)); + + for (int32_t j = 0; j < buf->getHeight(); j++) { + uint8_t* iter = pixels + (buf->getStride() * j) * 4; + for (int32_t i = 0; i < buf->getWidth(); i++) { + iter[0] = uint8_t(r * 255); + iter[1] = uint8_t(g * 255); + iter[2] = uint8_t(b * 255); + iter[3] = OpaquenessVariant::getAlphaChannel(); + iter += 4; + } + } + + buf->unlock(); + + layer.source.buffer.buffer = buf; + layer.source.buffer.textureName = texName; + OpaquenessVariant::setOpaqueBit(layer); + } +}; + +template <typename SourceVariant> +void RenderEngineTest::fillBuffer(half r, half g, half b, half a) { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.geometry.boundaries = fullscreenRect().toFloatRect(); + SourceVariant::fillColor(layer, r, g, b, this); + layer.alpha = a; + + layers.push_back(layer); + + invokeDraw(settings, layers, mBuffer); +} + +template <typename SourceVariant> +void RenderEngineTest::fillRedBuffer() { + fillBuffer<SourceVariant>(1.0f, 0.0f, 0.0f, 1.0f); + expectBufferColor(fullscreenRect(), 255, 0, 0, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillGreenBuffer() { + fillBuffer<SourceVariant>(0.0f, 1.0f, 0.0f, 1.0f); + expectBufferColor(fullscreenRect(), 0, 255, 0, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBlueBuffer() { + fillBuffer<SourceVariant>(0.0f, 0.0f, 1.0f, 1.0f); + expectBufferColor(fullscreenRect(), 0, 0, 255, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillRedTransparentBuffer() { + fillBuffer<SourceVariant>(1.0f, 0.0f, 0.0f, .2f); + expectBufferColor(fullscreenRect(), 51, 0, 0, 51); +} + +template <typename SourceVariant> +void RenderEngineTest::fillRedOffsetBuffer() { + renderengine::DisplaySettings settings; + settings.physicalDisplay = offsetRect(); + settings.clip = offsetRectAtZero(); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.geometry.boundaries = offsetRectAtZero().toFloatRect(); + SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this); + layer.alpha = 1.0f; + + layers.push_back(layer); + invokeDraw(settings, layers, mBuffer); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferPhysicalOffset() { + fillRedOffsetBuffer<SourceVariant>(); + + expectBufferColor(Rect(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT), + 255, 0, 0, 255); + Rect offsetRegionLeft(DEFAULT_DISPLAY_OFFSET, DEFAULT_DISPLAY_HEIGHT); + Rect offsetRegionTop(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_OFFSET); + + expectBufferColor(offsetRegionLeft, 0, 0, 0, 0); + expectBufferColor(offsetRegionTop, 0, 0, 0, 0); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferCheckers(mat4 transform) { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + // Here logical space is 2x2 + settings.clip = Rect(2, 2); + settings.globalTransform = transform; + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layerOne; + Rect rectOne(0, 0, 1, 1); + layerOne.geometry.boundaries = rectOne.toFloatRect(); + SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this); + layerOne.alpha = 1.0f; + + renderengine::LayerSettings layerTwo; + Rect rectTwo(0, 1, 1, 2); + layerTwo.geometry.boundaries = rectTwo.toFloatRect(); + SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this); + layerTwo.alpha = 1.0f; + + renderengine::LayerSettings layerThree; + Rect rectThree(1, 0, 2, 1); + layerThree.geometry.boundaries = rectThree.toFloatRect(); + SourceVariant::fillColor(layerThree, 0.0f, 0.0f, 1.0f, this); + layerThree.alpha = 1.0f; + + layers.push_back(layerOne); + layers.push_back(layerTwo); + layers.push_back(layerThree); + + invokeDraw(settings, layers, mBuffer); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferCheckersRotate0() { + fillBufferCheckers<SourceVariant>(mat4()); + expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 255, 0, 0, + 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT / 2), + 0, 0, 255, 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 0, 0, 0, 0); + expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2, + DEFAULT_DISPLAY_HEIGHT), + 0, 255, 0, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferCheckersRotate90() { + mat4 matrix = mat4(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1); + fillBufferCheckers<SourceVariant>(matrix); + expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 255, 0, + 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT / 2), + 255, 0, 0, 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 0, 0, 255, 255); + expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2, + DEFAULT_DISPLAY_HEIGHT), + 0, 0, 0, 0); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferCheckersRotate180() { + mat4 matrix = mat4(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 2, 2, 0, 1); + fillBufferCheckers<SourceVariant>(matrix); + expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0, + 0); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT / 2), + 0, 255, 0, 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 255, 0, 0, 255); + expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2, + DEFAULT_DISPLAY_HEIGHT), + 0, 0, 255, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferCheckersRotate270() { + mat4 matrix = mat4(0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1); + fillBufferCheckers<SourceVariant>(matrix); + expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 255, + 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT / 2), + 0, 0, 0, 0); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 0, 255, 0, 255); + expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT / 2, DEFAULT_DISPLAY_WIDTH / 2, + DEFAULT_DISPLAY_HEIGHT), + 255, 0, 0, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferWithLayerTransform() { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + // Here logical space is 2x2 + settings.clip = Rect(2, 2); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.geometry.boundaries = Rect(1, 1).toFloatRect(); + // Translate one pixel diagonally + layer.geometry.positionTransform = mat4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1); + SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this); + layer.source.solidColor = half3(1.0f, 0.0f, 0.0f); + layer.alpha = 1.0f; + + layers.push_back(layer); + + invokeDraw(settings, layers, mBuffer); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferLayerTransform() { + fillBufferWithLayerTransform<SourceVariant>(); + expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT / 2), 0, 0, 0, 0); + expectBufferColor(Rect(0, 0, DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT / 2, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 255, 0, 0, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferWithColorTransform() { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = Rect(1, 1); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.geometry.boundaries = Rect(1, 1).toFloatRect(); + SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this); + layer.alpha = 1.0f; + + // construct a fake color matrix + // annihilate green and blue channels + settings.colorTransform = mat4::scale(vec4(1, 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); + + layer.alpha = 1.0f; + layer.geometry.boundaries = Rect(1, 1).toFloatRect(); + + layers.push_back(layer); + + invokeDraw(settings, layers, mBuffer); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferColorTransform() { + fillBufferWithColorTransform<SourceVariant>(); + expectBufferColor(fullscreenRect(), 191, 0, 0, 255); +} + +template <typename SourceVariant> +void RenderEngineTest::fillRedBufferWithRoundedCorners() { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.geometry.boundaries = fullscreenRect().toFloatRect(); + layer.geometry.roundedCornersRadius = 5.0f; + layer.geometry.roundedCornersCrop = fullscreenRect().toFloatRect(); + SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this); + layer.alpha = 1.0f; + + layers.push_back(layer); + + invokeDraw(settings, layers, mBuffer); +} + +template <typename SourceVariant> +void RenderEngineTest::fillBufferWithRoundedCorners() { + fillRedBufferWithRoundedCorners<SourceVariant>(); + // Corners should be ignored... + expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 0); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, 0, DEFAULT_DISPLAY_WIDTH, 1), 0, 0, 0, 0); + expectBufferColor(Rect(0, DEFAULT_DISPLAY_HEIGHT - 1, 1, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 0); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH - 1, DEFAULT_DISPLAY_HEIGHT - 1, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 0, 0, 0, 0); + // ...And the non-rounded portion should be red. + // Other pixels may be anti-aliased, so let's not check those. + expectBufferColor(Rect(5, 5, DEFAULT_DISPLAY_WIDTH - 5, DEFAULT_DISPLAY_HEIGHT - 5), 255, 0, 0, + 255); +} + +template <typename SourceVariant> +void RenderEngineTest::overlayCorners() { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<renderengine::LayerSettings> layersFirst; + + renderengine::LayerSettings layerOne; + layerOne.geometry.boundaries = + FloatRect(0, 0, DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0); + SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this); + layerOne.alpha = 0.2; + + layersFirst.push_back(layerOne); + invokeDraw(settings, layersFirst, mBuffer); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 0, 0, 0, 0); + + std::vector<renderengine::LayerSettings> layersSecond; + renderengine::LayerSettings layerTwo; + layerTwo.geometry.boundaries = + FloatRect(DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); + SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this); + layerTwo.alpha = 1.0f; + + layersSecond.push_back(layerTwo); + invokeDraw(settings, layersSecond, mBuffer); + + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1, + DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + 0, 255, 0, 255); +} + +void RenderEngineTest::fillRedBufferTextureTransform() { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = Rect(1, 1); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + // Here will allocate a checker board texture, but transform texture + // coordinates so that only the upper left is applied. + sp<GraphicBuffer> buf = allocateSourceBuffer(2, 2); + uint32_t texName; + RenderEngineTest::sRE->genTextures(1, &texName); + this->mTexNames.push_back(texName); + + uint8_t* pixels; + buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + // Red top left, Green top right, Blue bottom left, Black bottom right + pixels[0] = 255; + pixels[1] = 0; + pixels[2] = 0; + pixels[3] = 255; + pixels[4] = 0; + pixels[5] = 255; + pixels[6] = 0; + pixels[7] = 255; + pixels[8] = 0; + pixels[9] = 0; + pixels[10] = 255; + pixels[11] = 255; + buf->unlock(); + + layer.source.buffer.buffer = buf; + layer.source.buffer.textureName = texName; + // Transform coordinates to only be inside the red quadrant. + layer.source.buffer.textureTransform = mat4::scale(vec4(0.2, 0.2, 1, 1)); + layer.alpha = 1.0f; + layer.geometry.boundaries = Rect(1, 1).toFloatRect(); + + layers.push_back(layer); + + invokeDraw(settings, layers, mBuffer); +} + +void RenderEngineTest::fillBufferTextureTransform() { + fillRedBufferTextureTransform(); + expectBufferColor(fullscreenRect(), 255, 0, 0, 255); +} + +void RenderEngineTest::fillRedBufferWithPremultiplyAlpha() { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + // Here logical space is 1x1 + settings.clip = Rect(1, 1); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1); + uint32_t texName; + RenderEngineTest::sRE->genTextures(1, &texName); + this->mTexNames.push_back(texName); + + uint8_t* pixels; + buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + pixels[0] = 255; + pixels[1] = 0; + pixels[2] = 0; + pixels[3] = 255; + buf->unlock(); + + layer.source.buffer.buffer = buf; + layer.source.buffer.textureName = texName; + layer.source.buffer.usePremultipliedAlpha = true; + layer.alpha = 0.5f; + layer.geometry.boundaries = Rect(1, 1).toFloatRect(); + + layers.push_back(layer); + + invokeDraw(settings, layers, mBuffer); +} + +void RenderEngineTest::fillBufferWithPremultiplyAlpha() { + fillRedBufferWithPremultiplyAlpha(); + expectBufferColor(fullscreenRect(), 128, 0, 0, 128); +} + +void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + // Here logical space is 1x1 + settings.clip = Rect(1, 1); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1); + uint32_t texName; + RenderEngineTest::sRE->genTextures(1, &texName); + this->mTexNames.push_back(texName); + + uint8_t* pixels; + buf->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + pixels[0] = 255; + pixels[1] = 0; + pixels[2] = 0; + pixels[3] = 255; + buf->unlock(); + + layer.source.buffer.buffer = buf; + layer.source.buffer.textureName = texName; + layer.source.buffer.usePremultipliedAlpha = false; + layer.alpha = 0.5f; + layer.geometry.boundaries = Rect(1, 1).toFloatRect(); + + layers.push_back(layer); + + invokeDraw(settings, layers, mBuffer); +} + +void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() { + fillRedBufferWithoutPremultiplyAlpha(); + expectBufferColor(fullscreenRect(), 128, 0, 0, 64, 1); +} + +void RenderEngineTest::clearLeftRegion() { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + // Here logical space is 4x4 + settings.clip = Rect(4, 4); + settings.globalTransform = mat4::scale(vec4(2, 4, 0, 1)); + settings.clearRegion = Region(Rect(1, 1)); + std::vector<renderengine::LayerSettings> layers; + // dummy layer, without bounds should not render anything + renderengine::LayerSettings layer; + layers.push_back(layer); + invokeDraw(settings, layers, mBuffer); +} + +void RenderEngineTest::clearRegion() { + // Reuse mBuffer + clearLeftRegion(); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT), 0, 0, 0, 255); + expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 2, 0, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT), + 0, 0, 0, 0); +} + +TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) { + drawEmptyLayers(); +} + +TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) { + renderengine::DisplaySettings settings; + std::vector<renderengine::LayerSettings> layers; + renderengine::LayerSettings layer; + layer.geometry.boundaries = fullscreenRect().toFloatRect(); + BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); + layers.push_back(layer); + base::unique_fd fence; + status_t status = sRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence); + + ASSERT_EQ(BAD_VALUE, status); +} + +TEST_F(RenderEngineTest, drawLayers_nullOutputFence) { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<renderengine::LayerSettings> layers; + renderengine::LayerSettings layer; + layer.geometry.boundaries = fullscreenRect().toFloatRect(); + BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); + layer.alpha = 1.0; + layers.push_back(layer); + + status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, + base::unique_fd(), nullptr); + sCurrentBuffer = mBuffer; + ASSERT_EQ(NO_ERROR, status); + expectBufferColor(fullscreenRect(), 255, 0, 0, 255); +} + +TEST_F(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<renderengine::LayerSettings> layers; + renderengine::LayerSettings layer; + layer.geometry.boundaries = fullscreenRect().toFloatRect(); + BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); + layer.alpha = 1.0; + layers.push_back(layer); + + status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false, + base::unique_fd(), nullptr); + sCurrentBuffer = mBuffer; + ASSERT_EQ(NO_ERROR, status); + ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId())); + expectBufferColor(fullscreenRect(), 255, 0, 0, 255); +} + +TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { + fillRedBuffer<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_colorSource) { + fillGreenBuffer<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_colorSource) { + fillBlueBuffer<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_colorSource) { + fillRedTransparentBuffer<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_colorSource) { + fillBufferPhysicalOffset<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_colorSource) { + fillBufferCheckersRotate0<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_colorSource) { + fillBufferCheckersRotate90<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_colorSource) { + fillBufferCheckersRotate180<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_colorSource) { + fillBufferCheckersRotate270<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_colorSource) { + fillBufferLayerTransform<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_colorSource) { + fillBufferLayerTransform<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) { + fillBufferWithRoundedCorners<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) { + overlayCorners<ColorSourceVariant>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_opaqueBufferSource) { + fillRedBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_opaqueBufferSource) { + fillGreenBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_opaqueBufferSource) { + fillBlueBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_opaqueBufferSource) { + fillRedTransparentBuffer<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_opaqueBufferSource) { + fillBufferPhysicalOffset<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_opaqueBufferSource) { + fillBufferCheckersRotate0<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_opaqueBufferSource) { + fillBufferCheckersRotate90<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_opaqueBufferSource) { + fillBufferCheckersRotate180<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_opaqueBufferSource) { + fillBufferCheckersRotate270<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_opaqueBufferSource) { + fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_opaqueBufferSource) { + fillBufferLayerTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) { + fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) { + overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_bufferSource) { + fillRedBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillGreenBuffer_bufferSource) { + fillGreenBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBlueBuffer_bufferSource) { + fillBlueBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillRedTransparentBuffer_bufferSource) { + fillRedTransparentBuffer<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferPhysicalOffset_bufferSource) { + fillBufferPhysicalOffset<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate0_bufferSource) { + fillBufferCheckersRotate0<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate90_bufferSource) { + fillBufferCheckersRotate90<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate180_bufferSource) { + fillBufferCheckersRotate180<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferCheckersRotate270_bufferSource) { + fillBufferCheckersRotate270<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferLayerTransform_bufferSource) { + fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferColorTransform_bufferSource) { + fillBufferLayerTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) { + fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_overlayCorners_bufferSource) { + overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBufferTextureTransform) { + fillBufferTextureTransform(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBuffer_premultipliesAlpha) { + fillBufferWithPremultiplyAlpha(); +} + +TEST_F(RenderEngineTest, drawLayers_fillBuffer_withoutPremultiplyingAlpha) { + fillBufferWithoutPremultiplyAlpha(); +} + +TEST_F(RenderEngineTest, drawLayers_clearRegion) { + clearRegion(); +} + +TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.geometry.boundaries = fullscreenRect().toFloatRect(); + BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); + + layers.push_back(layer); + invokeDraw(settings, layers, mBuffer); + uint64_t bufferId = layer.source.buffer.buffer->getId(); + EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); + sRE->unbindExternalTextureBuffer(bufferId); + EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); +} + +TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) { + status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr); + ASSERT_EQ(BAD_VALUE, result); +} + +TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) { + sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1); + uint32_t texName; + sRE->genTextures(1, &texName); + mTexNames.push_back(texName); + + sRE->bindExternalTextureBuffer(texName, buf, nullptr); + uint64_t bufferId = buf->getId(); + EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); + sRE->unbindExternalTextureBuffer(bufferId); + EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); +} + +TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferWithNullBuffer) { + status_t result = sRE->cacheExternalTextureBuffer(nullptr); + ASSERT_EQ(BAD_VALUE, result); +} + +TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferCachesImages) { + sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1); + uint64_t bufferId = buf->getId(); + sRE->cacheExternalTextureBuffer(buf); + EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); + sRE->unbindExternalTextureBuffer(bufferId); + EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); +} + +} // namespace android
diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS index d4393d6..81099e8 100644 --- a/libs/sensor/OWNERS +++ b/libs/sensor/OWNERS
@@ -1,2 +1,3 @@ arthuri@google.com bduddie@google.com +bstack@google.com
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index 2383516..abc9103 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp
@@ -22,6 +22,13 @@ #include <binder/IPermissionController.h> #include <binder/IServiceManager.h> +/* + * The permission to use for activity recognition sensors (like step counter). + * See sensor types for more details on what sensors should require this + * permission. + */ +#define SENSOR_PERMISSION_ACTIVITY_RECOGNITION "android.permission.ACTIVITY_RECOGNITION" + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- @@ -116,7 +123,7 @@ mStringType = SENSOR_STRING_TYPE_HEART_RATE; mRequiredPermission = SENSOR_PERMISSION_BODY_SENSORS; AppOpsManager appOps; - mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS)); + mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission)); mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; } break; case SENSOR_TYPE_LIGHT: @@ -165,14 +172,22 @@ mFlags |= SENSOR_FLAG_WAKE_UP; } break; - case SENSOR_TYPE_STEP_COUNTER: + case SENSOR_TYPE_STEP_COUNTER: { mStringType = SENSOR_STRING_TYPE_STEP_COUNTER; + mRequiredPermission = SENSOR_PERMISSION_ACTIVITY_RECOGNITION; + AppOpsManager appOps; + mRequiredAppOp = + appOps.permissionToOpCode(String16(mRequiredPermission)); mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; - break; - case SENSOR_TYPE_STEP_DETECTOR: + } break; + case SENSOR_TYPE_STEP_DETECTOR: { mStringType = SENSOR_STRING_TYPE_STEP_DETECTOR; + mRequiredPermission = SENSOR_PERMISSION_ACTIVITY_RECOGNITION; + AppOpsManager appOps; + mRequiredAppOp = + appOps.permissionToOpCode(String16(mRequiredPermission)); mFlags |= SENSOR_FLAG_SPECIAL_REPORTING_MODE; - break; + } break; case SENSOR_TYPE_TEMPERATURE: mStringType = SENSOR_STRING_TYPE_TEMPERATURE; mFlags |= SENSOR_FLAG_ON_CHANGE_MODE; @@ -318,7 +333,7 @@ // If the sensor is protected by a permission we need to know if it is // a runtime one to determine whether we can use the permission cache. sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); - if (binder != 0) { + if (binder != nullptr) { sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder); mRequiredPermissionRuntime = permCtrl->isRuntimePermission( String16(mRequiredPermission));
diff --git a/libs/sensor/SensorEventQueue.cpp b/libs/sensor/SensorEventQueue.cpp index 6f68fb5..4438d45 100644 --- a/libs/sensor/SensorEventQueue.cpp +++ b/libs/sensor/SensorEventQueue.cpp
@@ -29,6 +29,7 @@ #include <sensor/ISensorEventConnection.h> #include <android/sensor.h> +#include <hardware/sensors-base.h> using std::min; @@ -37,7 +38,7 @@ // ---------------------------------------------------------------------------- SensorEventQueue::SensorEventQueue(const sp<ISensorEventConnection>& connection) - : mSensorEventConnection(connection), mRecBuffer(NULL), mAvailable(0), mConsumed(0), + : mSensorEventConnection(connection), mRecBuffer(nullptr), mAvailable(0), mConsumed(0), mNumAcksToSend(0) { mRecBuffer = new ASensorEvent[MAX_RECEIVE_BUFFER_EVENT_COUNT]; } @@ -82,9 +83,9 @@ sp<Looper> SensorEventQueue::getLooper() const { Mutex::Autolock _l(mLock); - if (mLooper == 0) { + if (mLooper == nullptr) { mLooper = new Looper(true); - mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, NULL, NULL); + mLooper->addFd(getFd(), getFd(), ALOOPER_EVENT_INPUT, nullptr, nullptr); } return mLooper; } @@ -97,7 +98,7 @@ int events; int32_t result; do { - result = looper->pollOnce(-1, NULL, &events, NULL); + result = looper->pollOnce(-1, nullptr, &events, nullptr); if (result == ALOOPER_POLL_ERROR) { ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno); result = -EPIPE; // unknown error, so we make up one @@ -188,6 +189,52 @@ return; } +ssize_t SensorEventQueue::filterEvents(ASensorEvent* events, size_t count) const { + // Check if this Sensor Event Queue is registered to receive each type of event. If it is not, + // then do not copy the event into the final buffer. Minimize the number of copy operations by + // finding consecutive sequences of events that the Sensor Event Queue should receive and only + // copying the events once an unregistered event type is reached. + bool intervalStartLocSet = false; + size_t intervalStartLoc = 0; + size_t eventsInInterval = 0; + ssize_t eventsCopied = 0; + + for (size_t i = 0; i < count; i++) { + bool includeEvent = + (events[i].type != SENSOR_TYPE_ADDITIONAL_INFO || requestAdditionalInfo); + + if (includeEvent) { + // Do not copy events yet since there may be more consecutive events that should be + // copied together. Track the start location and number of events in the current + // sequence. + if (!intervalStartLocSet) { + intervalStartLoc = i; + intervalStartLocSet = true; + eventsInInterval = 0; + } + eventsInInterval++; + } + + // Shift the events from the already processed interval once an event that should not be + // included is reached or if this is the final event to be processed. + if (!includeEvent || (i + 1 == count)) { + // Only shift the events if the interval did not start with the first event. If the + // interval started with the first event, the events are already in their correct + // location. + if (intervalStartLoc > 0) { + memmove(&events[eventsCopied], &events[intervalStartLoc], + eventsInInterval * sizeof(ASensorEvent)); + } + eventsCopied += eventsInInterval; + + // Reset the interval information + eventsInInterval = 0; + intervalStartLocSet = false; + } + } + return eventsCopied; +} + // ---------------------------------------------------------------------------- }; // namespace android
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index c97e4da..96d5eb9 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp
@@ -62,7 +62,7 @@ // to the wrong package and stats based on app ops may be slightly off. if (opPackageName.size() <= 0) { sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); - if (binder != 0) { + if (binder != nullptr) { const uid_t uid = IPCThreadState::self()->getCallingUid(); Vector<String16> packages; interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages); @@ -93,7 +93,7 @@ } SensorManager::SensorManager(const String16& opPackageName) - : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) { + : mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) { Mutex::Autolock _l(mLock); assertStateLocked(); } @@ -128,13 +128,13 @@ Mutex::Autolock _l(mLock); mSensorServer.clear(); free(mSensorList); - mSensorList = NULL; + mSensorList = nullptr; mSensors.clear(); } status_t SensorManager::assertStateLocked() { bool initSensorManager = false; - if (mSensorServer == NULL) { + if (mSensorServer == nullptr) { initSensorManager = true; } else { // Ping binder to check if sensorservice is alive. @@ -164,7 +164,7 @@ size_t count = mSensors.size(); mSensorList = static_cast<Sensor const**>(malloc(count * sizeof(Sensor*))); - LOG_ALWAYS_FATAL_IF(mSensorList == NULL, "mSensorList NULL"); + LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL"); for (size_t i=0 ; i<count ; i++) { mSensorList[i] = mSensors.array() + i; @@ -222,7 +222,7 @@ } } } - return NULL; + return nullptr; } sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) { @@ -232,10 +232,10 @@ while (assertStateLocked() == NO_ERROR) { sp<ISensorEventConnection> connection = mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName); - if (connection == NULL) { + if (connection == nullptr) { // SensorService just died or the app doesn't have required permissions. ALOGE("createEventQueue: connection is NULL."); - return NULL; + return nullptr; } queue = new SensorEventQueue(connection); break;
diff --git a/libs/sensor/include/sensor/Sensor.h b/libs/sensor/include/sensor/Sensor.h index 6926f7f..324d443 100644 --- a/libs/sensor/include/sensor/Sensor.h +++ b/libs/sensor/include/sensor/Sensor.h
@@ -57,15 +57,15 @@ uint8_t b[16]; int64_t i64[2]; }; - uuid_t(const uint8_t (&uuid)[16]) { memcpy(b, uuid, sizeof(b));} + explicit uuid_t(const uint8_t (&uuid)[16]) { memcpy(b, uuid, sizeof(b));} uuid_t() : b{0} {} }; Sensor(const Sensor&) = default; Sensor& operator=(const Sensor&) = default; - Sensor(const char * name = ""); - Sensor(struct sensor_t const* hwSensor, int halVersion = 0); + explicit Sensor(const char * name = ""); + explicit Sensor(struct sensor_t const* hwSensor, int halVersion = 0); Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersion = 0); ~Sensor();
diff --git a/libs/sensor/include/sensor/SensorEventQueue.h b/libs/sensor/include/sensor/SensorEventQueue.h index baed2ee..8c3fde0 100644 --- a/libs/sensor/include/sensor/SensorEventQueue.h +++ b/libs/sensor/include/sensor/SensorEventQueue.h
@@ -34,6 +34,7 @@ // Concrete types for the NDK struct ASensorEventQueue { ALooper* looper; + bool requestAdditionalInfo; }; // ---------------------------------------------------------------------------- @@ -64,7 +65,7 @@ // Default sensor sample period static constexpr int32_t SENSOR_DELAY_NORMAL = 200000; - SensorEventQueue(const sp<ISensorEventConnection>& connection); + explicit SensorEventQueue(const sp<ISensorEventConnection>& connection); virtual ~SensorEventQueue(); virtual void onFirstRef(); @@ -92,6 +93,13 @@ void sendAck(const ASensorEvent* events, int count); status_t injectSensorEvent(const ASensorEvent& event); + + // Filters the given sensor events in place and returns the new number of events. + // + // The filtering is controlled by ASensorEventQueue.requestAdditionalInfo, and if this value is + // false, then all SENSOR_TYPE_ADDITIONAL_INFO sensor events will be removed. + ssize_t filterEvents(ASensorEvent* events, size_t count) const; + private: sp<Looper> getLooper() const; sp<ISensorEventConnection> mSensorEventConnection;
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h index 23f7a91..f09c9c6 100644 --- a/libs/sensor/include/sensor/SensorManager.h +++ b/libs/sensor/include/sensor/SensorManager.h
@@ -71,7 +71,7 @@ void sensorManagerDied(); static status_t waitForSensorService(sp<ISensorServer> *server); - SensorManager(const String16& opPackageName); + explicit SensorManager(const String16& opPackageName); status_t assertStateLocked(); private:
diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp index 9fd84bc..c9a7668 100644 --- a/libs/sensor/tests/Android.bp +++ b/libs/sensor/tests/Android.bp
@@ -21,6 +21,7 @@ srcs: [ "Sensor_test.cpp", + "SensorEventQueue_test.cpp", ], shared_libs: [
diff --git a/libs/sensor/tests/SensorEventQueue_test.cpp b/libs/sensor/tests/SensorEventQueue_test.cpp new file mode 100644 index 0000000..1eb5883 --- /dev/null +++ b/libs/sensor/tests/SensorEventQueue_test.cpp
@@ -0,0 +1,172 @@ +/* + * 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. + */ + +#include <stdint.h> + +#include <gtest/gtest.h> +#include <utils/Errors.h> + +#include <android/sensor.h> +#include <hardware/sensors-base.h> +#include <sensor/SensorManager.h> +#include <sensor/SensorEventQueue.h> + +namespace android { + +class SensorEventQueueTest : public ::testing::Test { +protected: + typedef std::vector<int32_t> Events; + + SensorEventQueueTest() {}; + + virtual void SetUp() override { + SensorManager& manager = SensorManager::getInstanceForPackage(String16("SensorEventQueueTest")); + mQueue = manager.createEventQueue(); + } + + void configureAdditionalInfo(bool enable) { + mQueue->requestAdditionalInfo = enable; + } + + Events filterEvents(const Events &types) const { + // Convert the events into SensorEvent array + ASensorEvent* events = new ASensorEvent[types.size()]; + for (size_t i = 0; i < types.size(); i++) { + events[i].type = types[i]; + } + + // Filter the events + ssize_t filteredCount = mQueue->filterEvents(events, types.size()); + + // Copy the result into an output vector + Events result; + for (size_t i = 0; i < filteredCount; i++) { + result.push_back(events[i].type); + } + delete[] events; + + return result; + } + + Events getExpectedEvents(const Events &events) const { + Events output; + for (size_t i = 0; i != events.size(); i++) { + // Copy events if the event queue is configured to receive them + if (events[i] != SENSOR_TYPE_ADDITIONAL_INFO || mQueue->requestAdditionalInfo) { + output.push_back(events[i]); + } + } + return output; + } + + void runFilterTest(const Events& events) { + Events filtered = filterEvents(events); + Events expected = getExpectedEvents(events); + EXPECT_EQ(expected.size(), filtered.size()); + EXPECT_EQ(expected, filtered); + } + +private: + sp<SensorEventQueue> mQueue; +}; + +TEST_F(SensorEventQueueTest, FilterZeroEvents) { + configureAdditionalInfo(false /* enable */); + runFilterTest({}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_ReceiveAdditionalInfo) { + configureAdditionalInfo(true /* enable */); + runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_MAGNETIC_FIELD}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_RemoveAll) { + configureAdditionalInfo(false /* enable */); + runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_RemoveFirst) { + configureAdditionalInfo(false /* enable */); + runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_MAGNETIC_FIELD}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_RemoveAllButOne) { + configureAdditionalInfo(false /* enable */); + runFilterTest({SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_ADDITIONAL_INFO}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_RemoveLast) { + configureAdditionalInfo(false /* enable */); + runFilterTest({SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_MAGNETIC_FIELD, + SENSOR_TYPE_ADDITIONAL_INFO}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_RemoveConsecutive) { + configureAdditionalInfo(false /* enable */); + runFilterTest({SENSOR_TYPE_MAGNETIC_FIELD, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ACCELEROMETER}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_RemoveInterleaved) { + configureAdditionalInfo(false /* enable */); + runFilterTest({SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_MAGNETIC_FIELD}); +} + +TEST_F(SensorEventQueueTest, FilterEvents_ReconfigureAdditionalInfo) { + configureAdditionalInfo(false /* enable */); + const Events events = {SENSOR_TYPE_ACCELEROMETER, + SENSOR_TYPE_GYROSCOPE, + SENSOR_TYPE_ADDITIONAL_INFO, + SENSOR_TYPE_MAGNETIC_FIELD, + SENSOR_TYPE_ADDITIONAL_INFO}; + runFilterTest(events); + + // Update setting to request Additional Info + configureAdditionalInfo(true /* enable */); + runFilterTest(events); + + // Update setting to stop requesting Additional Info + configureAdditionalInfo(true /* enable */); + runFilterTest(events); +} + +} // namespace android
diff --git a/libs/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp new file mode 100644 index 0000000..e0e3469 --- /dev/null +++ b/libs/sensorprivacy/Android.bp
@@ -0,0 +1,47 @@ +// Copyright 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_shared { + name: "libsensorprivacy", + + aidl: { + export_aidl_headers: true, + local_include_dirs: ["aidl"], + }, + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-Wzero-as-null-pointer-constant", + ], + + srcs: [ + "aidl/android/hardware/ISensorPrivacyListener.aidl", + "aidl/android/hardware/ISensorPrivacyManager.aidl", + "SensorPrivacyManager.cpp", + ], + + shared_libs: [ + "libbinder", + "libcutils", + "libutils", + "liblog", + "libhardware", + ], + + export_include_dirs: ["include"], + + export_shared_lib_headers: ["libbinder"], +}
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp new file mode 100644 index 0000000..f973cba --- /dev/null +++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -0,0 +1,106 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <mutex> +#include <unistd.h> + +#include <binder/Binder.h> +#include <binder/IServiceManager.h> +#include <sensorprivacy/SensorPrivacyManager.h> + +#include <utils/SystemClock.h> + +namespace android { + +SensorPrivacyManager::SensorPrivacyManager() +{ +} + +sp<hardware::ISensorPrivacyManager> SensorPrivacyManager::getService() +{ + std::lock_guard<Mutex> scoped_lock(mLock); + int64_t startTime = 0; + sp<hardware::ISensorPrivacyManager> service = mService; + while (service == nullptr || !IInterface::asBinder(service)->isBinderAlive()) { + sp<IBinder> binder = defaultServiceManager()->checkService(String16("sensor_privacy")); + if (binder == nullptr) { + // Wait for the sensor privacy service to come back... + if (startTime == 0) { + startTime = uptimeMillis(); + ALOGI("Waiting for sensor privacy service"); + } else if ((uptimeMillis() - startTime) > 1000000) { + ALOGW("Waiting too long for sensor privacy service, giving up"); + service = nullptr; + break; + } + usleep(25000); + } else { + service = interface_cast<hardware::ISensorPrivacyManager>(binder); + mService = service; + } + } + return service; +} + +void SensorPrivacyManager::addSensorPrivacyListener( + const sp<hardware::ISensorPrivacyListener>& listener) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + service->addSensorPrivacyListener(listener); + } +} + +void SensorPrivacyManager::removeSensorPrivacyListener( + const sp<hardware::ISensorPrivacyListener>& listener) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + service->removeSensorPrivacyListener(listener); + } +} + +bool SensorPrivacyManager::isSensorPrivacyEnabled() +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + bool result; + service->isSensorPrivacyEnabled(&result); + return result; + } + // if the SensorPrivacyManager is not available then assume sensor privacy is disabled + return false; +} + +status_t SensorPrivacyManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + return IInterface::asBinder(service)->linkToDeath(recipient); + } + return INVALID_OPERATION; +} + +status_t SensorPrivacyManager::unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + return IInterface::asBinder(service)->unlinkToDeath(recipient); + } + return INVALID_OPERATION; +} + +}; // namespace android
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl new file mode 100644 index 0000000..58177d8 --- /dev/null +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl
@@ -0,0 +1,24 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +/** + * @hide + */ +oneway interface ISensorPrivacyListener { + void onSensorPrivacyChanged(boolean enabled); +}
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl new file mode 100644 index 0000000..4c2d5db --- /dev/null +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
@@ -0,0 +1,30 @@ +/** + * Copyright (c) 2018, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +import android.hardware.ISensorPrivacyListener; + +/** @hide */ +interface ISensorPrivacyManager { + void addSensorPrivacyListener(in ISensorPrivacyListener listener); + + void removeSensorPrivacyListener(in ISensorPrivacyListener listener); + + boolean isSensorPrivacyEnabled(); + + void setSensorPrivacy(boolean enable); +}
diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h new file mode 100644 index 0000000..2546a68 --- /dev/null +++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
@@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_SENSOR_PRIVACY_MANAGER_H +#define ANDROID_SENSOR_PRIVACY_MANAGER_H + +#include "android/hardware/ISensorPrivacyListener.h" +#include "android/hardware/ISensorPrivacyManager.h" + +#include <utils/threads.h> + +// --------------------------------------------------------------------------- +namespace android { + +class SensorPrivacyManager +{ +public: + SensorPrivacyManager(); + + void addSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener); + void removeSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener); + bool isSensorPrivacyEnabled(); + + status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient); + status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient); + +private: + Mutex mLock; + sp<hardware::ISensorPrivacyManager> mService; + sp<hardware::ISensorPrivacyManager> getService(); +}; + + +}; // namespace android +// --------------------------------------------------------------------------- + +#endif // ANDROID_SENSOR_PRIVACY_MANAGER_H
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index ff9d19e..0407d88 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp
@@ -18,6 +18,7 @@ vndk: { enabled: true, }, + double_loadable: true, clang: true, cflags: [ @@ -44,6 +45,8 @@ // Don't warn about struct padding "-Wno-padded", + + "-Wno-switch-enum", ], sanitize: { @@ -52,18 +55,26 @@ srcs: [ "ColorSpace.cpp", + "BufferHubBuffer.cpp", + "BufferHubEventFd.cpp", + "BufferHubMetadata.cpp", "DebugUtils.cpp", "Fence.cpp", "FenceTime.cpp", "FrameStats.cpp", + "Gralloc.cpp", "Gralloc2.cpp", + "Gralloc3.cpp", "GraphicBuffer.cpp", "GraphicBufferAllocator.cpp", "GraphicBufferMapper.cpp", "HdrCapabilities.cpp", "PixelFormat.cpp", + "PublicFormat.cpp", "Rect.cpp", "Region.cpp", + "Size.cpp", + "Transform.cpp", "UiConfig.cpp", ], @@ -71,13 +82,17 @@ "frameworks/native/include", ], + // Uncomment the following line to enable VALIDATE_REGIONS traces + //defaults: ["libui-validate-regions-defaults"], + shared_libs: [ + "android.frameworks.bufferhub@1.0", "android.hardware.graphics.allocator@2.0", - "android.hardware.graphics.common@1.1", + "android.hardware.graphics.allocator@3.0", + "android.hardware.graphics.common@1.2", "android.hardware.graphics.mapper@2.0", "android.hardware.graphics.mapper@2.1", - "android.hardware.configstore@1.0", - "android.hardware.configstore-utils", + "android.hardware.graphics.mapper@3.0", "libbase", "libcutils", "libhardware", @@ -86,12 +101,11 @@ "libhwbinder", "libsync", "libutils", - "libutilscallstack", "liblog", ], export_shared_lib_headers: [ - "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", ], static_libs: [ @@ -100,9 +114,32 @@ "libmath", ], + // bufferhub is not used when building libgui for vendors + target: { + vendor: { + cflags: ["-DLIBUI_IN_VNDK"], + exclude_srcs: [ + "BufferHubBuffer.cpp", + "BufferHubEventFd.cpp", + "BufferHubMetadata.cpp", + ], + exclude_header_libs: [ + "libbufferhub_headers", + "libdvr_headers", + ], + exclude_shared_libs: [ + "android.frameworks.bufferhub@1.0", + "libpdx_default_transport", + ], + }, + }, + header_libs: [ "libbase_headers", + "libbufferhub_headers", + "libdvr_headers", "libnativebase_headers", + "libnativewindow_headers", "libhardware_headers", "libui_headers", "libpdx_headers", @@ -116,6 +153,7 @@ export_header_lib_headers: [ "libbase_headers", "libnativebase_headers", + "libnativewindow_headers", "libhardware_headers", "libui_headers", ], @@ -127,9 +165,23 @@ vendor_available: true, target: { vendor: { + cflags: ["-DLIBUI_IN_VNDK"], override_export_include_dirs: ["include_vndk"], }, }, + header_libs: [ + "libnativewindow_headers", + ], + export_header_lib_headers: [ + "libnativewindow_headers", + ], +} + +// defaults to enable VALIDATE_REGIONS traces +cc_defaults { + name: "libui-validate-regions-defaults", + shared_libs: ["libutilscallstack"], + cflags: ["-DVALIDATE_REGIONS"], } subdirs = [
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp new file mode 100644 index 0000000..da91a97 --- /dev/null +++ b/libs/ui/BufferHubBuffer.cpp
@@ -0,0 +1,357 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <poll.h> + +#include <android-base/unique_fd.h> +#include <android/frameworks/bufferhub/1.0/IBufferHub.h> +#include <log/log.h> +#include <ui/BufferHubBuffer.h> +#include <ui/BufferHubDefs.h> +#include <utils/Trace.h> + +using ::android::base::unique_fd; +using ::android::BufferHubDefs::isAnyClientAcquired; +using ::android::BufferHubDefs::isAnyClientGained; +using ::android::BufferHubDefs::isClientAcquired; +using ::android::BufferHubDefs::isClientGained; +using ::android::BufferHubDefs::isClientPosted; +using ::android::BufferHubDefs::isClientReleased; +using ::android::frameworks::bufferhub::V1_0::BufferHubStatus; +using ::android::frameworks::bufferhub::V1_0::BufferTraits; +using ::android::frameworks::bufferhub::V1_0::IBufferClient; +using ::android::frameworks::bufferhub::V1_0::IBufferHub; +using ::android::hardware::hidl_handle; +using ::android::hardware::graphics::common::V1_2::HardwareBufferDescription; + +namespace android { + +std::unique_ptr<BufferHubBuffer> BufferHubBuffer::create(uint32_t width, uint32_t height, + uint32_t layerCount, uint32_t format, + uint64_t usage, size_t userMetadataSize) { + auto buffer = std::unique_ptr<BufferHubBuffer>( + new BufferHubBuffer(width, height, layerCount, format, usage, userMetadataSize)); + return buffer->isValid() ? std::move(buffer) : nullptr; +} + +std::unique_ptr<BufferHubBuffer> BufferHubBuffer::import(const sp<NativeHandle>& token) { + if (token == nullptr || token.get() == nullptr) { + ALOGE("%s: token cannot be nullptr!", __FUNCTION__); + return nullptr; + } + + auto buffer = std::unique_ptr<BufferHubBuffer>(new BufferHubBuffer(token)); + return buffer->isValid() ? std::move(buffer) : nullptr; +} + +BufferHubBuffer::BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, + uint32_t format, uint64_t usage, size_t userMetadataSize) { + ATRACE_CALL(); + ALOGD("%s: width=%u height=%u layerCount=%u, format=%u " + "usage=%" PRIx64 " mUserMetadataSize=%zu", + __FUNCTION__, width, height, layerCount, format, usage, userMetadataSize); + + sp<IBufferHub> bufferhub = IBufferHub::getService(); + if (bufferhub.get() == nullptr) { + ALOGE("%s: BufferHub service not found!", __FUNCTION__); + return; + } + + AHardwareBuffer_Desc aDesc = {width, height, layerCount, format, + usage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL}; + HardwareBufferDescription desc; + memcpy(&desc, &aDesc, sizeof(HardwareBufferDescription)); + + BufferHubStatus ret; + sp<IBufferClient> client; + BufferTraits bufferTraits; + IBufferHub::allocateBuffer_cb allocCb = [&](const auto& status, const auto& outClient, + const auto& outTraits) { + ret = status; + client = std::move(outClient); + bufferTraits = std::move(outTraits); + }; + + if (!bufferhub->allocateBuffer(desc, static_cast<uint32_t>(userMetadataSize), allocCb).isOk()) { + ALOGE("%s: allocateBuffer transaction failed!", __FUNCTION__); + return; + } else if (ret != BufferHubStatus::NO_ERROR) { + ALOGE("%s: allocateBuffer failed with error %u.", __FUNCTION__, ret); + return; + } else if (client == nullptr) { + ALOGE("%s: allocateBuffer got null BufferClient.", __FUNCTION__); + return; + } + + const int importRet = initWithBufferTraits(bufferTraits); + if (importRet < 0) { + ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-importRet)); + client->close(); + } + mBufferClient = std::move(client); +} + +BufferHubBuffer::BufferHubBuffer(const sp<NativeHandle>& token) { + sp<IBufferHub> bufferhub = IBufferHub::getService(); + if (bufferhub.get() == nullptr) { + ALOGE("%s: BufferHub service not found!", __FUNCTION__); + return; + } + + BufferHubStatus ret; + sp<IBufferClient> client; + BufferTraits bufferTraits; + IBufferHub::importBuffer_cb importCb = [&](const auto& status, const auto& outClient, + const auto& outTraits) { + ret = status; + client = std::move(outClient); + bufferTraits = std::move(outTraits); + }; + + // hidl_handle(native_handle_t*) simply creates a raw pointer reference withouth ownership + // transfer. + if (!bufferhub->importBuffer(hidl_handle(token.get()->handle()), importCb).isOk()) { + ALOGE("%s: importBuffer transaction failed!", __FUNCTION__); + return; + } else if (ret != BufferHubStatus::NO_ERROR) { + ALOGE("%s: importBuffer failed with error %u.", __FUNCTION__, ret); + return; + } else if (client == nullptr) { + ALOGE("%s: importBuffer got null BufferClient.", __FUNCTION__); + return; + } + + const int importRet = initWithBufferTraits(bufferTraits); + if (importRet < 0) { + ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-importRet)); + client->close(); + } + mBufferClient = std::move(client); +} + +BufferHubBuffer::~BufferHubBuffer() { + // Close buffer client to avoid possible race condition: user could first duplicate and hold + // token with the original buffer gone, and then try to import the token. The close function + // will explicitly invalidate the token to avoid this. + if (mBufferClient != nullptr) { + if (!mBufferClient->close().isOk()) { + ALOGE("%s: close BufferClient transaction failed!", __FUNCTION__); + } + } +} + +int BufferHubBuffer::initWithBufferTraits(const BufferTraits& bufferTraits) { + ATRACE_CALL(); + + if (bufferTraits.bufferInfo.getNativeHandle() == nullptr) { + ALOGE("%s: missing buffer info handle.", __FUNCTION__); + return -EINVAL; + } + + if (bufferTraits.bufferHandle.getNativeHandle() == nullptr) { + ALOGE("%s: missing gralloc handle.", __FUNCTION__); + return -EINVAL; + } + + // Import fds. Dup fds because hidl_handle owns the fds. + unique_fd ashmemFd(fcntl(bufferTraits.bufferInfo->data[0], F_DUPFD_CLOEXEC, 0)); + mMetadata = BufferHubMetadata::import(std::move(ashmemFd)); + if (!mMetadata.isValid()) { + ALOGE("%s: Received an invalid metadata.", __FUNCTION__); + return -EINVAL; + } + + mEventFd = BufferHubEventFd(fcntl(bufferTraits.bufferInfo->data[1], F_DUPFD_CLOEXEC, 0)); + if (!mEventFd.isValid()) { + ALOGE("%s: Received ad invalid event fd.", __FUNCTION__); + return -EINVAL; + } + + int bufferId = bufferTraits.bufferInfo->data[2]; + if (bufferId < 0) { + ALOGE("%s: Received an invalid (negative) id.", __FUNCTION__); + return -EINVAL; + } + + uint32_t clientBitMask; + memcpy(&clientBitMask, &bufferTraits.bufferInfo->data[3], sizeof(clientBitMask)); + if (clientBitMask == 0U) { + ALOGE("%s: Received an invalid client state mask.", __FUNCTION__); + return -EINVAL; + } + + uint32_t userMetadataSize; + memcpy(&userMetadataSize, &bufferTraits.bufferInfo->data[4], sizeof(userMetadataSize)); + if (mMetadata.userMetadataSize() != userMetadataSize) { + ALOGE("%s: user metadata size not match: expected %u, actual %zu.", __FUNCTION__, + userMetadataSize, mMetadata.userMetadataSize()); + return -EINVAL; + } + + size_t metadataSize = static_cast<size_t>(mMetadata.metadataSize()); + if (metadataSize < BufferHubDefs::kMetadataHeaderSize) { + ALOGE("%s: metadata too small: %zu", __FUNCTION__, metadataSize); + return -EINVAL; + } + + // Populate shortcuts to the atomics in metadata. + auto metadataHeader = mMetadata.metadataHeader(); + mBufferState = &metadataHeader->bufferState; + mFenceState = &metadataHeader->fenceState; + mActiveClientsBitMask = &metadataHeader->activeClientsBitMask; + // The C++ standard recommends (but does not require) that lock-free atomic operations are + // also address-free, that is, suitable for communication between processes using shared + // memory. + LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(mBufferState) || + !std::atomic_is_lock_free(mFenceState) || + !std::atomic_is_lock_free(mActiveClientsBitMask), + "Atomic variables in ashmen are not lock free."); + + // Import the buffer: We only need to hold on the native_handle_t here so that + // GraphicBuffer instance can be created in future. + mBufferHandle = std::move(bufferTraits.bufferHandle); + memcpy(&mBufferDesc, &bufferTraits.bufferDesc, sizeof(AHardwareBuffer_Desc)); + + mId = bufferId; + mClientStateMask = clientBitMask; + + // TODO(b/112012161) Set up shared fences. + ALOGD("%s: id=%d, mBufferState=%" PRIx32 ".", __FUNCTION__, mId, + mBufferState->load(std::memory_order_acquire)); + return 0; +} + +int BufferHubBuffer::gain() { + uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire); + if (isClientGained(currentBufferState, mClientStateMask)) { + ALOGV("%s: Buffer is already gained by this client %" PRIx32 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + do { + if (isAnyClientGained(currentBufferState & (~mClientStateMask)) || + isAnyClientAcquired(currentBufferState)) { + ALOGE("%s: Buffer is in use, id=%d mClientStateMask=%" PRIx32 " state=%" PRIx32 ".", + __FUNCTION__, mId, mClientStateMask, currentBufferState); + return -EBUSY; + } + // Change the buffer state to gained state, whose value happens to be the same as + // mClientStateMask. + } while (!mBufferState->compare_exchange_weak(currentBufferState, mClientStateMask, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence. + return 0; +} + +int BufferHubBuffer::post() { + uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire); + uint32_t updatedBufferState = (~mClientStateMask) & BufferHubDefs::kHighBitsMask; + do { + if (!isClientGained(currentBufferState, mClientStateMask)) { + ALOGE("%s: Cannot post a buffer that is not gained by this client. buffer_id=%d " + "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".", + __FUNCTION__, mId, mClientStateMask, currentBufferState); + return -EBUSY; + } + // Set the producer client buffer state to released, other clients' buffer state to posted. + // Post to all existing and non-existing clients. + } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence if needed. + return 0; +} + +int BufferHubBuffer::acquire() { + uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire); + if (isClientAcquired(currentBufferState, mClientStateMask)) { + ALOGV("%s: Buffer is already acquired by this client %" PRIx32 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + uint32_t updatedBufferState = 0U; + do { + if (!isClientPosted(currentBufferState, mClientStateMask)) { + ALOGE("%s: Cannot acquire a buffer that is not in posted state. buffer_id=%d " + "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".", + __FUNCTION__, mId, mClientStateMask, currentBufferState); + return -EBUSY; + } + // Change the buffer state for this consumer from posted to acquired. + updatedBufferState = currentBufferState ^ mClientStateMask; + } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence. + return 0; +} + +int BufferHubBuffer::release() { + uint32_t currentBufferState = mBufferState->load(std::memory_order_acquire); + if (isClientReleased(currentBufferState, mClientStateMask)) { + ALOGV("%s: Buffer is already released by this client %" PRIx32 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + uint32_t updatedBufferState = 0U; + do { + updatedBufferState = currentBufferState & (~mClientStateMask); + } while (!mBufferState->compare_exchange_weak(currentBufferState, updatedBufferState, + std::memory_order_acq_rel, + std::memory_order_acquire)); + // TODO(b/119837586): Update fence state and return GPU fence if needed. + return 0; +} + +bool BufferHubBuffer::isReleased() const { + return (mBufferState->load(std::memory_order_acquire) & + mActiveClientsBitMask->load(std::memory_order_acquire)) == 0; +} + +bool BufferHubBuffer::isValid() const { + return mBufferHandle.getNativeHandle() != nullptr && mId >= 0 && mClientStateMask != 0U && + mEventFd.get() >= 0 && mMetadata.isValid() && mBufferClient != nullptr; +} + +sp<NativeHandle> BufferHubBuffer::duplicate() { + if (mBufferClient == nullptr) { + ALOGE("%s: missing BufferClient!", __FUNCTION__); + return nullptr; + } + + hidl_handle token; + BufferHubStatus ret; + IBufferClient::duplicate_cb dupCb = [&](const auto& outToken, const auto& status) { + token = std::move(outToken); + ret = status; + }; + + if (!mBufferClient->duplicate(dupCb).isOk()) { + ALOGE("%s: duplicate transaction failed!", __FUNCTION__); + return nullptr; + } else if (ret != BufferHubStatus::NO_ERROR) { + ALOGE("%s: duplicate failed with error %u.", __FUNCTION__, ret); + return nullptr; + } else if (token.getNativeHandle() == nullptr) { + ALOGE("%s: duplicate got null token.", __FUNCTION__); + return nullptr; + } + + return NativeHandle::create(native_handle_clone(token.getNativeHandle()), /*ownsHandle=*/true); +} + +} // namespace android
diff --git a/libs/ui/BufferHubEventFd.cpp b/libs/ui/BufferHubEventFd.cpp new file mode 100644 index 0000000..bffc2ca --- /dev/null +++ b/libs/ui/BufferHubEventFd.cpp
@@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/eventfd.h> + +#include <log/log.h> +#include <ui/BufferHubEventFd.h> + +namespace android { + +BufferHubEventFd::BufferHubEventFd() : mFd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)) {} + +BufferHubEventFd::BufferHubEventFd(int fd) : mFd(fd) {} + +status_t BufferHubEventFd::signal() const { + if (!isValid()) { + ALOGE("%s: cannot signal an invalid eventfd.", __FUNCTION__); + return DEAD_OBJECT; + } + + eventfd_write(mFd.get(), 1); + return OK; +} + +status_t BufferHubEventFd::clear() const { + if (!isValid()) { + ALOGE("%s: cannot clear an invalid eventfd.", __FUNCTION__); + return DEAD_OBJECT; + } + + eventfd_t value; + eventfd_read(mFd.get(), &value); + return OK; +} + +} // namespace android
diff --git a/libs/ui/BufferHubMetadata.cpp b/libs/ui/BufferHubMetadata.cpp new file mode 100644 index 0000000..05bc7dd --- /dev/null +++ b/libs/ui/BufferHubMetadata.cpp
@@ -0,0 +1,104 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <sys/mman.h> +#include <limits> + +#include <cutils/ashmem.h> +#include <log/log.h> +#include <ui/BufferHubMetadata.h> + +namespace android { + +namespace { + +static const int kAshmemProt = PROT_READ | PROT_WRITE; + +} // namespace + +using BufferHubDefs::kMetadataHeaderSize; +using BufferHubDefs::MetadataHeader; + +/* static */ +BufferHubMetadata BufferHubMetadata::create(size_t userMetadataSize) { + // The size the of metadata buffer is used as the "width" parameter during allocation. Thus it + // cannot overflow uint32_t. + if (userMetadataSize >= (std::numeric_limits<uint32_t>::max() - kMetadataHeaderSize)) { + ALOGE("BufferHubMetadata::Create: metadata size too big: %zu.", userMetadataSize); + return {}; + } + + const size_t metadataSize = userMetadataSize + kMetadataHeaderSize; + int fd = ashmem_create_region(/*name=*/"BufferHubMetadata", metadataSize); + if (fd < 0) { + ALOGE("BufferHubMetadata::Create: failed to create ashmem region."); + return {}; + } + + // Hand over the ownership of the fd to a unique_fd immediately after the successful + // return of ashmem_create_region. The ashmemFd is going to own the fd and to prevent fd + // leaks during error handling. + unique_fd ashmemFd{fd}; + + if (ashmem_set_prot_region(ashmemFd.get(), kAshmemProt) != 0) { + ALOGE("BufferHubMetadata::Create: failed to set protect region."); + return {}; + } + + return BufferHubMetadata::import(std::move(ashmemFd)); +} + +/* static */ +BufferHubMetadata BufferHubMetadata::import(unique_fd ashmemFd) { + if (!ashmem_valid(ashmemFd.get())) { + ALOGE("BufferHubMetadata::Import: invalid ashmem fd."); + return {}; + } + + size_t metadataSize = static_cast<size_t>(ashmem_get_size_region(ashmemFd.get())); + size_t userMetadataSize = metadataSize - kMetadataHeaderSize; + + // Note that here the buffer state is mapped from shared memory as an atomic object. The + // std::atomic's constructor will not be called so that the original value stored in the memory + // region can be preserved. + auto metadataHeader = static_cast<MetadataHeader*>(mmap(nullptr, metadataSize, kAshmemProt, + MAP_SHARED, ashmemFd.get(), + /*offset=*/0)); + if (metadataHeader == nullptr) { + ALOGE("BufferHubMetadata::Import: failed to map region."); + return {}; + } + + return BufferHubMetadata(userMetadataSize, std::move(ashmemFd), metadataHeader); +} + +BufferHubMetadata::BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd, + MetadataHeader* metadataHeader) + : mUserMetadataSize(userMetadataSize), + mAshmemFd(std::move(ashmemFd)), + mMetadataHeader(metadataHeader) {} + +BufferHubMetadata::~BufferHubMetadata() { + if (mMetadataHeader != nullptr) { + int ret = munmap(mMetadataHeader, metadataSize()); + ALOGE_IF(ret != 0, + "BufferHubMetadata::~BufferHubMetadata: failed to unmap ashmem, error=%d.", errno); + mMetadataHeader = nullptr; + } +} + +} // namespace android
diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp index 5b4bf23..7a14af1 100644 --- a/libs/ui/ColorSpace.cpp +++ b/libs/ui/ColorSpace.cpp
@@ -351,13 +351,12 @@ }; } -std::unique_ptr<float3> ColorSpace::createLUT(uint32_t size, - const ColorSpace& src, const ColorSpace& dst) { - +std::unique_ptr<float3[]> ColorSpace::createLUT(uint32_t size, const ColorSpace& src, + const ColorSpace& dst) { size = clamp(size, 2u, 256u); float m = 1.0f / float(size - 1); - std::unique_ptr<float3> lut(new float3[size * size * size]); + std::unique_ptr<float3[]> lut(new float3[size * size * size]); float3* data = lut.get(); ColorSpaceConnector connector(src, dst);
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp index 61df02d..ee06d93 100644 --- a/libs/ui/DebugUtils.cpp +++ b/libs/ui/DebugUtils.cpp
@@ -234,6 +234,9 @@ case ColorMode::BT2020: return std::string("ColorMode::BT2020"); + case ColorMode::DISPLAY_BT2020: + return std::string("ColorMode::DISPLAY_BT2020"); + case ColorMode::BT2100_PQ: return std::string("ColorMode::BT2100_PQ");
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp index ff53aa8..4ce891e 100644 --- a/libs/ui/Fence.cpp +++ b/libs/ui/Fence.cpp
@@ -109,25 +109,25 @@ return SIGNAL_TIME_INVALID; } - struct sync_fence_info_data* finfo = sync_fence_info(mFenceFd); - if (finfo == NULL) { - ALOGE("sync_fence_info returned NULL for fd %d", mFenceFd.get()); + struct sync_file_info* finfo = sync_file_info(mFenceFd); + if (finfo == nullptr) { + ALOGE("sync_file_info returned NULL for fd %d", mFenceFd.get()); return SIGNAL_TIME_INVALID; } if (finfo->status != 1) { - sync_fence_info_free(finfo); + sync_file_info_free(finfo); return SIGNAL_TIME_PENDING; } - struct sync_pt_info* pinfo = NULL; uint64_t timestamp = 0; - while ((pinfo = sync_pt_info(finfo, pinfo)) != NULL) { - if (pinfo->timestamp_ns > timestamp) { - timestamp = pinfo->timestamp_ns; + struct sync_fence_info* pinfo = sync_get_fence_info(finfo); + for (size_t i = 0; i < finfo->num_fences; i++) { + if (pinfo[i].timestamp_ns > timestamp) { + timestamp = pinfo[i].timestamp_ns; } } - sync_fence_info_free(finfo); + sync_file_info_free(finfo); return nsecs_t(timestamp); }
diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp index 1414766..bdfe04b 100644 --- a/libs/ui/FenceTime.cpp +++ b/libs/ui/FenceTime.cpp
@@ -33,18 +33,6 @@ const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE); -void* FenceTime::operator new(size_t byteCount) noexcept { - void *p = nullptr; - if (posix_memalign(&p, alignof(FenceTime), byteCount)) { - return nullptr; - } - return p; -} - -void FenceTime::operator delete(void *p) { - free(p); -} - FenceTime::FenceTime(const sp<Fence>& fence) : mState(((fence.get() != nullptr) && fence->isValid()) ? State::VALID : State::INVALID), @@ -291,8 +279,8 @@ } void FenceTimeline::updateSignalTimes() { + std::lock_guard<std::mutex> lock(mMutex); while (!mQueue.empty()) { - std::lock_guard<std::mutex> lock(mMutex); std::shared_ptr<FenceTime> fence = mQueue.front().lock(); if (!fence) { // The shared_ptr no longer exists and no one cares about the
diff --git a/libs/ui/Gralloc.cpp b/libs/ui/Gralloc.cpp new file mode 100644 index 0000000..8e09a13 --- /dev/null +++ b/libs/ui/Gralloc.cpp
@@ -0,0 +1,27 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Gralloc" + +#include <ui/Gralloc.h> + +namespace android { + +GrallocMapper::~GrallocMapper() {} + +GrallocAllocator::~GrallocAllocator() {} + +} // namespace android
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp index b92cbf3..5dc4530 100644 --- a/libs/ui/Gralloc2.cpp +++ b/libs/ui/Gralloc2.cpp
@@ -27,9 +27,15 @@ #include <sync/sync.h> #pragma clang diagnostic pop -namespace android { +using android::hardware::graphics::allocator::V2_0::IAllocator; +using android::hardware::graphics::common::V1_1::BufferUsage; +using android::hardware::graphics::common::V1_1::PixelFormat; +using android::hardware::graphics::mapper::V2_0::BufferDescriptor; +using android::hardware::graphics::mapper::V2_0::Error; +using android::hardware::graphics::mapper::V2_0::YCbCrLayout; +using android::hardware::graphics::mapper::V2_1::IMapper; -namespace Gralloc2 { +namespace android { namespace { @@ -39,12 +45,9 @@ static const uint64_t valid10UsageBits = []() -> uint64_t { using hardware::graphics::common::V1_0::BufferUsage; uint64_t bits = 0; - for (const auto bit : hardware::hidl_enum_iterator<BufferUsage>()) { + for (const auto bit : hardware::hidl_enum_range<BufferUsage>()) { bits = bits | bit; } - // TODO(b/72323293, b/72703005): Remove these additional bits - bits = bits | (1 << 10) | (1 << 13); - return bits; }(); return valid10UsageBits; @@ -54,7 +57,7 @@ static const uint64_t valid11UsageBits = []() -> uint64_t { using hardware::graphics::common::V1_1::BufferUsage; uint64_t bits = 0; - for (const auto bit : hardware::hidl_enum_iterator<BufferUsage>()) { + for (const auto bit : hardware::hidl_enum_range<BufferUsage>()) { bits = bits | bit; } return bits; @@ -62,17 +65,26 @@ return valid11UsageBits; } +static inline IMapper::Rect sGralloc2Rect(const Rect& rect) { + IMapper::Rect outRect{}; + outRect.left = rect.left; + outRect.top = rect.top; + outRect.width = rect.width(); + outRect.height = rect.height(); + return outRect; +} + } // anonymous namespace -void Mapper::preload() { +void Gralloc2Mapper::preload() { android::hardware::preloadPassthroughService<hardware::graphics::mapper::V2_0::IMapper>(); } -Mapper::Mapper() -{ +Gralloc2Mapper::Gralloc2Mapper() { mMapper = hardware::graphics::mapper::V2_0::IMapper::getService(); if (mMapper == nullptr) { - LOG_ALWAYS_FATAL("gralloc-mapper is missing"); + ALOGW("mapper 2.x is not supported"); + return; } if (mMapper->isRemote()) { LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode"); @@ -82,30 +94,37 @@ mMapperV2_1 = IMapper::castFrom(mMapper); } -Gralloc2::Error Mapper::validateBufferDescriptorInfo( - const IMapper::BufferDescriptorInfo& descriptorInfo) const { +bool Gralloc2Mapper::isLoaded() const { + return mMapper != nullptr; +} + +status_t Gralloc2Mapper::validateBufferDescriptorInfo( + IMapper::BufferDescriptorInfo* descriptorInfo) const { uint64_t validUsageBits = getValid10UsageBits(); if (mMapperV2_1 != nullptr) { validUsageBits = validUsageBits | getValid11UsageBits(); } - if (descriptorInfo.usage & ~validUsageBits) { + if (descriptorInfo->usage & ~validUsageBits) { ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64, - descriptorInfo.usage & ~validUsageBits); - return Error::BAD_VALUE; + descriptorInfo->usage & ~validUsageBits); + return BAD_VALUE; } - return Error::NONE; + return NO_ERROR; } -Error Mapper::createDescriptor( - const IMapper::BufferDescriptorInfo& descriptorInfo, - BufferDescriptor* outDescriptor) const -{ - Error error = validateBufferDescriptorInfo(descriptorInfo); - if (error != Error::NONE) { - return error; +status_t Gralloc2Mapper::createDescriptor(void* bufferDescriptorInfo, + void* outBufferDescriptor) const { + IMapper::BufferDescriptorInfo* descriptorInfo = + static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo); + BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor); + + status_t status = validateBufferDescriptorInfo(descriptorInfo); + if (status != NO_ERROR) { + return status; } + Error error; auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor) { error = tmpError; @@ -118,24 +137,23 @@ hardware::Return<void> ret; if (mMapperV2_1 != nullptr) { - ret = mMapperV2_1->createDescriptor_2_1(descriptorInfo, hidl_cb); + ret = mMapperV2_1->createDescriptor_2_1(*descriptorInfo, hidl_cb); } else { const hardware::graphics::mapper::V2_0::IMapper::BufferDescriptorInfo info = { - descriptorInfo.width, - descriptorInfo.height, - descriptorInfo.layerCount, - static_cast<hardware::graphics::common::V1_0::PixelFormat>(descriptorInfo.format), - descriptorInfo.usage, + descriptorInfo->width, + descriptorInfo->height, + descriptorInfo->layerCount, + static_cast<hardware::graphics::common::V1_0::PixelFormat>(descriptorInfo->format), + descriptorInfo->usage, }; ret = mMapper->createDescriptor(info, hidl_cb); } - return (ret.isOk()) ? error : kTransactionError; + return static_cast<status_t>((ret.isOk()) ? error : kTransactionError); } -Error Mapper::importBuffer(const hardware::hidl_handle& rawHandle, - buffer_handle_t* outBufferHandle) const -{ +status_t Gralloc2Mapper::importBuffer(const hardware::hidl_handle& rawHandle, + buffer_handle_t* outBufferHandle) const { Error error; auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) @@ -148,11 +166,10 @@ *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer); }); - return (ret.isOk()) ? error : kTransactionError; + return static_cast<status_t>((ret.isOk()) ? error : kTransactionError); } -void Mapper::freeBuffer(buffer_handle_t bufferHandle) const -{ +void Gralloc2Mapper::freeBuffer(buffer_handle_t bufferHandle) const { auto buffer = const_cast<native_handle_t*>(bufferHandle); auto ret = mMapper->freeBuffer(buffer); @@ -161,23 +178,29 @@ buffer, error); } -Error Mapper::validateBufferSize(buffer_handle_t bufferHandle, - const IMapper::BufferDescriptorInfo& descriptorInfo, - uint32_t stride) const -{ +status_t Gralloc2Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, + uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, + uint32_t stride) const { if (mMapperV2_1 == nullptr) { - return Error::NONE; + return NO_ERROR; } + IMapper::BufferDescriptorInfo descriptorInfo = {}; + descriptorInfo.width = width; + descriptorInfo.height = height; + descriptorInfo.layerCount = layerCount; + descriptorInfo.format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format); + descriptorInfo.usage = usage; + auto buffer = const_cast<native_handle_t*>(bufferHandle); auto ret = mMapperV2_1->validateBufferSize(buffer, descriptorInfo, stride); - return (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError; + return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError); } -void Mapper::getTransportSize(buffer_handle_t bufferHandle, - uint32_t* outNumFds, uint32_t* outNumInts) const -{ +void Gralloc2Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds, + uint32_t* outNumInts) const { *outNumFds = uint32_t(bufferHandle->numFds); *outNumInts = uint32_t(bufferHandle->numInts); @@ -198,19 +221,24 @@ *outNumInts = tmpNumInts; }); - if (!ret.isOk()) { - error = kTransactionError; - } - ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", - buffer, error); + error = (ret.isOk()) ? error : kTransactionError; + + ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error); } -Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, - const IMapper::Rect& accessRegion, - int acquireFence, void** outData) const -{ +status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, void** outData, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) const { + if (outBytesPerPixel) { + *outBytesPerPixel = -1; + } + if (outBytesPerStride) { + *outBytesPerStride = -1; + } auto buffer = const_cast<native_handle_t*>(bufferHandle); + IMapper::Rect accessRegion = sGralloc2Rect(bounds); + // put acquireFence in a hidl_handle hardware::hidl_handle acquireFenceHandle; NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); @@ -237,15 +265,19 @@ close(acquireFence); } - return (ret.isOk()) ? error : kTransactionError; + error = (ret.isOk()) ? error : kTransactionError; + + ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error); + + return static_cast<status_t>(error); } -Error Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, - const IMapper::Rect& accessRegion, - int acquireFence, YCbCrLayout* outLayout) const -{ +status_t Gralloc2Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, android_ycbcr* ycbcr) const { auto buffer = const_cast<native_handle_t*>(bufferHandle); + IMapper::Rect accessRegion = sGralloc2Rect(bounds); + // put acquireFence in a hidl_handle hardware::hidl_handle acquireFenceHandle; NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); @@ -255,6 +287,7 @@ acquireFenceHandle = h; } + YCbCrLayout layout; Error error; auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion, acquireFenceHandle, @@ -265,19 +298,27 @@ return; } - *outLayout = tmpLayout; + layout = tmpLayout; }); + if (error == Error::NONE) { + ycbcr->y = layout.y; + ycbcr->cb = layout.cb; + ycbcr->cr = layout.cr; + ycbcr->ystride = static_cast<size_t>(layout.yStride); + ycbcr->cstride = static_cast<size_t>(layout.cStride); + ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep); + } + // we own acquireFence even on errors if (acquireFence >= 0) { close(acquireFence); } - return (ret.isOk()) ? error : kTransactionError; + return static_cast<status_t>((ret.isOk()) ? error : kTransactionError); } -int Mapper::unlock(buffer_handle_t bufferHandle) const -{ +int Gralloc2Mapper::unlock(buffer_handle_t bufferHandle) const { auto buffer = const_cast<native_handle_t*>(bufferHandle); int releaseFence = -1; @@ -302,10 +343,7 @@ } }); - if (!ret.isOk()) { - error = kTransactionError; - } - + error = (ret.isOk()) ? error : kTransactionError; if (error != Error::NONE) { ALOGE("unlock(%p) failed with %d", buffer, error); } @@ -313,17 +351,25 @@ return releaseFence; } -Allocator::Allocator(const Mapper& mapper) - : mMapper(mapper) -{ +status_t Gralloc2Mapper::isSupported(uint32_t /*width*/, uint32_t /*height*/, + android::PixelFormat /*format*/, uint32_t /*layerCount*/, + uint64_t /*usage*/, bool* /*outSupported*/) const { + return INVALID_OPERATION; +} + +Gralloc2Allocator::Gralloc2Allocator(const Gralloc2Mapper& mapper) : mMapper(mapper) { mAllocator = IAllocator::getService(); if (mAllocator == nullptr) { - LOG_ALWAYS_FATAL("gralloc-alloc is missing"); + ALOGW("allocator 2.x is not supported"); + return; } } -std::string Allocator::dumpDebugInfo() const -{ +bool Gralloc2Allocator::isLoaded() const { + return mAllocator != nullptr; +} + +std::string Gralloc2Allocator::dumpDebugInfo() const { std::string debugInfo; mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { @@ -333,40 +379,51 @@ return debugInfo; } -Error Allocator::allocate(BufferDescriptor descriptor, uint32_t count, - uint32_t* outStride, buffer_handle_t* outBufferHandles) const -{ - Error error; - auto ret = mAllocator->allocate(descriptor, count, - [&](const auto& tmpError, const auto& tmpStride, - const auto& tmpBuffers) { - error = tmpError; - if (tmpError != Error::NONE) { - return; - } +status_t Gralloc2Allocator::allocate(uint32_t width, uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, uint32_t bufferCount, + uint32_t* outStride, buffer_handle_t* outBufferHandles) const { + IMapper::BufferDescriptorInfo descriptorInfo = {}; + descriptorInfo.width = width; + descriptorInfo.height = height; + descriptorInfo.layerCount = layerCount; + descriptorInfo.format = static_cast<hardware::graphics::common::V1_1::PixelFormat>(format); + descriptorInfo.usage = usage; - // import buffers - for (uint32_t i = 0; i < count; i++) { - error = mMapper.importBuffer(tmpBuffers[i], - &outBufferHandles[i]); - if (error != Error::NONE) { - for (uint32_t j = 0; j < i; j++) { - mMapper.freeBuffer(outBufferHandles[j]); - outBufferHandles[j] = nullptr; - } - return; - } - } + BufferDescriptor descriptor; + status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo), + static_cast<void*>(&descriptor)); + if (error != NO_ERROR) { + return error; + } - *outStride = tmpStride; - }); + auto ret = mAllocator->allocate(descriptor, bufferCount, + [&](const auto& tmpError, const auto& tmpStride, + const auto& tmpBuffers) { + error = static_cast<status_t>(tmpError); + if (tmpError != Error::NONE) { + return; + } + + // import buffers + for (uint32_t i = 0; i < bufferCount; i++) { + error = mMapper.importBuffer(tmpBuffers[i], + &outBufferHandles[i]); + if (error != NO_ERROR) { + for (uint32_t j = 0; j < i; j++) { + mMapper.freeBuffer(outBufferHandles[j]); + outBufferHandles[j] = nullptr; + } + return; + } + } + + *outStride = tmpStride; + }); // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now hardware::IPCThreadState::self()->flushCommands(); - return (ret.isOk()) ? error : kTransactionError; + return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError); } -} // namespace Gralloc2 - } // namespace android
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp new file mode 100644 index 0000000..eb43765 --- /dev/null +++ b/libs/ui/Gralloc3.cpp
@@ -0,0 +1,405 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Gralloc3" + +#include <hidl/ServiceManagement.h> +#include <hwbinder/IPCThreadState.h> +#include <ui/Gralloc3.h> + +#include <inttypes.h> +#include <log/log.h> +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wzero-length-array" +#include <sync/sync.h> +#pragma clang diagnostic pop + +using android::hardware::graphics::allocator::V3_0::IAllocator; +using android::hardware::graphics::common::V1_2::BufferUsage; +using android::hardware::graphics::mapper::V3_0::BufferDescriptor; +using android::hardware::graphics::mapper::V3_0::Error; +using android::hardware::graphics::mapper::V3_0::IMapper; +using android::hardware::graphics::mapper::V3_0::YCbCrLayout; + +namespace android { + +namespace { + +static constexpr Error kTransactionError = Error::NO_RESOURCES; + +uint64_t getValidUsageBits() { + static const uint64_t validUsageBits = []() -> uint64_t { + uint64_t bits = 0; + for (const auto bit : + hardware::hidl_enum_range<hardware::graphics::common::V1_2::BufferUsage>()) { + bits = bits | bit; + } + return bits; + }(); + return validUsageBits; +} + +static inline IMapper::Rect sGralloc3Rect(const Rect& rect) { + IMapper::Rect outRect{}; + outRect.left = rect.left; + outRect.top = rect.top; + outRect.width = rect.width(); + outRect.height = rect.height(); + return outRect; +} +static inline void sBufferDescriptorInfo(uint32_t width, uint32_t height, + android::PixelFormat format, uint32_t layerCount, + uint64_t usage, + IMapper::BufferDescriptorInfo* outDescriptorInfo) { + outDescriptorInfo->width = width; + outDescriptorInfo->height = height; + outDescriptorInfo->layerCount = layerCount; + outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_2::PixelFormat>(format); + outDescriptorInfo->usage = usage; +} + +} // anonymous namespace + +void Gralloc3Mapper::preload() { + android::hardware::preloadPassthroughService<IMapper>(); +} + +Gralloc3Mapper::Gralloc3Mapper() { + mMapper = IMapper::getService(); + if (mMapper == nullptr) { + ALOGW("mapper 3.x is not supported"); + return; + } + if (mMapper->isRemote()) { + LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode"); + } +} + +bool Gralloc3Mapper::isLoaded() const { + return mMapper != nullptr; +} + +status_t Gralloc3Mapper::validateBufferDescriptorInfo( + IMapper::BufferDescriptorInfo* descriptorInfo) const { + uint64_t validUsageBits = getValidUsageBits(); + + if (descriptorInfo->usage & ~validUsageBits) { + ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64, + descriptorInfo->usage & ~validUsageBits); + return BAD_VALUE; + } + return NO_ERROR; +} + +status_t Gralloc3Mapper::createDescriptor(void* bufferDescriptorInfo, + void* outBufferDescriptor) const { + IMapper::BufferDescriptorInfo* descriptorInfo = + static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo); + BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor); + + status_t status = validateBufferDescriptorInfo(descriptorInfo); + if (status != NO_ERROR) { + return status; + } + + Error error; + auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor) { + error = tmpError; + if (error != Error::NONE) { + return; + } + *outDescriptor = tmpDescriptor; + }; + + hardware::Return<void> ret = mMapper->createDescriptor(*descriptorInfo, hidl_cb); + + return static_cast<status_t>((ret.isOk()) ? error : kTransactionError); +} + +status_t Gralloc3Mapper::importBuffer(const hardware::hidl_handle& rawHandle, + buffer_handle_t* outBufferHandle) const { + Error error; + auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) { + error = tmpError; + if (error != Error::NONE) { + return; + } + *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer); + }); + + return static_cast<status_t>((ret.isOk()) ? error : kTransactionError); +} + +void Gralloc3Mapper::freeBuffer(buffer_handle_t bufferHandle) const { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + auto ret = mMapper->freeBuffer(buffer); + + auto error = (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError; + ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d", buffer, error); +} + +status_t Gralloc3Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, + uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, + uint32_t stride) const { + IMapper::BufferDescriptorInfo descriptorInfo; + sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); + + auto buffer = const_cast<native_handle_t*>(bufferHandle); + auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride); + + return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError); +} + +void Gralloc3Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds, + uint32_t* outNumInts) const { + *outNumFds = uint32_t(bufferHandle->numFds); + *outNumInts = uint32_t(bufferHandle->numInts); + + Error error; + auto buffer = const_cast<native_handle_t*>(bufferHandle); + auto ret = mMapper->getTransportSize(buffer, + [&](const auto& tmpError, const auto& tmpNumFds, + const auto& tmpNumInts) { + error = tmpError; + if (error != Error::NONE) { + return; + } + *outNumFds = tmpNumFds; + *outNumInts = tmpNumInts; + }); + + error = (ret.isOk()) ? error : kTransactionError; + + ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error); +} + +status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, void** outData, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) const { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + IMapper::Rect accessRegion = sGralloc3Rect(bounds); + + // put acquireFence in a hidl_handle + hardware::hidl_handle acquireFenceHandle; + NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); + if (acquireFence >= 0) { + auto h = native_handle_init(acquireFenceStorage, 1, 0); + h->data[0] = acquireFence; + acquireFenceHandle = h; + } + + Error error; + auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle, + [&](const auto& tmpError, const auto& tmpData, + const auto& tmpBytesPerPixel, const auto& tmpBytesPerStride) { + error = tmpError; + if (error != Error::NONE) { + return; + } + *outData = tmpData; + if (outBytesPerPixel) { + *outBytesPerPixel = tmpBytesPerPixel; + } + if (outBytesPerStride) { + *outBytesPerStride = tmpBytesPerStride; + } + }); + + // we own acquireFence even on errors + if (acquireFence >= 0) { + close(acquireFence); + } + + error = (ret.isOk()) ? error : kTransactionError; + + ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error); + + return static_cast<status_t>(error); +} + +status_t Gralloc3Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, android_ycbcr* ycbcr) const { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + IMapper::Rect accessRegion = sGralloc3Rect(bounds); + + // put acquireFence in a hidl_handle + hardware::hidl_handle acquireFenceHandle; + NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); + if (acquireFence >= 0) { + auto h = native_handle_init(acquireFenceStorage, 1, 0); + h->data[0] = acquireFence; + acquireFenceHandle = h; + } + + YCbCrLayout layout; + Error error; + auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion, acquireFenceHandle, + [&](const auto& tmpError, const auto& tmpLayout) { + error = tmpError; + if (error != Error::NONE) { + return; + } + + layout = tmpLayout; + }); + + if (error == Error::NONE) { + ycbcr->y = layout.y; + ycbcr->cb = layout.cb; + ycbcr->cr = layout.cr; + ycbcr->ystride = static_cast<size_t>(layout.yStride); + ycbcr->cstride = static_cast<size_t>(layout.cStride); + ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep); + } + + // we own acquireFence even on errors + if (acquireFence >= 0) { + close(acquireFence); + } + + return static_cast<status_t>((ret.isOk()) ? error : kTransactionError); +} + +int Gralloc3Mapper::unlock(buffer_handle_t bufferHandle) const { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + int releaseFence = -1; + Error error; + auto ret = mMapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) { + error = tmpError; + if (error != Error::NONE) { + return; + } + + auto fenceHandle = tmpReleaseFence.getNativeHandle(); + if (fenceHandle && fenceHandle->numFds == 1) { + int fd = dup(fenceHandle->data[0]); + if (fd >= 0) { + releaseFence = fd; + } else { + ALOGD("failed to dup unlock release fence"); + sync_wait(fenceHandle->data[0], -1); + } + } + }); + + if (!ret.isOk()) { + error = kTransactionError; + } + + if (error != Error::NONE) { + ALOGE("unlock(%p) failed with %d", buffer, error); + } + + return releaseFence; +} + +status_t Gralloc3Mapper::isSupported(uint32_t width, uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, + bool* outSupported) const { + IMapper::BufferDescriptorInfo descriptorInfo; + sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); + + Error error; + auto ret = mMapper->isSupported(descriptorInfo, + [&](const auto& tmpError, const auto& tmpSupported) { + error = tmpError; + if (error != Error::NONE) { + return; + } + if (outSupported) { + *outSupported = tmpSupported; + } + }); + + if (!ret.isOk()) { + error = kTransactionError; + } + + if (error != Error::NONE) { + ALOGE("isSupported(%u, %u, %d, %u, ...) failed with %d", width, height, format, layerCount, + error); + } + + return static_cast<status_t>(error); +} + +Gralloc3Allocator::Gralloc3Allocator(const Gralloc3Mapper& mapper) : mMapper(mapper) { + mAllocator = IAllocator::getService(); + if (mAllocator == nullptr) { + ALOGW("allocator 3.x is not supported"); + return; + } +} + +bool Gralloc3Allocator::isLoaded() const { + return mAllocator != nullptr; +} + +std::string Gralloc3Allocator::dumpDebugInfo() const { + std::string debugInfo; + + mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); }); + + return debugInfo; +} + +status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, uint32_t bufferCount, + uint32_t* outStride, buffer_handle_t* outBufferHandles) const { + IMapper::BufferDescriptorInfo descriptorInfo; + sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); + + BufferDescriptor descriptor; + status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo), + static_cast<void*>(&descriptor)); + if (error != NO_ERROR) { + return error; + } + + auto ret = mAllocator->allocate(descriptor, bufferCount, + [&](const auto& tmpError, const auto& tmpStride, + const auto& tmpBuffers) { + error = static_cast<status_t>(tmpError); + if (tmpError != Error::NONE) { + return; + } + + // import buffers + for (uint32_t i = 0; i < bufferCount; i++) { + error = mMapper.importBuffer(tmpBuffers[i], + &outBufferHandles[i]); + if (error != NO_ERROR) { + for (uint32_t j = 0; j < i; j++) { + mMapper.freeBuffer(outBufferHandles[j]); + outBufferHandles[j] = nullptr; + } + return; + } + } + *outStride = tmpStride; + }); + + // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now + hardware::IPCThreadState::self()->flushCommands(); + + return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError); +} + +} // namespace android
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 7670ac6..3fc6a2d 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp
@@ -15,6 +15,7 @@ */ #define LOG_TAG "GraphicBuffer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <ui/GraphicBuffer.h> @@ -22,10 +23,14 @@ #include <grallocusage/GrallocUsageConversion.h> -#include <ui/DetachedBufferHandle.h> +#ifndef LIBUI_IN_VNDK +#include <ui/BufferHubBuffer.h> +#endif // LIBUI_IN_VNDK + #include <ui/Gralloc2.h> #include <ui/GraphicBufferAllocator.h> #include <ui/GraphicBufferMapper.h> +#include <utils/Trace.h> namespace android { @@ -44,6 +49,22 @@ return static_cast<GraphicBuffer *>(anwb); } +GraphicBuffer* GraphicBuffer::fromAHardwareBuffer(AHardwareBuffer* buffer) { + return reinterpret_cast<GraphicBuffer*>(buffer); +} + +GraphicBuffer const* GraphicBuffer::fromAHardwareBuffer(AHardwareBuffer const* buffer) { + return reinterpret_cast<GraphicBuffer const*>(buffer); +} + +AHardwareBuffer* GraphicBuffer::toAHardwareBuffer() { + return reinterpret_cast<AHardwareBuffer*>(this); +} + +AHardwareBuffer const* GraphicBuffer::toAHardwareBuffer() const { + return reinterpret_cast<AHardwareBuffer const*>(this); +} + GraphicBuffer::GraphicBuffer() : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0) @@ -55,7 +76,7 @@ usage_deprecated = 0; usage = 0; layerCount = 0; - handle = NULL; + handle = nullptr; } // deprecated @@ -65,12 +86,11 @@ { } -GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, - PixelFormat inFormat, uint32_t inLayerCount, uint64_t usage, std::string requestorName) - : GraphicBuffer() -{ - mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount, - usage, std::move(requestorName)); +GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, + uint32_t inLayerCount, uint64_t inUsage, std::string requestorName) + : GraphicBuffer() { + mInitCheck = initWithSize(inWidth, inHeight, inFormat, inLayerCount, inUsage, + std::move(requestorName)); } // deprecated @@ -83,22 +103,39 @@ { } -GraphicBuffer::GraphicBuffer(const native_handle_t* handle, - HandleWrapMethod method, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, - uint64_t usage, - uint32_t stride) - : GraphicBuffer() -{ - mInitCheck = initWithHandle(handle, method, width, height, format, - layerCount, usage, stride); +GraphicBuffer::GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method, + uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, + uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride) + : GraphicBuffer() { + mInitCheck = initWithHandle(inHandle, method, inWidth, inHeight, inFormat, inLayerCount, + inUsage, inStride); } +#ifndef LIBUI_IN_VNDK +GraphicBuffer::GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer) : GraphicBuffer() { + if (buffer == nullptr) { + mInitCheck = BAD_VALUE; + return; + } + + mInitCheck = initWithHandle(buffer->duplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE, + buffer->desc().width, buffer->desc().height, + static_cast<PixelFormat>(buffer->desc().format), + buffer->desc().layers, buffer->desc().usage, buffer->desc().stride); + mBufferId = buffer->id(); + mBufferHubBuffer = std::move(buffer); +} +#endif // LIBUI_IN_VNDK + GraphicBuffer::~GraphicBuffer() { + ATRACE_CALL(); if (handle) { free_handle(); } + for (auto& [callback, context] : mDeathCallbacks) { + callback(context, mId); + } } void GraphicBuffer::free_handle() @@ -109,7 +146,7 @@ GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); allocator.free(handle); } - handle = NULL; + handle = nullptr; } status_t GraphicBuffer::initCheck() const { @@ -123,7 +160,6 @@ ANativeWindowBuffer* GraphicBuffer::getNativeBuffer() const { - LOG_ALWAYS_FATAL_IF(this == NULL, "getNativeBuffer() called on NULL GraphicBuffer"); return static_cast<ANativeWindowBuffer*>( const_cast<GraphicBuffer*>(this)); } @@ -145,7 +181,7 @@ if (handle) { GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); allocator.free(handle); - handle = 0; + handle = nullptr; } return initWithSize(inWidth, inHeight, inFormat, inLayerCount, inUsage, "[Reallocation]"); } @@ -158,6 +194,7 @@ if (inFormat != format) return true; if (inLayerCount != layerCount) return true; if ((usage & inUsage) != inUsage) return true; + if ((usage & USAGE_PROTECTED) != (inUsage & USAGE_PROTECTED)) return true; return false; } @@ -184,26 +221,24 @@ return err; } -status_t GraphicBuffer::initWithHandle(const native_handle_t* handle, - HandleWrapMethod method, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, uint64_t usage, - uint32_t stride) -{ - ANativeWindowBuffer::width = static_cast<int>(width); - ANativeWindowBuffer::height = static_cast<int>(height); - ANativeWindowBuffer::stride = static_cast<int>(stride); - ANativeWindowBuffer::format = format; - ANativeWindowBuffer::usage = usage; - ANativeWindowBuffer::usage_deprecated = int(usage); +status_t GraphicBuffer::initWithHandle(const native_handle_t* inHandle, HandleWrapMethod method, + uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, + uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride) { + ANativeWindowBuffer::width = static_cast<int>(inWidth); + ANativeWindowBuffer::height = static_cast<int>(inHeight); + ANativeWindowBuffer::stride = static_cast<int>(inStride); + ANativeWindowBuffer::format = inFormat; + ANativeWindowBuffer::usage = inUsage; + ANativeWindowBuffer::usage_deprecated = int(inUsage); - ANativeWindowBuffer::layerCount = layerCount; + ANativeWindowBuffer::layerCount = inLayerCount; mOwner = (method == WRAP_HANDLE) ? ownNone : ownHandle; if (method == TAKE_UNREGISTERED_HANDLE || method == CLONE_HANDLE) { buffer_handle_t importedHandle; - status_t err = mBufferMapper.importBuffer(handle, width, height, - layerCount, format, usage, stride, &importedHandle); + status_t err = mBufferMapper.importBuffer(inHandle, inWidth, inHeight, inLayerCount, + inFormat, inUsage, inStride, &importedHandle); if (err != NO_ERROR) { initWithHandle(nullptr, WRAP_HANDLE, 0, 0, 0, 0, 0, 0); @@ -211,28 +246,28 @@ } if (method == TAKE_UNREGISTERED_HANDLE) { - native_handle_close(handle); - native_handle_delete(const_cast<native_handle_t*>(handle)); + native_handle_close(inHandle); + native_handle_delete(const_cast<native_handle_t*>(inHandle)); } - handle = importedHandle; - mBufferMapper.getTransportSize(handle, &mTransportNumFds, &mTransportNumInts); + inHandle = importedHandle; + mBufferMapper.getTransportSize(inHandle, &mTransportNumFds, &mTransportNumInts); } - ANativeWindowBuffer::handle = handle; + ANativeWindowBuffer::handle = inHandle; return NO_ERROR; } -status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr) -{ +status_t GraphicBuffer::lock(uint32_t inUsage, void** vaddr, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) { const Rect lockBounds(width, height); - status_t res = lock(inUsage, lockBounds, vaddr); + status_t res = lock(inUsage, lockBounds, vaddr, outBytesPerPixel, outBytesPerStride); return res; } -status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr) -{ +status_t GraphicBuffer::lock(uint32_t inUsage, const Rect& rect, void** vaddr, + int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { if (rect.left < 0 || rect.right > width || rect.top < 0 || rect.bottom > height) { ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)", @@ -240,7 +275,10 @@ width, height); return BAD_VALUE; } - status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr); + + status_t res = getBufferMapper().lock(handle, inUsage, rect, vaddr, outBytesPerPixel, + outBytesPerStride); + return res; } @@ -271,22 +309,22 @@ return res; } -status_t GraphicBuffer::lockAsync(uint32_t inUsage, void** vaddr, int fenceFd) -{ +status_t GraphicBuffer::lockAsync(uint32_t inUsage, void** vaddr, int fenceFd, + int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { const Rect lockBounds(width, height); - status_t res = lockAsync(inUsage, lockBounds, vaddr, fenceFd); + status_t res = + lockAsync(inUsage, lockBounds, vaddr, fenceFd, outBytesPerPixel, outBytesPerStride); return res; } -status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect, - void** vaddr, int fenceFd) -{ - return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd); +status_t GraphicBuffer::lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd, + int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { + return lockAsync(inUsage, inUsage, rect, vaddr, fenceFd, outBytesPerPixel, outBytesPerStride); } -status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage, - uint64_t inConsumerUsage, const Rect& rect, void** vaddr, int fenceFd) -{ +status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage, + const Rect& rect, void** vaddr, int fenceFd, + int32_t* outBytesPerPixel, int32_t* outBytesPerStride) { if (rect.left < 0 || rect.right > width || rect.top < 0 || rect.bottom > height) { ALOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)", @@ -294,8 +332,10 @@ width, height); return BAD_VALUE; } - status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, - inConsumerUsage, rect, vaddr, fenceFd); + + status_t res = getBufferMapper().lockAsync(handle, inProducerUsage, inConsumerUsage, rect, + vaddr, fenceFd, outBytesPerPixel, outBytesPerStride); + return res; } @@ -327,15 +367,37 @@ return res; } +status_t GraphicBuffer::isSupported(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, + uint32_t inLayerCount, uint64_t inUsage, + bool* outSupported) const { + return mBufferMapper.isSupported(inWidth, inHeight, inFormat, inLayerCount, inUsage, + outSupported); +} + size_t GraphicBuffer::getFlattenedSize() const { +#ifndef LIBUI_IN_VNDK + if (mBufferHubBuffer != nullptr) { + return 48; + } +#endif return static_cast<size_t>(13 + (handle ? mTransportNumInts : 0)) * sizeof(int); } size_t GraphicBuffer::getFdCount() const { +#ifndef LIBUI_IN_VNDK + if (mBufferHubBuffer != nullptr) { + return 0; + } +#endif return static_cast<size_t>(handle ? mTransportNumFds : 0); } status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const { +#ifndef LIBUI_IN_VNDK + if (mBufferHubBuffer != nullptr) { + return flattenBufferHubBuffer(buffer, size); + } +#endif size_t sizeNeeded = GraphicBuffer::getFlattenedSize(); if (size < sizeNeeded) return NO_MEMORY; @@ -362,7 +424,7 @@ buf[11] = int32_t(mTransportNumInts); memcpy(fds, handle->data, static_cast<size_t>(mTransportNumFds) * sizeof(int)); memcpy(buf + 13, handle->data + handle->numFds, - static_cast<size_t>(mTransportNumInts) * sizeof(int)); + static_cast<size_t>(mTransportNumInts) * sizeof(int)); } buffer = static_cast<void*>(static_cast<uint8_t*>(buffer) + sizeNeeded); @@ -371,14 +433,13 @@ fds += mTransportNumFds; count -= static_cast<size_t>(mTransportNumFds); } - return NO_ERROR; } -status_t GraphicBuffer::unflatten( - void const*& buffer, size_t& size, int const*& fds, size_t& count) { - if (size < 12 * sizeof(int)) { - android_errorWriteLog(0x534e4554, "114223584"); +status_t GraphicBuffer::unflatten(void const*& buffer, size_t& size, int const*& fds, + size_t& count) { + // Check if size is not smaller than buf[0] is supposed to take. + if (size < sizeof(int)) { return NO_MEMORY; } @@ -393,10 +454,21 @@ } else if (buf[0] == 'GBFR') { // old version, when usage bits were 32-bits flattenWordCount = 12; + } else if (buf[0] == 'BHBB') { // BufferHub backed buffer. +#ifndef LIBUI_IN_VNDK + return unflattenBufferHubBuffer(buffer, size); +#else + return BAD_TYPE; +#endif } else { return BAD_TYPE; } + if (size < 12 * sizeof(int)) { + android_errorWriteLog(0x534e4554, "114223584"); + return NO_MEMORY; + } + const size_t numFds = static_cast<size_t>(buf[10]); const size_t numInts = static_cast<size_t>(buf[11]); @@ -409,7 +481,7 @@ width = height = stride = format = usage_deprecated = 0; layerCount = 0; usage = 0; - handle = NULL; + handle = nullptr; ALOGE("unflatten: numFds or numInts is too large: %zd, %zd", numFds, numInts); return BAD_VALUE; } @@ -437,13 +509,13 @@ } else { usage = uint64_t(usage_deprecated); } - native_handle* h = native_handle_create( - static_cast<int>(numFds), static_cast<int>(numInts)); + native_handle* h = + native_handle_create(static_cast<int>(numFds), static_cast<int>(numInts)); if (!h) { width = height = stride = format = usage_deprecated = 0; layerCount = 0; usage = 0; - handle = NULL; + handle = nullptr; ALOGE("unflatten: native_handle_create failed"); return NO_MEMORY; } @@ -454,7 +526,7 @@ width = height = stride = format = usage_deprecated = 0; layerCount = 0; usage = 0; - handle = NULL; + handle = nullptr; } mId = static_cast<uint64_t>(buf[7]) << 32; @@ -464,7 +536,7 @@ mOwner = ownHandle; - if (handle != 0) { + if (handle != nullptr) { buffer_handle_t importedHandle; status_t err = mBufferMapper.importBuffer(handle, uint32_t(width), uint32_t(height), uint32_t(layerCount), format, usage, uint32_t(stride), &importedHandle); @@ -472,7 +544,7 @@ width = height = stride = format = usage_deprecated = 0; layerCount = 0; usage = 0; - handle = NULL; + handle = nullptr; ALOGE("unflatten: registerBuffer failed: %s (%d)", strerror(-err), err); return err; } @@ -487,28 +559,83 @@ size -= sizeNeeded; fds += numFds; count -= numFds; - return NO_ERROR; } -bool GraphicBuffer::isDetachedBuffer() const { - return mDetachedBufferHandle && mDetachedBufferHandle->isValid(); +void GraphicBuffer::addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context) { + mDeathCallbacks.emplace_back(deathCallback, context); } -status_t GraphicBuffer::setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> channel) { - if (isDetachedBuffer()) { - ALOGW("setDetachedBuffer: there is already a BufferHub channel associated with this " - "GraphicBuffer. Replacing the old one."); +#ifndef LIBUI_IN_VNDK +status_t GraphicBuffer::flattenBufferHubBuffer(void*& buffer, size_t& size) const { + sp<NativeHandle> tokenHandle = mBufferHubBuffer->duplicate(); + if (tokenHandle == nullptr || tokenHandle->handle() == nullptr || + tokenHandle->handle()->numFds != 0) { + return BAD_VALUE; } - mDetachedBufferHandle = std::move(channel); + // Size needed for one label, one number of ints inside the token, one generation number and + // the token itself. + int numIntsInToken = tokenHandle->handle()->numInts; + const size_t sizeNeeded = static_cast<size_t>(3 + numIntsInToken) * sizeof(int); + if (size < sizeNeeded) { + ALOGE("%s: needed size %d, given size %d. Not enough memory.", __FUNCTION__, + static_cast<int>(sizeNeeded), static_cast<int>(size)); + return NO_MEMORY; + } + size -= sizeNeeded; + + int* buf = static_cast<int*>(buffer); + buf[0] = 'BHBB'; + buf[1] = numIntsInToken; + memcpy(buf + 2, tokenHandle->handle()->data, static_cast<size_t>(numIntsInToken) * sizeof(int)); + buf[2 + numIntsInToken] = static_cast<int32_t>(mGenerationNumber); + return NO_ERROR; } -std::unique_ptr<DetachedBufferHandle> GraphicBuffer::takeDetachedBufferHandle() { - return std::move(mDetachedBufferHandle); +status_t GraphicBuffer::unflattenBufferHubBuffer(void const*& buffer, size_t& size) { + const int* buf = static_cast<const int*>(buffer); + int numIntsInToken = buf[1]; + // Size needed for one label, one number of ints inside the token, one generation number and + // the token itself. + const size_t sizeNeeded = static_cast<size_t>(3 + numIntsInToken) * sizeof(int); + if (size < sizeNeeded) { + ALOGE("%s: needed size %d, given size %d. Not enough memory.", __FUNCTION__, + static_cast<int>(sizeNeeded), static_cast<int>(size)); + return NO_MEMORY; + } + size -= sizeNeeded; + native_handle_t* importToken = native_handle_create(/*numFds=*/0, /*numInts=*/numIntsInToken); + memcpy(importToken->data, buf + 2, static_cast<size_t>(buf[1]) * sizeof(int)); + sp<NativeHandle> importTokenHandle = NativeHandle::create(importToken, /*ownHandle=*/true); + std::unique_ptr<BufferHubBuffer> bufferHubBuffer = BufferHubBuffer::import(importTokenHandle); + if (bufferHubBuffer == nullptr || bufferHubBuffer.get() == nullptr) { + return BAD_VALUE; + } + // Reconstruct this GraphicBuffer object using the new BufferHubBuffer object. + if (handle) { + free_handle(); + } + mId = 0; + mGenerationNumber = static_cast<uint32_t>(buf[2 + numIntsInToken]); + mInitCheck = + initWithHandle(bufferHubBuffer->duplicateHandle(), /*method=*/TAKE_UNREGISTERED_HANDLE, + bufferHubBuffer->desc().width, bufferHubBuffer->desc().height, + static_cast<PixelFormat>(bufferHubBuffer->desc().format), + bufferHubBuffer->desc().layers, bufferHubBuffer->desc().usage, + bufferHubBuffer->desc().stride); + mBufferId = bufferHubBuffer->id(); + mBufferHubBuffer.reset(std::move(bufferHubBuffer.get())); + + return NO_ERROR; } +bool GraphicBuffer::isBufferHubBuffer() const { + return mBufferHubBuffer != nullptr; +} +#endif // LIBUI_IN_VNDK + // --------------------------------------------------------------------------- }; // namespace android
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index eaba1ed..0861a1f 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -24,72 +24,82 @@ #include <grallocusage/GrallocUsageConversion.h> +#include <android-base/stringprintf.h> #include <log/log.h> #include <utils/Singleton.h> -#include <utils/String8.h> #include <utils/Trace.h> +#include <ui/Gralloc.h> #include <ui/Gralloc2.h> +#include <ui/Gralloc3.h> #include <ui/GraphicBufferMapper.h> namespace android { // --------------------------------------------------------------------------- +using base::StringAppendF; + ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferAllocator ) Mutex GraphicBufferAllocator::sLock; KeyedVector<buffer_handle_t, GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList; -GraphicBufferAllocator::GraphicBufferAllocator() - : mMapper(GraphicBufferMapper::getInstance()), - mAllocator(std::make_unique<Gralloc2::Allocator>( - mMapper.getGrallocMapper())) -{ +GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) { + mAllocator = std::make_unique<const Gralloc3Allocator>( + reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper())); + if (!mAllocator->isLoaded()) { + mAllocator = std::make_unique<const Gralloc2Allocator>( + reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper())); + } + + if (!mAllocator->isLoaded()) { + LOG_ALWAYS_FATAL("gralloc-allocator is missing"); + } } GraphicBufferAllocator::~GraphicBufferAllocator() {} -void GraphicBufferAllocator::dump(String8& result) const -{ +size_t GraphicBufferAllocator::getTotalSize() const { + Mutex::Autolock _l(sLock); + size_t total = 0; + for (size_t i = 0; i < sAllocList.size(); ++i) { + total += sAllocList.valueAt(i).size; + } + return total; +} + +void GraphicBufferAllocator::dump(std::string& result) const { Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); size_t total = 0; - const size_t SIZE = 4096; - char buffer[SIZE]; - snprintf(buffer, SIZE, "Allocated buffers:\n"); - result.append(buffer); + result.append("Allocated buffers:\n"); const size_t c = list.size(); for (size_t i=0 ; i<c ; i++) { const alloc_rec_t& rec(list.valueAt(i)); if (rec.size) { - snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 - " | %s\n", - list.keyAt(i), rec.size/1024.0, - rec.width, rec.stride, rec.height, rec.layerCount, rec.format, - rec.usage, rec.requestorName.c_str()); + StringAppendF(&result, + "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n", + list.keyAt(i), rec.size / 1024.0, rec.width, rec.stride, rec.height, + rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str()); } else { - snprintf(buffer, SIZE, "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 - " | %s\n", - list.keyAt(i), - rec.width, rec.stride, rec.height, rec.layerCount, rec.format, - rec.usage, rec.requestorName.c_str()); + StringAppendF(&result, + "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n", + list.keyAt(i), rec.width, rec.stride, rec.height, rec.layerCount, + rec.format, rec.usage, rec.requestorName.c_str()); } - result.append(buffer); total += rec.size; } - snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0); - result.append(buffer); + StringAppendF(&result, "Total allocated (estimate): %.2f KB\n", total / 1024.0); - std::string deviceDump = mAllocator->dumpDebugInfo(); - result.append(deviceDump.c_str(), deviceDump.size()); + result.append(mAllocator->dumpDebugInfo()); } void GraphicBufferAllocator::dumpToSystemLog() { - String8 s; + std::string s; GraphicBufferAllocator::getInstance().dump(s); - ALOGD("%s", s.string()); + ALOGD("%s", s.c_str()); } status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, @@ -108,15 +118,12 @@ if (layerCount < 1) layerCount = 1; - Gralloc2::IMapper::BufferDescriptorInfo info = {}; - info.width = width; - info.height = height; - info.layerCount = layerCount; - info.format = static_cast<Gralloc2::PixelFormat>(format); - info.usage = usage; + // TODO(b/72323293, b/72703005): Remove these invalid bits from callers + usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13)); - Gralloc2::Error error = mAllocator->allocate(info, stride, handle); - if (error == Gralloc2::Error::NONE) { + status_t error = + mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle); + if (error == NO_ERROR) { Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); uint32_t bpp = bytesPerPixel(format);
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index 2d8e582..25b7247 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp
@@ -32,7 +32,9 @@ #include <utils/Log.h> #include <utils/Trace.h> +#include <ui/Gralloc.h> #include <ui/Gralloc2.h> +#include <ui/Gralloc3.h> #include <ui/GraphicBuffer.h> #include <system/graphics.h> @@ -43,12 +45,22 @@ ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper ) void GraphicBufferMapper::preloadHal() { - Gralloc2::Mapper::preload(); + Gralloc2Mapper::preload(); + Gralloc3Mapper::preload(); } -GraphicBufferMapper::GraphicBufferMapper() - : mMapper(std::make_unique<const Gralloc2::Mapper>()) -{ +GraphicBufferMapper::GraphicBufferMapper() { + mMapper = std::make_unique<const Gralloc3Mapper>(); + if (!mMapper->isLoaded()) { + mMapper = std::make_unique<const Gralloc2Mapper>(); + mMapperVersion = Version::GRALLOC_2; + } else { + mMapperVersion = Version::GRALLOC_3; + } + + if (!mMapper->isLoaded()) { + LOG_ALWAYS_FATAL("gralloc-mapper is missing"); + } } status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle, @@ -59,22 +71,15 @@ ATRACE_CALL(); buffer_handle_t bufferHandle; - Gralloc2::Error error = mMapper->importBuffer( - hardware::hidl_handle(rawHandle), &bufferHandle); - if (error != Gralloc2::Error::NONE) { + status_t error = mMapper->importBuffer(hardware::hidl_handle(rawHandle), &bufferHandle); + if (error != NO_ERROR) { ALOGW("importBuffer(%p) failed: %d", rawHandle, error); - return static_cast<status_t>(error); + return error; } - Gralloc2::IMapper::BufferDescriptorInfo info = {}; - info.width = width; - info.height = height; - info.layerCount = layerCount; - info.format = static_cast<Gralloc2::PixelFormat>(format); - info.usage = usage; - - error = mMapper->validateBufferSize(bufferHandle, info, stride); - if (error != Gralloc2::Error::NONE) { + error = mMapper->validateBufferSize(bufferHandle, width, height, format, layerCount, usage, + stride); + if (error != NO_ERROR) { ALOGE("validateBufferSize(%p) failed: %d", rawHandle, error); freeBuffer(bufferHandle); return static_cast<status_t>(error); @@ -100,19 +105,10 @@ return NO_ERROR; } -static inline Gralloc2::IMapper::Rect asGralloc2Rect(const Rect& rect) { - Gralloc2::IMapper::Rect outRect{}; - outRect.left = rect.left; - outRect.top = rect.top; - outRect.width = rect.width(); - outRect.height = rect.height(); - return outRect; -} - -status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, - const Rect& bounds, void** vaddr) -{ - return lockAsync(handle, usage, bounds, vaddr, -1); +status_t GraphicBufferMapper::lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, + void** vaddr, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) { + return lockAsync(handle, usage, bounds, vaddr, -1, outBytesPerPixel, outBytesPerStride); } status_t GraphicBufferMapper::lockYCbCr(buffer_handle_t handle, uint32_t usage, @@ -132,27 +128,23 @@ return error; } -status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, - uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd) -{ - return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd); +status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, + void** vaddr, int fenceFd, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) { + return lockAsync(handle, usage, usage, bounds, vaddr, fenceFd, outBytesPerPixel, + outBytesPerStride); } -status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, - uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds, - void** vaddr, int fenceFd) -{ +status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage, + uint64_t consumerUsage, const Rect& bounds, void** vaddr, + int fenceFd, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) { ATRACE_CALL(); const uint64_t usage = static_cast<uint64_t>( android_convertGralloc1To0Usage(producerUsage, consumerUsage)); - Gralloc2::Error error = mMapper->lock(handle, usage, - asGralloc2Rect(bounds), fenceFd, vaddr); - - ALOGW_IF(error != Gralloc2::Error::NONE, "lock(%p, ...) failed: %d", - handle, error); - - return static_cast<status_t>(error); + return mMapper->lock(handle, usage, bounds, fenceFd, vaddr, outBytesPerPixel, + outBytesPerStride); } status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, @@ -160,19 +152,7 @@ { ATRACE_CALL(); - Gralloc2::YCbCrLayout layout; - Gralloc2::Error error = mMapper->lock(handle, usage, - asGralloc2Rect(bounds), fenceFd, &layout); - if (error == Gralloc2::Error::NONE) { - ycbcr->y = layout.y; - ycbcr->cb = layout.cb; - ycbcr->cr = layout.cr; - ycbcr->ystride = static_cast<size_t>(layout.yStride); - ycbcr->cstride = static_cast<size_t>(layout.cStride); - ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep); - } - - return static_cast<status_t>(error); + return mMapper->lock(handle, usage, bounds, fenceFd, ycbcr); } status_t GraphicBufferMapper::unlockAsync(buffer_handle_t handle, int *fenceFd) @@ -184,5 +164,10 @@ return NO_ERROR; } +status_t GraphicBufferMapper::isSupported(uint32_t width, uint32_t height, + android::PixelFormat format, uint32_t layerCount, + uint64_t usage, bool* outSupported) { + return mMapper->isSupported(width, height, format, layerCount, usage, outSupported); +} // --------------------------------------------------------------------------- }; // namespace android
diff --git a/libs/ui/HdrCapabilities.cpp b/libs/ui/HdrCapabilities.cpp index a36911d..a5b3e89 100644 --- a/libs/ui/HdrCapabilities.cpp +++ b/libs/ui/HdrCapabilities.cpp
@@ -24,8 +24,8 @@ #endif HdrCapabilities::~HdrCapabilities() = default; -HdrCapabilities::HdrCapabilities(HdrCapabilities&& other) = default; -HdrCapabilities& HdrCapabilities::operator=(HdrCapabilities&& other) = default; +HdrCapabilities::HdrCapabilities(HdrCapabilities&& other) noexcept = default; +HdrCapabilities& HdrCapabilities::operator=(HdrCapabilities&& other) noexcept = default; size_t HdrCapabilities::getFlattenedSize() const { return sizeof(mMaxLuminance) +
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS new file mode 100644 index 0000000..97ead21 --- /dev/null +++ b/libs/ui/OWNERS
@@ -0,0 +1,7 @@ +lpy@google.com +marissaw@google.com +mathias@google.com +romainguy@google.com +stoza@google.com +jwcai@google.com +tianyuj@google.com
diff --git a/libs/ui/PublicFormat.cpp b/libs/ui/PublicFormat.cpp new file mode 100644 index 0000000..70e3ce7 --- /dev/null +++ b/libs/ui/PublicFormat.cpp
@@ -0,0 +1,155 @@ +/* + * 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. + */ + +#include <ui/GraphicTypes.h> // ui::Dataspace +#include <ui/PublicFormat.h> + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +using ui::Dataspace; + +int mapPublicFormatToHalFormat(PublicFormat f) { + switch (f) { + case PublicFormat::JPEG: + case PublicFormat::DEPTH_POINT_CLOUD: + case PublicFormat::DEPTH_JPEG: + case PublicFormat::HEIC: + return HAL_PIXEL_FORMAT_BLOB; + case PublicFormat::DEPTH16: + return HAL_PIXEL_FORMAT_Y16; + case PublicFormat::RAW_SENSOR: + case PublicFormat::RAW_DEPTH: + return HAL_PIXEL_FORMAT_RAW16; + default: + // Most formats map 1:1 + return static_cast<int>(f); + } +} + +android_dataspace mapPublicFormatToHalDataspace(PublicFormat f) { + Dataspace dataspace; + switch (f) { + case PublicFormat::JPEG: + dataspace = Dataspace::V0_JFIF; + break; + case PublicFormat::DEPTH_POINT_CLOUD: + case PublicFormat::DEPTH16: + case PublicFormat::RAW_DEPTH: + dataspace = Dataspace::DEPTH; + break; + case PublicFormat::RAW_SENSOR: + case PublicFormat::RAW_PRIVATE: + case PublicFormat::RAW10: + case PublicFormat::RAW12: + dataspace = Dataspace::ARBITRARY; + break; + case PublicFormat::YUV_420_888: + case PublicFormat::NV21: + case PublicFormat::YV12: + dataspace = Dataspace::V0_JFIF; + break; + case PublicFormat::DEPTH_JPEG: + dataspace = Dataspace::DYNAMIC_DEPTH; + break; + case PublicFormat::HEIC: + dataspace = Dataspace::HEIF; + break; + default: + // Most formats map to UNKNOWN + dataspace = Dataspace::UNKNOWN; + break; + } + return static_cast<android_dataspace>(dataspace); +} + +PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) { + Dataspace ds = static_cast<Dataspace>(dataSpace); + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_RGBA_FP16: + case HAL_PIXEL_FORMAT_RGBA_1010102: + case HAL_PIXEL_FORMAT_RGB_888: + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_Y8: + case HAL_PIXEL_FORMAT_RAW10: + case HAL_PIXEL_FORMAT_RAW12: + case HAL_PIXEL_FORMAT_YCbCr_420_888: + case HAL_PIXEL_FORMAT_YV12: + // Enums overlap in both name and value + return static_cast<PublicFormat>(format); + case HAL_PIXEL_FORMAT_RAW16: + switch (ds) { + case Dataspace::DEPTH: + return PublicFormat::RAW_DEPTH; + default: + return PublicFormat::RAW_SENSOR; + } + case HAL_PIXEL_FORMAT_RAW_OPAQUE: + // Name differs, though value is the same + return PublicFormat::RAW_PRIVATE; + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + // Name differs, though the value is the same + return PublicFormat::NV16; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + // Name differs, though the value is the same + return PublicFormat::NV21; + case HAL_PIXEL_FORMAT_YCbCr_422_I: + // Name differs, though the value is the same + return PublicFormat::YUY2; + case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: + // Name differs, though the value is the same + return PublicFormat::PRIVATE; + case HAL_PIXEL_FORMAT_Y16: + // Dataspace-dependent + switch (ds) { + case Dataspace::DEPTH: + return PublicFormat::DEPTH16; + default: + // Assume non-depth Y16 is just Y16. + return PublicFormat::Y16; + } + case HAL_PIXEL_FORMAT_BLOB: + // Dataspace-dependent + switch (ds) { + case Dataspace::DEPTH: + return PublicFormat::DEPTH_POINT_CLOUD; + case Dataspace::V0_JFIF: + return PublicFormat::JPEG; + case Dataspace::HEIF: + return PublicFormat::HEIC; + default: + if (dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_DYNAMIC_DEPTH)) { + return PublicFormat::DEPTH_JPEG; + } else { + // Assume otherwise-marked blobs are also JPEG + return PublicFormat::JPEG; + } + } + case HAL_PIXEL_FORMAT_BGRA_8888: + // Not defined in public API + return PublicFormat::UNKNOWN; + + default: + return PublicFormat::UNKNOWN; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ----------------------------------------------------------------------------
diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp index d8702e5..13fed3a 100644 --- a/libs/ui/Rect.cpp +++ b/libs/ui/Rect.cpp
@@ -72,6 +72,14 @@ return *this; } +Rect& Rect::inset(int32_t _left, int32_t _top, int32_t _right, int32_t _bottom) { + this->left += _left; + this->top += _top; + this->right -= _right; + this->bottom -= _bottom; + return *this; +} + const Rect Rect::operator +(const Point& rhs) const { const Rect result(left + rhs.x, top + rhs.y, right + rhs.x, bottom + rhs.y); return result;
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index fe4ae6c..55e3b99 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp
@@ -19,9 +19,9 @@ #include <inttypes.h> #include <limits.h> +#include <android-base/stringprintf.h> + #include <utils/Log.h> -#include <utils/String8.h> -#include <utils/CallStack.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -30,10 +30,18 @@ #include <private/ui/RegionHelper.h> // ---------------------------------------------------------------------------- -#define VALIDATE_REGIONS (false) + +// ### VALIDATE_REGIONS ### +// To enable VALIDATE_REGIONS traces, use the "libui-validate-regions-defaults" +// in Android.bp. Do not #define VALIDATE_REGIONS here as it requires extra libs. + #define VALIDATE_WITH_CORECG (false) // ---------------------------------------------------------------------------- +#if defined(VALIDATE_REGIONS) +#include <utils/CallStack.h> +#endif + #if VALIDATE_WITH_CORECG #include <core/SkRegion.h> #endif @@ -41,6 +49,8 @@ namespace android { // ---------------------------------------------------------------------------- +using base::StringAppendF; + enum { op_nand = region_operator<Rect>::op_nand, op_and = region_operator<Rect>::op_and, @@ -64,7 +74,7 @@ Region::Region(const Region& rhs) : mStorage(rhs.mStorage) { -#if VALIDATE_REGIONS +#if defined(VALIDATE_REGIONS) validate(rhs, "rhs copy-ctor"); #endif } @@ -200,7 +210,7 @@ outputRegion.mStorage, direction_LTR); outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds -#if VALIDATE_REGIONS +#if defined(VALIDATE_REGIONS) validate(outputRegion, "T-Junction free region"); #endif @@ -209,7 +219,7 @@ Region& Region::operator = (const Region& rhs) { -#if VALIDATE_REGIONS +#if defined(VALIDATE_REGIONS) validate(*this, "this->operator="); validate(rhs, "rhs.operator="); #endif @@ -325,6 +335,20 @@ return *this; } +Region& Region::scaleSelf(float sx, float sy) { + size_t count = mStorage.size(); + Rect* rects = mStorage.editArray(); + while (count) { + rects->left = static_cast<int32_t>(rects->left * sx + 0.5f); + rects->right = static_cast<int32_t>(rects->right * sx + 0.5f); + rects->top = static_cast<int32_t>(rects->top * sy + 0.5f); + rects->bottom = static_cast<int32_t>(rects->bottom * sy + 0.5f); + rects++; + count--; + } + return *this; +} + // ---------------------------------------------------------------------------- const Region Region::merge(const Rect& rhs) const { @@ -582,10 +606,12 @@ result = false; ALOGE_IF(!silent, "%s: mStorage size is 2, which is never valid", name); } +#if defined(VALIDATE_REGIONS) if (result == false && !silent) { reg.dump(name); CallStack stack(LOG_TAG); } +#endif return result; } @@ -593,7 +619,7 @@ const Region& lhs, const Region& rhs, int dx, int dy) { -#if VALIDATE_REGIONS +#if defined(VALIDATE_REGIONS) validate(lhs, "boolean_operation (before): lhs"); validate(rhs, "boolean_operation (before): rhs"); validate(dst, "boolean_operation (before): dst"); @@ -613,7 +639,7 @@ operation(r); } -#if VALIDATE_REGIONS +#if defined(VALIDATE_REGIONS) validate(lhs, "boolean_operation: lhs"); validate(rhs, "boolean_operation: rhs"); validate(dst, "boolean_operation: dst"); @@ -711,7 +737,7 @@ return; } -#if VALIDATE_WITH_CORECG || VALIDATE_REGIONS +#if VALIDATE_WITH_CORECG || defined(VALIDATE_REGIONS) boolean_operation(op, dst, lhs, Region(rhs), dx, dy); #else size_t lhs_count; @@ -743,7 +769,7 @@ void Region::translate(Region& reg, int dx, int dy) { if ((dx || dy) && !reg.isEmpty()) { -#if VALIDATE_REGIONS +#if defined(VALIDATE_REGIONS) validate(reg, "translate (before)"); #endif size_t count = reg.mStorage.size(); @@ -753,7 +779,7 @@ rects++; count--; } -#if VALIDATE_REGIONS +#if defined(VALIDATE_REGIONS) validate(reg, "translate (after)"); #endif } @@ -772,7 +798,7 @@ } status_t Region::flatten(void* buffer, size_t size) const { -#if VALIDATE_REGIONS +#if defined(VALIDATE_REGIONS) validate(*this, "Region::flatten"); #endif if (size < getFlattenedSize()) { @@ -803,7 +829,7 @@ } if (numRects > (UINT32_MAX / sizeof(Rect))) { - android_errorWriteWithInfoLog(0x534e4554, "29983260", -1, NULL, 0); + android_errorWriteWithInfoLog(0x534e4554, "29983260", -1, nullptr, 0); return NO_MEMORY; } @@ -819,7 +845,7 @@ result.mStorage.push_back(rect); } -#if VALIDATE_REGIONS +#if defined(VALIDATE_REGIONS) validate(result, "Region::unflatten"); #endif @@ -854,16 +880,14 @@ // ---------------------------------------------------------------------------- -void Region::dump(String8& out, const char* what, uint32_t /* flags */) const -{ +void Region::dump(std::string& out, const char* what, uint32_t /* flags */) const { const_iterator head = begin(); const_iterator const tail = end(); - out.appendFormat(" Region %s (this=%p, count=%" PRIdPTR ")\n", - what, this, tail - head); + StringAppendF(&out, " Region %s (this=%p, count=%" PRIdPTR ")\n", what, this, tail - head); while (head != tail) { - out.appendFormat(" [%3d, %3d, %3d, %3d]\n", head->left, head->top, - head->right, head->bottom); + StringAppendF(&out, " [%3d, %3d, %3d, %3d]\n", head->left, head->top, head->right, + head->bottom); ++head; } }
diff --git a/libs/ui/Size.cpp b/libs/ui/Size.cpp new file mode 100644 index 0000000..d2996d1 --- /dev/null +++ b/libs/ui/Size.cpp
@@ -0,0 +1,24 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ui/Size.h> + +namespace android::ui { + +const Size Size::INVALID{-1, -1}; +const Size Size::EMPTY{0, 0}; + +} // namespace android::ui
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp new file mode 100644 index 0000000..28c3f7b --- /dev/null +++ b/libs/ui/Transform.cpp
@@ -0,0 +1,459 @@ +/* + * Copyright (C) 2007 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 <math.h> + +#include <android-base/stringprintf.h> +#include <cutils/compiler.h> +#include <ui/Region.h> +#include <ui/Transform.h> +#include <utils/String8.h> + +namespace android { +namespace ui { + +Transform::Transform() { + reset(); +} + +Transform::Transform(const Transform& other) + : mMatrix(other.mMatrix), mType(other.mType) { +} + +Transform::Transform(uint32_t orientation) { + set(orientation, 0, 0); +} + +Transform::~Transform() = default; + +static const float EPSILON = 0.0f; + +bool Transform::isZero(float f) { + return fabs(f) <= EPSILON; +} + +bool Transform::absIsOne(float f) { + return isZero(fabs(f) - 1.0f); +} + +Transform Transform::operator * (const Transform& rhs) const +{ + if (CC_LIKELY(mType == IDENTITY)) + return rhs; + + Transform r(*this); + if (rhs.mType == IDENTITY) + return r; + + // TODO: we could use mType to optimize the matrix multiply + const mat33& A(mMatrix); + const mat33& B(rhs.mMatrix); + mat33& D(r.mMatrix); + for (size_t i = 0; i < 3; i++) { + const float v0 = A[0][i]; + const float v1 = A[1][i]; + const float v2 = A[2][i]; + D[0][i] = v0*B[0][0] + v1*B[0][1] + v2*B[0][2]; + D[1][i] = v0*B[1][0] + v1*B[1][1] + v2*B[1][2]; + D[2][i] = v0*B[2][0] + v1*B[2][1] + v2*B[2][2]; + } + r.mType |= rhs.mType; + + // TODO: we could recompute this value from r and rhs + r.mType &= 0xFF; + r.mType |= UNKNOWN_TYPE; + return r; +} + +Transform& Transform::operator=(const Transform& other) { + mMatrix = other.mMatrix; + mType = other.mType; + return *this; +} + +const vec3& Transform::operator [] (size_t i) const { + return mMatrix[i]; +} + +float Transform::tx() const { + return mMatrix[2][0]; +} + +float Transform::ty() const { + return mMatrix[2][1]; +} + +float Transform::sx() const { + return mMatrix[0][0]; +} + +float Transform::sy() const { + return mMatrix[1][1]; +} + +void Transform::reset() { + mType = IDENTITY; + for(size_t i = 0; i < 3; i++) { + vec3& v(mMatrix[i]); + for (size_t j = 0; j < 3; j++) + v[j] = ((i == j) ? 1.0f : 0.0f); + } +} + +void Transform::set(float tx, float ty) +{ + mMatrix[2][0] = tx; + mMatrix[2][1] = ty; + mMatrix[2][2] = 1.0f; + + if (isZero(tx) && isZero(ty)) { + mType &= ~TRANSLATE; + } else { + mType |= TRANSLATE; + } +} + +void Transform::set(float a, float b, float c, float d) +{ + mat33& M(mMatrix); + M[0][0] = a; M[1][0] = b; + M[0][1] = c; M[1][1] = d; + M[0][2] = 0; M[1][2] = 0; + mType = UNKNOWN_TYPE; +} + +status_t Transform::set(uint32_t flags, float w, float h) +{ + if (flags & ROT_INVALID) { + // that's not allowed! + reset(); + return BAD_VALUE; + } + + Transform H, V, R; + if (flags & ROT_90) { + // w & h are inverted when rotating by 90 degrees + std::swap(w, h); + } + + if (flags & FLIP_H) { + H.mType = (FLIP_H << 8) | SCALE; + H.mType |= isZero(w) ? IDENTITY : TRANSLATE; + mat33& M(H.mMatrix); + M[0][0] = -1; + M[2][0] = w; + } + + if (flags & FLIP_V) { + V.mType = (FLIP_V << 8) | SCALE; + V.mType |= isZero(h) ? IDENTITY : TRANSLATE; + mat33& M(V.mMatrix); + M[1][1] = -1; + M[2][1] = h; + } + + if (flags & ROT_90) { + const float original_w = h; + R.mType = (ROT_90 << 8) | ROTATE; + R.mType |= isZero(original_w) ? IDENTITY : TRANSLATE; + mat33& M(R.mMatrix); + M[0][0] = 0; M[1][0] =-1; M[2][0] = original_w; + M[0][1] = 1; M[1][1] = 0; + } + + *this = (R*(H*V)); + return NO_ERROR; +} + +vec2 Transform::transform(const vec2& v) const { + vec2 r; + const mat33& M(mMatrix); + r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]; + r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]; + return r; +} + +vec3 Transform::transform(const vec3& v) const { + vec3 r; + const mat33& M(mMatrix); + r[0] = M[0][0]*v[0] + M[1][0]*v[1] + M[2][0]*v[2]; + r[1] = M[0][1]*v[0] + M[1][1]*v[1] + M[2][1]*v[2]; + r[2] = M[0][2]*v[0] + M[1][2]*v[1] + M[2][2]*v[2]; + return r; +} + +vec2 Transform::transform(int x, int y) const +{ + return transform(vec2(x,y)); +} + +Rect Transform::makeBounds(int w, int h) const +{ + return transform( Rect(w, h) ); +} + +Rect Transform::transform(const Rect& bounds, bool roundOutwards) const +{ + Rect r; + vec2 lt( bounds.left, bounds.top ); + vec2 rt( bounds.right, bounds.top ); + vec2 lb( bounds.left, bounds.bottom ); + vec2 rb( bounds.right, bounds.bottom ); + + lt = transform(lt); + rt = transform(rt); + lb = transform(lb); + rb = transform(rb); + + if (roundOutwards) { + r.left = static_cast<int32_t>(floorf(std::min({lt[0], rt[0], lb[0], rb[0]}))); + r.top = static_cast<int32_t>(floorf(std::min({lt[1], rt[1], lb[1], rb[1]}))); + r.right = static_cast<int32_t>(ceilf(std::max({lt[0], rt[0], lb[0], rb[0]}))); + r.bottom = static_cast<int32_t>(ceilf(std::max({lt[1], rt[1], lb[1], rb[1]}))); + } else { + r.left = static_cast<int32_t>(floorf(std::min({lt[0], rt[0], lb[0], rb[0]}) + 0.5f)); + r.top = static_cast<int32_t>(floorf(std::min({lt[1], rt[1], lb[1], rb[1]}) + 0.5f)); + r.right = static_cast<int32_t>(floorf(std::max({lt[0], rt[0], lb[0], rb[0]}) + 0.5f)); + r.bottom = static_cast<int32_t>(floorf(std::max({lt[1], rt[1], lb[1], rb[1]}) + 0.5f)); + } + + return r; +} + +FloatRect Transform::transform(const FloatRect& bounds) const +{ + vec2 lt(bounds.left, bounds.top); + vec2 rt(bounds.right, bounds.top); + vec2 lb(bounds.left, bounds.bottom); + vec2 rb(bounds.right, bounds.bottom); + + lt = transform(lt); + rt = transform(rt); + lb = transform(lb); + rb = transform(rb); + + FloatRect r; + r.left = std::min({lt[0], rt[0], lb[0], rb[0]}); + r.top = std::min({lt[1], rt[1], lb[1], rb[1]}); + r.right = std::max({lt[0], rt[0], lb[0], rb[0]}); + r.bottom = std::max({lt[1], rt[1], lb[1], rb[1]}); + + return r; +} + +Region Transform::transform(const Region& reg) const +{ + Region out; + if (CC_UNLIKELY(type() > TRANSLATE)) { + if (CC_LIKELY(preserveRects())) { + Region::const_iterator it = reg.begin(); + Region::const_iterator const end = reg.end(); + while (it != end) { + out.orSelf(transform(*it++)); + } + } else { + out.set(transform(reg.bounds())); + } + } else { + int xpos = static_cast<int>(floorf(tx() + 0.5f)); + int ypos = static_cast<int>(floorf(ty() + 0.5f)); + out = reg.translate(xpos, ypos); + } + return out; +} + +uint32_t Transform::type() const +{ + if (mType & UNKNOWN_TYPE) { + // recompute what this transform is + + const mat33& M(mMatrix); + const float a = M[0][0]; + const float b = M[1][0]; + const float c = M[0][1]; + const float d = M[1][1]; + const float x = M[2][0]; + const float y = M[2][1]; + + bool scale = false; + uint32_t flags = ROT_0; + if (isZero(b) && isZero(c)) { + if (a<0) flags |= FLIP_H; + if (d<0) flags |= FLIP_V; + if (!absIsOne(a) || !absIsOne(d)) { + scale = true; + } + } else if (isZero(a) && isZero(d)) { + flags |= ROT_90; + if (b>0) flags |= FLIP_V; + if (c<0) flags |= FLIP_H; + if (!absIsOne(b) || !absIsOne(c)) { + scale = true; + } + } else { + // there is a skew component and/or a non 90 degrees rotation + flags = ROT_INVALID; + } + + mType = flags << 8; + if (flags & ROT_INVALID) { + mType |= UNKNOWN; + } else { + if ((flags & ROT_90) || ((flags & ROT_180) == ROT_180)) + mType |= ROTATE; + if (flags & FLIP_H) + mType ^= SCALE; + if (flags & FLIP_V) + mType ^= SCALE; + if (scale) + mType |= SCALE; + } + + if (!isZero(x) || !isZero(y)) + mType |= TRANSLATE; + } + return mType; +} + +Transform Transform::inverse() const { + // our 3x3 matrix is always of the form of a 2x2 transformation + // followed by a translation: T*M, therefore: + // (T*M)^-1 = M^-1 * T^-1 + Transform result; + if (mType <= TRANSLATE) { + // 1 0 0 + // 0 1 0 + // x y 1 + result = *this; + result.mMatrix[2][0] = -result.mMatrix[2][0]; + result.mMatrix[2][1] = -result.mMatrix[2][1]; + } else { + // a c 0 + // b d 0 + // x y 1 + const mat33& M(mMatrix); + const float a = M[0][0]; + const float b = M[1][0]; + const float c = M[0][1]; + const float d = M[1][1]; + const float x = M[2][0]; + const float y = M[2][1]; + + const float idet = 1.0f / (a*d - b*c); + result.mMatrix[0][0] = d*idet; + result.mMatrix[0][1] = -c*idet; + result.mMatrix[1][0] = -b*idet; + result.mMatrix[1][1] = a*idet; + result.mType = mType; + + vec2 T(-x, -y); + T = result.transform(T); + result.mMatrix[2][0] = T[0]; + result.mMatrix[2][1] = T[1]; + } + return result; +} + +uint32_t Transform::getType() const { + return type() & 0xFF; +} + +uint32_t Transform::getOrientation() const +{ + return (type() >> 8) & 0xFF; +} + +bool Transform::preserveRects() const +{ + return (getOrientation() & ROT_INVALID) ? false : true; +} + +mat4 Transform::asMatrix4() const { + // Internally Transform uses a 3x3 matrix since the transform is meant for + // two-dimensional values. An equivalent 4x4 matrix means inserting an extra + // row and column which adds as an identity transform on the third + // dimension. + + mat4 m = mat4{mat4::NO_INIT}; // NO_INIT since we explicitly set every element + + m[0][0] = mMatrix[0][0]; + m[0][1] = mMatrix[0][1]; + m[0][2] = 0.f; + m[0][3] = mMatrix[0][2]; + + m[1][0] = mMatrix[1][0]; + m[1][1] = mMatrix[1][1]; + m[1][2] = 0.f; + m[1][3] = mMatrix[1][2]; + + m[2][0] = 0.f; + m[2][1] = 0.f; + m[2][2] = 1.f; + m[2][3] = 0.f; + + m[3][0] = mMatrix[2][0]; + m[3][1] = mMatrix[2][1]; + m[3][2] = 0.f; + m[3][3] = mMatrix[2][2]; + + return m; +} + +void Transform::dump(std::string& out, const char* name) const { + using android::base::StringAppendF; + + type(); // Ensure the information in mType is up to date + + const uint32_t type = mType; + const uint32_t orient = type >> 8; + + StringAppendF(&out, "%s 0x%08x (", name, orient); + + if (orient & ROT_INVALID) { + out.append("ROT_INVALID "); + } else { + if (orient & ROT_90) { + out.append("ROT_90 "); + } else { + out.append("ROT_0 "); + } + if (orient & FLIP_V) out.append("FLIP_V "); + if (orient & FLIP_H) out.append("FLIP_H "); + } + + StringAppendF(&out, ") 0x%02x (", type); + + if (!(type & (SCALE | ROTATE | TRANSLATE))) out.append("IDENTITY "); + if (type & SCALE) out.append("SCALE "); + if (type & ROTATE) out.append("ROTATE "); + if (type & TRANSLATE) out.append("TRANSLATE "); + + out.append(")\n"); + + for (size_t i = 0; i < 3; i++) { + StringAppendF(&out, " %.4f %.4f %.4f\n", static_cast<double>(mMatrix[0][i]), + static_cast<double>(mMatrix[1][i]), static_cast<double>(mMatrix[2][i])); + } +} + +void Transform::dump(const char* name) const { + std::string out; + dump(out, name); + ALOGD("%s", out.c_str()); +} + +} // namespace ui +} // namespace android
diff --git a/libs/ui/UiConfig.cpp b/libs/ui/UiConfig.cpp index 7730690..0ac863d 100644 --- a/libs/ui/UiConfig.cpp +++ b/libs/ui/UiConfig.cpp
@@ -18,8 +18,7 @@ namespace android { -void appendUiConfigString(String8& configStr) -{ +void appendUiConfigString(std::string& configStr) { static const char* config = " [libui]"; configStr.append(config);
diff --git a/libs/ui/include/ui/BufferHubBuffer.h b/libs/ui/include/ui/BufferHubBuffer.h new file mode 100644 index 0000000..5ba189c --- /dev/null +++ b/libs/ui/include/ui/BufferHubBuffer.h
@@ -0,0 +1,144 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BUFFER_HUB_BUFFER_H_ +#define ANDROID_BUFFER_HUB_BUFFER_H_ + +#include <android/frameworks/bufferhub/1.0/IBufferClient.h> +#include <android/hardware_buffer.h> +#include <cutils/native_handle.h> +#include <ui/BufferHubDefs.h> +#include <ui/BufferHubEventFd.h> +#include <ui/BufferHubMetadata.h> +#include <utils/NativeHandle.h> + +namespace android { + +class BufferHubBuffer { +public: + // Allocates a standalone BufferHubBuffer. + static std::unique_ptr<BufferHubBuffer> create(uint32_t width, uint32_t height, + uint32_t layerCount, uint32_t format, + uint64_t usage, size_t userMetadataSize); + + // Imports the given token to a BufferHubBuffer. Not taking ownership of the token. + static std::unique_ptr<BufferHubBuffer> import(const sp<NativeHandle>& token); + + BufferHubBuffer(const BufferHubBuffer&) = delete; + void operator=(const BufferHubBuffer&) = delete; + + virtual ~BufferHubBuffer(); + + // Gets ID of the buffer client. All BufferHubBuffer clients derived from the same buffer in + // BufferHub share the same buffer id. + int id() const { return mId; } + + // Returns the buffer description, which is guaranteed to be faithful values from BufferHub. + const AHardwareBuffer_Desc& desc() const { return mBufferDesc; } + + // Duplicate the underlying Gralloc buffer handle. Caller is responsible to free the handle + // after use. + native_handle_t* duplicateHandle() { + return native_handle_clone(mBufferHandle.getNativeHandle()); + } + + const BufferHubEventFd& eventFd() const { return mEventFd; } + + // Returns the current value of MetadataHeader::bufferState. + uint32_t bufferState() const { return mBufferState->load(std::memory_order_acquire); } + + // A state mask which is unique to a buffer hub client among all its siblings sharing the same + // concrete graphic buffer. + uint32_t clientStateMask() const { return mClientStateMask; } + + size_t userMetadataSize() const { return mMetadata.userMetadataSize(); } + + // Returns true if the BufferClient is still alive. + bool isConnected() const { return mBufferClient->ping().isOk(); } + + // Returns true if the buffer is valid: non-null buffer handle, valid id, valid client bit mask, + // valid metadata and valid buffer client + bool isValid() const; + + // Gains the buffer for exclusive write permission. Read permission is implied once a buffer is + // gained. + // The buffer can be gained as long as there is no other client in acquired or gained state. + int gain(); + + // Posts the gained buffer for other buffer clients to use the buffer. + // The buffer can be posted iff the buffer state for this client is gained. + // After posting the buffer, this client is put to released state and does not have access to + // the buffer for this cycle of the usage of the buffer. + int post(); + + // Acquires the buffer for shared read permission. + // The buffer can be acquired iff the buffer state for this client is posted. + int acquire(); + + // Releases the buffer. + // The buffer can be released from any buffer state. + // After releasing the buffer, this client no longer have any permissions to the buffer for the + // current cycle of the usage of the buffer. + int release(); + + // Returns whether the buffer is released by all active clients or not. + bool isReleased() const; + + // Creates a token that stands for this BufferHubBuffer client and could be used for Import to + // create another BufferHubBuffer. The new BufferHubBuffer will share the same underlying + // gralloc buffer and ashmem region for metadata. Not taking ownership of the token. + // Returns a valid token on success, nullptr on failure. + sp<NativeHandle> duplicate(); + +private: + BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format, + uint64_t usage, size_t userMetadataSize); + + BufferHubBuffer(const sp<NativeHandle>& token); + + int initWithBufferTraits(const frameworks::bufferhub::V1_0::BufferTraits& bufferTraits); + + // Global id for the buffer that is consistent across processes. + int mId = 0; + + // Client state mask of this BufferHubBuffer object. It is unique amoung all + // clients/users of the buffer. + uint32_t mClientStateMask = 0U; + + // Stores ground truth of the buffer. + AHardwareBuffer_Desc mBufferDesc; + + // Wraps the gralloc buffer handle of this buffer. + hardware::hidl_handle mBufferHandle; + + // Event fd used for signalling buffer state changes. Shared by all clients of the same buffer. + BufferHubEventFd mEventFd; + + // An ashmem-based metadata object. The same shared memory are mapped to the + // bufferhubd daemon and all buffer clients. + BufferHubMetadata mMetadata; + // Shortcuts to the atomics inside the header of mMetadata. + std::atomic<uint32_t>* mBufferState = nullptr; + std::atomic<uint32_t>* mFenceState = nullptr; + std::atomic<uint32_t>* mActiveClientsBitMask = nullptr; + + // HwBinder backend + sp<frameworks::bufferhub::V1_0::IBufferClient> mBufferClient; +}; + +} // namespace android + +#endif // ANDROID_BUFFER_HUB_BUFFER_H_
diff --git a/libs/ui/include/ui/BufferHubDefs.h b/libs/ui/include/ui/BufferHubDefs.h new file mode 100644 index 0000000..10f274f --- /dev/null +++ b/libs/ui/include/ui/BufferHubDefs.h
@@ -0,0 +1,184 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BUFFER_HUB_DEFS_H_ +#define ANDROID_BUFFER_HUB_DEFS_H_ + +#include <atomic> + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpacked" +// TODO(b/118893702): remove dependency once DvrNativeBufferMetadata moved out of libdvr +#include <dvr/dvr_api.h> +#pragma clang diagnostic pop + +namespace android { + +namespace BufferHubDefs { + +// Single buffer clients (up to 16) ownership signal. +// 32-bit atomic unsigned int. +// Each client takes 2 bits. The first bit locates in the first 16 bits of +// bufferState; the second bit locates in the last 16 bits of bufferState. +// Client states: +// Gained state 11. Exclusive write state. +// Posted state 10. +// Acquired state 01. Shared read state. +// Released state 00. +// +// MSB LSB +// | | +// v v +// [C15|...|C1|C0|C15| ... |C1|C0] + +// Maximum number of clients a buffer can have. +static constexpr int kMaxNumberOfClients = 16; + +// Definition of bit masks. +// MSB LSB +// | kHighBitsMask | kLowbitsMask | +// v v v +// [b31| ... |b16|b15| ... |b0] + +// The location of lower 16 bits in the 32-bit buffer state. +static constexpr uint32_t kLowbitsMask = (1U << kMaxNumberOfClients) - 1U; + +// The location of higher 16 bits in the 32-bit buffer state. +static constexpr uint32_t kHighBitsMask = ~kLowbitsMask; + +// The client bit mask of the first client. +static constexpr uint32_t kFirstClientBitMask = (1U << kMaxNumberOfClients) + 1U; + +// Returns true if any of the client is in gained state. +static inline bool isAnyClientGained(uint32_t state) { + uint32_t highBits = state >> kMaxNumberOfClients; + uint32_t lowBits = state & kLowbitsMask; + return highBits == lowBits && lowBits != 0U; +} + +// Returns true if the input client is in gained state. +static inline bool isClientGained(uint32_t state, uint32_t client_bit_mask) { + return state == client_bit_mask; +} + +// Returns true if any of the client is in posted state. +static inline bool isAnyClientPosted(uint32_t state) { + uint32_t highBits = state >> kMaxNumberOfClients; + uint32_t lowBits = state & kLowbitsMask; + uint32_t postedOrAcquired = highBits ^ lowBits; + return postedOrAcquired & highBits; +} + +// Returns true if the input client is in posted state. +static inline bool isClientPosted(uint32_t state, uint32_t client_bit_mask) { + uint32_t clientBits = state & client_bit_mask; + if (clientBits == 0U) return false; + uint32_t lowBits = clientBits & kLowbitsMask; + return lowBits == 0U; +} + +// Return true if any of the client is in acquired state. +static inline bool isAnyClientAcquired(uint32_t state) { + uint32_t highBits = state >> kMaxNumberOfClients; + uint32_t lowBits = state & kLowbitsMask; + uint32_t postedOrAcquired = highBits ^ lowBits; + return postedOrAcquired & lowBits; +} + +// Return true if the input client is in acquired state. +static inline bool isClientAcquired(uint32_t state, uint32_t client_bit_mask) { + uint32_t clientBits = state & client_bit_mask; + if (clientBits == 0U) return false; + uint32_t highBits = clientBits & kHighBitsMask; + return highBits == 0U; +} + +// Returns true if the input client is in released state. +static inline bool isClientReleased(uint32_t state, uint32_t client_bit_mask) { + return (state & client_bit_mask) == 0U; +} + +// Returns the next available buffer client's client_state_masks. +// @params union_bits. Union of all existing clients' client_state_masks. +static inline uint32_t findNextAvailableClientStateMask(uint32_t union_bits) { + uint32_t lowUnion = union_bits & kLowbitsMask; + if (lowUnion == kLowbitsMask) return 0U; + uint32_t incremented = lowUnion + 1U; + uint32_t difference = incremented ^ lowUnion; + uint32_t newLowBit = (difference + 1U) >> 1; + return newLowBit + (newLowBit << kMaxNumberOfClients); +} + +struct __attribute__((aligned(8))) MetadataHeader { + // Internal data format, which can be updated as long as the size, padding and field alignment + // of the struct is consistent within the same ABI. As this part is subject for future updates, + // it's not stable cross Android version, so don't have it visible from outside of the Android + // platform (include Apps and vendor HAL). + + // Every client takes up one bit from the higher 32 bits and one bit from the lower 32 bits in + // bufferState. + std::atomic<uint32_t> bufferState; + + // Every client takes up one bit in fenceState. Only the lower 32 bits are valid. The upper 32 + // bits are there for easier manipulation, but the value should be ignored. + std::atomic<uint32_t> fenceState; + + // Every client takes up one bit from the higher 32 bits and one bit from the lower 32 bits in + // activeClientsBitMask. + std::atomic<uint32_t> activeClientsBitMask; + + // Explicit padding 4 bytes. + uint32_t padding; + + // The index of the buffer queue where the buffer belongs to. + uint64_t queueIndex; + + // Public data format, which should be updated with caution. See more details in dvr_api.h + DvrNativeBufferMetadata metadata; +}; + +static_assert(sizeof(MetadataHeader) == 128, "Unexpected MetadataHeader size"); +static constexpr size_t kMetadataHeaderSize = sizeof(MetadataHeader); + +/** + * android.frameworks.bufferhub@1.0::BufferTraits.bufferInfo is an opaque handle. See + * https://cs.corp.google.com/android/frameworks/hardware/interfaces/bufferhub/1.0/types.hal for + * more details about android.frameworks.bufferhub@1.0::BufferTraits. + * + * This definition could be changed, but implementation of BufferHubService::buildBufferInfo + * (frameworks/native/services/bufferhub), VtsHalBufferHubV1_0TargetTest + * (frameworks/hardware/interfaces/bufferhub) and BufferHubBuffer::readBufferTraits (libui) will + * also need to be updated. + * + * It's definition should follow the following format: + * { + * NumFds = 2, + * NumInts = 3, + * data[0] = Ashmem fd for BufferHubMetadata, + * data[1] = event fd, + * data[2] = buffer id, + * data[3] = client state bit mask, + * data[4] = user metadata size, + * } + */ +static constexpr int kBufferInfoNumFds = 2; +static constexpr int kBufferInfoNumInts = 3; + +} // namespace BufferHubDefs + +} // namespace android + +#endif // ANDROID_BUFFER_HUB_DEFS_H_
diff --git a/libs/ui/include/ui/BufferHubEventFd.h b/libs/ui/include/ui/BufferHubEventFd.h new file mode 100644 index 0000000..8772304 --- /dev/null +++ b/libs/ui/include/ui/BufferHubEventFd.h
@@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BUFFER_HUB_EVENT_FD_H_ +#define ANDROID_BUFFER_HUB_EVENT_FD_H_ + +#include <android-base/unique_fd.h> +#include <utils/Errors.h> + +namespace android { + +class BufferHubEventFd { +public: + /** + * Constructs a valid event fd. + */ + BufferHubEventFd(); + + /** + * Constructs from a valid event fd. Caller is responsible for the validity of the fd. Takes + * ownership. + */ + BufferHubEventFd(int fd); + + /** + * Returns whether this BufferHubEventFd holds a valid event_fd. + */ + bool isValid() const { return get() >= 0; } + + /** + * Returns the fd number of the BufferHubEventFd object. Note that there is no ownership + * transfer. + */ + int get() const { return mFd.get(); } + + /** + * Signals the eventfd. + */ + status_t signal() const; + + /** + * Clears the signal from this eventfd if it is signaled. + */ + status_t clear() const; + +private: + base::unique_fd mFd; +}; + +} // namespace android + +#endif // ANDROID_BUFFER_HUB_EVENT_FD_H_
diff --git a/libs/ui/include/ui/BufferHubMetadata.h b/libs/ui/include/ui/BufferHubMetadata.h new file mode 100644 index 0000000..3482507 --- /dev/null +++ b/libs/ui/include/ui/BufferHubMetadata.h
@@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BUFFER_HUB_METADATA_H_ +#define ANDROID_BUFFER_HUB_METADATA_H_ + +#include <android-base/unique_fd.h> +#include <ui/BufferHubDefs.h> + +namespace android { + +namespace { +using base::unique_fd; +} // namespace + +class BufferHubMetadata { +public: + // Creates a new BufferHubMetadata backed by an ashmem region. + // + // @param userMetadataSize Size in bytes of the user defined metadata. The entire metadata + // shared memory region to be allocated is the size of canonical + // BufferHubDefs::MetadataHeader plus userMetadataSize. + static BufferHubMetadata create(size_t userMetadataSize); + + // Imports an existing BufferHubMetadata from an ashmem FD. + // + // @param ashmemFd Ashmem file descriptor representing an ashmem region. + static BufferHubMetadata import(unique_fd ashmemFd); + + BufferHubMetadata() = default; + + BufferHubMetadata(BufferHubMetadata&& other) { *this = std::move(other); } + + ~BufferHubMetadata(); + + BufferHubMetadata& operator=(BufferHubMetadata&& other) { + if (this != &other) { + mUserMetadataSize = other.mUserMetadataSize; + other.mUserMetadataSize = 0; + + mAshmemFd = std::move(other.mAshmemFd); + + // The old raw mMetadataHeader pointer must be cleared, otherwise the destructor will + // automatically mummap() the shared memory. + mMetadataHeader = other.mMetadataHeader; + other.mMetadataHeader = nullptr; + } + return *this; + } + + // Returns true if the metadata is valid, i.e. the metadata has a valid ashmem fd and the ashmem + // has been mapped into virtual address space. + bool isValid() const { return mAshmemFd.get() != -1 && mMetadataHeader != nullptr; } + + size_t userMetadataSize() const { return mUserMetadataSize; } + size_t metadataSize() const { return mUserMetadataSize + BufferHubDefs::kMetadataHeaderSize; } + + const unique_fd& ashmemFd() const { return mAshmemFd; } + BufferHubDefs::MetadataHeader* metadataHeader() { return mMetadataHeader; } + +private: + BufferHubMetadata(size_t userMetadataSize, unique_fd ashmemFd, + BufferHubDefs::MetadataHeader* metadataHeader); + + BufferHubMetadata(const BufferHubMetadata&) = delete; + void operator=(const BufferHubMetadata&) = delete; + + size_t mUserMetadataSize = 0; + unique_fd mAshmemFd; + BufferHubDefs::MetadataHeader* mMetadataHeader = nullptr; +}; + +} // namespace android + +#endif // ANDROID_BUFFER_HUB_METADATA_H_
diff --git a/libs/ui/include/ui/BufferQueueDefs.h b/libs/ui/include/ui/BufferQueueDefs.h index 56de181..0fecda9 100644 --- a/libs/ui/include/ui/BufferQueueDefs.h +++ b/libs/ui/include/ui/BufferQueueDefs.h
@@ -23,6 +23,16 @@ // Attempts at runtime to increase the number of buffers past this // will fail. static constexpr int NUM_BUFFER_SLOTS = 64; + + enum { + // A flag returned by dequeueBuffer when the client needs to call + // requestBuffer immediately thereafter. + BUFFER_NEEDS_REALLOCATION = 0x1, + // A flag returned by dequeueBuffer when all mirrored slots should be + // released by the client. This flag should always be processed first. + RELEASE_ALL_BUFFERS = 0x2, + }; + } // namespace BufferQueueDefs } // namespace android
diff --git a/libs/ui/include/ui/ColorSpace.h b/libs/ui/include/ui/ColorSpace.h index 8ccf6d3..241ec10 100644 --- a/libs/ui/include/ui/ColorSpace.h +++ b/libs/ui/include/ui/ColorSpace.h
@@ -250,8 +250,8 @@ // axis is thus already flipped // The source color space must define its values in the domain [0..1] // The generated LUT transforms from gamma space to gamma space - static std::unique_ptr<float3> createLUT(uint32_t size, - const ColorSpace& src, const ColorSpace& dst); + static std::unique_ptr<float3[]> createLUT(uint32_t size, const ColorSpace& src, + const ColorSpace& dst); private: static constexpr mat3 computeXYZMatrix(
diff --git a/libs/ui/include/ui/ConfigStoreTypes.h b/libs/ui/include/ui/ConfigStoreTypes.h new file mode 100644 index 0000000..4445ae9 --- /dev/null +++ b/libs/ui/include/ui/ConfigStoreTypes.h
@@ -0,0 +1,37 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +// android::ui::* in this header file will alias different types as +// the HIDL interface is updated. +namespace android { +namespace ui { + +struct CieXyz { + float X; + float Y; + float Z; +}; +struct DisplayPrimaries { + CieXyz red; + CieXyz green; + CieXyz blue; + CieXyz white; +}; + +} // namespace ui +} // namespace android
diff --git a/libs/ui/include/ui/DetachedBufferHandle.h b/libs/ui/include/ui/DetachedBufferHandle.h deleted file mode 100644 index f3c328d..0000000 --- a/libs/ui/include/ui/DetachedBufferHandle.h +++ /dev/null
@@ -1,52 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_DETACHED_BUFFER_HUB_HANDLE_H -#define ANDROID_DETACHED_BUFFER_HUB_HANDLE_H - -#include <pdx/channel_handle.h> - -#include <memory> - -namespace android { - -// A wrapper that holds a pdx::LocalChannelHandle object. From the handle, a BufferHub buffer can be -// created. Current implementation assumes that the underlying transport is using libpdx (thus -// holding a pdx::LocalChannelHandle object), but future implementation can change it to a Binder -// backend if ever needed. -class DetachedBufferHandle { -public: - static std::unique_ptr<DetachedBufferHandle> Create(pdx::LocalChannelHandle handle) { - return std::unique_ptr<DetachedBufferHandle>(new DetachedBufferHandle(std::move(handle))); - } - - // Accessors to get or take the internal pdx::LocalChannelHandle. - pdx::LocalChannelHandle& handle() { return mHandle; } - const pdx::LocalChannelHandle& handle() const { return mHandle; } - - // Returns whether the DetachedBufferHandle holds a BufferHub channel. - bool isValid() const { return mHandle.valid(); } - -private: - // Constructs a DetachedBufferHandle from a pdx::LocalChannelHandle. - explicit DetachedBufferHandle(pdx::LocalChannelHandle handle) : mHandle(std::move(handle)) {} - - pdx::LocalChannelHandle mHandle; -}; - -} // namespace android - -#endif // ANDROID_DETACHED_BUFFER_HUB_HANDLE_H
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h index 94caf6b..8976d2d 100644 --- a/libs/ui/include/ui/DisplayInfo.h +++ b/libs/ui/include/ui/DisplayInfo.h
@@ -35,6 +35,8 @@ bool secure{false}; nsecs_t appVsyncOffset{0}; nsecs_t presentationDeadline{0}; + uint32_t viewportW{0}; + uint32_t viewportH{0}; }; /* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
diff --git a/libs/ui/include/ui/DisplayedFrameStats.h b/libs/ui/include/ui/DisplayedFrameStats.h new file mode 100644 index 0000000..7a70ea1 --- /dev/null +++ b/libs/ui/include/ui/DisplayedFrameStats.h
@@ -0,0 +1,41 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <vector> + +namespace android { + +struct DisplayedFrameStats { + /* The number of frames represented by this sample. */ + uint64_t numFrames = 0; + /* A histogram counting how many times a pixel of a given value was displayed onscreen for + * FORMAT_COMPONENT_0. The buckets of the histogram are evenly weighted, the number of buckets + * is device specific. eg, for RGBA_8888, if sampleComponent0 is {10, 6, 4, 1} this means that + * 10 red pixels were displayed onscreen in range 0x00->0x3F, 6 red pixels + * were displayed onscreen in range 0x40->0x7F, etc. + */ + std::vector<uint64_t> component_0_sample = {}; + /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_1. */ + std::vector<uint64_t> component_1_sample = {}; + /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_2. */ + std::vector<uint64_t> component_2_sample = {}; + /* The same sample definition as sampleComponent0, but for FORMAT_COMPONENT_3. */ + std::vector<uint64_t> component_3_sample = {}; +}; + +} // namespace android
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h index ec67fa9..6efecd3 100644 --- a/libs/ui/include/ui/Fence.h +++ b/libs/ui/include/ui/Fence.h
@@ -99,6 +99,12 @@ // be returned and errno will indicate the problem. int dup() const; + // Return the underlying file descriptor without giving up ownership. The + // returned file descriptor is only valid for as long as the owning Fence + // object lives. (If the situation is unclear, dup() is always a safer + // option.) + int get() const { return mFenceFd.get(); } + // getSignalTime returns the system monotonic clock time at which the // fence transitioned to the signaled state. If the fence is not signaled // then SIGNAL_TIME_PENDING is returned. If the fence is invalid or if an
diff --git a/libs/ui/include/ui/FenceTime.h b/libs/ui/include/ui/FenceTime.h index 871fcf2..ecba7f7 100644 --- a/libs/ui/include/ui/FenceTime.h +++ b/libs/ui/include/ui/FenceTime.h
@@ -19,6 +19,7 @@ #include <ui/Fence.h> #include <utils/Flattenable.h> +#include <utils/Mutex.h> #include <utils/Timers.h> #include <atomic> @@ -113,11 +114,6 @@ void signalForTest(nsecs_t signalTime); - // Override new and delete since this needs 8-byte alignment, which - // is not guaranteed on x86. - static void* operator new(size_t nbytes) noexcept; - static void operator delete(void *p); - private: // For tests only. If forceValidForTest is true, then getSignalTime will // never return SIGNAL_TIME_INVALID and isValid will always return true. @@ -164,7 +160,7 @@ private: mutable std::mutex mMutex; - std::queue<std::weak_ptr<FenceTime>> mQueue; + std::queue<std::weak_ptr<FenceTime>> mQueue GUARDED_BY(mMutex); }; // Used by test code to create or get FenceTimes for a given Fence.
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h index 6a7479a..4cd9a0b 100644 --- a/libs/ui/include/ui/FloatRect.h +++ b/libs/ui/include/ui/FloatRect.h
@@ -28,7 +28,7 @@ float getHeight() const { return bottom - top; } FloatRect intersect(const FloatRect& other) const { - return { + FloatRect intersection = { // Inline to avoid tromping on other min/max defines or adding a // dependency on STL (left > other.left) ? left : other.left, @@ -36,6 +36,10 @@ (right < other.right) ? right : other.right, (bottom < other.bottom) ? bottom : other.bottom }; + if (intersection.getWidth() < 0 || intersection.getHeight() < 0) { + return {0, 0, 0, 0}; + } + return intersection; } float left = 0.0f;
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h new file mode 100644 index 0000000..6cc23f0 --- /dev/null +++ b/libs/ui/include/ui/Gralloc.h
@@ -0,0 +1,102 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_UI_GRALLOC_H +#define ANDROID_UI_GRALLOC_H + +#include <string> + +#include <hidl/HidlSupport.h> +#include <ui/PixelFormat.h> +#include <ui/Rect.h> +#include <utils/StrongPointer.h> + +namespace android { + +// A wrapper to IMapper +class GrallocMapper { +public: + virtual ~GrallocMapper(); + + virtual bool isLoaded() const = 0; + + virtual status_t createDescriptor(void* bufferDescriptorInfo, + void* outBufferDescriptor) const = 0; + + // Import a buffer that is from another HAL, another process, or is + // cloned. + // + // The returned handle must be freed with freeBuffer. + virtual status_t importBuffer(const hardware::hidl_handle& rawHandle, + buffer_handle_t* outBufferHandle) const = 0; + + virtual void freeBuffer(buffer_handle_t bufferHandle) const = 0; + + virtual status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, + uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, + uint32_t stride) const = 0; + + virtual void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds, + uint32_t* outNumInts) const = 0; + + // The ownership of acquireFence is always transferred to the callee, even + // on errors. + virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, void** outData, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) const = 0; + + // The ownership of acquireFence is always transferred to the callee, even + // on errors. + virtual status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, android_ycbcr* ycbcr) const = 0; + + // unlock returns a fence sync object (or -1) and the fence sync object is + // owned by the caller + virtual int unlock(buffer_handle_t bufferHandle) const = 0; + + // isSupported queries whether or not a buffer with the given width, height, + // format, layer count, and usage can be allocated on the device. If + // *outSupported is set to true, a buffer with the given specifications may be successfully + // allocated if resources are available. If false, a buffer with the given specifications will + // never successfully allocate on this device. Note that this function is not guaranteed to be + // supported on all devices, in which case a status_t of INVALID_OPERATION will be returned. + virtual status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, bool* outSupported) const = 0; +}; + +// A wrapper to IAllocator +class GrallocAllocator { +public: + virtual ~GrallocAllocator(); + + virtual bool isLoaded() const = 0; + + virtual std::string dumpDebugInfo() const = 0; + + /* + * The returned buffers are already imported and must not be imported + * again. outBufferHandles must point to a space that can contain at + * least "bufferCount" buffer_handle_t. + */ + virtual status_t allocate(uint32_t width, uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, uint32_t bufferCount, + uint32_t* outStride, buffer_handle_t* outBufferHandles) const = 0; +}; + +} // namespace android + +#endif // ANDROID_UI_GRALLOC_H
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h index 5a8dbda..948f597 100644 --- a/libs/ui/include/ui/Gralloc2.h +++ b/libs/ui/include/ui/Gralloc2.h
@@ -23,119 +23,75 @@ #include <android/hardware/graphics/common/1.1/types.h> #include <android/hardware/graphics/mapper/2.0/IMapper.h> #include <android/hardware/graphics/mapper/2.1/IMapper.h> +#include <ui/Gralloc.h> +#include <ui/PixelFormat.h> +#include <ui/Rect.h> #include <utils/StrongPointer.h> namespace android { -namespace Gralloc2 { - -using hardware::graphics::allocator::V2_0::IAllocator; -using hardware::graphics::common::V1_1::BufferUsage; -using hardware::graphics::common::V1_1::PixelFormat; -using hardware::graphics::mapper::V2_1::IMapper; -using hardware::graphics::mapper::V2_0::BufferDescriptor; -using hardware::graphics::mapper::V2_0::Error; -using hardware::graphics::mapper::V2_0::YCbCrLayout; - -// A wrapper to IMapper -class Mapper { +class Gralloc2Mapper : public GrallocMapper { public: static void preload(); - Mapper(); + Gralloc2Mapper(); - Error createDescriptor( - const IMapper::BufferDescriptorInfo& descriptorInfo, - BufferDescriptor* outDescriptor) const; + bool isLoaded() const override; - // Import a buffer that is from another HAL, another process, or is - // cloned. - // - // The returned handle must be freed with freeBuffer. - Error importBuffer(const hardware::hidl_handle& rawHandle, - buffer_handle_t* outBufferHandle) const; + status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override; - void freeBuffer(buffer_handle_t bufferHandle) const; + status_t importBuffer(const hardware::hidl_handle& rawHandle, + buffer_handle_t* outBufferHandle) const override; - Error validateBufferSize(buffer_handle_t bufferHandle, - const IMapper::BufferDescriptorInfo& descriptorInfo, - uint32_t stride) const; + void freeBuffer(buffer_handle_t bufferHandle) const override; - void getTransportSize(buffer_handle_t bufferHandle, - uint32_t* outNumFds, uint32_t* outNumInts) const; + status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height, + android::PixelFormat format, uint32_t layerCount, uint64_t usage, + uint32_t stride) const override; - // The ownership of acquireFence is always transferred to the callee, even - // on errors. - Error lock(buffer_handle_t bufferHandle, uint64_t usage, - const IMapper::Rect& accessRegion, - int acquireFence, void** outData) const; + void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds, + uint32_t* outNumInts) const override; - // The ownership of acquireFence is always transferred to the callee, even - // on errors. - Error lock(buffer_handle_t bufferHandle, uint64_t usage, - const IMapper::Rect& accessRegion, - int acquireFence, YCbCrLayout* outLayout) const; + status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, void** outData, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) const override; - // unlock returns a fence sync object (or -1) and the fence sync object is - // owned by the caller - int unlock(buffer_handle_t bufferHandle) const; + status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, android_ycbcr* ycbcr) const override; + + int unlock(buffer_handle_t bufferHandle) const override; + + status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, bool* outSupported) const override; private: // Determines whether the passed info is compatible with the mapper. - Error validateBufferDescriptorInfo( - const IMapper::BufferDescriptorInfo& descriptorInfo) const; + status_t validateBufferDescriptorInfo( + hardware::graphics::mapper::V2_1::IMapper::BufferDescriptorInfo* descriptorInfo) const; sp<hardware::graphics::mapper::V2_0::IMapper> mMapper; - sp<IMapper> mMapperV2_1; + sp<hardware::graphics::mapper::V2_1::IMapper> mMapperV2_1; }; -// A wrapper to IAllocator -class Allocator { +class Gralloc2Allocator : public GrallocAllocator { public: // An allocator relies on a mapper, and that mapper must be alive at all // time. - Allocator(const Mapper& mapper); + Gralloc2Allocator(const Gralloc2Mapper& mapper); - std::string dumpDebugInfo() const; + bool isLoaded() const override; - /* - * The returned buffers are already imported and must not be imported - * again. outBufferHandles must point to a space that can contain at - * least "count" buffer_handle_t. - */ - Error allocate(BufferDescriptor descriptor, uint32_t count, - uint32_t* outStride, buffer_handle_t* outBufferHandles) const; + std::string dumpDebugInfo() const override; - Error allocate(BufferDescriptor descriptor, - uint32_t* outStride, buffer_handle_t* outBufferHandle) const - { - return allocate(descriptor, 1, outStride, outBufferHandle); - } - - Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, uint32_t count, - uint32_t* outStride, buffer_handle_t* outBufferHandles) const - { - BufferDescriptor descriptor; - Error error = mMapper.createDescriptor(descriptorInfo, &descriptor); - if (error == Error::NONE) { - error = allocate(descriptor, count, outStride, outBufferHandles); - } - return error; - } - - Error allocate(const IMapper::BufferDescriptorInfo& descriptorInfo, - uint32_t* outStride, buffer_handle_t* outBufferHandle) const - { - return allocate(descriptorInfo, 1, outStride, outBufferHandle); - } + status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, + uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + buffer_handle_t* outBufferHandles) const override; private: - const Mapper& mMapper; - sp<IAllocator> mAllocator; + const Gralloc2Mapper& mMapper; + sp<hardware::graphics::allocator::V2_0::IAllocator> mAllocator; }; -} // namespace Gralloc2 - } // namespace android #endif // ANDROID_UI_GRALLOC2_H
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h new file mode 100644 index 0000000..0965f52 --- /dev/null +++ b/libs/ui/include/ui/Gralloc3.h
@@ -0,0 +1,95 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_UI_GRALLOC3_H +#define ANDROID_UI_GRALLOC3_H + +#include <string> + +#include <android/hardware/graphics/allocator/3.0/IAllocator.h> +#include <android/hardware/graphics/common/1.1/types.h> +#include <android/hardware/graphics/mapper/3.0/IMapper.h> +#include <ui/Gralloc.h> +#include <ui/PixelFormat.h> +#include <ui/Rect.h> +#include <utils/StrongPointer.h> + +namespace android { + +class Gralloc3Mapper : public GrallocMapper { +public: + static void preload(); + + Gralloc3Mapper(); + + bool isLoaded() const override; + + status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override; + + status_t importBuffer(const hardware::hidl_handle& rawHandle, + buffer_handle_t* outBufferHandle) const override; + + void freeBuffer(buffer_handle_t bufferHandle) const override; + + status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height, + android::PixelFormat format, uint32_t layerCount, uint64_t usage, + uint32_t stride) const override; + + void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds, + uint32_t* outNumInts) const override; + + status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, void** outData, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) const override; + + status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, android_ycbcr* ycbcr) const override; + + int unlock(buffer_handle_t bufferHandle) const override; + + status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, bool* outSupported) const override; + +private: + // Determines whether the passed info is compatible with the mapper. + status_t validateBufferDescriptorInfo( + hardware::graphics::mapper::V3_0::IMapper::BufferDescriptorInfo* descriptorInfo) const; + + sp<hardware::graphics::mapper::V3_0::IMapper> mMapper; +}; + +class Gralloc3Allocator : public GrallocAllocator { +public: + // An allocator relies on a mapper, and that mapper must be alive at all + // time. + Gralloc3Allocator(const Gralloc3Mapper& mapper); + + bool isLoaded() const override; + + std::string dumpDebugInfo() const override; + + status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, + uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + buffer_handle_t* outBufferHandles) const override; + +private: + const Gralloc3Mapper& mMapper; + sp<hardware::graphics::allocator::V3_0::IAllocator> mAllocator; +}; + +} // namespace android + +#endif // ANDROID_UI_GRALLOC3_H
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index cc38982..c195342 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -21,8 +21,12 @@ #include <sys/types.h> #include <string> +#include <utility> +#include <vector> +#include <android/hardware_buffer.h> #include <ui/ANativeObjectBase.h> +#include <ui/GraphicBufferMapper.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> #include <utils/Flattenable.h> @@ -34,9 +38,14 @@ namespace android { -class DetachedBufferHandle; +#ifndef LIBUI_IN_VNDK +class BufferHubBuffer; +#endif // LIBUI_IN_VNDK + class GraphicBufferMapper; +using GraphicBufferDeathCallback = std::function<void(void* /*context*/, uint64_t bufferId)>; + // =========================================================================== // GraphicBuffer // =========================================================================== @@ -75,6 +84,10 @@ static sp<GraphicBuffer> from(ANativeWindowBuffer *); + static GraphicBuffer* fromAHardwareBuffer(AHardwareBuffer*); + static GraphicBuffer const* fromAHardwareBuffer(AHardwareBuffer const*); + AHardwareBuffer* toAHardwareBuffer(); + AHardwareBuffer const* toAHardwareBuffer() const; // Create a GraphicBuffer to be unflatten'ed into or be reallocated. GraphicBuffer(); @@ -118,24 +131,27 @@ // cannot be used directly, such as one from hidl_handle. CLONE_HANDLE, }; - GraphicBuffer(const native_handle_t* handle, HandleWrapMethod method, - uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t stride); + GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method, uint32_t inWidth, + uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, + uint32_t inStride); // These functions are deprecated because they only take 32 bits of usage - GraphicBuffer(const native_handle_t* handle, HandleWrapMethod method, - uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, - uint32_t usage, uint32_t stride) - : GraphicBuffer(handle, method, width, height, format, layerCount, - static_cast<uint64_t>(usage), stride) {} + GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod method, uint32_t inWidth, + uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage, + uint32_t inStride) + : GraphicBuffer(inHandle, method, inWidth, inHeight, inFormat, inLayerCount, + static_cast<uint64_t>(inUsage), inStride) {} GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint32_t inUsage, uint32_t inStride, native_handle_t* inHandle, bool keepOwnership); GraphicBuffer(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inUsage, std::string requestorName = "<Unknown>"); +#ifndef LIBUI_IN_VNDK + // Create a GraphicBuffer from an existing BufferHubBuffer. + GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer); +#endif // LIBUI_IN_VNDK + // return status status_t initCheck() const; @@ -147,6 +163,7 @@ uint32_t getLayerCount() const { return static_cast<uint32_t>(layerCount); } Rect getBounds() const { return Rect(width, height); } uint64_t getId() const { return mId; } + int32_t getBufferId() const { return mBufferId; } uint32_t getGenerationNumber() const { return mGenerationNumber; } void setGenerationNumber(uint32_t generation) { @@ -162,24 +179,35 @@ bool needsReallocation(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage); - status_t lock(uint32_t inUsage, void** vaddr); - status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr); + // For the following two lock functions, if bytesPerStride or bytesPerPixel + // are unknown or variable, -1 will be returned + status_t lock(uint32_t inUsage, void** vaddr, int32_t* outBytesPerPixel = nullptr, + int32_t* outBytesPerStride = nullptr); + status_t lock(uint32_t inUsage, const Rect& rect, void** vaddr, + int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); // For HAL_PIXEL_FORMAT_YCbCr_420_888 status_t lockYCbCr(uint32_t inUsage, android_ycbcr *ycbcr); status_t lockYCbCr(uint32_t inUsage, const Rect& rect, android_ycbcr *ycbcr); status_t unlock(); - status_t lockAsync(uint32_t inUsage, void** vaddr, int fenceFd); - status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, - int fenceFd); - status_t lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage, - const Rect& rect, void** vaddr, int fenceFd); + // For the following three lockAsync functions, if bytesPerStride or bytesPerPixel + // are unknown or variable, -1 will be returned + status_t lockAsync(uint32_t inUsage, void** vaddr, int fenceFd, + int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); + status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd, + int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); + status_t lockAsync(uint64_t inProducerUsage, uint64_t inConsumerUsage, const Rect& rect, + void** vaddr, int fenceFd, int32_t* outBytesPerPixel = nullptr, + int32_t* outBytesPerStride = nullptr); status_t lockAsyncYCbCr(uint32_t inUsage, android_ycbcr *ycbcr, int fenceFd); status_t lockAsyncYCbCr(uint32_t inUsage, const Rect& rect, android_ycbcr *ycbcr, int fenceFd); status_t unlockAsync(int *fenceFd); + status_t isSupported(uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, + uint32_t inLayerCount, uint64_t inUsage, bool* outSupported) const; + ANativeWindowBuffer* getNativeBuffer() const; // for debugging @@ -191,10 +219,16 @@ status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); - // Sets and takes DetachedBuffer. Should only be called from BufferHub. - bool isDetachedBuffer() const; - status_t setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> detachedBuffer); - std::unique_ptr<DetachedBufferHandle> takeDetachedBufferHandle(); + GraphicBufferMapper::Version getBufferMapperVersion() const { + return mBufferMapper.getMapperVersion(); + } + + void addDeathCallback(GraphicBufferDeathCallback deathCallback, void* context); + +#ifndef LIBUI_IN_VNDK + // Returns whether this GraphicBuffer is backed by BufferHubBuffer. + bool isBufferHubBuffer() const; +#endif // LIBUI_IN_VNDK private: ~GraphicBuffer(); @@ -226,10 +260,9 @@ PixelFormat inFormat, uint32_t inLayerCount, uint64_t inUsage, std::string requestorName); - status_t initWithHandle(const native_handle_t* handle, - HandleWrapMethod method, uint32_t width, uint32_t height, - PixelFormat format, uint32_t layerCount, - uint64_t usage, uint32_t stride); + status_t initWithHandle(const native_handle_t* inHandle, HandleWrapMethod method, + uint32_t inWidth, uint32_t inHeight, PixelFormat inFormat, + uint32_t inLayerCount, uint64_t inUsage, uint32_t inStride); void free_handle(); @@ -242,21 +275,46 @@ uint64_t mId; + // System unique buffer ID. Note that this is different from mId, which is process unique. For + // GraphicBuffer backed by BufferHub, the mBufferId is a system unique identifier that stays the + // same cross process for the same chunck of underlying memory. Also note that this only applies + // to GraphicBuffers that are backed by BufferHub. + int32_t mBufferId = -1; + // Stores the generation number of this buffer. If this number does not // match the BufferQueue's internal generation number (set through // IGBP::setGenerationNumber), attempts to attach the buffer will fail. uint32_t mGenerationNumber; - // Stores a BufferHub handle that can be used to re-attach this GraphicBuffer back into a - // BufferHub producer/consumer set. In terms of GraphicBuffer's relationship with BufferHub, - // there are three different modes: - // 1. Legacy mode: GraphicBuffer is not backed by BufferHub and mDetachedBufferHandle must be - // invalid. - // 2. Detached mode: GraphicBuffer is backed by BufferHub, but not part of a producer/consumer - // set. In this mode, mDetachedBufferHandle must be valid. - // 3. Attached mode: GraphicBuffer is backed by BufferHub and it's part of a producer/consumer - // set. In this mode, mDetachedBufferHandle must be invalid. - std::unique_ptr<DetachedBufferHandle> mDetachedBufferHandle; + // Send a callback when a GraphicBuffer dies. + // + // This is used for BufferStateLayer caching. GraphicBuffers are refcounted per process. When + // A GraphicBuffer doesn't have any more sp<> in a process, it is destroyed. This causes + // problems when trying to implicitcly cache across process boundaries. Ideally, both sides + // of the cache would hold onto wp<> references. When an app dropped its sp<>, the GraphicBuffer + // would be destroyed. Unfortunately, when SurfaceFlinger has only a wp<> reference to the + // GraphicBuffer, it immediately goes out of scope in the SurfaceFlinger process. SurfaceFlinger + // must hold onto a sp<> to the buffer. When the GraphicBuffer goes out of scope in the app's + // process, the client side cache will get this callback. It erases the buffer from its cache + // and informs SurfaceFlinger that it should drop its strong pointer reference to the buffer. + std::vector<std::pair<GraphicBufferDeathCallback, void* /*mDeathCallbackContext*/>> + mDeathCallbacks; + +#ifndef LIBUI_IN_VNDK + // Flatten this GraphicBuffer object if backed by BufferHubBuffer. + status_t flattenBufferHubBuffer(void*& buffer, size_t& size) const; + + // Unflatten into BufferHubBuffer backed GraphicBuffer. + // Unflatten will fail if the original GraphicBuffer object is destructed. For instance, a + // GraphicBuffer backed by BufferHubBuffer_1 flatten in process/thread A, transport the token + // to process/thread B through a socket, BufferHubBuffer_1 dies and bufferhub invalidated the + // token. Race condition occurs between the invalidation of the token in bufferhub process and + // process/thread B trying to unflatten and import the buffer with that token. + status_t unflattenBufferHubBuffer(void const*& buffer, size_t& size); + + // Stores a BufferHubBuffer that handles buffer signaling, identification. + std::unique_ptr<BufferHubBuffer> mBufferHubBuffer; +#endif // LIBUI_IN_VNDK }; }; // namespace android
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h index 14a865e..25d4512 100644 --- a/libs/ui/include/ui/GraphicBufferAllocator.h +++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -34,12 +34,8 @@ namespace android { -namespace Gralloc2 { -class Allocator; -} - +class GrallocAllocator; class GraphicBufferMapper; -class String8; class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator> { @@ -53,7 +49,9 @@ status_t free(buffer_handle_t handle); - void dump(String8& res) const; + size_t getTotalSize() const; + + void dump(std::string& res) const; static void dumpToSystemLog(); private: @@ -76,7 +74,7 @@ ~GraphicBufferAllocator(); GraphicBufferMapper& mMapper; - const std::unique_ptr<const Gralloc2::Allocator> mAllocator; + std::unique_ptr<const GrallocAllocator> mAllocator; }; // ---------------------------------------------------------------------------
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h index 7cf003d..2461454 100644 --- a/libs/ui/include/ui/GraphicBufferMapper.h +++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -35,15 +35,16 @@ // --------------------------------------------------------------------------- -namespace Gralloc2 { -class Mapper; -} - +class GrallocMapper; class Rect; class GraphicBufferMapper : public Singleton<GraphicBufferMapper> { public: + enum Version { + GRALLOC_2, + GRALLOC_3, + }; static void preloadHal(); static inline GraphicBufferMapper& get() { return getInstance(); } @@ -59,20 +60,21 @@ void getTransportSize(buffer_handle_t handle, uint32_t* outTransportNumFds, uint32_t* outTransportNumInts); - status_t lock(buffer_handle_t handle, - uint32_t usage, const Rect& bounds, void** vaddr); + status_t lock(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, + int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); status_t lockYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr); status_t unlock(buffer_handle_t handle); - status_t lockAsync(buffer_handle_t handle, - uint32_t usage, const Rect& bounds, void** vaddr, int fenceFd); + status_t lockAsync(buffer_handle_t handle, uint32_t usage, const Rect& bounds, void** vaddr, + int fenceFd, int32_t* outBytesPerPixel = nullptr, + int32_t* outBytesPerStride = nullptr); - status_t lockAsync(buffer_handle_t handle, - uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds, - void** vaddr, int fenceFd); + status_t lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage, + const Rect& bounds, void** vaddr, int fenceFd, + int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr); status_t lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage, const Rect& bounds, android_ycbcr *ycbcr, @@ -80,17 +82,23 @@ status_t unlockAsync(buffer_handle_t handle, int *fenceFd); - const Gralloc2::Mapper& getGrallocMapper() const - { - return *mMapper; + status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, bool* outSupported); + + const GrallocMapper& getGrallocMapper() const { + return reinterpret_cast<const GrallocMapper&>(*mMapper); } + Version getMapperVersion() const { return mMapperVersion; } + private: friend class Singleton<GraphicBufferMapper>; GraphicBufferMapper(); - const std::unique_ptr<const Gralloc2::Mapper> mMapper; + std::unique_ptr<const GrallocMapper> mMapper; + + Version mMapperVersion; }; // ---------------------------------------------------------------------------
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h index 0fa819d..5dc56c8 100644 --- a/libs/ui/include/ui/GraphicTypes.h +++ b/libs/ui/include/ui/GraphicTypes.h
@@ -16,19 +16,28 @@ #pragma once +#include <cinttypes> +#include <cstdint> + #include <android/hardware/graphics/common/1.1/types.h> +#include <android/hardware/graphics/common/1.2/types.h> #include <system/graphics.h> +#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64 + +namespace android { + +using PhysicalDisplayId = uint64_t; + // android::ui::* in this header file will alias different types as // the HIDL interface is updated. -namespace android { namespace ui { -using android::hardware::graphics::common::V1_0::Hdr; -using android::hardware::graphics::common::V1_1::ColorMode; -using android::hardware::graphics::common::V1_1::Dataspace; -using android::hardware::graphics::common::V1_1::PixelFormat; using android::hardware::graphics::common::V1_1::RenderIntent; +using android::hardware::graphics::common::V1_2::ColorMode; +using android::hardware::graphics::common::V1_2::Dataspace; +using android::hardware::graphics::common::V1_2::Hdr; +using android::hardware::graphics::common::V1_2::PixelFormat; } // namespace ui } // namespace android
diff --git a/libs/ui/include/ui/HdrCapabilities.h b/libs/ui/include/ui/HdrCapabilities.h index 4e98c28..65ac26c 100644 --- a/libs/ui/include/ui/HdrCapabilities.h +++ b/libs/ui/include/ui/HdrCapabilities.h
@@ -37,8 +37,8 @@ mMinLuminance(minLuminance) {} // Make this move-constructable and move-assignable - HdrCapabilities(HdrCapabilities&& other); - HdrCapabilities& operator=(HdrCapabilities&& other); + HdrCapabilities(HdrCapabilities&& other) noexcept; + HdrCapabilities& operator=(HdrCapabilities&& other) noexcept; HdrCapabilities() : mSupportedHdrTypes(),
diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h new file mode 100644 index 0000000..1152cc5 --- /dev/null +++ b/libs/ui/include/ui/PublicFormat.h
@@ -0,0 +1,76 @@ +/* + * 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. + */ + +#ifndef UI_PUBLICFORMAT_H +#define UI_PUBLICFORMAT_H + +#include <system/graphics.h> + +namespace android { + +/** + * Enum mirroring the public API definitions for image and pixel formats. + * Some of these are hidden in the public API + * + * Keep up to date with android.graphics.ImageFormat and + * android.graphics.PixelFormat + * + * TODO: PublicFormat is going to be deprecated(b/126594675) + */ +enum class PublicFormat { + UNKNOWN = 0x0, + RGBA_8888 = 0x1, + RGBX_8888 = 0x2, + RGB_888 = 0x3, + RGB_565 = 0x4, + NV16 = 0x10, + NV21 = 0x11, + YUY2 = 0x14, + RGBA_FP16 = 0x16, + RAW_SENSOR = 0x20, + PRIVATE = 0x22, + YUV_420_888 = 0x23, + RAW_PRIVATE = 0x24, + RAW10 = 0x25, + RAW12 = 0x26, + RGBA_1010102 = 0x2b, + JPEG = 0x100, + DEPTH_POINT_CLOUD = 0x101, + RAW_DEPTH = 0x1002, // @hide + YV12 = 0x32315659, + Y8 = 0x20203859, + Y16 = 0x20363159, // @hide + DEPTH16 = 0x44363159, + DEPTH_JPEG = 0x69656963, + HEIC = 0x48454946, +}; + +/* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL + * format */ +extern int mapPublicFormatToHalFormat(PublicFormat f); + +/* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL + * dataspace */ +extern android_dataspace mapPublicFormatToHalDataspace(PublicFormat f); + +/* Convert from HAL format, dataspace pair to + * android.graphics.ImageFormat/PixelFormat. + * For unknown/unspecified pairs, returns PublicFormat::UNKNOWN */ +extern PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace); + +}; // namespace android + +#endif // UI_PUBLICFORMAT_H
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h index 0bec0b7..1768805 100644 --- a/libs/ui/include/ui/Rect.h +++ b/libs/ui/include/ui/Rect.h
@@ -24,6 +24,7 @@ #include <ui/FloatRect.h> #include <ui/Point.h> +#include <ui/Size.h> #include <android/rect.h> @@ -78,6 +79,13 @@ bottom = static_cast<int32_t>(floatRect.bottom + 0.5f); } + inline explicit Rect(const ui::Size& size) { + left = 0; + top = 0; + right = size.width; + bottom = size.height; + } + void makeInvalid(); inline void clear() { @@ -106,6 +114,8 @@ return bottom - top; } + ui::Size getSize() const { return ui::Size(getWidth(), getHeight()); } + __attribute__((no_sanitize("signed-integer-overflow"))) inline Rect getBounds() const { return Rect(right - left, bottom - top); @@ -120,7 +130,7 @@ right = rb.x; bottom = rb.y; } - + // the following 4 functions return the 4 corners of the rect as Point Point leftTop() const { return Point(left, top); @@ -175,6 +185,11 @@ Rect& offsetTo(int32_t x, int32_t y); Rect& offsetBy(int32_t x, int32_t y); + /** + * Insets the rectangle on all sides specified by the insets. + */ + Rect& inset(int32_t _left, int32_t _top, int32_t _right, int32_t _bottom); + bool intersect(const Rect& with, Rect* result) const; // Create a new Rect by transforming this one using a graphics HAL
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h index 7788452..79642ae 100644 --- a/libs/ui/include/ui/Region.h +++ b/libs/ui/include/ui/Region.h
@@ -25,12 +25,13 @@ #include <ui/Rect.h> #include <utils/Flattenable.h> +#include <android-base/macros.h> + +#include <string> + namespace android { // --------------------------------------------------------------------------- -class String8; - -// --------------------------------------------------------------------------- class Region : public LightFlattenable<Region> { public: @@ -87,17 +88,19 @@ // these translate rhs first Region& translateSelf(int dx, int dy); + Region& scaleSelf(float sx, float sy); Region& orSelf(const Region& rhs, int dx, int dy); Region& xorSelf(const Region& rhs, int dx, int dy); Region& andSelf(const Region& rhs, int dx, int dy); Region& subtractSelf(const Region& rhs, int dx, int dy); + // these translate rhs first - const Region translate(int dx, int dy) const; - const Region merge(const Region& rhs, int dx, int dy) const; - const Region mergeExclusive(const Region& rhs, int dx, int dy) const; - const Region intersect(const Region& rhs, int dx, int dy) const; - const Region subtract(const Region& rhs, int dx, int dy) const; + const Region translate(int dx, int dy) const WARN_UNUSED; + const Region merge(const Region& rhs, int dx, int dy) const WARN_UNUSED; + const Region mergeExclusive(const Region& rhs, int dx, int dy) const WARN_UNUSED; + const Region intersect(const Region& rhs, int dx, int dy) const WARN_UNUSED; + const Region subtract(const Region& rhs, int dx, int dy) const WARN_UNUSED; // convenience operators overloads inline const Region operator | (const Region& rhs) const; @@ -140,8 +143,8 @@ status_t flatten(void* buffer, size_t size) const; status_t unflatten(void const* buffer, size_t size); - void dump(String8& out, const char* what, uint32_t flags=0) const; - void dump(const char* what, uint32_t flags=0) const; + void dump(std::string& out, const char* what, uint32_t flags=0) const; + void dump(const char* what, uint32_t flags=0) const; private: class rasterizer;
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h new file mode 100644 index 0000000..c39d8af --- /dev/null +++ b/libs/ui/include/ui/Size.h
@@ -0,0 +1,158 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <algorithm> +#include <cstdint> +#include <limits> +#include <type_traits> +#include <utility> + +namespace android { +namespace ui { + +// Forward declare a few things. +struct Size; +bool operator==(const Size& lhs, const Size& rhs); + +/** + * A simple value type representing a two-dimensional size + */ +struct Size { + int32_t width; + int32_t height; + + // Special values + static const Size INVALID; + static const Size EMPTY; + + // ------------------------------------------------------------------------ + // Construction + // ------------------------------------------------------------------------ + + Size() : Size(INVALID) {} + template <typename T> + Size(T&& w, T&& h) + : width(Size::clamp<int32_t, T>(std::forward<T>(w))), + height(Size::clamp<int32_t, T>(std::forward<T>(h))) {} + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + + int32_t getWidth() const { return width; } + int32_t getHeight() const { return height; } + + template <typename T> + void setWidth(T&& v) { + width = Size::clamp<int32_t, T>(std::forward<T>(v)); + } + template <typename T> + void setHeight(T&& v) { + height = Size::clamp<int32_t, T>(std::forward<T>(v)); + } + + // ------------------------------------------------------------------------ + // Assignment + // ------------------------------------------------------------------------ + + void set(const Size& size) { *this = size; } + template <typename T> + void set(T&& w, T&& h) { + set(Size(std::forward<T>(w), std::forward<T>(h))); + } + + // Sets the value to INVALID + void makeInvalid() { set(INVALID); } + + // Sets the value to EMPTY + void clear() { set(EMPTY); } + + // ------------------------------------------------------------------------ + // Semantic checks + // ------------------------------------------------------------------------ + + // Valid means non-negative width and height + bool isValid() const { return width >= 0 && height >= 0; } + + // Empty means zero width and height + bool isEmpty() const { return *this == EMPTY; } + + // ------------------------------------------------------------------------ + // Clamp Helpers + // ------------------------------------------------------------------------ + + // Note: We use only features available in C++11 here for compatibility with + // external targets which include this file directly or indirectly and which + // themselves use C++11. + + // C++11 compatible replacement for std::remove_cv_reference_t [C++20] + template <typename T> + using remove_cv_reference_t = + typename std::remove_cv<typename std::remove_reference<T>::type>::type; + + // Takes a value of type FromType, and ensures it can be represented as a value of type ToType, + // clamping the input value to the output range if necessary. + template <typename ToType, typename FromType> + static Size::remove_cv_reference_t<ToType> clamp( + typename std::enable_if< + std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_bounded && + std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_bounded, + FromType&&>::type v) { + static constexpr auto toHighest = std::numeric_limits<remove_cv_reference_t<ToType>>::max(); + static constexpr auto toLowest = + std::numeric_limits<remove_cv_reference_t<ToType>>::lowest(); + static constexpr auto fromHighest = + std::numeric_limits<remove_cv_reference_t<FromType>>::max(); + static constexpr auto fromLowest = + std::numeric_limits<remove_cv_reference_t<FromType>>::lowest(); + + // A clamp is needed if the range of FromType is not a subset of the range of ToType + static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest); + + // If a clamp is not needed, the conversion is just a trivial cast. + if (!isClampNeeded) { + return static_cast<ToType>(v); + } + + // Otherwise we leverage implicit conversion to safely compare values of + // different types, to ensure we return a value clamped to the range of + // ToType. + return v < toLowest ? toLowest : (v > toHighest ? toHighest : static_cast<ToType>(v)); + } +}; + +// ------------------------------------------------------------------------ +// Comparisons +// ------------------------------------------------------------------------ + +inline bool operator==(const Size& lhs, const Size& rhs) { + return lhs.width == rhs.width && lhs.height == rhs.height; +} + +inline bool operator!=(const Size& lhs, const Size& rhs) { + return !operator==(lhs, rhs); +} + +inline bool operator<(const Size& lhs, const Size& rhs) { + // Orders by increasing width, then height. + if (lhs.width != rhs.width) return lhs.width < rhs.width; + return lhs.height < rhs.height; +} + +} // namespace ui +} // namespace android
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h new file mode 100644 index 0000000..f29a370 --- /dev/null +++ b/libs/ui/include/ui/Transform.h
@@ -0,0 +1,121 @@ +/* + * Copyright (C) 2007 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_TRANSFORM_H +#define ANDROID_TRANSFORM_H + +#include <stdint.h> +#include <sys/types.h> +#include <string> + +#include <hardware/hardware.h> +#include <math/mat4.h> +#include <math/vec2.h> +#include <math/vec3.h> +#include <ui/Point.h> +#include <ui/Rect.h> + +namespace android { + +class Region; + +namespace ui { + +class Transform { +public: + Transform(); + Transform(const Transform& other); + explicit Transform(uint32_t orientation); + ~Transform(); + + enum orientation_flags { + ROT_0 = 0x00000000, + FLIP_H = HAL_TRANSFORM_FLIP_H, + FLIP_V = HAL_TRANSFORM_FLIP_V, + ROT_90 = HAL_TRANSFORM_ROT_90, + ROT_180 = FLIP_H|FLIP_V, + ROT_270 = ROT_180|ROT_90, + ROT_INVALID = 0x80 + }; + + enum type_mask : uint32_t { + IDENTITY = 0, + TRANSLATE = 0x1, + ROTATE = 0x2, + SCALE = 0x4, + UNKNOWN = 0x8 + }; + + // query the transform + bool preserveRects() const; + uint32_t getType() const; + uint32_t getOrientation() const; + + const vec3& operator [] (size_t i) const; // returns column i + float tx() const; + float ty() const; + float sx() const; + float sy() const; + + // modify the transform + void reset(); + void set(float tx, float ty); + void set(float a, float b, float c, float d); + status_t set(uint32_t flags, float w, float h); + + // transform data + Rect makeBounds(int w, int h) const; + vec2 transform(int x, int y) const; + Region transform(const Region& reg) const; + Rect transform(const Rect& bounds, + bool roundOutwards = false) const; + FloatRect transform(const FloatRect& bounds) const; + Transform& operator = (const Transform& other); + Transform operator * (const Transform& rhs) const; + // assumes the last row is < 0 , 0 , 1 > + vec2 transform(const vec2& v) const; + vec3 transform(const vec3& v) const; + + // Expands from the internal 3x3 matrix to an equivalent 4x4 matrix + mat4 asMatrix4() const; + + Transform inverse() const; + + // for debugging + void dump(std::string& result, const char* name) const; + void dump(const char* name) const; + +private: + struct mat33 { + vec3 v[3]; + inline const vec3& operator [] (size_t i) const { return v[i]; } + inline vec3& operator [] (size_t i) { return v[i]; } + }; + + enum { UNKNOWN_TYPE = 0x80000000 }; + + uint32_t type() const; + static bool absIsOne(float f); + static bool isZero(float f); + + mat33 mMatrix; + mutable uint32_t mType; +}; + +} // namespace ui +} // namespace android + +#endif /* ANDROID_TRANSFORM_H */
diff --git a/libs/ui/include/ui/UiConfig.h b/libs/ui/include/ui/UiConfig.h index fcf8ed5..d1d6014 100644 --- a/libs/ui/include/ui/UiConfig.h +++ b/libs/ui/include/ui/UiConfig.h
@@ -17,12 +17,12 @@ #ifndef ANDROID_UI_CONFIG_H #define ANDROID_UI_CONFIG_H -#include <utils/String8.h> +#include <string> namespace android { // Append the libui configuration details to configStr. -void appendUiConfigString(String8& configStr); +void appendUiConfigString(std::string& configStr); }; // namespace android
diff --git a/libs/ui/include_vndk/ui/ConfigStoreTypes.h b/libs/ui/include_vndk/ui/ConfigStoreTypes.h new file mode 100644 index 0000000..4445ae9 --- /dev/null +++ b/libs/ui/include_vndk/ui/ConfigStoreTypes.h
@@ -0,0 +1,37 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +// android::ui::* in this header file will alias different types as +// the HIDL interface is updated. +namespace android { +namespace ui { + +struct CieXyz { + float X; + float Y; + float Z; +}; +struct DisplayPrimaries { + CieXyz red; + CieXyz green; + CieXyz blue; + CieXyz white; +}; + +} // namespace ui +} // namespace android
diff --git a/libs/ui/include_vndk/ui/DisplayedFrameStats.h b/libs/ui/include_vndk/ui/DisplayedFrameStats.h new file mode 120000 index 0000000..6014e19 --- /dev/null +++ b/libs/ui/include_vndk/ui/DisplayedFrameStats.h
@@ -0,0 +1 @@ +../../include/ui/DisplayedFrameStats.h \ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/Size.h b/libs/ui/include_vndk/ui/Size.h new file mode 120000 index 0000000..fd2b21b --- /dev/null +++ b/libs/ui/include_vndk/ui/Size.h
@@ -0,0 +1 @@ +../../include/ui/Size.h \ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/Transform.h b/libs/ui/include_vndk/ui/Transform.h new file mode 120000 index 0000000..60633c2 --- /dev/null +++ b/libs/ui/include_vndk/ui/Transform.h
@@ -0,0 +1 @@ +../../include/ui/Transform.h \ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index aef6428..373fa4f 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp
@@ -30,7 +30,51 @@ cc_test { name: "GraphicBuffer_test", - shared_libs: ["libpdx_default_transport", "libui", "libutils"], + header_libs: [ + "libdvr_headers", + "libnativewindow_headers", + ], + shared_libs: [ + "android.frameworks.bufferhub@1.0", + "libcutils", + "libhidlbase", + "libhwbinder", + "libui", + "libutils", + ], srcs: ["GraphicBuffer_test.cpp"], cflags: ["-Wall", "-Werror"], } + +cc_test { + name: "BufferHub_test", + header_libs: [ + "libdvr_headers", + "libnativewindow_headers", + ], + static_libs: [ + "libgmock", + ], + shared_libs: [ + "android.frameworks.bufferhub@1.0", + "libcutils", + "libhidlbase", + "libhwbinder", + "liblog", + "libui", + "libutils" + ], + srcs: [ + "BufferHubBuffer_test.cpp", + "BufferHubEventFd_test.cpp", + "BufferHubMetadata_test.cpp", + ], + cflags: ["-Wall", "-Werror"], +} + +cc_test { + name: "Size_test", + shared_libs: ["libui"], + srcs: ["Size_test.cpp"], + cflags: ["-Wall", "-Werror"], +}
diff --git a/libs/ui/tests/BufferHubBuffer_test.cpp b/libs/ui/tests/BufferHubBuffer_test.cpp new file mode 100644 index 0000000..0c73a72 --- /dev/null +++ b/libs/ui/tests/BufferHubBuffer_test.cpp
@@ -0,0 +1,477 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BufferHubBufferTest" + +#include <errno.h> +#include <sys/epoll.h> + +#include <android/frameworks/bufferhub/1.0/IBufferHub.h> +#include <android/hardware_buffer.h> +#include <cutils/native_handle.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <hidl/ServiceManagement.h> +#include <hwbinder/IPCThreadState.h> +#include <ui/BufferHubBuffer.h> +#include <ui/BufferHubEventFd.h> + +namespace android { + +namespace { + +using ::android::BufferHubDefs::isAnyClientAcquired; +using ::android::BufferHubDefs::isAnyClientGained; +using ::android::BufferHubDefs::isAnyClientPosted; +using ::android::BufferHubDefs::isClientAcquired; +using ::android::BufferHubDefs::isClientGained; +using ::android::BufferHubDefs::isClientPosted; +using ::android::BufferHubDefs::isClientReleased; +using ::android::BufferHubDefs::kMetadataHeaderSize; +using ::android::frameworks::bufferhub::V1_0::IBufferHub; +using ::testing::IsNull; +using ::testing::NotNull; + +const int kWidth = 640; +const int kHeight = 480; +const int kLayerCount = 1; +const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888; +const int kUsage = 0; +const AHardwareBuffer_Desc kDesc = {kWidth, kHeight, kLayerCount, kFormat, + kUsage, /*stride=*/0UL, /*rfu0=*/0UL, /*rfu1=*/0ULL}; +const size_t kUserMetadataSize = 1; + +class BufferHubBufferTest : public ::testing::Test { +protected: + void SetUp() override { + android::hardware::ProcessState::self()->startThreadPool(); + + if (!BufferHubServiceRunning()) { + // TODO(b/112940221): Enforce the test cross all devices once BufferHub lands in Android + // R for all Android varieties. + GTEST_SKIP() << "Skip test as the BufferHub service is not running."; + } + } + + bool BufferHubServiceRunning() { + sp<IBufferHub> bufferhub = IBufferHub::getService(); + return bufferhub.get() != nullptr; + } +}; + +bool cmpAHardwareBufferDesc(const AHardwareBuffer_Desc& desc, const AHardwareBuffer_Desc& other) { + // Not comparing stride because it's unknown before allocation + return desc.format == other.format && desc.height == other.height && + desc.layers == other.layers && desc.usage == other.usage && desc.width == other.width; +} + +class BufferHubBufferStateTransitionTest : public BufferHubBufferTest { +protected: + void SetUp() override { + BufferHubBufferTest::SetUp(); + + if (IsSkipped()) { + // If the base class' SetUp() stated the test should be skipped, we should short + // circuit this sub-class' logic. + return; + } + + CreateTwoClientsOfABuffer(); + } + + std::unique_ptr<BufferHubBuffer> b1; + uint32_t b1ClientMask = 0U; + std::unique_ptr<BufferHubBuffer> b2; + uint32_t b2ClientMask = 0U; + +private: + // Creates b1 and b2 as the clients of the same buffer for testing. + void CreateTwoClientsOfABuffer(); +}; + +void BufferHubBufferStateTransitionTest::CreateTwoClientsOfABuffer() { + b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); + ASSERT_THAT(b1, NotNull()); + b1ClientMask = b1->clientStateMask(); + ASSERT_NE(b1ClientMask, 0U); + + sp<NativeHandle> token = b1->duplicate(); + ASSERT_THAT(token, NotNull()); + + b2 = BufferHubBuffer::import(token); + ASSERT_THAT(b2, NotNull()); + + b2ClientMask = b2->clientStateMask(); + ASSERT_NE(b2ClientMask, 0U); + ASSERT_NE(b2ClientMask, b1ClientMask); +} + +TEST_F(BufferHubBufferTest, CreateBufferFails) { + // Buffer Creation will fail: BLOB format requires height to be 1. + auto b1 = BufferHubBuffer::create(kWidth, /*height=*/2, kLayerCount, + /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, kUserMetadataSize); + + EXPECT_THAT(b1, IsNull()); + + // Buffer Creation will fail: user metadata size too large. + auto b2 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + /*userMetadataSize=*/std::numeric_limits<size_t>::max()); + + EXPECT_THAT(b2, IsNull()); + + // Buffer Creation will fail: user metadata size too large. + const size_t userMetadataSize = std::numeric_limits<size_t>::max() - kMetadataHeaderSize; + auto b3 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + userMetadataSize); + + EXPECT_THAT(b3, IsNull()); +} + +TEST_F(BufferHubBufferTest, CreateBuffer) { + auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + kUserMetadataSize); + ASSERT_THAT(b1, NotNull()); + EXPECT_TRUE(b1->isConnected()); + EXPECT_TRUE(b1->isValid()); + EXPECT_TRUE(cmpAHardwareBufferDesc(b1->desc(), kDesc)); + EXPECT_EQ(b1->userMetadataSize(), kUserMetadataSize); +} + +TEST_F(BufferHubBufferTest, DuplicateAndImportBuffer) { + auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + kUserMetadataSize); + ASSERT_THAT(b1, NotNull()); + EXPECT_TRUE(b1->isValid()); + + sp<NativeHandle> token = b1->duplicate(); + ASSERT_THAT(token, NotNull()); + + // The detached buffer should still be valid. + EXPECT_TRUE(b1->isConnected()); + EXPECT_TRUE(b1->isValid()); + + std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token); + + ASSERT_THAT(b2, NotNull()); + EXPECT_TRUE(b2->isValid()); + + EXPECT_TRUE(cmpAHardwareBufferDesc(b1->desc(), b2->desc())); + EXPECT_EQ(b1->userMetadataSize(), b2->userMetadataSize()); + + // These two buffer instances are based on the same physical buffer under the + // hood, so they should share the same id. + EXPECT_EQ(b1->id(), b2->id()); + // We use clientStateMask() to tell those two instances apart. + EXPECT_NE(b1->clientStateMask(), b2->clientStateMask()); + + // Both buffer instances should be in released state currently. + EXPECT_TRUE(b1->isReleased()); + EXPECT_TRUE(b2->isReleased()); + + // The event fd should behave like duped event fds. + const BufferHubEventFd& eventFd1 = b1->eventFd(); + ASSERT_GE(eventFd1.get(), 0); + const BufferHubEventFd& eventFd2 = b2->eventFd(); + ASSERT_GE(eventFd2.get(), 0); + + base::unique_fd epollFd(epoll_create(64)); + ASSERT_GE(epollFd.get(), 0); + + // Add eventFd1 to epoll set, and signal eventFd2. + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e), 0) << strerror(errno); + + std::array<epoll_event, 1> events; + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + eventFd2.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + + // The epoll fd is edge triggered, so it only responds to the eventFd once. + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + eventFd2.signal(); + eventFd2.clear(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); +} + +TEST_F(BufferHubBufferTest, ImportFreedBuffer) { + auto b1 = BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + kUserMetadataSize); + ASSERT_THAT(b1, NotNull()); + EXPECT_TRUE(b1->isValid()); + + sp<NativeHandle> token = b1->duplicate(); + ASSERT_THAT(token, NotNull()); + + // Explicitly destroy b1. Backend buffer should be freed and token becomes invalid + b1.reset(); + + std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token); + + // Import should fail with INVALID_TOKEN + EXPECT_THAT(b2, IsNull()); +} + +// nullptr must not crash the service +TEST_F(BufferHubBufferTest, ImportNullToken) { + auto b1 = BufferHubBuffer::import(nullptr); + EXPECT_THAT(b1, IsNull()); +} + +TEST_F(BufferHubBufferTest, ImportInvalidToken) { + native_handle_t* token = native_handle_create(/*numFds=*/0, /*numInts=*/1); + token->data[0] = 0; + + sp<NativeHandle> tokenHandle = NativeHandle::create(token, /*ownHandle=*/true); + auto b1 = BufferHubBuffer::import(tokenHandle); + + EXPECT_THAT(b1, IsNull()); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) { + ASSERT_TRUE(b1->isReleased()); + + // Successful gaining the buffer should change the buffer state bit of b1 to + // gained state, other client state bits to released state. + EXPECT_EQ(b1->gain(), 0); + EXPECT_TRUE(isClientGained(b1->bufferState(), b1ClientMask)); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromGainedState) { + ASSERT_EQ(b1->gain(), 0); + auto currentBufferState = b1->bufferState(); + ASSERT_TRUE(isClientGained(currentBufferState, b1ClientMask)); + + // Gaining from gained state by the same client should not return error. + EXPECT_EQ(b1->gain(), 0); + + // Gaining from gained state by another client should return error. + EXPECT_EQ(b2->gain(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromAcquiredState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_EQ(b1->post(), 0); + ASSERT_EQ(b2->acquire(), 0); + ASSERT_TRUE(isAnyClientAcquired(b1->bufferState())); + + // Gaining from acquired state should fail. + EXPECT_EQ(b1->gain(), -EBUSY); + EXPECT_EQ(b2->gain(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromOtherClientInPostedState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_EQ(b1->post(), 0); + ASSERT_TRUE(isAnyClientPosted(b1->bufferState())); + + // Gaining a buffer who has other posted client should succeed. + EXPECT_EQ(b1->gain(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_EQ(b1->post(), 0); + ASSERT_TRUE(isAnyClientPosted(b1->bufferState())); + + // A posted client should be able to gain the buffer when there is no other clients in + // acquired state. + EXPECT_EQ(b2->gain(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromOtherInGainedState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_TRUE(isClientGained(b1->bufferState(), b1ClientMask)); + + EXPECT_EQ(b2->post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromSelfInGainedState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_TRUE(isClientGained(b1->bufferState(), b1ClientMask)); + + EXPECT_EQ(b1->post(), 0); + auto currentBufferState = b1->bufferState(); + EXPECT_TRUE(isClientReleased(currentBufferState, b1ClientMask)); + EXPECT_TRUE(isClientPosted(currentBufferState, b2ClientMask)); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromPostedState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_EQ(b1->post(), 0); + ASSERT_TRUE(isAnyClientPosted(b1->bufferState())); + + // Post from posted state should fail. + EXPECT_EQ(b1->post(), -EBUSY); + EXPECT_EQ(b2->post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromAcquiredState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_EQ(b1->post(), 0); + ASSERT_EQ(b2->acquire(), 0); + ASSERT_TRUE(isAnyClientAcquired(b1->bufferState())); + + // Posting from acquired state should fail. + EXPECT_EQ(b1->post(), -EBUSY); + EXPECT_EQ(b2->post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromReleasedState) { + ASSERT_TRUE(b1->isReleased()); + + // Posting from released state should fail. + EXPECT_EQ(b1->post(), -EBUSY); + EXPECT_EQ(b2->post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_EQ(b1->post(), 0); + ASSERT_TRUE(isClientPosted(b1->bufferState(), b2ClientMask)); + + // Acquire from posted state should pass. + EXPECT_EQ(b2->acquire(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromOtherInPostedState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_EQ(b1->post(), 0); + ASSERT_TRUE(isClientPosted(b1->bufferState(), b2ClientMask)); + + // Acquire from released state should fail, although there are other clients + // in posted state. + EXPECT_EQ(b1->acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromSelfInAcquiredState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_EQ(b1->post(), 0); + ASSERT_EQ(b2->acquire(), 0); + auto currentBufferState = b1->bufferState(); + ASSERT_TRUE(isClientAcquired(currentBufferState, b2ClientMask)); + + // Acquiring from acquired state by the same client should not error out. + EXPECT_EQ(b2->acquire(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromReleasedState) { + ASSERT_TRUE(b1->isReleased()); + + // Acquiring form released state should fail. + EXPECT_EQ(b1->acquire(), -EBUSY); + EXPECT_EQ(b2->acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, AcquireBuffer_fromGainedState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_TRUE(isAnyClientGained(b1->bufferState())); + + // Acquiring from gained state should fail. + EXPECT_EQ(b1->acquire(), -EBUSY); + EXPECT_EQ(b2->acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInReleasedState) { + ASSERT_TRUE(b1->isReleased()); + + EXPECT_EQ(b1->release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInGainedState) { + ASSERT_TRUE(b1->isReleased()); + ASSERT_EQ(b1->gain(), 0); + ASSERT_TRUE(isAnyClientGained(b1->bufferState())); + + EXPECT_EQ(b1->release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_EQ(b1->post(), 0); + ASSERT_TRUE(isAnyClientPosted(b1->bufferState())); + + EXPECT_EQ(b2->release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInAcquiredState) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_EQ(b1->post(), 0); + ASSERT_EQ(b2->acquire(), 0); + ASSERT_TRUE(isAnyClientAcquired(b1->bufferState())); + + EXPECT_EQ(b2->release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, BasicUsage) { + // 1 producer buffer and 1 consumer buffer initialised in testcase setup. + // Test if this set of basic operation succeed: + // Producer post three times to the consumer, and released by consumer. + for (int i = 0; i < 3; ++i) { + ASSERT_EQ(b1->gain(), 0); + ASSERT_EQ(b1->post(), 0); + ASSERT_EQ(b2->acquire(), 0); + ASSERT_EQ(b2->release(), 0); + } +} + +TEST_F(BufferHubBufferTest, createNewConsumerAfterGain) { + // Create a poducer buffer and gain. + std::unique_ptr<BufferHubBuffer> b1 = + BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + kUserMetadataSize); + ASSERT_THAT(b1, NotNull()); + ASSERT_EQ(b1->gain(), 0); + + // Create a consumer of the buffer and test if the consumer can acquire the + // buffer if producer posts. + sp<NativeHandle> token = b1->duplicate(); + ASSERT_THAT(token, NotNull()); + + std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token); + + ASSERT_THAT(b2, NotNull()); + ASSERT_NE(b1->clientStateMask(), b2->clientStateMask()); + + ASSERT_EQ(b1->post(), 0); + EXPECT_EQ(b2->acquire(), 0); +} + +TEST_F(BufferHubBufferTest, createNewConsumerAfterPost) { + // Create a poducer buffer and post. + std::unique_ptr<BufferHubBuffer> b1 = + BufferHubBuffer::create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + kUserMetadataSize); + ASSERT_EQ(b1->gain(), 0); + ASSERT_EQ(b1->post(), 0); + + // Create a consumer of the buffer and test if the consumer can acquire the + // buffer if producer posts. + sp<NativeHandle> token = b1->duplicate(); + ASSERT_THAT(token, NotNull()); + + std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::import(token); + + ASSERT_THAT(b2, NotNull()); + ASSERT_NE(b1->clientStateMask(), b2->clientStateMask()); + + EXPECT_EQ(b2->acquire(), 0); +} + +} // namespace + +} // namespace android
diff --git a/libs/ui/tests/BufferHubEventFd_test.cpp b/libs/ui/tests/BufferHubEventFd_test.cpp new file mode 100644 index 0000000..ef1781f --- /dev/null +++ b/libs/ui/tests/BufferHubEventFd_test.cpp
@@ -0,0 +1,428 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BufferHubEventFdTest" + +#include <sys/epoll.h> +#include <sys/eventfd.h> + +#include <array> +#include <condition_variable> +#include <mutex> +#include <thread> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <ui/BufferHubEventFd.h> + +namespace android { + +namespace { + +const int kTimeout = 100; +const std::chrono::milliseconds kTimeoutMs(kTimeout); +const int kTestRuns = 5; + +using ::testing::Contains; +using BufferHubEventFdTest = ::testing::Test; + +} // namespace + +TEST_F(BufferHubEventFdTest, EventFd_testSingleEpollFd) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + + base::unique_fd epollFd(epoll_create(64)); + ASSERT_GE(epollFd.get(), 0); + + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + std::array<epoll_event, 1> events; + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + eventFd.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + + // The epoll fd is edge triggered, so it only responds to the eventFd once. + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + // Check that it can receive consecutive signal. + eventFd.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + // Check that it can receive consecutive signal from a duplicated eventfd. + BufferHubEventFd dupEventFd(dup(eventFd.get())); + ASSERT_TRUE(dupEventFd.isValid()); + dupEventFd.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + dupEventFd.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); +} + +TEST_F(BufferHubEventFdTest, EventFd_testCreateEpollFdAndAddSignaledEventFd) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + eventFd.signal(); + + base::unique_fd epollFd(epoll_create(64)); + ASSERT_GE(epollFd.get(), 0); + + // Make sure that the epoll set has not been signal yet. + std::array<epoll_event, 1> events; + ASSERT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + // Check that adding an signaled fd into this epoll set will trigger the epoll set. + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + + // The epoll fd is edge triggered, so it only responds to the eventFd once. + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); +} + +TEST_F(BufferHubEventFdTest, EventFd_testAddSignaledEventFdToEpollFd) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + + base::unique_fd epollFd(epoll_create(64)); + ASSERT_GE(epollFd.get(), 0); + + eventFd.signal(); + + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + std::array<epoll_event, 1> events; + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + + // The epoll fd is edge triggered, so it only responds to the eventFd once. + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); +} + +TEST_F(BufferHubEventFdTest, EventFd_testConsecutiveSignalsFromAEventFd) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + base::unique_fd epollFd(epoll_create(64)); + ASSERT_GE(epollFd.get(), 0); + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + std::array<epoll_event, 1> events; + for (int i = 0; i < kTestRuns; ++i) { + eventFd.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + } +} + +TEST_F(BufferHubEventFdTest, EventFd_testConsecutiveSignalsFromADuplicatedEventFd) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + base::unique_fd epollFd(epoll_create(64)); + ASSERT_GE(epollFd.get(), 0); + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + BufferHubEventFd dupEventFd(dup(eventFd.get())); + ASSERT_TRUE(dupEventFd.isValid()); + + std::array<epoll_event, 1> events; + for (int i = 0; i < kTestRuns; ++i) { + dupEventFd.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + } +} + +TEST_F(BufferHubEventFdTest, EventFd_testClear) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + + base::unique_fd epollFd(epoll_create(64)); + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + + ASSERT_GE(epollFd.get(), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + eventFd.signal(); + eventFd.clear(); + + std::array<epoll_event, 1> events; + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); +} + +TEST_F(BufferHubEventFdTest, EventFd_testDupEventFd) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + + base::unique_fd epollFd(epoll_create(64)); + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + + ASSERT_GE(epollFd.get(), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + // Technically, the dupliated eventFd and the original eventFd are pointing + // to the same kernel object. This test signals the duplicated eventFd but epolls the origianl + // eventFd. + BufferHubEventFd dupedEventFd(dup(eventFd.get())); + ASSERT_GE(dupedEventFd.get(), 0); + + std::array<epoll_event, 1> events; + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + dupedEventFd.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + + // The epoll fd is edge triggered, so it only responds to the eventFd once. + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + dupedEventFd.signal(); + + dupedEventFd.clear(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); +} + +TEST_F(BufferHubEventFdTest, EventFd_testTwoEpollFds) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + + base::unique_fd epollFd1(epoll_create(64)); + base::unique_fd epollFd2(epoll_create(64)); + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + + ASSERT_GE(epollFd1.get(), 0); + ASSERT_GE(epollFd2.get(), 0); + + // Register the same eventFd to two EpollFds. + ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + std::array<epoll_event, 1> events; + EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0); + EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0); + + eventFd.signal(); + EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1); + EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 1); + + // The epoll fd is edge triggered, so it only responds to the eventFd once. + EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0); + EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0); + + eventFd.signal(); + EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 1); + + eventFd.clear(); + EXPECT_EQ(epoll_wait(epollFd1.get(), events.data(), events.size(), 0), 0); + EXPECT_EQ(epoll_wait(epollFd2.get(), events.data(), events.size(), 0), 0); +} + +TEST_F(BufferHubEventFdTest, EventFd_testTwoEventFds) { + BufferHubEventFd eventFd1; + BufferHubEventFd eventFd2; + + ASSERT_TRUE(eventFd1.isValid()); + ASSERT_TRUE(eventFd2.isValid()); + + base::unique_fd epollFd(epoll_create(64)); + epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}}; + epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}}; + + ASSERT_GE(epollFd.get(), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0); + + std::array<epoll_event, 2> events; + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + // Signal one by one. + eventFd1.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + EXPECT_EQ(events[0].data.u32, e1.data.u32); + + eventFd2.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); + EXPECT_EQ(events[0].data.u32, e2.data.u32); + + // Signal both. + eventFd1.signal(); + eventFd2.signal(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 2); + + uint32_t u32s[] = {events[0].data.u32, events[1].data.u32}; + EXPECT_THAT(u32s, Contains(e1.data.u32)); + EXPECT_THAT(u32s, Contains(e2.data.u32)); + + // The epoll fd is edge triggered, so it only responds to the eventFd once. + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 0); + + eventFd1.signal(); + eventFd2.signal(); + eventFd2.clear(); + EXPECT_EQ(epoll_wait(epollFd.get(), events.data(), events.size(), 0), 1); +} + +TEST_F(BufferHubEventFdTest, EventFd_testPollingThreadWithTwoEventFds) { + BufferHubEventFd eventFd1; + BufferHubEventFd eventFd2; + + ASSERT_TRUE(eventFd1.isValid()); + ASSERT_TRUE(eventFd2.isValid()); + + base::unique_fd epollFd(epoll_create(64)); + epoll_event e1 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 1}}; + epoll_event e2 = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 2}}; + + ASSERT_GE(epollFd.get(), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd1.get(), &e1), 0); + ASSERT_EQ(epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd2.get(), &e2), 0); + + int countEvent1 = 0; + int countEvent2 = 0; + std::atomic<bool> stop{false}; + std::mutex mx; + std::condition_variable cv; + + std::thread pollingThread([&] { + std::array<epoll_event, 2> events; + while (true) { + if (stop.load()) { + break; + } + int ret = epoll_wait(epollFd.get(), events.data(), events.size(), kTimeout); + ALOGE_IF(ret < 0 && errno != ETIMEDOUT, "Epoll failed."); + + std::lock_guard<std::mutex> lock(mx); + for (int i = 0; i < ret; i++) { + if (events[i].data.u32 == e1.data.u32) { + countEvent1++; + cv.notify_one(); + } else if (events[i].data.u32 == e2.data.u32) { + countEvent2++; + cv.notify_one(); + } + } + } + }); + + { + std::unique_lock<std::mutex> lock(mx); + + eventFd1.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 1; })); + + eventFd1.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 2; })); + + eventFd2.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 1; })); + + eventFd1.clear(); + eventFd2.clear(); + EXPECT_EQ(countEvent1, 2); + EXPECT_EQ(countEvent2, 1); + + eventFd1.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent1 == 3; })); + + eventFd2.signal(); + EXPECT_TRUE(cv.wait_for(lock, kTimeoutMs, [&] { return countEvent2 == 2; })); + } + + stop.store(true); + pollingThread.join(); +} + +TEST_F(BufferHubEventFdTest, EventFd_testTwoPollingThreads) { + BufferHubEventFd eventFd; + ASSERT_TRUE(eventFd.isValid()); + + base::unique_fd epollFd1(epoll_create(64)); + base::unique_fd epollFd2(epoll_create(64)); + epoll_event e = {.events = EPOLLIN | EPOLLET, .data = {.u32 = 0}}; + + ASSERT_GE(epollFd1.get(), 0); + ASSERT_GE(epollFd2.get(), 0); + + // Register the same eventFd to two EpollFds. + ASSERT_EQ(epoll_ctl(epollFd1.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + ASSERT_EQ(epoll_ctl(epollFd2.get(), EPOLL_CTL_ADD, eventFd.get(), &e), 0); + + int countEpoll1 = 0; + int countEp