diff options
29 files changed, 526 insertions, 72 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 142c4cda17..59a0047acf 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -2048,6 +2048,7 @@ static inline const char* ModeToString(Dumpstate::BugreportMode mode) { } 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; diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 5412d4df7b..8fbea8a163 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -389,7 +389,7 @@ status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::mi 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/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index a6d7a78a0f..c706d911ec 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -376,23 +376,23 @@ void ListCommand::postprocess() { } 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."); + "| 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."); + "| 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."); } bool ListCommand::addEntryWithInstance(const TableEntry& entry, @@ -972,10 +972,10 @@ void ListCommand::registerAllOptions() { thiz->mSelectedColumns.push_back(TableColumnType::VINTF); return OK; }, "print VINTF info. This column contains a comma-separated list of:\n" - " - DM: device manifest\n" - " - DC: device compatibility matrix\n" - " - FM: framework manifest\n" - " - FC: framework compatibility matrix"}); + " - 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; @@ -1054,7 +1054,7 @@ void ListCommand::registerAllOptions() { 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), and (v|vintf).\nDefault is `bcl`."}); + "passthrough_libs), (v|vintf), and (z|lazy).\nDefault is `bcl`."}); } // Create 'longopts' argument to getopt_long. Caller is responsible for maintaining @@ -1150,7 +1150,7 @@ Status ListCommand::parseArgs(const Arg &arg) { } if (mSelectedColumns.empty()) { - mSelectedColumns = {TableColumnType::RELEASED, + mSelectedColumns = {TableColumnType::VINTF, TableColumnType::RELEASED, TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS}; } @@ -1210,7 +1210,7 @@ void ListCommand::usage() const { err() << "list:" << std::endl << " lshal" << std::endl << " lshal list" << std::endl - << " List all hals with default ordering and columns (`lshal list -liepc`)" << 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; diff --git a/cmds/rss_hwm_reset/Android.bp b/cmds/rss_hwm_reset/Android.bp new file mode 100644 index 0000000000..15f10efdee --- /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 0000000000..1626e7ec50 --- /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 0000000000..fbbc8200b2 --- /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/include/android/multinetwork.h b/include/android/multinetwork.h index 4d24680246..ed9531ec62 100644 --- a/include/android/multinetwork.h +++ b/include/android/multinetwork.h @@ -110,6 +110,47 @@ int android_getaddrinfofornetwork(net_handle_t network, #endif /* __ANDROID_API__ >= 23 */ +#if __ANDROID_API__ >= 29 + +/** + * 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). + * + * 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) __INTRODUCED_IN(29); + +/** + * Issue the query |msg| on the given |network|. + * + * 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 unsigned char *msg, int msglen) __INTRODUCED_IN(29); + +/** + * Read a result for the query associated with the |fd| descriptor. + * + * 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, unsigned char *answer, int 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/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index d799c5f0c6..1b69dfd6a7 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -23,6 +23,8 @@ cc_library { "include_apex", ], + cflags: ["-Wall", "-Wextra", "-Werror"], + srcs: [ "ibinder.cpp", "ibinder_jni.cpp", @@ -40,6 +42,10 @@ cc_library { ], version_script: "libbinder_ndk.map.txt", + stubs: { + symbol_file: "libbinder_ndk.map.txt", + versions: ["29"], + }, } ndk_headers { diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h index 866af70157..2258210f2e 100644 --- a/libs/binder/ndk/include_ndk/android/binder_parcel.h +++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h @@ -149,7 +149,49 @@ typedef bool (*AParcel_stringArrayElementAllocator)(void* arrayData, size_t inde * 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, - size_t* outLength); + 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 /** @@ -497,6 +539,40 @@ binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData, 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. diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h index f99c3a92e2..fcdf7af22c 100644 --- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h +++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h @@ -299,7 +299,7 @@ static inline bool AParcel_stdVectorStringElementAllocator(void* vectorData, siz * index. */ static inline const char* AParcel_stdVectorStringElementGetter(const void* vectorData, size_t index, - size_t* outLength) { + int32_t* outLength) { const std::vector<std::string>* vec = static_cast<const std::vector<std::string>*>(vectorData); const std::string& element = vec->at(index); @@ -327,7 +327,7 @@ static inline bool AParcel_nullableStdVectorStringElementAllocator(void* vectorD */ static inline const char* AParcel_nullableStdVectorStringElementGetter(const void* vectorData, size_t index, - size_t* outLength) { + 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); @@ -420,6 +420,46 @@ static inline binder_status_t AParcel_readVector( 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. diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 4328b6ea6d..ee7132f87f 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -40,6 +40,7 @@ LIBBINDER_NDK { # introduced=29 AParcel_readInt32Array; AParcel_readInt64; AParcel_readInt64Array; + AParcel_readParcelableArray; AParcel_readParcelFileDescriptor; AParcel_readStatusHeader; AParcel_readString; @@ -64,6 +65,7 @@ LIBBINDER_NDK { # introduced=29 AParcel_writeInt32Array; AParcel_writeInt64; AParcel_writeInt64Array; + AParcel_writeParcelableArray; AParcel_writeParcelFileDescriptor; AParcel_writeStatusHeader; AParcel_writeString; diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp index 2d68559395..ae2276e794 100644 --- a/libs/binder/ndk/parcel.cpp +++ b/libs/binder/ndk/parcel.cpp @@ -173,7 +173,7 @@ binder_status_t WriteArray(AParcel* parcel, const void* arrayData, int32_t lengt Parcel* rawParcel = parcel->get(); - for (size_t i = 0; i < length; i++) { + for (int32_t i = 0; i < length; i++) { status = (rawParcel->*write)(getter(arrayData, i)); if (status != STATUS_OK) return PruneStatusT(status); @@ -197,7 +197,7 @@ binder_status_t ReadArray(const AParcel* parcel, void* arrayData, ArrayAllocator if (length <= 0) return STATUS_OK; - for (size_t i = 0; i < length; i++) { + for (int32_t i = 0; i < length; i++) { T readTarget; status = (rawParcel->*read)(&readTarget); if (status != STATUS_OK) return PruneStatusT(status); @@ -376,12 +376,12 @@ binder_status_t AParcel_writeStringArray(AParcel* parcel, const void* arrayData, if (status != STATUS_OK) return status; if (length <= 0) return STATUS_OK; - for (size_t i = 0; i < length; i++) { - size_t length = 0; - const char* str = getter(arrayData, i, &length); - if (str == nullptr && length != -1) return STATUS_BAD_VALUE; + 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, length); + binder_status_t status = AParcel_writeString(parcel, str, elementLength); if (status != STATUS_OK) return status; } @@ -392,7 +392,7 @@ binder_status_t AParcel_writeStringArray(AParcel* parcel, const void* arrayData, // allocator. struct StringArrayElementAllocationAdapter { void* arrayData; // stringData from the NDK - size_t index; // index into the string array + int32_t index; // index into the string array AParcel_stringArrayElementAllocator elementAllocator; static bool Allocator(void* stringData, int32_t length, char** buffer) { @@ -433,6 +433,45 @@ binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData, 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 diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 73c2eba1d5..2dd86ba7e1 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -1295,7 +1295,7 @@ int run_server(int index, int readypipefd, bool usePoll) } IPCThreadState::self()->flushCommands(); // flush BC_ENTER_LOOPER - epoll_fd = epoll_create1(0); + epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (epoll_fd == -1) { return 1; } diff --git a/libs/binder/tests/binderValueTypeTest.cpp b/libs/binder/tests/binderValueTypeTest.cpp index 15949d48c0..f8922b0784 100644 --- a/libs/binder/tests/binderValueTypeTest.cpp +++ b/libs/binder/tests/binderValueTypeTest.cpp @@ -75,13 +75,13 @@ using ::std::vector; 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/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index e22bc708c9..d2d27e8239 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -332,6 +332,34 @@ public: 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; @@ -724,6 +752,26 @@ status_t BnSurfaceComposer::onTransact( } 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(); diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 63560c4b89..f3c6fd2f87 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -718,6 +718,10 @@ status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display, 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); } diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index e40157206d..99a3a75502 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -157,6 +157,9 @@ public: 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; @@ -250,7 +253,8 @@ public: ENABLE_VSYNC_INJECTIONS, INJECT_VSYNC, GET_LAYER_DEBUG_INFO, - CREATE_SCOPED_CONNECTION + CREATE_SCOPED_CONNECTION, + GET_DISPLAY_VIEWPORT }; virtual status_t onTransact(uint32_t code, const Parcel& data, diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 377fe68c41..ad8a8b09d0 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -79,6 +79,9 @@ public: 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); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 2c02ba657d..6e196bfac8 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -581,6 +581,9 @@ public: 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 { diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index c1f1d2583f..4aa9f628ce 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -370,6 +370,10 @@ status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& 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"); + return NO_MEMORY; + } int const* buf = static_cast<int const*>(buffer); diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h b/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h index 6e303a5e5a..2f14f7cd91 100644 --- a/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h +++ b/libs/vr/libbufferhubqueue/include/private/dvr/epoll_file_descriptor.h @@ -28,7 +28,7 @@ class EpollFileDescriptor { return -EALREADY; } - fd_.reset(epoll_create(64)); + fd_.reset(epoll_create1(EPOLL_CLOEXEC)); if (fd_.get() < 0) return -errno; diff --git a/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp b/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp index fa0adf0182..f72dabc658 100644 --- a/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp +++ b/libs/vr/libpdx_default_transport/pdx_benchmarks.cpp @@ -66,7 +66,7 @@ void SetThreadName(const std::string& name) { prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name.c_str()), 0, 0, 0); } -constexpr uint64_t kNanosPerSecond = 1000000000llu; +constexpr uint64_t kNanosPerSecond = 1000000000LLU; uint64_t GetClockNs() { timespec t; diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp index 962c7459fd..1cf5f17f62 100644 --- a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp +++ b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp @@ -11,7 +11,7 @@ namespace android { namespace dvr { EpollEventDispatcher::EpollEventDispatcher() { - epoll_fd_.Reset(epoll_create(64)); + epoll_fd_.Reset(epoll_create1(EPOLL_CLOEXEC)); if (!epoll_fd_) { ALOGE("Failed to create epoll fd: %s", strerror(errno)); return; diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp index 4d9a2a0254..ccc24b950e 100644 --- a/services/inputflinger/EventHub.cpp +++ b/services/inputflinger/EventHub.cpp @@ -206,7 +206,7 @@ EventHub::EventHub(void) : mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); - mEpollFd = epoll_create(EPOLL_SIZE_HINT); + mEpollFd = epoll_create1(EPOLL_CLOEXEC); LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); mINotifyFd = inotify_init(); diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index b37b7fd392..efa6f88a87 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -3089,6 +3089,7 @@ TouchInputMapper::TouchInputMapper(InputDevice* device) : InputMapper(device), mSource(0), mDeviceMode(DEVICE_MODE_DISABLED), mSurfaceWidth(-1), mSurfaceHeight(-1), mSurfaceLeft(0), mSurfaceTop(0), + mPhysicalWidth(-1), mPhysicalHeight(-1), mPhysicalLeft(0), mPhysicalTop(0), mSurfaceOrientation(DISPLAY_ORIENTATION_0) { } @@ -3596,6 +3597,11 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { break; } + mPhysicalWidth = naturalPhysicalWidth; + mPhysicalHeight = naturalPhysicalHeight; + mPhysicalLeft = naturalPhysicalLeft; + mPhysicalTop = naturalPhysicalTop; + mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; @@ -3604,6 +3610,11 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0; } else { + mPhysicalWidth = rawWidth; + mPhysicalHeight = rawHeight; + mPhysicalLeft = 0; + mPhysicalTop = 0; + mSurfaceWidth = rawWidth; mSurfaceHeight = rawHeight; mSurfaceLeft = 0; @@ -3914,6 +3925,10 @@ void TouchInputMapper::dumpSurface(std::string& dump) { dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight); dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop); + dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); + dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); + dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); + dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop); dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation); } @@ -5118,10 +5133,10 @@ void TouchInputMapper::cookPointerData() { } break; case DISPLAY_ORIENTATION_180: - x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate; + x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale; y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate; - left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate; - right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; + left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; + right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; orientation -= M_PI; @@ -5130,10 +5145,10 @@ void TouchInputMapper::cookPointerData() { } break; case DISPLAY_ORIENTATION_270: - x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate; + x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale; y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; - right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; + left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; + right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; orientation += M_PI_2; @@ -6531,8 +6546,12 @@ void TouchInputMapper::cancelTouch(nsecs_t when) { } bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { + const float scaledX = x * mXScale; + const float scaledY = y * mYScale; return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue - && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue; + && scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth + && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue + && scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight; } const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit( diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h index cef3212684..2f98e69800 100644 --- a/services/inputflinger/InputReader.h +++ b/services/inputflinger/InputReader.h @@ -1517,13 +1517,21 @@ private: // in the natural orientation. // The surface origin specifies how the surface coordinates should be translated // to align with the logical display coordinate space. - // The orientation may be different from the viewport orientation as it specifies - // the rotation of the surface coordinates required to produce the viewport's - // requested orientation, so it will depend on whether the device is orientation aware. int32_t mSurfaceWidth; int32_t mSurfaceHeight; int32_t mSurfaceLeft; int32_t mSurfaceTop; + + // Similar to the surface coordinates, but in the raw display coordinate space rather than in + // the logical coordinate space. + int32_t mPhysicalWidth; + int32_t mPhysicalHeight; + int32_t mPhysicalLeft; + int32_t mPhysicalTop; + + // The orientation may be different from the viewport orientation as it specifies + // the rotation of the surface coordinates required to produce the viewport's + // requested orientation, so it will depend on whether the device is orientation aware. int32_t mSurfaceOrientation; // Translation and scaling factors, orientation-independent. diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 3db633b7d1..f5b5eda9ec 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -704,6 +704,8 @@ bool BufferLayer::isOpaque(const Layer::State& s) const { } void BufferLayer::onFirstRef() { + Layer::onFirstRef(); + // Creates a custom BufferQueue for SurfaceFlingerConsumer to use sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index cf53930fa1..28b447f7aa 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -981,6 +981,21 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& /* display */, return NO_ERROR; } +status_t SurfaceFlinger::getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) { + if (outViewport == nullptr || display.get() == nullptr) { + return BAD_VALUE; + } + + sp<const DisplayDevice> device(getDisplayDevice(display)); + if (device == nullptr) { + return BAD_VALUE; + } + + *outViewport = device->getViewport(); + + return NO_ERROR; +} + int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) { if (display == nullptr) { ALOGE("%s : display is nullptr", __func__); @@ -2180,11 +2195,15 @@ void SurfaceFlinger::postFramebuffer() displayDevice->onSwapBuffersCompleted(); displayDevice->makeCurrent(); for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + sp<Fence> releaseFence = Fence::NO_FENCE; + // The layer buffer from the previous frame (if any) is released // by HWC only when the release fence from this frame (if any) is // signaled. Always get the release fence from HWC first. auto hwcLayer = layer->getHwcLayer(hwcId); - sp<Fence> releaseFence = getBE().mHwc->getLayerReleaseFence(hwcId, hwcLayer); + if (hwcId >= 0) { + releaseFence = getBE().mHwc->getLayerReleaseFence(hwcId, hwcLayer); + } // If the layer was client composited in the previous frame, we // need to merge with the previous client target acquire fence. @@ -2331,8 +2350,10 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( const sp<DisplaySurface>& dispSurface, const sp<IGraphicBufferProducer>& producer) { bool hasWideColorGamut = false; std::unordered_map<ColorMode, std::vector<RenderIntent>> hwcColorModes; + HdrCapabilities hdrCapabilities; + int32_t supportedPerFrameMetadata = 0; - if (hasWideColorDisplay) { + if (hasWideColorDisplay && hwcId >= 0) { std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId); for (ColorMode colorMode : modes) { switch (colorMode) { @@ -2351,8 +2372,10 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( } } - HdrCapabilities hdrCapabilities; - getHwComposer().getHdrCapabilities(hwcId, &hdrCapabilities); + if (hwcId >= 0) { + getHwComposer().getHdrCapabilities(hwcId, &hdrCapabilities); + supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(hwcId); + } auto nativeWindowSurface = mCreateNativeWindowSurface(producer); auto nativeWindow = nativeWindowSurface->getNativeWindow(); @@ -2386,8 +2409,7 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( new DisplayDevice(this, state.type, hwcId, state.isSecure, display, nativeWindow, dispSurface, std::move(renderSurface), displayWidth, displayHeight, hasWideColorGamut, hdrCapabilities, - getHwComposer().getSupportedPerFrameMetadata(hwcId), - hwcColorModes, initialPowerMode); + supportedPerFrameMetadata, hwcColorModes, initialPowerMode); if (maxFrameBufferAcquiredBuffers >= 3) { nativeWindowSurface->preallocateBuffers(); @@ -3038,22 +3060,17 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev } } - if (displayDevice->getDisplayType() != DisplayDevice::DISPLAY_PRIMARY) { - // just to be on the safe side, we don't set the - // scissor on the main display. It should never be needed - // anyways (though in theory it could since the API allows it). - const Rect& bounds(displayDevice->getBounds()); - const Rect& scissor(displayDevice->getScissor()); - if (scissor != bounds) { - // scissor doesn't match the screen's dimensions, so we - // need to clear everything outside of it and enable - // the GL scissor so we don't draw anything where we shouldn't - - // enable scissor for this frame - const uint32_t height = displayDevice->getHeight(); - getBE().mRenderEngine->setScissor(scissor.left, height - scissor.bottom, - scissor.getWidth(), scissor.getHeight()); - } + const Rect& bounds(displayDevice->getBounds()); + const Rect& scissor(displayDevice->getScissor()); + if (scissor != bounds) { + // scissor doesn't match the screen's dimensions, so we + // need to clear everything outside of it and enable + // the GL scissor so we don't draw anything where we shouldn't + + // enable scissor for this frame + const uint32_t height = displayDevice->getHeight(); + getBE().mRenderEngine->setScissor(scissor.left, height - scissor.bottom, + scissor.getWidth(), scissor.getHeight()); } } @@ -4821,6 +4838,16 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, sp<GraphicBuf const sp<const DisplayDevice> device(getDisplayDeviceLocked(display)); if (CC_UNLIKELY(device == 0)) return BAD_VALUE; + const Rect& dispScissor = device->getScissor(); + if (!dispScissor.isEmpty()) { + sourceCrop.set(dispScissor); + // adb shell screencap will default reqWidth and reqHeight to zeros. + if (reqWidth == 0 || reqHeight == 0) { + reqWidth = uint32_t(device->getViewport().width()); + reqHeight = uint32_t(device->getViewport().height()); + } + } + DisplayRenderArea renderArea(device, sourceCrop, reqHeight, reqWidth, rotation); auto traverseLayers = std::bind(std::mem_fn(&SurfaceFlinger::traverseLayersInDisplay), this, diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 8f724e98d1..0148ab6754 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -429,6 +429,7 @@ private: const Rect& sourceCrop, float frameScale, bool childrenOnly); virtual status_t getDisplayStats(const sp<IBinder>& display, DisplayStatInfo* stats); + virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport); virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayInfo>* configs); virtual int getActiveConfig(const sp<IBinder>& display); |