diff options
-rw-r--r-- | cmds/dumpstate/dumpstate.cpp | 4 | ||||
-rw-r--r-- | cmds/dumpsys/dumpsys.cpp | 74 | ||||
-rw-r--r-- | cmds/dumpsys/dumpsys.h | 13 | ||||
-rw-r--r-- | cmds/dumpsys/tests/dumpsys_test.cpp | 24 | ||||
-rw-r--r-- | libs/binder/RpcServer.cpp | 2 | ||||
-rw-r--r-- | libs/binder/RpcSession.cpp | 42 | ||||
-rw-r--r-- | libs/binder/include/binder/RpcServer.h | 2 | ||||
-rw-r--r-- | libs/binder/include/binder/RpcSession.h | 7 | ||||
-rw-r--r-- | services/surfaceflinger/Android.bp | 1 | ||||
-rw-r--r-- | services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp | 6 | ||||
-rw-r--r-- | services/surfaceflinger/DisplayHardware/Hash.cpp | 93 | ||||
-rw-r--r-- | services/surfaceflinger/DisplayHardware/Hash.h | 27 | ||||
-rw-r--r-- | services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp | 9 |
13 files changed, 242 insertions, 62 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 8f466ca57e..1db2867bf2 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1236,7 +1236,7 @@ static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, i std::string path(title); path.append(" - ").append(String8(service).c_str()); size_t bytes_written = 0; - status_t status = dumpsys.startDumpThread(Dumpsys::Type::DUMP, service, args); + status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args); if (status == OK) { dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority); std::chrono::duration<double> elapsed_seconds; @@ -1315,7 +1315,7 @@ static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priori path.append("_HIGH"); } path.append(kProtoExt); - status_t status = dumpsys.startDumpThread(Dumpsys::Type::DUMP, service, args); + status_t status = dumpsys.startDumpThread(Dumpsys::TYPE_DUMP, service, args); if (status == OK) { status = ds.AddZipEntryFromFd(path, dumpsys.getDumpFd(), service_timeout); bool dumpTerminated = (status == OK); diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 83a52b85a3..f8fdaa094e 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -62,13 +62,14 @@ static void usage() { "usage: dumpsys\n" " To dump all services.\n" "or:\n" - " dumpsys [-t TIMEOUT] [--priority LEVEL] [--pid] [--thread] [--help | -l | " - "--skip SERVICES " + " dumpsys [-t TIMEOUT] [--priority LEVEL] [--dump] [--pid] [--thread] [--help | " + "-l | --skip SERVICES " "| SERVICE [ARGS]]\n" " --help: shows this help\n" " -l: only list services, do not dump them\n" " -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n" " -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n" + " --dump: ask the service to dump itself (this is the default)\n" " --pid: dump PID instead of usual dump\n" " --proto: filter services that support dumping data in proto format. Dumps\n" " will be in proto format.\n" @@ -127,10 +128,11 @@ int Dumpsys::main(int argc, char* const argv[]) { bool showListOnly = false; bool skipServices = false; bool asProto = false; - Type type = Type::DUMP; + int dumpTypeFlags = 0; int timeoutArgMs = 10000; int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL; static struct option longOptions[] = {{"help", no_argument, 0, 0}, + {"dump", no_argument, 0, 0}, {"pid", no_argument, 0, 0}, {"priority", required_argument, 0, 0}, {"proto", no_argument, 0, 0}, @@ -168,12 +170,14 @@ int Dumpsys::main(int argc, char* const argv[]) { usage(); return -1; } + } else if (!strcmp(longOptions[optionIndex].name, "dump")) { + dumpTypeFlags |= TYPE_DUMP; } else if (!strcmp(longOptions[optionIndex].name, "pid")) { - type = Type::PID; + dumpTypeFlags |= TYPE_PID; } else if (!strcmp(longOptions[optionIndex].name, "stability")) { - type = Type::STABILITY; + dumpTypeFlags |= TYPE_STABILITY; } else if (!strcmp(longOptions[optionIndex].name, "thread")) { - type = Type::THREAD; + dumpTypeFlags |= TYPE_THREAD; } break; @@ -211,6 +215,10 @@ int Dumpsys::main(int argc, char* const argv[]) { } } + if (dumpTypeFlags == 0) { + dumpTypeFlags = TYPE_DUMP; + } + for (int i = optind; i < argc; i++) { if (skipServices) { skippedServices.add(String16(argv[i])); @@ -263,7 +271,7 @@ int Dumpsys::main(int argc, char* const argv[]) { const String16& serviceName = services[i]; if (IsSkipped(skippedServices, serviceName)) continue; - if (startDumpThread(type, serviceName, args) == OK) { + if (startDumpThread(dumpTypeFlags, serviceName, args) == OK) { bool addSeparator = (N > 1); if (addSeparator) { writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags); @@ -330,18 +338,21 @@ void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityF } } -static status_t dumpPidToFd(const sp<IBinder>& service, const unique_fd& fd) { +static status_t dumpPidToFd(const sp<IBinder>& service, const unique_fd& fd, bool exclusive) { pid_t pid; status_t status = service->getDebugPid(&pid); if (status != OK) { return status; } + if (!exclusive) { + WriteStringToFd("Service host process PID: ", fd.get()); + } WriteStringToFd(std::to_string(pid) + "\n", fd.get()); return OK; } static status_t dumpStabilityToFd(const sp<IBinder>& service, const unique_fd& fd) { - WriteStringToFd(internal::Stability::debugToString(service) + "\n", fd); + WriteStringToFd("Stability: " + internal::Stability::debugToString(service) + "\n", fd); return OK; } @@ -362,7 +373,14 @@ static status_t dumpThreadsToFd(const sp<IBinder>& service, const unique_fd& fd) return OK; } -status_t Dumpsys::startDumpThread(Type type, const String16& serviceName, +static void reportDumpError(const String16& serviceName, status_t error, const char* context) { + if (error == OK) return; + + std::cerr << "Error with service '" << serviceName << "' while " << context << ": " + << statusToString(error) << std::endl; +} + +status_t Dumpsys::startDumpThread(int dumpTypeFlags, const String16& serviceName, const Vector<String16>& args) { sp<IBinder> service = sm_->checkService(serviceName); if (service == nullptr) { @@ -383,29 +401,23 @@ status_t Dumpsys::startDumpThread(Type type, const String16& serviceName, // dump blocks until completion, so spawn a thread.. activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable { - status_t err = 0; - - switch (type) { - case Type::DUMP: - err = service->dump(remote_end.get(), args); - break; - case Type::PID: - err = dumpPidToFd(service, remote_end); - break; - case Type::STABILITY: - err = dumpStabilityToFd(service, remote_end); - break; - case Type::THREAD: - err = dumpThreadsToFd(service, remote_end); - break; - default: - std::cerr << "Unknown dump type" << static_cast<int>(type) << std::endl; - return; + if (dumpTypeFlags & TYPE_PID) { + status_t err = dumpPidToFd(service, remote_end, dumpTypeFlags == TYPE_PID); + reportDumpError(serviceName, err, "dumping PID"); + } + if (dumpTypeFlags & TYPE_STABILITY) { + status_t err = dumpStabilityToFd(service, remote_end); + reportDumpError(serviceName, err, "dumping stability"); + } + if (dumpTypeFlags & TYPE_THREAD) { + status_t err = dumpThreadsToFd(service, remote_end); + reportDumpError(serviceName, err, "dumping thread info"); } - if (err != OK) { - std::cerr << "Error dumping service info status_t: " << statusToString(err) << " " - << serviceName << std::endl; + // other types always act as a header, this is usually longer + if (dumpTypeFlags & TYPE_DUMP) { + status_t err = service->dump(remote_end.get(), args); + reportDumpError(serviceName, err, "dumping"); } }); return OK; diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h index 1b3ae6a81c..05c5d5e9d1 100644 --- a/cmds/dumpsys/dumpsys.h +++ b/cmds/dumpsys/dumpsys.h @@ -51,24 +51,25 @@ class Dumpsys { */ static void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags); - enum class Type { - DUMP, // dump using `dump` function - PID, // dump pid of server only - STABILITY, // dump stability information of server - THREAD, // dump thread usage of server only + enum Type { + TYPE_DUMP = 0x1, // dump using `dump` function + TYPE_PID = 0x2, // dump pid of server only + TYPE_STABILITY = 0x4, // dump stability information of server + TYPE_THREAD = 0x8, // dump thread usage of server only }; /** * 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 call to {@code * stopDumpThread}. + * @param dumpTypeFlags operations to perform * @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(Type type, const String16& serviceName, + status_t startDumpThread(int dumpTypeFlags, const String16& serviceName, const Vector<String16>& args); /** diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 277f445931..312f4d714a 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -200,7 +200,7 @@ class DumpsysTest : public Test { CaptureStdout(); CaptureStderr(); dump_.setServiceArgs(args, supportsProto, priorityFlags); - status_t status = dump_.startDumpThread(Dumpsys::Type::DUMP, serviceName, args); + status_t status = dump_.startDumpThread(Dumpsys::TYPE_DUMP, serviceName, args); EXPECT_THAT(status, Eq(0)); status = dump_.writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(500), false, elapsedDuration, bytesWritten); @@ -627,6 +627,28 @@ TEST_F(DumpsysTest, ListServiceWithThread) { AssertOutputFormat(format); } +// Tests 'dumpsys --thread --stability' +TEST_F(DumpsysTest, ListAllServicesWithMultipleOptions) { + ExpectListServices({"Locksmith", "Valet"}); + ExpectCheckService("Locksmith"); + ExpectCheckService("Valet"); + + CallMain({"--pid", "--stability"}); + AssertRunningServices({"Locksmith", "Valet"}); + + AssertOutputContains(std::to_string(getpid())); + AssertOutputContains("stability"); +} + +// Tests 'dumpsys --pid --stability service_name' +TEST_F(DumpsysTest, ListServiceWithMultipleOptions) { + ExpectCheckService("Locksmith"); + CallMain({"--pid", "--stability", "Locksmith"}); + + AssertOutputContains(std::to_string(getpid())); + AssertOutputContains("stability"); +} + TEST_F(DumpsysTest, GetBytesWritten) { const char* serviceName = "service2"; const char* dumpContents = "dump1"; diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index c6cf2c5ee8..200d923b6d 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -369,7 +369,7 @@ bool RpcServer::setupSocketServer(const RpcSocketAddress& addr) { return true; } -void RpcServer::onSessionLockedAllIncomingThreadsEnded(const sp<RpcSession>& session) { +void RpcServer::onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) { auto id = session->mId; LOG_ALWAYS_FATAL_IF(id == std::nullopt, "Server sessions must be initialized with ID"); LOG_RPC_DETAIL("Dropping session with address %s", id->toString().c_str()); diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index c01a03d4c6..1c376518f0 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -132,6 +132,7 @@ bool RpcSession::shutdownAndWait(bool wait) { if (wait) { LOG_ALWAYS_FATAL_IF(mShutdownListener == nullptr, "Shutdown listener not installed"); mShutdownListener->waitForShutdown(_l); + LOG_ALWAYS_FATAL_IF(!mThreads.empty(), "Shutdown failed"); } @@ -181,9 +182,7 @@ bool RpcSession::FdTrigger::isTriggered() { status_t RpcSession::FdTrigger::triggerablePoll(base::borrowed_fd fd, int16_t event) { while (true) { - pollfd pfd[]{{.fd = fd.get(), - .events = static_cast<int16_t>(event | POLLHUP), - .revents = 0}, + pollfd pfd[]{{.fd = fd.get(), .events = static_cast<int16_t>(event), .revents = 0}, {.fd = mRead.get(), .events = POLLHUP, .revents = 0}}; int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1)); if (ret < 0) { @@ -261,7 +260,7 @@ status_t RpcSession::readId() { return OK; } -void RpcSession::WaitForShutdownListener::onSessionLockedAllIncomingThreadsEnded( +void RpcSession::WaitForShutdownListener::onSessionAllIncomingThreadsEnded( const sp<RpcSession>& session) { (void)session; mShutdown = true; @@ -293,7 +292,13 @@ RpcSession::PreJoinSetupResult RpcSession::preJoinSetup(base::unique_fd fd) { // be able to do nested calls (we can't only read from it) sp<RpcConnection> connection = assignIncomingConnectionToThisThread(std::move(fd)); - status_t status = mState->readConnectionInit(connection, sp<RpcSession>::fromExisting(this)); + status_t status; + + if (connection == nullptr) { + status = DEAD_OBJECT; + } else { + status = mState->readConnectionInit(connection, sp<RpcSession>::fromExisting(this)); + } return PreJoinSetupResult{ .connection = std::move(connection), @@ -360,6 +365,7 @@ void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult sp<RpcConnection>& connection = setupResult.connection; if (setupResult.status == OK) { + LOG_ALWAYS_FATAL_IF(!connection, "must have connection if setup succeeded"); JavaThreadAttacher javaThreadAttacher; while (true) { status_t status = session->state()->getAndExecuteCommand(connection, session, @@ -375,9 +381,6 @@ void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult statusToString(setupResult.status).c_str()); } - LOG_ALWAYS_FATAL_IF(!session->removeIncomingConnection(connection), - "bad state: connection object guaranteed to be in list"); - sp<RpcSession::EventListener> listener; { std::lock_guard<std::mutex> _l(session->mMutex); @@ -389,6 +392,12 @@ void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult listener = session->mEventListener.promote(); } + // done after all cleanup, since session shutdown progresses via callbacks here + if (connection != nullptr) { + LOG_ALWAYS_FATAL_IF(!session->removeIncomingConnection(connection), + "bad state: connection object guaranteed to be in list"); + } + session = nullptr; if (listener != nullptr) { @@ -579,24 +588,35 @@ bool RpcSession::setForServer(const wp<RpcServer>& server, const wp<EventListene sp<RpcSession::RpcConnection> RpcSession::assignIncomingConnectionToThisThread(unique_fd fd) { std::lock_guard<std::mutex> _l(mMutex); + + // Don't accept any more connections, some have shutdown. Usually this + // happens when new connections are still being established as part of a + // very short-lived session which shuts down after it already started + // accepting new connections. + if (mIncomingConnections.size() < mMaxIncomingConnections) { + return nullptr; + } + sp<RpcConnection> session = sp<RpcConnection>::make(); session->fd = std::move(fd); session->exclusiveTid = gettid(); + mIncomingConnections.push_back(session); + mMaxIncomingConnections = mIncomingConnections.size(); return session; } bool RpcSession::removeIncomingConnection(const sp<RpcConnection>& connection) { - std::lock_guard<std::mutex> _l(mMutex); + std::unique_lock<std::mutex> _l(mMutex); if (auto it = std::find(mIncomingConnections.begin(), mIncomingConnections.end(), connection); it != mIncomingConnections.end()) { mIncomingConnections.erase(it); if (mIncomingConnections.size() == 0) { sp<EventListener> listener = mEventListener.promote(); if (listener) { - listener->onSessionLockedAllIncomingThreadsEnded( - sp<RpcSession>::fromExisting(this)); + _l.unlock(); + listener->onSessionAllIncomingThreadsEnded(sp<RpcSession>::fromExisting(this)); } } return true; diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index c8d2857347..a8094dd081 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -156,7 +156,7 @@ private: friend sp<RpcServer>; RpcServer(); - void onSessionLockedAllIncomingThreadsEnded(const sp<RpcSession>& session) override; + void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override; void onSessionIncomingThreadEnded() override; static void establishConnection(sp<RpcServer>&& server, base::unique_fd clientFd); diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index fdca2a987c..2101df85c4 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -177,19 +177,19 @@ private: class EventListener : public virtual RefBase { public: - virtual void onSessionLockedAllIncomingThreadsEnded(const sp<RpcSession>& session) = 0; + virtual void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) = 0; virtual void onSessionIncomingThreadEnded() = 0; }; class WaitForShutdownListener : public EventListener { public: - void onSessionLockedAllIncomingThreadsEnded(const sp<RpcSession>& session) override; + void onSessionAllIncomingThreadsEnded(const sp<RpcSession>& session) override; void onSessionIncomingThreadEnded() override; void waitForShutdown(std::unique_lock<std::mutex>& lock); private: std::condition_variable mCv; - bool mShutdown = false; + volatile bool mShutdown = false; }; struct RpcConnection : public RefBase { @@ -297,6 +297,7 @@ private: // hint index into clients, ++ when sending an async transaction size_t mOutgoingConnectionsOffset = 0; std::vector<sp<RpcConnection>> mOutgoingConnections; + size_t mMaxIncomingConnections = 0; std::vector<sp<RpcConnection>> mIncomingConnections; std::map<std::thread::id, std::thread> mThreads; }; diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 6b3bf8d865..4e84e52eba 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -144,6 +144,7 @@ filegroup { "DisplayHardware/ComposerHal.cpp", "DisplayHardware/DisplayIdentification.cpp", "DisplayHardware/FramebufferSurface.cpp", + "DisplayHardware/Hash.cpp", "DisplayHardware/HWC2.cpp", "DisplayHardware/HWComposer.cpp", "DisplayHardware/PowerAdvisor.cpp", diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp index 4dfc7431de..20f3fd2ec1 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp +++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp @@ -29,6 +29,7 @@ #include <log/log.h> #include "DisplayIdentification.h" +#include "Hash.h" namespace android { namespace { @@ -281,8 +282,9 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { } // Hash model string instead of using product code or (integer) serial number, since the latter - // have been observed to change on some displays with multiple inputs. - const auto modelHash = static_cast<uint32_t>(std::hash<std::string_view>()(modelString)); + // have been observed to change on some displays with multiple inputs. Use a stable hash instead + // of std::hash which is only required to be same within a single execution of a program. + const uint32_t modelHash = static_cast<uint32_t>(cityHash64Len0To16(modelString)); // Parse extension blocks. std::optional<Cea861ExtensionBlock> cea861Block; diff --git a/services/surfaceflinger/DisplayHardware/Hash.cpp b/services/surfaceflinger/DisplayHardware/Hash.cpp new file mode 100644 index 0000000000..6056c8d6f8 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/Hash.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "DisplayIdentification" + +#include <cstring> +#include <type_traits> + +#include <log/log.h> + +#include "Hash.h" + +namespace android { +namespace { + +template <class T> +inline T load(const void* p) { + static_assert(std::is_integral<T>::value, "T must be integral"); + + T r; + std::memcpy(&r, p, sizeof(r)); + return r; +} + +uint64_t rotateByAtLeast1(uint64_t val, uint8_t shift) { + return (val >> shift) | (val << (64 - shift)); +} + +uint64_t shiftMix(uint64_t val) { + return val ^ (val >> 47); +} + +uint64_t hash64Len16(uint64_t u, uint64_t v) { + constexpr uint64_t kMul = 0x9ddfea08eb382d69; + uint64_t a = (u ^ v) * kMul; + a ^= (a >> 47); + uint64_t b = (v ^ a) * kMul; + b ^= (b >> 47); + b *= kMul; + return b; +} + +uint64_t hash64Len0To16(const char* s, uint64_t len) { + constexpr uint64_t k2 = 0x9ae16a3b2f90404f; + constexpr uint64_t k3 = 0xc949d7c7509e6557; + + if (len > 8) { + const uint64_t a = load<uint64_t>(s); + const uint64_t b = load<uint64_t>(s + len - 8); + return hash64Len16(a, rotateByAtLeast1(b + len, static_cast<uint8_t>(len))) ^ b; + } + if (len >= 4) { + const uint32_t a = load<uint32_t>(s); + const uint32_t b = load<uint32_t>(s + len - 4); + return hash64Len16(len + (a << 3), b); + } + if (len > 0) { + const unsigned char a = static_cast<unsigned char>(s[0]); + const unsigned char b = static_cast<unsigned char>(s[len >> 1]); + const unsigned char c = static_cast<unsigned char>(s[len - 1]); + const uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8); + const uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2); + return shiftMix(y * k2 ^ z * k3) * k2; + } + return k2; +} + +} // namespace + +uint64_t cityHash64Len0To16(std::string_view sv) { + auto len = sv.length(); + if (len > 16) { + ALOGE("%s called with length %zu. Only hashing the first 16 chars", __FUNCTION__, len); + len = 16; + } + return hash64Len0To16(sv.data(), len); +} + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/DisplayHardware/Hash.h b/services/surfaceflinger/DisplayHardware/Hash.h new file mode 100644 index 0000000000..a7b6c717f6 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/Hash.h @@ -0,0 +1,27 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> +#include <string_view> + +namespace android { + +// CityHash64 implementation that only hashes at most the first 16 characters of the given string. +uint64_t cityHash64Len0To16(std::string_view sv); + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp index 2a0e913e7a..300ef26f25 100644 --- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp @@ -21,6 +21,7 @@ #include <gtest/gtest.h> #include "DisplayHardware/DisplayIdentification.h" +#include "DisplayHardware/Hash.h" namespace android { namespace { @@ -128,7 +129,7 @@ DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&byte } uint32_t hash(const char* str) { - return static_cast<uint32_t>(std::hash<std::string_view>()(str)); + return static_cast<uint32_t>(cityHash64Len0To16(str)); } } // namespace @@ -303,9 +304,9 @@ TEST(DisplayIdentificationTest, parseDisplayIdentificationData) { ASSERT_TRUE(tertiaryInfo); // Display IDs should be unique. - EXPECT_NE(primaryInfo->id, secondaryInfo->id); - EXPECT_NE(primaryInfo->id, tertiaryInfo->id); - EXPECT_NE(secondaryInfo->id, tertiaryInfo->id); + EXPECT_EQ(21571479025788672, primaryInfo->id.value); + EXPECT_EQ(9834267132873217, secondaryInfo->id.value); + EXPECT_EQ(21441883803501570, tertiaryInfo->id.value); } TEST(DisplayIdentificationTest, deviceProductInfo) { |