Merge "BufferHub: Clean up unnecessary DVR dependency"
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index ce3a6aa..35cff5f 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -83,7 +83,9 @@
],
srcs: [
"DumpstateInternal.cpp",
+ "DumpstateSectionReporter.cpp",
"DumpstateService.cpp",
+ "main.cpp",
"utils.cpp",
"dumpstate.cpp",
],
diff --git a/cmds/dumpstate/DumpstateSectionReporter.cpp b/cmds/dumpstate/DumpstateSectionReporter.cpp
new file mode 100644
index 0000000..f814bde
--- /dev/null
+++ b/cmds/dumpstate/DumpstateSectionReporter.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "dumpstate"
+
+#include "DumpstateSectionReporter.h"
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+DumpstateSectionReporter::DumpstateSectionReporter(const std::string& title,
+ sp<android::os::IDumpstateListener> listener,
+ bool sendReport)
+ : title_(title), listener_(listener), sendReport_(sendReport), status_(OK), size_(-1) {
+ started_ = std::chrono::steady_clock::now();
+}
+
+DumpstateSectionReporter::~DumpstateSectionReporter() {
+ if ((listener_ != nullptr) && (sendReport_)) {
+ auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::steady_clock::now() - started_);
+ listener_->onSectionComplete(title_, status_, size_, (int32_t)elapsed.count());
+ }
+}
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/DumpstateSectionReporter.h b/cmds/dumpstate/DumpstateSectionReporter.h
new file mode 100644
index 0000000..e971de8
--- /dev/null
+++ b/cmds/dumpstate/DumpstateSectionReporter.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_OS_DUMPSTATESECTIONREPORTER_H_
+#define ANDROID_OS_DUMPSTATESECTIONREPORTER_H_
+
+#include <android/os/IDumpstateListener.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace os {
+namespace dumpstate {
+
+
+/*
+ * Helper class used to report per section details to a listener.
+ *
+ * Typical usage:
+ *
+ * DumpstateSectionReporter sectionReporter(title, listener, sendReport);
+ * sectionReporter.setSize(5000);
+ *
+ */
+class DumpstateSectionReporter {
+ public:
+ DumpstateSectionReporter(const std::string& title, sp<android::os::IDumpstateListener> listener,
+ bool sendReport);
+
+ ~DumpstateSectionReporter();
+
+ void setStatus(status_t status) {
+ status_ = status;
+ }
+
+ void setSize(int size) {
+ size_ = size;
+ }
+
+ private:
+ std::string title_;
+ android::sp<android::os::IDumpstateListener> listener_;
+ bool sendReport_;
+ status_t status_;
+ int size_;
+ std::chrono::time_point<std::chrono::steady_clock> started_;
+};
+
+} // namespace dumpstate
+} // namespace os
+} // namespace android
+
+#endif // ANDROID_OS_DUMPSTATESECTIONREPORTER_H_
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index efe0466..49a78e7 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -52,6 +52,7 @@
binder::Status DumpstateService::setListener(const std::string& name,
const sp<IDumpstateListener>& listener,
+ bool getSectionDetails,
sp<IDumpstateToken>* returned_token) {
*returned_token = nullptr;
if (name.empty()) {
@@ -70,6 +71,7 @@
ds_.listener_name_ = name;
ds_.listener_ = listener;
+ ds_.report_section_ = getSectionDetails;
*returned_token = new DumpstateToken();
return binder::Status::ok();
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 4352d3d..7bca24a 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -38,6 +38,7 @@
status_t dump(int fd, const Vector<String16>& args) override;
binder::Status setListener(const std::string& name, const sp<IDumpstateListener>& listener,
+ bool getSectionDetails,
sp<IDumpstateToken>* returned_token) override;
private:
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 4becccf..9b11b96 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -30,6 +30,9 @@
*
* Returns a token used to monitor dumpstate death, or `nullptr` if the listener was already
* set (the listener behaves like a Highlander: There Can be Only One).
+ * Set {@code getSectionDetails} to true in order to receive callbacks with per section
+ * progress details
*/
- IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener);
+ IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener,
+ boolean getSectionDetails);
}
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index 32717f4..030d69d 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -24,4 +24,16 @@
interface IDumpstateListener {
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
+ */
+ void onSectionComplete(@utf8InCpp String name, int status, int size, int durationMs);
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 665695c..93f8d43 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -833,6 +833,17 @@
static const long MINIMUM_LOGCAT_TIMEOUT_MS = 50000;
+static void DoKernelLogcat() {
+ unsigned long timeout_ms = logcat_timeout("kernel");
+ if (timeout_ms < MINIMUM_LOGCAT_TIMEOUT_MS) {
+ timeout_ms = MINIMUM_LOGCAT_TIMEOUT_MS;
+ }
+ RunCommand(
+ "KERNEL LOG",
+ {"logcat", "-b", "kernel", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+ CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+}
+
static void DoLogcat() {
unsigned long timeout_ms;
// DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
@@ -848,25 +859,24 @@
if (timeout_ms < MINIMUM_LOGCAT_TIMEOUT_MS) {
timeout_ms = MINIMUM_LOGCAT_TIMEOUT_MS;
}
- RunCommand("EVENT LOG",
- {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid",
- "-d", "*:v"},
- CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+ RunCommand(
+ "EVENT LOG",
+ {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+ CommandOptions::WithTimeoutInMs(timeout_ms).Build());
timeout_ms = logcat_timeout("radio");
if (timeout_ms < MINIMUM_LOGCAT_TIMEOUT_MS) {
timeout_ms = MINIMUM_LOGCAT_TIMEOUT_MS;
}
- RunCommand("RADIO LOG",
- {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid",
- "-d", "*:v"},
- CommandOptions::WithTimeoutInMs(timeout_ms).Build());
+ RunCommand(
+ "RADIO LOG",
+ {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+ CommandOptions::WithTimeoutInMs(timeout_ms).Build());
RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
/* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
- RunCommand("LAST LOGCAT",
- {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-v", "uid",
- "-d", "*:v"});
+ RunCommand("LAST LOGCAT", {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable",
+ "-v", "uid", "-d", "*:v"});
}
static void DumpIpTablesAsRoot() {
@@ -1187,7 +1197,12 @@
RunCommand("LSMOD", {"lsmod"});
}
- do_dmesg();
+ if (__android_logger_property_get_bool(
+ "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE)) {
+ DoKernelLogcat();
+ } else {
+ do_dmesg();
+ }
RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
@@ -1588,7 +1603,8 @@
// clang-format on
}
-int main(int argc, char *argv[]) {
+/** 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;
@@ -1601,6 +1617,8 @@
bool show_header_only = false;
bool do_start_service = false;
bool telephony_only = false;
+ int dup_stdout_fd;
+ int dup_stderr_fd;
/* set as high priority, and protect from OOM killer */
setpriority(PRIO_PROCESS, 0, -20);
@@ -1872,11 +1890,13 @@
}
if (is_redirecting) {
+ 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));
}
+ TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout)));
/* 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. */
@@ -1950,7 +1970,7 @@
/* close output if needed */
if (is_redirecting) {
- fclose(stdout);
+ TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
}
/* rename or zip the (now complete) .tmp file to its final location */
@@ -2081,7 +2101,7 @@
MYLOGI("done (id %d)\n", ds.id_);
if (is_redirecting) {
- fclose(stderr);
+ TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr)));
}
if (use_control_socket && ds.control_socket_fd_ != -1) {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 8db23a9..843c545 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -341,9 +341,10 @@
// Pointer to the zip structure.
std::unique_ptr<ZipWriter> zip_writer_;
- // Binder object listing to progress.
+ // Binder object listening to progress.
android::sp<android::os::IDumpstateListener> listener_;
std::string listener_name_;
+ bool report_section_;
// Notification title and description
std::string notification_title;
@@ -433,6 +434,9 @@
/** Gets command-line arguments. */
void format_args(int argc, const char *argv[], std::string *args);
+/** Main entry point for dumpstate. */
+int run_main(int argc, char* argv[]);
+
#ifdef __cplusplus
}
#endif
diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp
new file mode 100644
index 0000000..78aad11
--- /dev/null
+++ b/cmds/dumpstate/main.cpp
@@ -0,0 +1,21 @@
+/*
+ * 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 "dumpstate.h"
+
+int main(int argc, char* argv[]) {
+ return run_main(argc, argv);
+}
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index a2e9453..838b385 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -58,6 +58,8 @@
public:
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,
+ int32_t size, int32_t durationMs));
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
@@ -601,27 +603,43 @@
TEST_F(DumpstateServiceTest, SetListenerNoName) {
sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
sp<IDumpstateToken> token;
- EXPECT_TRUE(dss.setListener("", listener, &token).isOk());
+ EXPECT_TRUE(dss.setListener("", listener, /* getSectionDetails = */ false, &token).isOk());
ASSERT_THAT(token, IsNull());
}
TEST_F(DumpstateServiceTest, SetListenerNoPointer) {
sp<IDumpstateToken> token;
- EXPECT_TRUE(dss.setListener("whatever", nullptr, &token).isOk());
+ EXPECT_TRUE(
+ dss.setListener("whatever", nullptr, /* getSectionDetails = */ false, &token).isOk());
ASSERT_THAT(token, IsNull());
}
TEST_F(DumpstateServiceTest, SetListenerTwice) {
sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
sp<IDumpstateToken> token;
- EXPECT_TRUE(dss.setListener("whatever", listener, &token).isOk());
+ EXPECT_TRUE(
+ dss.setListener("whatever", listener, /* getSectionDetails = */ false, &token).isOk());
ASSERT_THAT(token, NotNull());
EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+ EXPECT_FALSE(Dumpstate::GetInstance().report_section_);
token.clear();
- EXPECT_TRUE(dss.setListener("whatsoever", listener, &token).isOk());
+ EXPECT_TRUE(
+ dss.setListener("whatsoever", listener, /* getSectionDetails = */ false, &token).isOk());
ASSERT_THAT(token, IsNull());
EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+ EXPECT_FALSE(Dumpstate::GetInstance().report_section_);
+}
+
+TEST_F(DumpstateServiceTest, SetListenerWithSectionDetails) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ sp<IDumpstateToken> token;
+ Dumpstate::GetInstance().listener_ = nullptr;
+ EXPECT_TRUE(
+ dss.setListener("whatever", listener, /* getSectionDetails = */ true, &token).isOk());
+ ASSERT_THAT(token, NotNull());
+ EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+ EXPECT_TRUE(Dumpstate::GetInstance().report_section_);
}
class ProgressTest : public DumpstateBaseTest {
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 0862a40..ae0cc01 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -43,9 +43,11 @@
#include "dumpsys.h"
using namespace android;
-using android::base::StringPrintf;
-using android::base::unique_fd;
-using android::base::WriteFully;
+using ::android::base::StringAppendF;
+using ::android::base::StringPrintf;
+using ::android::base::unique_fd;
+using ::android::base::WriteFully;
+using ::android::base::WriteStringToFd;
static int sort_func(const String16* lhs, const String16* rhs)
{
@@ -96,6 +98,19 @@
return false;
}
+String16 ConvertBitmaskToPriorityType(int bitmask) {
+ if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) {
+ return String16(PriorityDumper::PRIORITY_ARG_CRITICAL);
+ }
+ if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) {
+ return String16(PriorityDumper::PRIORITY_ARG_HIGH);
+ }
+ if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
+ return String16(PriorityDumper::PRIORITY_ARG_NORMAL);
+ }
+ return String16("");
+}
+
int Dumpsys::main(int argc, char* const argv[]) {
Vector<String16> services;
Vector<String16> args;
@@ -104,9 +119,9 @@
Vector<String16> protoServices;
bool showListOnly = false;
bool skipServices = false;
- bool filterByProto = false;
+ bool asProto = false;
int timeoutArgMs = 10000;
- int dumpPriorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
+ int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
static struct option longOptions[] = {{"priority", required_argument, 0, 0},
{"proto", no_argument, 0, 0},
{"skip", no_argument, 0, 0},
@@ -131,13 +146,13 @@
if (!strcmp(longOptions[optionIndex].name, "skip")) {
skipServices = true;
} else if (!strcmp(longOptions[optionIndex].name, "proto")) {
- filterByProto = true;
+ asProto = true;
} else if (!strcmp(longOptions[optionIndex].name, "help")) {
usage();
return 0;
} else if (!strcmp(longOptions[optionIndex].name, "priority")) {
priorityType = String16(String8(optarg));
- if (!ConvertPriorityTypeToBitmask(priorityType, dumpPriorityFlags)) {
+ if (!ConvertPriorityTypeToBitmask(priorityType, priorityFlags)) {
fprintf(stderr, "\n");
usage();
return -1;
@@ -198,28 +213,11 @@
}
if (services.empty() || showListOnly) {
- // gets all services
- services = sm_->listServices(dumpPriorityFlags);
- services.sort(sort_func);
- if (filterByProto) {
- protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO);
- protoServices.sort(sort_func);
- Vector<String16> intersection;
- std::set_intersection(services.begin(), services.end(), protoServices.begin(),
- protoServices.end(), std::back_inserter(intersection));
- services = std::move(intersection);
- args.insertAt(String16(PriorityDumper::PROTO_ARG), 0);
- }
- if (dumpPriorityFlags != IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
- args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0);
- args.insertAt(priorityType, 1);
- } else {
- args.add(String16("-a"));
- }
+ services = listServices(priorityFlags, asProto);
+ setServiceArgs(args, asProto, priorityFlags);
}
const size_t N = services.size();
-
if (N > 1) {
// first print a list of the current services
aout << "Currently running services:" << endl;
@@ -239,129 +237,204 @@
}
for (size_t i = 0; i < N; i++) {
- const String16& service_name = std::move(services[i]);
- if (IsSkipped(skippedServices, service_name)) continue;
+ const String16& serviceName = services[i];
+ if (IsSkipped(skippedServices, serviceName)) continue;
- sp<IBinder> service = sm_->checkService(service_name);
- if (service != nullptr) {
- int sfd[2];
-
- if (pipe(sfd) != 0) {
- aerr << "Failed to create pipe to dump service info for " << service_name
- << ": " << strerror(errno) << endl;
- continue;
+ if (startDumpThread(serviceName, args) == OK) {
+ bool addSeparator = (N > 1);
+ if (addSeparator) {
+ writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags);
}
+ std::chrono::duration<double> elapsedDuration;
+ size_t bytesWritten = 0;
+ status_t status =
+ writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(timeoutArgMs),
+ asProto, elapsedDuration, bytesWritten);
- unique_fd local_end(sfd[0]);
- unique_fd remote_end(sfd[1]);
- sfd[0] = sfd[1] = -1;
-
- if (N > 1) {
- aout << "------------------------------------------------------------"
- "-------------------" << endl;
- if (dumpPriorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
- aout << "DUMP OF SERVICE " << service_name << ":" << endl;
- } else {
- aout << "DUMP OF SERVICE " << priorityType << " " << service_name << ":" << endl;
- }
- }
-
- // dump blocks until completion, so spawn a thread..
- std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable {
- int err = service->dump(remote_end.get(), args);
-
- // It'd be nice to be able to close the remote end of the socketpair before the dump
- // call returns, to terminate our reads if the other end closes their copy of the
- // file descriptor, but then hangs for some reason. There doesn't seem to be a good
- // way to do this, though.
- remote_end.reset();
-
- if (err != 0) {
- aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
- << endl;
- }
- });
-
- auto timeout = std::chrono::milliseconds(timeoutArgMs);
- auto start = std::chrono::steady_clock::now();
- auto end = start + timeout;
-
- struct pollfd pfd = {
- .fd = local_end.get(),
- .events = POLLIN
- };
-
- bool timed_out = false;
- bool error = false;
- while (true) {
- // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
- 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);
- };
-
- int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
- if (rc < 0) {
- aerr << "Error in poll while dumping service " << service_name << " : "
- << strerror(errno) << endl;
- error = true;
- break;
- } else if (rc == 0) {
- timed_out = true;
- break;
- }
-
- char buf[4096];
- rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf)));
- if (rc < 0) {
- aerr << "Failed to read while dumping service " << service_name << ": "
- << strerror(errno) << endl;
- error = true;
- break;
- } else if (rc == 0) {
- // EOF.
- break;
- }
-
- if (!WriteFully(STDOUT_FILENO, buf, rc)) {
- aerr << "Failed to write while dumping service " << service_name << ": "
- << strerror(errno) << endl;
- error = true;
- break;
- }
- }
-
- if (timed_out) {
+ if (status == TIMED_OUT) {
aout << endl
- << "*** SERVICE '" << service_name << "' DUMP TIMEOUT (" << timeoutArgMs
+ << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs
<< "ms) EXPIRED ***" << endl
<< endl;
}
- if (timed_out || error) {
- dump_thread.detach();
- } else {
- dump_thread.join();
+ if (addSeparator) {
+ writeDumpFooter(STDOUT_FILENO, serviceName, elapsedDuration);
}
-
- if (N > 1) {
- std::chrono::duration<double> elapsed_seconds =
- std::chrono::steady_clock::now() - start;
- aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str()
- << "was the duration of dumpsys " << service_name;
-
- using std::chrono::system_clock;
- const auto finish = system_clock::to_time_t(system_clock::now());
- std::tm finish_tm;
- localtime_r(&finish, &finish_tm);
- aout << ", ending at: " << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S")
- << endl;
- }
- } else {
- aerr << "Can't find service: " << service_name << endl;
+ bool dumpComplete = (status == OK);
+ stopDumpThread(dumpComplete);
}
}
return 0;
}
+
+Vector<String16> Dumpsys::listServices(int priorityFilterFlags, bool filterByProto) const {
+ Vector<String16> services = sm_->listServices(priorityFilterFlags);
+ services.sort(sort_func);
+ if (filterByProto) {
+ Vector<String16> protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO);
+ protoServices.sort(sort_func);
+ Vector<String16> intersection;
+ std::set_intersection(services.begin(), services.end(), protoServices.begin(),
+ protoServices.end(), std::back_inserter(intersection));
+ services = std::move(intersection);
+ }
+ return services;
+}
+
+void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) const {
+ if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) ||
+ (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL)) {
+ args.add(String16("-a"));
+ }
+ if (asProto) {
+ args.insertAt(String16(PriorityDumper::PROTO_ARG), 0);
+ }
+ if (priorityFlags != IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
+ String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags);
+ args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0);
+ args.insertAt(priorityType, 1);
+ }
+}
+
+status_t Dumpsys::startDumpThread(const String16& serviceName, const Vector<String16>& args) {
+ sp<IBinder> service = sm_->checkService(serviceName);
+ if (service == nullptr) {
+ aerr << "Can't find service: " << serviceName << endl;
+ return NAME_NOT_FOUND;
+ }
+
+ int sfd[2];
+ if (pipe(sfd) != 0) {
+ aerr << "Failed to create pipe to dump service info for " << serviceName << ": "
+ << strerror(errno) << endl;
+ return -errno;
+ }
+
+ redirectFd_ = unique_fd(sfd[0]);
+ unique_fd remote_end(sfd[1]);
+ sfd[0] = sfd[1] = -1;
+
+ // dump blocks until completion, so spawn a thread..
+ activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable {
+ int err = service->dump(remote_end.get(), args);
+
+ // It'd be nice to be able to close the remote end of the socketpair before the dump
+ // call returns, to terminate our reads if the other end closes their copy of the
+ // file descriptor, but then hangs for some reason. There doesn't seem to be a good
+ // way to do this, though.
+ remote_end.reset();
+
+ if (err != 0) {
+ aerr << "Error dumping service info: (" << strerror(err) << ") "
+ << serviceName << endl;
+ }
+ });
+ return OK;
+}
+
+void Dumpsys::stopDumpThread(bool dumpComplete) {
+ if (dumpComplete) {
+ activeThread_.join();
+ } else {
+ activeThread_.detach();
+ }
+ /* close read end of the dump output redirection pipe */
+ redirectFd_.reset();
+}
+
+void Dumpsys::writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const {
+ std::string msg(
+ "----------------------------------------"
+ "---------------------------------------\n");
+ if (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL ||
+ priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
+ StringAppendF(&msg, "DUMP OF SERVICE %s:\n", String8(serviceName).c_str());
+ } else {
+ String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags);
+ StringAppendF(&msg, "DUMP OF SERVICE %s %s:\n", String8(priorityType).c_str(),
+ String8(serviceName).c_str());
+ }
+ WriteStringToFd(msg, fd);
+}
+
+status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout,
+ bool asProto, std::chrono::duration<double>& elapsedDuration,
+ size_t& bytesWritten) const {
+ status_t status = OK;
+ size_t totalBytes = 0;
+ auto start = std::chrono::steady_clock::now();
+ auto end = start + timeout;
+
+ int serviceDumpFd = redirectFd_.get();
+ if (serviceDumpFd == -1) {
+ return INVALID_OPERATION;
+ }
+
+ struct pollfd pfd = {.fd = serviceDumpFd, .events = POLLIN};
+
+ while (true) {
+ // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
+ 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);
+ };
+
+ int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
+ if (rc < 0) {
+ aerr << "Error in poll while dumping service " << serviceName << " : "
+ << strerror(errno) << endl;
+ status = -errno;
+ break;
+ } else if (rc == 0) {
+ status = TIMED_OUT;
+ break;
+ }
+
+ char buf[4096];
+ rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf)));
+ if (rc < 0) {
+ aerr << "Failed to read while dumping service " << serviceName << ": "
+ << strerror(errno) << endl;
+ status = -errno;
+ break;
+ } else if (rc == 0) {
+ // EOF.
+ break;
+ }
+
+ if (!WriteFully(fd, buf, rc)) {
+ aerr << "Failed to write while dumping service " << serviceName << ": "
+ << strerror(errno) << endl;
+ status = -errno;
+ break;
+ }
+ totalBytes += rc;
+ }
+
+ if ((status == TIMED_OUT) && (!asProto)) {
+ std::string msg = StringPrintf("\n*** SERVICE '%s' DUMP TIMEOUT (%llums) EXPIRED ***\n\n",
+ String8(serviceName).string(), timeout.count());
+ WriteStringToFd(msg, fd);
+ }
+
+ elapsedDuration = std::chrono::steady_clock::now() - start;
+ bytesWritten = totalBytes;
+ return status;
+}
+
+void Dumpsys::writeDumpFooter(int fd, const String16& serviceName,
+ const std::chrono::duration<double>& elapsedDuration) const {
+ using std::chrono::system_clock;
+ const auto finish = system_clock::to_time_t(system_clock::now());
+ std::tm finish_tm;
+ localtime_r(&finish, &finish_tm);
+ std::stringstream oss;
+ oss << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S");
+ std::string msg =
+ StringPrintf("--------- %.3fs was the duration of dumpsys %s, ending at: %s\n",
+ elapsedDuration.count(), String8(serviceName).string(), oss.str().c_str());
+ WriteStringToFd(msg, fd);
+}
diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h
index 2534dde..1d78aa4 100644
--- a/cmds/dumpsys/dumpsys.h
+++ b/cmds/dumpsys/dumpsys.h
@@ -17,6 +17,9 @@
#ifndef FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
#define FRAMEWORK_NATIVE_CMD_DUMPSYS_H_
+#include <thread>
+
+#include <android-base/unique_fd.h>
#include <binder/IServiceManager.h>
namespace android {
@@ -25,10 +28,97 @@
public:
Dumpsys(android::IServiceManager* sm) : sm_(sm) {
}
+ /**
+ * Main entry point into dumpsys.
+ */
int main(int argc, char* const argv[]);
+ /**
+ * Returns a list of services.
+ * @param priorityFlags filter services by specified priorities
+ * @param supportsProto filter services that support proto dumps
+ * @return list of services
+ */
+ Vector<String16> listServices(int priorityFlags, bool supportsProto) const;
+
+ /**
+ * Modifies @{code args} to add additional arguments to indicate if the service
+ * must dump as proto or dump to a certian priority bucket.
+ * @param args initial list of arguments to pass to service dump method.
+ * @param asProto dump service as proto by passing an additional --proto arg
+ * @param priorityFlags indicates priority of dump by passing additional priority args
+ * to the service
+ */
+ void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) const;
+
+ /**
+ * Starts a thread to connect to a service and get its dump output. The thread redirects
+ * the output to a pipe. Thread must be stopped by a subsequent callto {@code
+ * stopDumpThread}.
+ * @param serviceName
+ * @param args list of arguments to pass to service dump method.
+ * @return {@code OK} thread is started successfully.
+ * {@code NAME_NOT_FOUND} service could not be found.
+ * {@code != OK} error
+ */
+ status_t startDumpThread(const String16& serviceName, const Vector<String16>& args);
+
+ /**
+ * Writes a section header to a file descriptor.
+ * @param fd file descriptor to write data
+ * @param serviceName
+ * @param priorityFlags dump priority specified
+ */
+ void writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const;
+
+ /**
+ * Redirects service dump to a file descriptor. This requires
+ * {@code startDumpThread} to be called successfully otherwise the function will
+ * return {@code INVALID_OPERATION}.
+ * @param fd file descriptor to write data
+ * @param serviceName
+ * @param timeout timeout to terminate the dump if not completed
+ * @param asProto used to supresses additional output to the fd such as timeout
+ * error messages
+ * @param elapsedDuration returns elapsed time in seconds
+ * @param bytesWritten returns number of bytes written
+ * @return {@code OK} if successful
+ * {@code TIMED_OUT} dump timed out
+ * {@code INVALID_OPERATION} invalid state
+ * {@code != OK} error
+ */
+ status_t writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout,
+ bool asProto, std::chrono::duration<double>& elapsedDuration,
+ size_t& bytesWritten) const;
+
+ /**
+ * Writes a section footer to a file descriptor with duration info.
+ * @param fd file descriptor to write data
+ * @param serviceName
+ * @param elapsedDuration duration of dump
+ */
+ void writeDumpFooter(int fd, const String16& serviceName,
+ const std::chrono::duration<double>& elapsedDuration) const;
+
+ /**
+ * Terminates dump thread.
+ * @param dumpComplete If {@code true}, indicates the dump was successfully completed and
+ * tries to join the thread. Otherwise thread is detached.
+ */
+ void stopDumpThread(bool dumpComplete);
+
+ /**
+ * Returns file descriptor of the pipe used to dump service data. This assumes
+ * {@code startDumpThread} was called successfully.
+ */
+ int getDumpFd() const {
+ return redirectFd_.get();
+ }
+
private:
android::IServiceManager* sm_;
+ std::thread activeThread_;
+ mutable android::base::unique_fd redirectFd_;
};
}
diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp
index bdb0a9a..b13f59d 100644
--- a/cmds/dumpsys/tests/dumpsys_test.cpp
+++ b/cmds/dumpsys/tests/dumpsys_test.cpp
@@ -188,6 +188,22 @@
EXPECT_THAT(status, Eq(0));
}
+ void CallSingleService(const String16& serviceName, Vector<String16>& args, int priorityFlags,
+ bool supportsProto, std::chrono::duration<double>& elapsedDuration,
+ size_t& bytesWritten) {
+ CaptureStdout();
+ CaptureStderr();
+ dump_.setServiceArgs(args, supportsProto, priorityFlags);
+ status_t status = dump_.startDumpThread(serviceName, args);
+ EXPECT_THAT(status, Eq(0));
+ status = dump_.writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(500), false,
+ elapsedDuration, bytesWritten);
+ EXPECT_THAT(status, Eq(0));
+ dump_.stopDumpThread(/* dumpCompleted = */ true);
+ stdout_ = GetCapturedStdout();
+ stderr_ = GetCapturedStderr();
+ }
+
void AssertRunningServices(const std::vector<std::string>& services) {
std::string expected;
if (services.size() > 1) {
@@ -209,6 +225,7 @@
void AssertDumped(const std::string& service, const std::string& dump) {
EXPECT_THAT(stdout_, HasSubstr("DUMP OF SERVICE " + service + ":\n" + dump));
+ EXPECT_THAT(stdout_, HasSubstr("was the duration of dumpsys " + service + ", ending at: "));
}
void AssertDumpedWithPriority(const std::string& service, const std::string& dump,
@@ -216,6 +233,7 @@
std::string priority = String8(priorityType).c_str();
EXPECT_THAT(stdout_,
HasSubstr("DUMP OF SERVICE " + priority + " " + service + ":\n" + dump));
+ EXPECT_THAT(stdout_, HasSubstr("was the duration of dumpsys " + service + ", ending at: "));
}
void AssertNotDumped(const std::string& dump) {
@@ -425,8 +443,8 @@
CallMain({"--priority", "NORMAL"});
AssertRunningServices({"runningnormal1", "runningnormal2"});
- AssertDumpedWithPriority("runningnormal1", "dump1", PriorityDumper::PRIORITY_ARG_NORMAL);
- AssertDumpedWithPriority("runningnormal2", "dump2", PriorityDumper::PRIORITY_ARG_NORMAL);
+ AssertDumped("runningnormal1", "dump1");
+ AssertDumped("runningnormal2", "dump2");
}
// Tests 'dumpsys --proto'
@@ -461,3 +479,29 @@
AssertDumpedWithPriority("runninghigh1", "dump1", PriorityDumper::PRIORITY_ARG_HIGH);
AssertDumpedWithPriority("runninghigh2", "dump2", PriorityDumper::PRIORITY_ARG_HIGH);
}
+
+TEST_F(DumpsysTest, GetBytesWritten) {
+ const char* serviceName = "service2";
+ const char* dumpContents = "dump1";
+ ExpectDump(serviceName, dumpContents);
+
+ String16 service(serviceName);
+ Vector<String16> args;
+ std::chrono::duration<double> elapsedDuration;
+ size_t bytesWritten;
+
+ CallSingleService(service, args, IServiceManager::DUMP_FLAG_PRIORITY_ALL,
+ /* as_proto = */ false, elapsedDuration, bytesWritten);
+
+ AssertOutput(dumpContents);
+ EXPECT_THAT(bytesWritten, Eq(strlen(dumpContents)));
+}
+
+TEST_F(DumpsysTest, WriteDumpWithoutThreadStart) {
+ std::chrono::duration<double> elapsedDuration;
+ size_t bytesWritten;
+ status_t status =
+ dump_.writeDump(STDOUT_FILENO, String16("service"), std::chrono::milliseconds(500),
+ /* as_proto = */ false, elapsedDuration, bytesWritten);
+ EXPECT_THAT(status, Eq(INVALID_OPERATION));
+}
\ No newline at end of file
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 1053522..6f8c841 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -84,6 +84,8 @@
static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
static constexpr const char* IDMAP_SUFFIX = "@idmap";
+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;
@@ -2356,6 +2358,17 @@
return res ? ok() : error();
}
+binder::Status InstalldNativeService::installApkVerity(const std::string& /*filePath*/,
+ const ::android::base::unique_fd& /*verityInput*/) {
+ ENFORCE_UID(AID_SYSTEM);
+ if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) {
+ return ok();
+ }
+ // TODO: Append verity to filePath then issue ioctl to enable
+ // it and hide the tree. See b/30972906.
+ return error("not implemented yet");
+}
+
binder::Status InstalldNativeService::reconcileSecondaryDexFile(
const std::string& dexPath, const std::string& packageName, int32_t uid,
const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volumeUuid,
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 57dd834..b494c7a 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -117,6 +117,8 @@
const std::string& outputPath);
binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
const std::unique_ptr<std::string>& outputPath);
+ binder::Status installApkVerity(const std::string& filePath,
+ const ::android::base::unique_fd& verityInput);
binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 394c028..89585a0 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -81,6 +81,7 @@
@utf8InCpp String outputPath);
void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
@nullable @utf8InCpp String outputPath);
+ void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput);
boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
diff --git a/data/etc/android.hardware.telephony.mbms.xml b/data/etc/android.hardware.telephony.mbms.xml
new file mode 100644
index 0000000..271ea58
--- /dev/null
+++ b/data/etc/android.hardware.telephony.mbms.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Feature for devices that support MBMS. -->
+<permissions>
+ <feature name="android.hardware.telephony.mbms" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index b6abc1b..d790bed 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -46,7 +46,7 @@
<feature name="android.software.home_screen" />
<feature name="android.software.input_methods" />
<feature name="android.software.picture_in_picture" notLowRam="true" />
- <feature name="android.software.activities_on_secondary_displays" />
+ <feature name="android.software.activities_on_secondary_displays" notLowRam="true" />
<feature name="android.software.print" />
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index e54f147..57eee12 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -107,7 +107,7 @@
// this is the strategy that applications will actually use. Be very careful
// when adjusting the default strategy because it can dramatically affect
// (often in a bad way) the user experience.
-const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
+const char* VelocityTracker::DEFAULT_STRATEGY = "impulse";
VelocityTracker::VelocityTracker(const char* strategy) :
mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
diff --git a/libs/vr/libpdx/private/pdx/rpc/variant.h b/libs/vr/libpdx/private/pdx/rpc/variant.h
index 80b1769..2cc9664 100644
--- a/libs/vr/libpdx/private/pdx/rpc/variant.h
+++ b/libs/vr/libpdx/private/pdx/rpc/variant.h
@@ -450,12 +450,19 @@
Variant(Variant&& other)
: index_{other.index_}, value_{std::move(other.value_), other.index_} {}
+// Recent Clang versions has a regression that produces bogus
+// unused-lambda-capture warning. Suppress the warning as a temporary
+// workaround. http://b/71356631
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-lambda-capture"
// Copy and move construction from Variant types. Each element of OtherTypes
// must be convertible to an element of Types.
template <typename... OtherTypes>
explicit Variant(const Variant<OtherTypes...>& other) {
other.Visit([this](const auto& value) { Construct(value); });
}
+#pragma clang diagnostic pop
+
template <typename... OtherTypes>
explicit Variant(Variant<OtherTypes...>&& other) {
other.Visit([this](auto&& value) { Construct(std::move(value)); });
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 9822849..91a3455 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -407,9 +407,8 @@
DIR* d = opendir(search);
if (d != NULL) {
- struct dirent cur;
struct dirent* e;
- while (readdir_r(d, &cur, &e) == 0 && e) {
+ while ((e = readdir(d)) != NULL) {
if (e->d_type == DT_DIR) {
continue;
}
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 9772f9a..a663487 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -127,20 +127,6 @@
mPowerMode = (mType >= DisplayDevice::DISPLAY_VIRTUAL) ?
HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
- // Name the display. The name will be replaced shortly if the display
- // was created with createDisplay().
- switch (mType) {
- case DISPLAY_PRIMARY:
- mDisplayName = "Built-in Screen";
- break;
- case DISPLAY_EXTERNAL:
- mDisplayName = "HDMI Screen";
- break;
- default:
- mDisplayName = "Virtual Screen"; // e.g. Overlay #n
- break;
- }
-
// initialize the display orientation transform.
setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 8b5d4c9..4faba3b 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -76,6 +76,10 @@
SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
}
+void FramebufferSurface::resizeBuffers(const uint32_t width, const uint32_t height) {
+ mConsumer->setDefaultBufferSize(width, height);
+}
+
status_t FramebufferSurface::beginFrame(bool /*mustRecompose*/) {
return NO_ERROR;
}
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index eaa5455..ed756c4 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -46,9 +46,7 @@
virtual void onFrameCommitted();
virtual void dumpAsString(String8& result) const;
- // Cannot resize a buffers in a FramebufferSurface. Only works with virtual
- // displays.
- virtual void resizeBuffers(const uint32_t /*w*/, const uint32_t /*h*/) { };
+ virtual void resizeBuffers(const uint32_t width, const uint32_t height);
virtual const sp<Fence>& getClientTargetAcquireFence() const override;
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index affe505..070b691 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -52,24 +52,13 @@
class ComposerCallbackBridge : public Hwc2::IComposerCallback {
public:
ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId)
- : mCallback(callback), mSequenceId(sequenceId),
- mHasPrimaryDisplay(false) {}
+ : mCallback(callback), mSequenceId(sequenceId) {}
Return<void> onHotplug(Hwc2::Display display,
IComposerCallback::Connection conn) override
{
HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
- if (!mHasPrimaryDisplay) {
- LOG_ALWAYS_FATAL_IF(connection != HWC2::Connection::Connected,
- "Initial onHotplug callback should be "
- "primary display connected");
- mHasPrimaryDisplay = true;
- mCallback->onHotplugReceived(mSequenceId, display,
- connection, true);
- } else {
- mCallback->onHotplugReceived(mSequenceId, display,
- connection, false);
- }
+ mCallback->onHotplugReceived(mSequenceId, display, connection);
return Void();
}
@@ -85,12 +74,9 @@
return Void();
}
- bool HasPrimaryDisplay() { return mHasPrimaryDisplay; }
-
private:
ComposerCallback* mCallback;
int32_t mSequenceId;
- bool mHasPrimaryDisplay;
};
} // namespace anonymous
@@ -117,8 +103,6 @@
sp<ComposerCallbackBridge> callbackBridge(
new ComposerCallbackBridge(callback, sequenceId));
mComposer->registerCallback(callbackBridge);
- LOG_ALWAYS_FATAL_IF(!callbackBridge->HasPrimaryDisplay(),
- "Registered composer callback but didn't get primary display");
}
// Required by HWC2 device
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index a15c6d9..7b98b3e 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -65,8 +65,7 @@
class ComposerCallback {
public:
virtual void onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
- Connection connection,
- bool primaryDisplay) = 0;
+ Connection connection) = 0;
virtual void onRefreshReceived(int32_t sequenceId,
hwc2_display_t display) = 0;
virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index c0f8f96..1677b07 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -119,23 +119,22 @@
}
}
-void HWComposer::onHotplug(hwc2_display_t displayId,
+void HWComposer::onHotplug(hwc2_display_t displayId, int32_t displayType,
HWC2::Connection connection) {
- ALOGV("hotplug: %" PRIu64 ", %s", displayId,
+ if (displayType >= HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
+ ALOGE("Invalid display type of %d", displayType);
+ return;
+ }
+
+ ALOGV("hotplug: %" PRIu64 ", %s %s", displayId,
+ displayType == DisplayDevice::DISPLAY_PRIMARY ? "primary" : "external",
to_string(connection).c_str());
mHwcDevice->onHotplug(displayId, connection);
- if (!mDisplayData[0].hwcDisplay) {
- ALOGE_IF(connection != HWC2::Connection::Connected, "Assumed primary"
- " display would be connected");
- mDisplayData[0].hwcDisplay = mHwcDevice->getDisplayById(displayId);
- mHwcDisplaySlots[displayId] = 0;
- } else {
- // Disconnect is handled through HWComposer::disconnectDisplay via
- // SurfaceFlinger's onHotplugReceived callback handling
- if (connection == HWC2::Connection::Connected) {
- mDisplayData[1].hwcDisplay = mHwcDevice->getDisplayById(displayId);
- mHwcDisplaySlots[displayId] = 1;
- }
+ // Disconnect is handled through HWComposer::disconnectDisplay via
+ // SurfaceFlinger's onHotplugReceived callback handling
+ if (connection == HWC2::Connection::Connected) {
+ mDisplayData[displayType].hwcDisplay = mHwcDevice->getDisplayById(displayId);
+ mHwcDisplaySlots[displayId] = displayType;
}
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 74b3a38..ee0a725 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -136,7 +136,7 @@
// DisplayDevice::DisplayType of the display is returned as an output param.
bool onVsync(hwc2_display_t displayId, int64_t timestamp,
int32_t* outDisplay);
- void onHotplug(hwc2_display_t displayId, HWC2::Connection connection);
+ void onHotplug(hwc2_display_t displayId, int32_t displayType, HWC2::Connection connection);
void setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e967265..f21a691 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -136,6 +136,7 @@
bool SurfaceFlinger::hasSyncFramework;
bool SurfaceFlinger::useVrFlinger;
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
+// TODO(courtneygo): Rename hasWideColorDisplay to clarify its actual meaning.
bool SurfaceFlinger::hasWideColorDisplay;
@@ -368,17 +369,6 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
-void SurfaceFlinger::createBuiltinDisplayLocked(DisplayDevice::DisplayType type) {
- ALOGV("createBuiltinDisplayLocked(%d)", type);
- ALOGW_IF(mBuiltinDisplays[type],
- "Overwriting display token for display type %d", type);
- mBuiltinDisplays[type] = new BBinder();
- // All non-virtual displays are currently considered secure.
- DisplayDeviceState info(type, true);
- mCurrentState.displays.add(mBuiltinDisplays[type], info);
- mInterceptor.saveDisplayCreation(info);
-}
-
sp<IBinder> SurfaceFlinger::getBuiltInDisplay(int32_t id) {
if (uint32_t(id) >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
ALOGE("getDefaultDisplay: id=%d is not a valid default display id", id);
@@ -618,6 +608,14 @@
"Starting with vr flinger active is not currently supported.");
getBE().mHwc.reset(new HWComposer(getBE().mHwcServiceName));
getBE().mHwc->registerCallback(this, getBE().mComposerSequenceId);
+ // Process any initial hotplug and resulting display changes.
+ processDisplayHotplugEventsLocked();
+ LOG_ALWAYS_FATAL_IF(!getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY),
+ "Registered composer callback but didn't create the default primary display");
+
+ // make the default display GLContext current so that we can create textures
+ // when creating Layers (which may happens before we render something)
+ getDefaultDisplayDeviceLocked()->makeCurrent();
if (useVrFlinger) {
auto vrFlingerRequestDisplayCallback = [this] (bool requestDisplay) {
@@ -801,7 +799,7 @@
// TODO: this needs to go away (currently needed only by webkit)
sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked());
- info.orientation = hw->getOrientation();
+ info.orientation = hw ? hw->getOrientation() : 0;
} else {
// TODO: where should this value come from?
static const int TV_DENSITY = 213;
@@ -1266,94 +1264,25 @@
*compositorTiming = getBE().mCompositorTiming;
}
-void SurfaceFlinger::createDefaultDisplayDevice() {
- const DisplayDevice::DisplayType type = DisplayDevice::DISPLAY_PRIMARY;
- wp<IBinder> token = mBuiltinDisplays[type];
+void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
+ HWC2::Connection connection) {
+ ALOGV("onHotplugReceived(%d, %" PRIu64 ", %s)", sequenceId, display,
+ connection == HWC2::Connection::Connected ? "connected" : "disconnected");
- // All non-virtual displays are currently considered secure.
- const bool isSecure = true;
-
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
-
- sp<FramebufferSurface> fbs = new FramebufferSurface(*getBE().mHwc, type, consumer);
-
- bool hasWideColorModes = false;
- std::vector<android_color_mode_t> modes = getHwComposer().getColorModes(type);
- for (android_color_mode_t colorMode : modes) {
- switch (colorMode) {
- case HAL_COLOR_MODE_DISPLAY_P3:
- case HAL_COLOR_MODE_ADOBE_RGB:
- case HAL_COLOR_MODE_DCI_P3:
- hasWideColorModes = true;
- break;
- default:
- break;
- }
+ // Ignore events that do not have the right sequenceId.
+ if (sequenceId != getBE().mComposerSequenceId) {
+ return;
}
- bool useWideColorMode = hasWideColorModes && hasWideColorDisplay && !mForceNativeColorMode;
- sp<DisplayDevice> hw = new DisplayDevice(this, DisplayDevice::DISPLAY_PRIMARY, type, isSecure,
- token, fbs, producer, useWideColorMode);
- mDisplays.add(token, hw);
- android_color_mode defaultColorMode = HAL_COLOR_MODE_NATIVE;
- if (useWideColorMode) {
- defaultColorMode = HAL_COLOR_MODE_SRGB;
- }
- setActiveColorModeInternal(hw, defaultColorMode);
- hw->setCompositionDataSpace(HAL_DATASPACE_UNKNOWN);
-
- // Add the primary display token to mDrawingState so we don't try to
- // recreate the DisplayDevice for the primary display.
- mDrawingState.displays.add(token, DisplayDeviceState(type, true));
-
- // make the GLContext current so that we can create textures when creating
- // Layers (which may happens before we render something)
- hw->makeCurrent();
-}
-
-void SurfaceFlinger::onHotplugReceived(int32_t sequenceId,
- hwc2_display_t display, HWC2::Connection connection,
- bool primaryDisplay) {
- ALOGV("onHotplugReceived(%d, %" PRIu64 ", %s, %s)",
- sequenceId, display,
- connection == HWC2::Connection::Connected ?
- "connected" : "disconnected",
- primaryDisplay ? "primary" : "external");
// Only lock if we're not on the main thread. This function is normally
// called on a hwbinder thread, but for the primary display it's called on
// the main thread with the state lock already held, so don't attempt to
// acquire it here.
- ConditionalLock lock(mStateLock,
- std::this_thread::get_id() != mMainThreadId);
+ ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
- if (primaryDisplay) {
- getBE().mHwc->onHotplug(display, connection);
- if (!mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY].get()) {
- createBuiltinDisplayLocked(DisplayDevice::DISPLAY_PRIMARY);
- }
- createDefaultDisplayDevice();
- } else {
- if (sequenceId != getBE().mComposerSequenceId) {
- return;
- }
- if (getBE().mHwc->isUsingVrComposer()) {
- ALOGE("External displays are not supported by the vr hardware composer.");
- return;
- }
- getBE().mHwc->onHotplug(display, connection);
- auto type = DisplayDevice::DISPLAY_EXTERNAL;
- if (connection == HWC2::Connection::Connected) {
- createBuiltinDisplayLocked(type);
- } else {
- mCurrentState.displays.removeItem(mBuiltinDisplays[type]);
- mBuiltinDisplays[type].clear();
- }
- setTransactionFlags(eDisplayTransactionNeeded);
+ mPendingHotplugEvents.emplace_back(HotplugEvent{display, connection});
- // Defer EventThread notification until SF has updated mDisplays.
- }
+ setTransactionFlags(eDisplayTransactionNeeded);
}
void SurfaceFlinger::onRefreshReceived(int sequenceId,
@@ -1668,7 +1597,7 @@
getBE().mGlCompositionDoneTimeline.updateSignalTimes();
std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
- if (getBE().mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) {
+ if (hw && getBE().mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) {
glCompositionDoneFenceTime =
std::make_shared<FenceTime>(hw->getClientTargetAcquireFence());
getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
@@ -1713,7 +1642,7 @@
}
if (!hasSyncFramework) {
- if (hw->isDisplayOn()) {
+ if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) && hw->isDisplayOn()) {
enableHardwareVsync();
}
}
@@ -1724,7 +1653,7 @@
if (presentFenceTime->isValid()) {
mAnimFrameTracker.setActualPresentFence(
std::move(presentFenceTime));
- } else {
+ } else if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) {
// The HWC doesn't support present fences, so use the refresh
// timestamp instead.
nsecs_t presentTime =
@@ -1734,7 +1663,8 @@
mAnimFrameTracker.advanceFrame();
}
- if (hw->getPowerMode() == HWC_POWER_MODE_OFF) {
+ if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) &&
+ hw->getPowerMode() == HWC_POWER_MODE_OFF) {
return;
}
@@ -2077,9 +2007,11 @@
mDebugInSwapBuffers = 0;
// |mStateLock| not needed as we are on the main thread
- uint32_t flipCount = getDefaultDisplayDeviceLocked()->getPageFlipCount();
- if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
- logFrameStats();
+ if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) {
+ uint32_t flipCount = getDefaultDisplayDeviceLocked()->getPageFlipCount();
+ if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
+ logFrameStats();
+ }
}
}
@@ -2112,6 +2044,245 @@
// here the transaction has been committed
}
+DisplayDevice::DisplayType SurfaceFlinger::determineDisplayType(hwc2_display_t display,
+ HWC2::Connection connection) const {
+ // Figure out whether the event is for the primary display or an
+ // external display by matching the Hwc display id against one for a
+ // connected display. If we did not find a match, we then check what
+ // displays are not already connected to determine the type. If we don't
+ // have a connected primary display, we assume the new display is meant to
+ // be the primary display, and then if we don't have an external display,
+ // we assume it is that.
+ const auto primaryDisplayId =
+ getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_PRIMARY);
+ const auto externalDisplayId =
+ getBE().mHwc->getHwcDisplayId(DisplayDevice::DISPLAY_EXTERNAL);
+ if (primaryDisplayId && primaryDisplayId == display) {
+ return DisplayDevice::DISPLAY_PRIMARY;
+ } else if (externalDisplayId && externalDisplayId == display) {
+ return DisplayDevice::DISPLAY_EXTERNAL;
+ } else if (connection == HWC2::Connection::Connected && !primaryDisplayId) {
+ return DisplayDevice::DISPLAY_PRIMARY;
+ } else if (connection == HWC2::Connection::Connected && !externalDisplayId) {
+ return DisplayDevice::DISPLAY_EXTERNAL;
+ }
+
+ return DisplayDevice::DISPLAY_ID_INVALID;
+}
+
+void SurfaceFlinger::processDisplayHotplugEventsLocked() {
+ for (const auto& event : mPendingHotplugEvents) {
+ auto displayType = determineDisplayType(event.display, event.connection);
+ if (displayType == DisplayDevice::DISPLAY_ID_INVALID) {
+ ALOGW("Unable to determine the display type for display %" PRIu64, event.display);
+ continue;
+ }
+
+ if (getBE().mHwc->isUsingVrComposer() && displayType == DisplayDevice::DISPLAY_EXTERNAL) {
+ ALOGE("External displays are not supported by the vr hardware composer.");
+ continue;
+ }
+
+ getBE().mHwc->onHotplug(event.display, displayType, event.connection);
+
+ if (event.connection == HWC2::Connection::Connected) {
+ ALOGV("Creating built in display %d", displayType);
+ ALOGW_IF(mBuiltinDisplays[displayType],
+ "Overwriting display token for display type %d", displayType);
+ mBuiltinDisplays[displayType] = new BBinder();
+ // All non-virtual displays are currently considered secure.
+ DisplayDeviceState info(displayType, true);
+ info.displayName = displayType == DisplayDevice::DISPLAY_PRIMARY ?
+ "Built-in Screen" : "External Screen";
+ mCurrentState.displays.add(mBuiltinDisplays[displayType], info);
+ mInterceptor.saveDisplayCreation(info);
+ } else {
+ ALOGV("Removing built in display %d", displayType);
+
+ ssize_t idx = mCurrentState.displays.indexOfKey(mBuiltinDisplays[displayType]);
+ if (idx >= 0) {
+ const DisplayDeviceState& info(mCurrentState.displays.valueAt(idx));
+ mInterceptor.saveDisplayDeletion(info.displayId);
+ mCurrentState.displays.removeItemsAt(idx);
+ }
+ mBuiltinDisplays[displayType].clear();
+ }
+
+ processDisplayChangesLocked();
+ }
+
+ mPendingHotplugEvents.clear();
+}
+
+void SurfaceFlinger::processDisplayChangesLocked() {
+ // here we take advantage of Vector's copy-on-write semantics to
+ // improve performance by skipping the transaction entirely when
+ // know that the lists are identical
+ const KeyedVector<wp<IBinder>, DisplayDeviceState>& curr(mCurrentState.displays);
+ const KeyedVector<wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
+ if (!curr.isIdenticalTo(draw)) {
+ mVisibleRegionsDirty = true;
+ const size_t cc = curr.size();
+ size_t dc = draw.size();
+
+ // find the displays that were removed
+ // (ie: in drawing state but not in current state)
+ // also handle displays that changed
+ // (ie: displays that are in both lists)
+ for (size_t i = 0; i < dc;) {
+ const ssize_t j = curr.indexOfKey(draw.keyAt(i));
+ if (j < 0) {
+ // in drawing state but not in current state
+ // Call makeCurrent() on the primary display so we can
+ // be sure that nothing associated with this display
+ // is current.
+ const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDeviceLocked());
+ if (defaultDisplay != nullptr) defaultDisplay->makeCurrent();
+ sp<DisplayDevice> hw(getDisplayDeviceLocked(draw.keyAt(i)));
+ if (hw != nullptr) hw->disconnect(getHwComposer());
+ if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES)
+ mEventThread->onHotplugReceived(draw[i].type, false);
+ mDisplays.removeItem(draw.keyAt(i));
+ } else {
+ // this display is in both lists. see if something changed.
+ const DisplayDeviceState& state(curr[j]);
+ const wp<IBinder>& display(curr.keyAt(j));
+ const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
+ const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
+ if (state_binder != draw_binder) {
+ // changing the surface is like destroying and
+ // recreating the DisplayDevice, so we just remove it
+ // from the drawing state, so that it get re-added
+ // below.
+ sp<DisplayDevice> hw(getDisplayDeviceLocked(display));
+ if (hw != nullptr) hw->disconnect(getHwComposer());
+ mDisplays.removeItem(display);
+ mDrawingState.displays.removeItemsAt(i);
+ dc--;
+ // at this point we must loop to the next item
+ continue;
+ }
+
+ const sp<DisplayDevice> disp(getDisplayDeviceLocked(display));
+ if (disp != nullptr) {
+ if (state.layerStack != draw[i].layerStack) {
+ disp->setLayerStack(state.layerStack);
+ }
+ if ((state.orientation != draw[i].orientation) ||
+ (state.viewport != draw[i].viewport) || (state.frame != draw[i].frame)) {
+ disp->setProjection(state.orientation, state.viewport, state.frame);
+ }
+ if (state.width != draw[i].width || state.height != draw[i].height) {
+ disp->setDisplaySize(state.width, state.height);
+ }
+ }
+ }
+ ++i;
+ }
+
+ // find displays that were added
+ // (ie: in current state but not in drawing state)
+ for (size_t i = 0; i < cc; i++) {
+ if (draw.indexOfKey(curr.keyAt(i)) < 0) {
+ const DisplayDeviceState& state(curr[i]);
+
+ sp<DisplaySurface> dispSurface;
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferProducer> bqProducer;
+ sp<IGraphicBufferConsumer> bqConsumer;
+ BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
+
+ int32_t hwcId = -1;
+ if (state.isVirtualDisplay()) {
+ // Virtual displays without a surface are dormant:
+ // they have external state (layer stack, projection,
+ // etc.) but no internal state (i.e. a DisplayDevice).
+ if (state.surface != nullptr) {
+ // Allow VR composer to use virtual displays.
+ if (mUseHwcVirtualDisplays || getBE().mHwc->isUsingVrComposer()) {
+ int width = 0;
+ int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
+ ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
+ int height = 0;
+ status = state.surface->query(NATIVE_WINDOW_HEIGHT, &height);
+ ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
+ int intFormat = 0;
+ status = state.surface->query(NATIVE_WINDOW_FORMAT, &intFormat);
+ ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
+ auto format = static_cast<android_pixel_format_t>(intFormat);
+
+ getBE().mHwc->allocateVirtualDisplay(width, height, &format, &hwcId);
+ }
+
+ // TODO: Plumb requested format back up to consumer
+
+ sp<VirtualDisplaySurface> vds =
+ new VirtualDisplaySurface(*getBE().mHwc, hwcId, state.surface,
+ bqProducer, bqConsumer,
+ state.displayName);
+
+ dispSurface = vds;
+ producer = vds;
+ }
+ } else {
+ ALOGE_IF(state.surface != nullptr,
+ "adding a supported display, but rendering "
+ "surface is provided (%p), ignoring it",
+ state.surface.get());
+
+ hwcId = state.type;
+ dispSurface = new FramebufferSurface(*getBE().mHwc, hwcId, bqConsumer);
+ producer = bqProducer;
+ }
+
+ const wp<IBinder>& display(curr.keyAt(i));
+
+ if (dispSurface != nullptr) {
+ bool useWideColorMode = hasWideColorDisplay;
+ if (!mForceNativeColorMode) {
+ bool hasWideColorModes = false;
+ std::vector<android_color_mode_t> modes =
+ getHwComposer().getColorModes(state.type);
+ for (android_color_mode_t colorMode : modes) {
+ switch (colorMode) {
+ case HAL_COLOR_MODE_DISPLAY_P3:
+ case HAL_COLOR_MODE_ADOBE_RGB:
+ case HAL_COLOR_MODE_DCI_P3:
+ hasWideColorModes = true;
+ break;
+ default:
+ break;
+ }
+ }
+ useWideColorMode = hasWideColorModes && hasWideColorDisplay;
+ }
+
+ sp<DisplayDevice> hw =
+ new DisplayDevice(this, state.type, hwcId, state.isSecure, display,
+ dispSurface, producer, useWideColorMode);
+
+ android_color_mode defaultColorMode = HAL_COLOR_MODE_NATIVE;
+ if (useWideColorMode) {
+ defaultColorMode = HAL_COLOR_MODE_SRGB;
+ }
+ setActiveColorModeInternal(hw, defaultColorMode);
+ hw->setCompositionDataSpace(HAL_DATASPACE_UNKNOWN);
+ hw->setLayerStack(state.layerStack);
+ hw->setProjection(state.orientation, state.viewport, state.frame);
+ hw->setDisplayName(state.displayName);
+
+ mDisplays.add(display, hw);
+ if (!state.isVirtualDisplay()) {
+ mEventThread->onHotplugReceived(state.type, true);
+ }
+ }
+ }
+ }
+ }
+
+ mDrawingState.displays = mCurrentState.displays;
+}
+
void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
{
// Notify all layers of available frames
@@ -2140,161 +2311,8 @@
*/
if (transactionFlags & eDisplayTransactionNeeded) {
- // here we take advantage of Vector's copy-on-write semantics to
- // improve performance by skipping the transaction entirely when
- // know that the lists are identical
- const KeyedVector< wp<IBinder>, DisplayDeviceState>& curr(mCurrentState.displays);
- const KeyedVector< wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
- if (!curr.isIdenticalTo(draw)) {
- mVisibleRegionsDirty = true;
- const size_t cc = curr.size();
- size_t dc = draw.size();
-
- // find the displays that were removed
- // (ie: in drawing state but not in current state)
- // also handle displays that changed
- // (ie: displays that are in both lists)
- for (size_t i=0 ; i<dc ;) {
- const ssize_t j = curr.indexOfKey(draw.keyAt(i));
- if (j < 0) {
- // in drawing state but not in current state
- if (!draw[i].isMainDisplay()) {
- // Call makeCurrent() on the primary display so we can
- // be sure that nothing associated with this display
- // is current.
- const sp<const DisplayDevice> defaultDisplay(getDefaultDisplayDeviceLocked());
- defaultDisplay->makeCurrent();
- sp<DisplayDevice> hw(getDisplayDeviceLocked(draw.keyAt(i)));
- if (hw != nullptr)
- hw->disconnect(getHwComposer());
- if (draw[i].type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES)
- mEventThread->onHotplugReceived(draw[i].type, false);
- mDisplays.removeItem(draw.keyAt(i));
- } else {
- ALOGW("trying to remove the main display");
- }
- } else {
- // this display is in both lists. see if something changed.
- const DisplayDeviceState& state(curr[j]);
- const wp<IBinder>& display(curr.keyAt(j));
- const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
- const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
- if (state_binder != draw_binder) {
- // changing the surface is like destroying and
- // recreating the DisplayDevice, so we just remove it
- // from the drawing state, so that it get re-added
- // below.
- sp<DisplayDevice> hw(getDisplayDeviceLocked(display));
- if (hw != nullptr)
- hw->disconnect(getHwComposer());
- mDisplays.removeItem(display);
- mDrawingState.displays.removeItemsAt(i);
- dc--;
- // at this point we must loop to the next item
- continue;
- }
-
- const sp<DisplayDevice> disp(getDisplayDeviceLocked(display));
- if (disp != nullptr) {
- if (state.layerStack != draw[i].layerStack) {
- disp->setLayerStack(state.layerStack);
- }
- if ((state.orientation != draw[i].orientation)
- || (state.viewport != draw[i].viewport)
- || (state.frame != draw[i].frame))
- {
- disp->setProjection(state.orientation,
- state.viewport, state.frame);
- }
- if (state.width != draw[i].width || state.height != draw[i].height) {
- disp->setDisplaySize(state.width, state.height);
- }
- }
- }
- ++i;
- }
-
- // find displays that were added
- // (ie: in current state but not in drawing state)
- for (size_t i=0 ; i<cc ; i++) {
- if (draw.indexOfKey(curr.keyAt(i)) < 0) {
- const DisplayDeviceState& state(curr[i]);
-
- sp<DisplaySurface> dispSurface;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferProducer> bqProducer;
- sp<IGraphicBufferConsumer> bqConsumer;
- BufferQueue::createBufferQueue(&bqProducer, &bqConsumer);
-
- int32_t hwcId = -1;
- if (state.isVirtualDisplay()) {
- // Virtual displays without a surface are dormant:
- // they have external state (layer stack, projection,
- // etc.) but no internal state (i.e. a DisplayDevice).
- if (state.surface != nullptr) {
-
- // Allow VR composer to use virtual displays.
- if (mUseHwcVirtualDisplays || getBE().mHwc->isUsingVrComposer()) {
- int width = 0;
- int status = state.surface->query(
- NATIVE_WINDOW_WIDTH, &width);
- ALOGE_IF(status != NO_ERROR,
- "Unable to query width (%d)", status);
- int height = 0;
- status = state.surface->query(
- NATIVE_WINDOW_HEIGHT, &height);
- ALOGE_IF(status != NO_ERROR,
- "Unable to query height (%d)", status);
- int intFormat = 0;
- status = state.surface->query(
- NATIVE_WINDOW_FORMAT, &intFormat);
- ALOGE_IF(status != NO_ERROR,
- "Unable to query format (%d)", status);
- auto format = static_cast<android_pixel_format_t>(
- intFormat);
-
- getBE().mHwc->allocateVirtualDisplay(width, height, &format,
- &hwcId);
- }
-
- // TODO: Plumb requested format back up to consumer
-
- sp<VirtualDisplaySurface> vds =
- new VirtualDisplaySurface(*getBE().mHwc,
- hwcId, state.surface, bqProducer,
- bqConsumer, state.displayName);
-
- dispSurface = vds;
- producer = vds;
- }
- } else {
- ALOGE_IF(state.surface != nullptr,
- "adding a supported display, but rendering "
- "surface is provided (%p), ignoring it",
- state.surface.get());
-
- hwcId = state.type;
- dispSurface = new FramebufferSurface(*getBE().mHwc, hwcId, bqConsumer);
- producer = bqProducer;
- }
-
- const wp<IBinder>& display(curr.keyAt(i));
- if (dispSurface != nullptr) {
- sp<DisplayDevice> hw =
- new DisplayDevice(this, state.type, hwcId, state.isSecure, display,
- dispSurface, producer, hasWideColorDisplay);
- hw->setLayerStack(state.layerStack);
- hw->setProjection(state.orientation,
- state.viewport, state.frame);
- hw->setDisplayName(state.displayName);
- mDisplays.add(display, hw);
- if (!state.isVirtualDisplay()) {
- mEventThread->onHotplugReceived(state.type, true);
- }
- }
- }
- }
- }
+ processDisplayChangesLocked();
+ processDisplayHotplugEventsLocked();
}
if (transactionFlags & (eTraversalNeeded|eDisplayTransactionNeeded)) {
@@ -2356,6 +2374,9 @@
}
layer->updateTransformHint(disp);
}
+ if (disp != nullptr) {
+ layer->updateTransformHint(disp);
+ }
first = false;
});
@@ -3891,9 +3912,11 @@
getBE().mRenderEngine->dump(result);
- hw->undefinedRegion.dump(result, "undefinedRegion");
- result.appendFormat(" orientation=%d, isDisplayOn=%d\n",
- hw->getOrientation(), hw->isDisplayOn());
+ if (hw) {
+ hw->undefinedRegion.dump(result, "undefinedRegion");
+ result.appendFormat(" orientation=%d, isDisplayOn=%d\n",
+ hw->getOrientation(), hw->isDisplayOn());
+ }
result.appendFormat(
" last eglSwapBuffers() time: %f us\n"
" last transaction time : %f us\n"
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8030fbd..a18be9b 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -142,14 +142,14 @@
std::unique_ptr<RenderEngine> mRenderEngine;
EGLContext mEGLContext;
EGLDisplay mEGLDisplay;
-
+
FenceTimeline mGlCompositionDoneTimeline;
FenceTimeline mDisplayTimeline;
// protected by mCompositorTimingLock;
mutable std::mutex mCompositorTimingLock;
CompositorTiming mCompositorTiming;
-
+
// Only accessed from the main thread.
struct CompositePresentTime {
nsecs_t composite { -1 };
@@ -417,8 +417,7 @@
void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
int64_t timestamp) override;
void onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
- HWC2::Connection connection,
- bool primaryDisplay) override;
+ HWC2::Connection connection) override;
void onRefreshReceived(int32_t sequenceId, hwc2_display_t display) override;
/* ------------------------------------------------------------------------
@@ -547,10 +546,6 @@
// called when starting, or restarting after system_server death
void initializeDisplays();
- // Create an IBinder for a builtin display and add it to current state
- void createBuiltinDisplayLocked(DisplayDevice::DisplayType type);
-
-
sp<const DisplayDevice> getDisplayDevice(const wp<IBinder>& dpy) const {
Mutex::Autolock _l(mStateLock);
return getDisplayDeviceLocked(dpy);
@@ -575,8 +570,6 @@
return getDisplayDeviceLocked(mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]);
}
- void createDefaultDisplayDevice();
-
int32_t getDisplayType(const sp<IBinder>& display) {
if (!display.get()) return NAME_NOT_FOUND;
for (int i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; ++i) {
@@ -637,6 +630,10 @@
/* ------------------------------------------------------------------------
* Display management
*/
+ DisplayDevice::DisplayType determineDisplayType(hwc2_display_t display,
+ HWC2::Connection connection) const;
+ void processDisplayChangesLocked();
+ void processDisplayHotplugEventsLocked();
/* ------------------------------------------------------------------------
* VSync
@@ -738,6 +735,13 @@
sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
bool mHadClientComposition = false;
+ struct HotplugEvent {
+ hwc2_display_t display;
+ HWC2::Connection connection = HWC2::Connection::Invalid;
+ };
+ // protected by mStateLock
+ std::vector<HotplugEvent> mPendingHotplugEvents;
+
// this may only be written from the main thread with mStateLock held
// it may be read from other threads with mStateLock held
DefaultKeyedVector< wp<IBinder>, sp<DisplayDevice> > mDisplays;
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index cb22932..e16e7ec 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -181,6 +181,12 @@
}
}
+void FakeComposerClient::refreshDisplay(Display display) {
+ if (mCallbacksOn) {
+ mClient->onRefresh(display);
+ }
+}
+
uint32_t FakeComposerClient::getMaxVirtualDisplayCount() {
ALOGV("getMaxVirtualDisplayCount");
return 1;
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
index de8cffd..cef7f5b 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -142,6 +142,7 @@
Layer getLayer(size_t index) const;
void hotplugDisplay(Display display, IComposerCallback::Connection state);
+ void refreshDisplay(Display display);
private:
LayerImpl& getLayerImpl(Layer handle);
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 8a97ea4..1873832 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -22,25 +22,22 @@
#include "FakeComposerService.h"
#include "FakeComposerUtils.h"
+#include <gui/DisplayEventReceiver.h>
#include <gui/ISurfaceComposer.h>
#include <gui/LayerDebugInfo.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
-#include <private/gui/ComposerService.h>
-
-#include <ui/DisplayInfo.h>
-
-#include <android/native_window.h>
-
#include <android/hidl/manager/1.0/IServiceManager.h>
-
-#include <hwbinder/ProcessState.h>
-
+#include <android/looper.h>
+#include <android/native_window.h>
#include <binder/ProcessState.h>
-
+#include <hwbinder/ProcessState.h>
#include <log/log.h>
+#include <private/gui/ComposerService.h>
+#include <ui/DisplayInfo.h>
+#include <utils/Looper.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -142,13 +139,22 @@
};
protected:
+ static int processDisplayEvents(int fd, int events, void* data);
+
void SetUp() override;
void TearDown() override;
+ void waitForDisplayTransaction();
+ bool waitForHotplugEvent(uint32_t id, bool connected);
+
sp<IComposer> mFakeService;
sp<SurfaceComposerClient> mComposerClient;
MockComposerClient* mMockComposer;
+
+ std::unique_ptr<DisplayEventReceiver> mReceiver;
+ sp<Looper> mLooper;;
+ std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
};
void DisplayTest::SetUp() {
@@ -188,9 +194,16 @@
mComposerClient = new SurfaceComposerClient;
ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ mReceiver.reset(new DisplayEventReceiver());
+ mLooper = new Looper(false);
+ mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
}
void DisplayTest::TearDown() {
+ mLooper = nullptr;
+ mReceiver = nullptr;
+
mComposerClient->dispose();
mComposerClient = nullptr;
@@ -204,6 +217,55 @@
mMockComposer = nullptr;
}
+
+int DisplayTest::processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
+ auto self = static_cast<DisplayTest*>(data);
+
+ ssize_t n;
+ DisplayEventReceiver::Event buffer[1];
+
+ while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
+ for (int i=0 ; i<n ; i++) {
+ self->mReceivedDisplayEvents.push_back(buffer[i]);
+ }
+ }
+ ALOGD_IF(n < 0, "Error reading events (%s)\n", strerror(-n));
+ return 1;
+}
+
+void DisplayTest::waitForDisplayTransaction() {
+ // Both a refresh and a vsync event are needed to apply pending display
+ // transactions.
+ mMockComposer->refreshDisplay(EXTERNAL_DISPLAY);
+ mMockComposer->runVSyncAndWait();
+
+ // Extra vsync and wait to avoid a 10% flake due to a race.
+ mMockComposer->runVSyncAndWait();
+}
+
+bool DisplayTest::waitForHotplugEvent(uint32_t id, bool connected) {
+ int waitCount = 20;
+ while (waitCount--) {
+ while (!mReceivedDisplayEvents.empty()) {
+ auto event = mReceivedDisplayEvents.front();
+ mReceivedDisplayEvents.pop_front();
+
+ ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
+ "event hotplug: id %d, connected %d\t", event.header.id,
+ event.hotplug.connected);
+
+ if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
+ event.header.id == id && event.hotplug.connected == connected) {
+ return true;
+ }
+ }
+
+ mLooper->pollOnce(1);
+ }
+
+ return false;
+}
+
TEST_F(DisplayTest, Hotplug) {
ALOGD("DisplayTest::Hotplug");
@@ -215,7 +277,7 @@
EXPECT_CALL(*mMockComposer, getDisplayAttribute(EXTERNAL_DISPLAY, 1, _, _))
.Times(2 * 3)
.WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
- // ... and then special handling for dimensions. Specifying this
+ // ... and then special handling for dimensions. Specifying these
// rules later means that gmock will try them first, i.e.,
// ordering of width/height vs. the default implementation for
// other queries is significant.
@@ -229,11 +291,12 @@
.Times(2)
.WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
- // TODO: Width and height queries are not actually called. Display
- // info returns dimensions 0x0 in display info. Why?
-
mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
+ waitForDisplayTransaction();
+
+ EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, true));
+
{
sp<android::IBinder> display(
SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
@@ -264,6 +327,11 @@
mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
+ waitForDisplayTransaction();
+
+ EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, false));
+ EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdHdmi, true));
+
{
sp<android::IBinder> display(
SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi));
@@ -290,6 +358,64 @@
mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
}
+TEST_F(DisplayTest, HotplugPrimaryDisplay) {
+ ALOGD("DisplayTest::HotplugPrimaryDisplay");
+
+ mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
+
+ waitForDisplayTransaction();
+
+ EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdMain, false));
+
+ {
+ sp<android::IBinder> display(
+ SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ DisplayInfo info;
+ auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
+ EXPECT_NE(NO_ERROR, result);
+ }
+
+ mMockComposer->clearFrames();
+
+ EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _))
+ .Times(2)
+ .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
+ Return(Error::NONE)));
+ // The attribute queries will get done twice. This is for defaults
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
+ .Times(2 * 3)
+ .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
+ // ... and then special handling for dimensions. Specifying these
+ // rules later means that gmock will try them first, i.e.,
+ // ordering of width/height vs. the default implementation for
+ // other queries is significant.
+ EXPECT_CALL(*mMockComposer,
+ getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _))
+ .Times(2)
+ .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
+
+ EXPECT_CALL(*mMockComposer,
+ getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _))
+ .Times(2)
+ .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
+
+ mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
+
+ waitForDisplayTransaction();
+
+ EXPECT_TRUE(waitForHotplugEvent(ISurfaceComposer::eDisplayIdMain, true));
+
+ {
+ sp<android::IBinder> display(
+ SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
+ DisplayInfo info;
+ auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
+ EXPECT_EQ(NO_ERROR, result);
+ ASSERT_EQ(400u, info.w);
+ ASSERT_EQ(200u, info.h);
+ }
+}
+
////////////////////////////////////////////////
class TransactionTest : public ::testing::Test {
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index ade0bde..26f60fb 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -19,9 +19,9 @@
#include <string.h>
#include <sys/prctl.h>
+#include <dlfcn.h>
#include <algorithm>
#include <array>
-#include <dlfcn.h>
#include <new>
#include <log/log.h>
@@ -1047,17 +1047,54 @@
VkInstance instance,
uint32_t* pPhysicalDeviceGroupCount,
VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {
+ VkResult result = VK_SUCCESS;
const auto& data = GetData(instance);
- VkResult result = data.driver.EnumeratePhysicalDeviceGroups(
- instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties);
- if ((result == VK_SUCCESS || result == VK_INCOMPLETE) &&
- *pPhysicalDeviceGroupCount && pPhysicalDeviceGroupProperties) {
- for (uint32_t i = 0; i < *pPhysicalDeviceGroupCount; i++) {
- for (uint32_t j = 0;
- j < pPhysicalDeviceGroupProperties->physicalDeviceCount; j++) {
- SetData(pPhysicalDeviceGroupProperties->physicalDevices[j],
+ if (!data.driver.EnumeratePhysicalDeviceGroups) {
+ uint32_t device_count = 0;
+ result = EnumeratePhysicalDevices(instance, &device_count, nullptr);
+ if (result < 0)
+ return result;
+ if (!pPhysicalDeviceGroupProperties) {
+ *pPhysicalDeviceGroupCount = device_count;
+ return result;
+ }
+
+ device_count = std::min(device_count, *pPhysicalDeviceGroupCount);
+ if (!device_count) {
+ *pPhysicalDeviceGroupCount = 0;
+ return result;
+ }
+
+ android::Vector<VkPhysicalDevice> devices;
+ devices.resize(device_count);
+
+ result = EnumeratePhysicalDevices(instance, &device_count,
+ devices.editArray());
+ if (result < 0)
+ return result;
+
+ devices.resize(device_count);
+ *pPhysicalDeviceGroupCount = device_count;
+ for (uint32_t i = 0; i < device_count; ++i) {
+ pPhysicalDeviceGroupProperties[i].physicalDeviceCount = 1;
+ pPhysicalDeviceGroupProperties[i].physicalDevices[0] = devices[i];
+ pPhysicalDeviceGroupProperties[i].subsetAllocation = 0;
+ }
+ } else {
+ result = data.driver.EnumeratePhysicalDeviceGroups(
+ instance, pPhysicalDeviceGroupCount,
+ pPhysicalDeviceGroupProperties);
+ if ((result == VK_SUCCESS || result == VK_INCOMPLETE) &&
+ *pPhysicalDeviceGroupCount && pPhysicalDeviceGroupProperties) {
+ for (uint32_t i = 0; i < *pPhysicalDeviceGroupCount; i++) {
+ for (uint32_t j = 0;
+ j < pPhysicalDeviceGroupProperties[i].physicalDeviceCount;
+ j++) {
+ SetData(
+ pPhysicalDeviceGroupProperties[i].physicalDevices[j],
data);
+ }
}
}
}
diff --git a/vulkan/libvulkan/libvulkan.map.txt b/vulkan/libvulkan/libvulkan.map.txt
index 1745925..d1f6241 100644
--- a/vulkan/libvulkan/libvulkan.map.txt
+++ b/vulkan/libvulkan/libvulkan.map.txt
@@ -1,12 +1,15 @@
LIBVULKAN {
global:
+ vkAcquireNextImage2KHR; # introduced=28
vkAcquireNextImageKHR;
vkAllocateCommandBuffers;
vkAllocateDescriptorSets;
vkAllocateMemory;
vkBeginCommandBuffer;
vkBindBufferMemory;
+ vkBindBufferMemory2; # introduced=28
vkBindImageMemory;
+ vkBindImageMemory2; # introduced=28
vkCmdBeginQuery;
vkCmdBeginRenderPass;
vkCmdBindDescriptorSets;
@@ -23,6 +26,7 @@
vkCmdCopyImageToBuffer;
vkCmdCopyQueryPoolResults;
vkCmdDispatch;
+ vkCmdDispatchBase; # introduced=28
vkCmdDispatchIndirect;
vkCmdDraw;
vkCmdDrawIndexed;
@@ -41,6 +45,7 @@
vkCmdSetBlendConstants;
vkCmdSetDepthBias;
vkCmdSetDepthBounds;
+ vkCmdSetDeviceMask; # introduced=28
vkCmdSetEvent;
vkCmdSetLineWidth;
vkCmdSetScissor;
@@ -58,6 +63,7 @@
vkCreateComputePipelines;
vkCreateDescriptorPool;
vkCreateDescriptorSetLayout;
+ vkCreateDescriptorUpdateTemplate; # introduced=28
vkCreateDevice;
vkCreateEvent;
vkCreateFence;
@@ -71,6 +77,7 @@
vkCreateQueryPool;
vkCreateRenderPass;
vkCreateSampler;
+ vkCreateSamplerYcbcrConversion; # introduced=28
vkCreateSemaphore;
vkCreateShaderModule;
vkCreateSwapchainKHR;
@@ -79,6 +86,7 @@
vkDestroyCommandPool;
vkDestroyDescriptorPool;
vkDestroyDescriptorSetLayout;
+ vkDestroyDescriptorUpdateTemplate; # introduced=28
vkDestroyDevice;
vkDestroyEvent;
vkDestroyFence;
@@ -92,6 +100,7 @@
vkDestroyQueryPool;
vkDestroyRenderPass;
vkDestroySampler;
+ vkDestroySamplerYcbcrConversion; # introduced=28
vkDestroySemaphore;
vkDestroyShaderModule;
vkDestroySurfaceKHR;
@@ -102,28 +111,49 @@
vkEnumerateDeviceLayerProperties;
vkEnumerateInstanceExtensionProperties;
vkEnumerateInstanceLayerProperties;
+ vkEnumerateInstanceVersion; # introduced=28
+ vkEnumeratePhysicalDeviceGroups; # introduced=28
vkEnumeratePhysicalDevices;
vkFlushMappedMemoryRanges;
vkFreeCommandBuffers;
vkFreeDescriptorSets;
vkFreeMemory;
vkGetBufferMemoryRequirements;
+ vkGetBufferMemoryRequirements2; # introduced=28
+ vkGetDescriptorSetLayoutSupport; # introduced=28
+ vkGetDeviceGroupPeerMemoryFeatures; # introduced=28
+ vkGetDeviceGroupPresentCapabilitiesKHR; # introduced=28
+ vkGetDeviceGroupSurfacePresentModesKHR; # introduced=28
vkGetDeviceMemoryCommitment;
vkGetDeviceProcAddr;
vkGetDeviceQueue;
+ vkGetDeviceQueue2; # introduced=28
vkGetEventStatus;
vkGetFenceStatus;
vkGetImageMemoryRequirements;
+ vkGetImageMemoryRequirements2; # introduced=28
vkGetImageSparseMemoryRequirements;
+ vkGetImageSparseMemoryRequirements2; # introduced=28
vkGetImageSubresourceLayout;
vkGetInstanceProcAddr;
+ vkGetPhysicalDeviceExternalBufferProperties; # introduced=28
+ vkGetPhysicalDeviceExternalFenceProperties; # introduced=28
+ vkGetPhysicalDeviceExternalSemaphoreProperties; # introduced=28
vkGetPhysicalDeviceFeatures;
+ vkGetPhysicalDeviceFeatures2; # introduced=28
vkGetPhysicalDeviceFormatProperties;
+ vkGetPhysicalDeviceFormatProperties2; # introduced=28
vkGetPhysicalDeviceImageFormatProperties;
+ vkGetPhysicalDeviceImageFormatProperties2; # introduced=28
vkGetPhysicalDeviceMemoryProperties;
+ vkGetPhysicalDeviceMemoryProperties2; # introduced=28
+ vkGetPhysicalDevicePresentRectanglesKHR; # introduced=28
vkGetPhysicalDeviceProperties;
+ vkGetPhysicalDeviceProperties2; # introduced=28
vkGetPhysicalDeviceQueueFamilyProperties;
+ vkGetPhysicalDeviceQueueFamilyProperties2; # introduced=28
vkGetPhysicalDeviceSparseImageFormatProperties;
+ vkGetPhysicalDeviceSparseImageFormatProperties2; # introduced=28
vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
vkGetPhysicalDeviceSurfaceFormatsKHR;
vkGetPhysicalDeviceSurfacePresentModesKHR;
@@ -145,8 +175,10 @@
vkResetEvent;
vkResetFences;
vkSetEvent;
+ vkTrimCommandPool; # introduced=28
vkUnmapMemory;
vkUpdateDescriptorSets;
+ vkUpdateDescriptorSetWithTemplate; # introduced=28
vkWaitForFences;
local:
*;