Loading cmds/dumpstate/dumpstate.rc +0 −3 Original line number Diff line number Diff line Loading @@ -8,7 +8,6 @@ service dumpstate /system/bin/dumpstate -s socket dumpstate stream 0660 shell log disabled oneshot capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG # dumpstatez generates a zipped bugreport but also uses a socket to print the file location once # it is finished. Loading @@ -17,11 +16,9 @@ service dumpstatez /system/bin/dumpstate -S class main disabled oneshot capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG # bugreportd starts dumpstate binder service and makes it wait for a listener to connect. service bugreportd /system/bin/dumpstate -w class main disabled oneshot capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG cmds/servicemanager/ServiceManager.cpp +55 −36 Original line number Diff line number Diff line Loading @@ -227,6 +227,13 @@ static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::s } #endif // !VENDORSERVICEMANAGER ServiceManager::Service::~Service() { if (!hasClients) { // only expected to happen on process death LOG(WARNING) << "a service was removed when there are clients"; } } ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) { // TODO(b/151696835): reenable performance hack when we solve bug, since with // this hack and other fixes, it is unlikely we will see even an ephemeral Loading Loading @@ -293,8 +300,13 @@ sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfN } if (out) { // Setting this guarantee each time we hand out a binder ensures that the client-checking // loop knows about the event even if the client immediately drops the service // Force onClients to get sent, and then make sure the timerfd won't clear it // by setting guaranteeClient again. This logic could be simplified by using // a time-based guarantee. However, forcing onClients(true) to get sent // right here is always going to be important for processes serving multiple // lazy interfaces. service->guaranteeClient = true; CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false)); service->guaranteeClient = true; } Loading Loading @@ -384,8 +396,13 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi }; if (auto it = mNameToRegistrationCallback.find(name); it != mNameToRegistrationCallback.end()) { for (const sp<IServiceCallback>& cb : it->second) { // See also getService - handles case where client never gets the service, // we want the service to quit. mNameToService[name].guaranteeClient = true; CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false)); mNameToService[name].guaranteeClient = true; for (const sp<IServiceCallback>& cb : it->second) { // permission checked in registerForNotifications cb->onRegistration(name, binder); } Loading Loading @@ -706,28 +723,28 @@ ssize_t ServiceManager::Service::getNodeStrongRefCount() { void ServiceManager::handleClientCallbacks() { for (const auto& [name, service] : mNameToService) { handleServiceClientCallback(name, true); handleServiceClientCallback(1 /* sm has one refcount */, name, true); } } ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName, bool ServiceManager::handleServiceClientCallback(size_t knownClients, const std::string& serviceName, bool isCalledOnInterval) { auto serviceIt = mNameToService.find(serviceName); if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) { return -1; return true; // return we do have clients a.k.a. DON'T DO ANYTHING } Service& service = serviceIt->second; ssize_t count = service.getNodeStrongRefCount(); // binder driver doesn't support this feature if (count == -1) return count; // binder driver doesn't support this feature, consider we have clients if (count == -1) return true; bool hasClients = count > 1; // this process holds a strong count bool hasKernelReportedClients = static_cast<size_t>(count) > knownClients; if (service.guaranteeClient) { // we have no record of this client if (!service.hasClients && !hasClients) { if (!service.hasClients && !hasKernelReportedClients) { sendClientCallbackNotifications(serviceName, true, "service is guaranteed to be in use"); } Loading @@ -736,21 +753,23 @@ ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceNa service.guaranteeClient = false; } // only send notifications if this was called via the interval checking workflow if (isCalledOnInterval) { if (hasClients && !service.hasClients) { // client was retrieved in some other way // Regardless of this situation, we want to give this notification as soon as possible. // This way, we have a chance of preventing further thrashing. if (hasKernelReportedClients && !service.hasClients) { sendClientCallbackNotifications(serviceName, true, "we now have a record of a client"); } // there are no more clients, but the callback has not been called yet if (!hasClients && service.hasClients) { // But limit rate of shutting down service. if (isCalledOnInterval) { if (!hasKernelReportedClients && service.hasClients) { sendClientCallbackNotifications(serviceName, false, "we now have no record of a client"); } } return count; // May be different than 'hasKernelReportedClients'. We intentionally delay // information about clients going away to reduce thrashing. return service.hasClients; } void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, Loading @@ -763,13 +782,10 @@ void ServiceManager::sendClientCallbackNotifications(const std::string& serviceN } Service& service = serviceIt->second; CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients << " so we can't tell clients again that we have client: " << hasClients << " when: " << context; CHECK_NE(hasClients, service.hasClients) << context; ALOGI("Notifying %s they %s have clients when %s", serviceName.c_str(), hasClients ? "do" : "don't", context); ALOGI("Notifying %s they %s (previously: %s) have clients when %s", serviceName.c_str(), hasClients ? "do" : "don't", service.hasClients ? "do" : "don't", context); auto ccIt = mNameToClientCallback.find(serviceName); CHECK(ccIt != mNameToClientCallback.end()) Loading Loading @@ -813,26 +829,29 @@ Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IB return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } // important because we don't have timer-based guarantees, we don't want to clear // this if (serviceIt->second.guaranteeClient) { ALOGI("Tried to unregister %s, but there is about to be a client.", name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } int clients = handleServiceClientCallback(name, false); // clients < 0: feature not implemented or other error. Assume clients. // Otherwise: // - kernel driver will hold onto one refcount (during this transaction) // - servicemanager has a refcount (guaranteed by this transaction) // So, if clients > 2, then at least one other service on the system must hold a refcount. if (clients < 0 || clients > 2) { // client callbacks are either disabled or there are other clients ALOGI("Tried to unregister %s, but there are clients: %d", name.c_str(), clients); // Set this flag to ensure the clients are acknowledged in the next callback constexpr size_t kKnownClients = 2; if (handleServiceClientCallback(kKnownClients, name, false)) { ALOGI("Tried to unregister %s, but there are clients.", name.c_str()); // Since we had a failed registration attempt, and the HIDL implementation of // delaying service shutdown for multiple periods wasn't ported here... this may // help reduce thrashing, but we should be able to remove it. serviceIt->second.guaranteeClient = true; return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } ALOGI("Unregistering %s", name.c_str()); mNameToService.erase(name); return Status::ok(); Loading cmds/servicemanager/ServiceManager.h +5 −1 Original line number Diff line number Diff line Loading @@ -80,6 +80,8 @@ private: // the number of clients of the service, including servicemanager itself ssize_t getNodeStrongRefCount(); ~Service(); }; using ServiceCallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>; Loading @@ -91,7 +93,9 @@ private: void removeRegistrationCallback(const wp<IBinder>& who, ServiceCallbackMap::iterator* it, bool* found); ssize_t handleServiceClientCallback(const std::string& serviceName, bool isCalledOnInterval); // returns whether there are known clients in addition to the count provided bool handleServiceClientCallback(size_t knownClients, const std::string& serviceName, bool isCalledOnInterval); // Also updates mHasClients (of what the last callback was) void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients, const char* context); Loading cmds/servicemanager/servicemanager.rc +1 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ service servicemanager /system/bin/servicemanager critical file /dev/kmsg w onrestart setprop servicemanager.ready false onrestart restart apexd onrestart restart --only-if-running apexd onrestart restart audioserver onrestart restart gatekeeperd onrestart class_restart --only-enabled main Loading include/input/RingBuffer.h 0 → 100644 +293 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <algorithm> #include <compare> #include <cstddef> #include <iterator> #include <memory> #include <type_traits> #include <utility> #include <android-base/logging.h> #include <android-base/stringprintf.h> namespace android { // A fixed-size ring buffer of elements. // // Elements can only be removed from the front/back or added to the front/back, but with O(1) // performance. Elements from the opposing side are evicted when new elements are pushed onto a full // buffer. template <typename T> class RingBuffer { public: using value_type = T; using size_type = size_t; using difference_type = ptrdiff_t; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const value_type*; template <typename U> class Iterator; using iterator = Iterator<T>; using const_iterator = Iterator<const T>; // Creates an empty ring buffer that can hold some capacity. explicit RingBuffer(size_type capacity) : mBuffer(std::allocator<value_type>().allocate(capacity)), mCapacity(capacity) {} // Creates a full ring buffer holding a fixed number of elements initialised to some value. explicit RingBuffer(size_type count, const_reference value) : RingBuffer(count) { while (count) { pushBack(value); --count; } } RingBuffer(const RingBuffer& other) : RingBuffer(other.capacity()) { for (const auto& element : other) { pushBack(element); } } RingBuffer(RingBuffer&& other) noexcept { *this = std::move(other); } ~RingBuffer() { if (mBuffer) { clear(); std::allocator<value_type>().deallocate(mBuffer, mCapacity); } } RingBuffer& operator=(const RingBuffer& other) { return *this = RingBuffer(other); } RingBuffer& operator=(RingBuffer&& other) noexcept { if (this == &other) { return *this; } if (mBuffer) { clear(); std::allocator<value_type>().deallocate(mBuffer, mCapacity); } mBuffer = std::move(other.mBuffer); mCapacity = other.mCapacity; mBegin = other.mBegin; mSize = other.mSize; other.mBuffer = nullptr; other.mCapacity = 0; other.mBegin = 0; other.mSize = 0; return *this; } iterator begin() { return {*this, 0}; } const_iterator begin() const { return {*this, 0}; } iterator end() { return {*this, mSize}; } const_iterator end() const { return {*this, mSize}; } reference operator[](size_type i) { return mBuffer[bufferIndex(i)]; } const_reference operator[](size_type i) const { return mBuffer[bufferIndex(i)]; } // Removes all elements from the buffer. void clear() { std::destroy(begin(), end()); mSize = 0; } // Removes and returns the first element from the buffer. value_type popFront() { value_type element = mBuffer[mBegin]; std::destroy_at(std::addressof(mBuffer[mBegin])); mBegin = next(mBegin); --mSize; return element; } // Removes and returns the last element from the buffer. value_type popBack() { size_type backIndex = bufferIndex(mSize - 1); value_type element = mBuffer[backIndex]; std::destroy_at(std::addressof(mBuffer[backIndex])); --mSize; return element; } // Adds an element to the front of the buffer. void pushFront(const value_type& element) { pushFront(value_type(element)); } void pushFront(value_type&& element) { mBegin = previous(mBegin); if (size() == capacity()) { mBuffer[mBegin] = std::forward<value_type>(element); } else { // The space at mBuffer[mBegin] is uninitialised. // TODO: Use std::construct_at when it becomes available. new (std::addressof(mBuffer[mBegin])) value_type(std::forward<value_type>(element)); ++mSize; } } // Adds an element to the back of the buffer. void pushBack(const value_type& element) { pushBack(value_type(element)); } void pushBack(value_type&& element) { if (size() == capacity()) { mBuffer[mBegin] = std::forward<value_type>(element); mBegin = next(mBegin); } else { // The space at mBuffer[...] is uninitialised. // TODO: Use std::construct_at when it becomes available. new (std::addressof(mBuffer[bufferIndex(mSize)])) value_type(std::forward<value_type>(element)); ++mSize; } } bool empty() const { return mSize == 0; } size_type capacity() const { return mCapacity; } size_type size() const { return mSize; } void swap(RingBuffer& other) noexcept { using std::swap; swap(mBuffer, other.mBuffer); swap(mCapacity, other.mCapacity); swap(mBegin, other.mBegin); swap(mSize, other.mSize); } friend void swap(RingBuffer& lhs, RingBuffer& rhs) noexcept { lhs.swap(rhs); } template <typename U> class Iterator { private: using ContainerType = std::conditional_t<std::is_const_v<U>, const RingBuffer, RingBuffer>; public: using iterator_category = std::random_access_iterator_tag; using size_type = ContainerType::size_type; using difference_type = ContainerType::difference_type; using value_type = std::remove_cv_t<U>; using pointer = U*; using reference = U&; Iterator(ContainerType& container, size_type index) : mContainer(container), mIndex(index) {} Iterator(const Iterator&) = default; Iterator& operator=(const Iterator&) = default; Iterator& operator++() { ++mIndex; return *this; } Iterator operator++(int) { Iterator iterator(*this); ++(*this); return iterator; } Iterator& operator--() { --mIndex; return *this; } Iterator operator--(int) { Iterator iterator(*this); --(*this); return iterator; } Iterator& operator+=(difference_type n) { mIndex += n; return *this; } Iterator operator+(difference_type n) { Iterator iterator(*this); return iterator += n; } Iterator& operator-=(difference_type n) { return *this += -n; } Iterator operator-(difference_type n) { Iterator iterator(*this); return iterator -= n; } difference_type operator-(const Iterator& other) { return mIndex - other.mIndex; } bool operator==(const Iterator& rhs) const { return mIndex == rhs.mIndex; } bool operator!=(const Iterator& rhs) const { return !(*this == rhs); } friend auto operator<=>(const Iterator& lhs, const Iterator& rhs) { return lhs.mIndex <=> rhs.mIndex; } reference operator[](difference_type n) { return *(*this + n); } reference operator*() const { return mContainer[mIndex]; } pointer operator->() const { return std::addressof(mContainer[mIndex]); } private: ContainerType& mContainer; size_type mIndex = 0; }; private: // Returns the index of the next element in mBuffer. size_type next(size_type index) const { if (index == capacity() - 1) { return 0; } else { return index + 1; } } // Returns the index of the previous element in mBuffer. size_type previous(size_type index) const { if (index == 0) { return capacity() - 1; } else { return index - 1; } } // Converts the index of an element in [0, size()] to its corresponding index in mBuffer. size_type bufferIndex(size_type elementIndex) const { CHECK_LE(elementIndex, size()); size_type index = mBegin + elementIndex; if (index >= capacity()) { index -= capacity(); } CHECK_LT(index, capacity()) << android::base::StringPrintf("Invalid index calculated for element (%zu) " "in buffer of size %zu", elementIndex, size()); return index; } pointer mBuffer = nullptr; size_type mCapacity = 0; // Total capacity of mBuffer. size_type mBegin = 0; // Index of the first initialised element in mBuffer. size_type mSize = 0; // Total number of initialised elements. }; } // namespace android Loading
cmds/dumpstate/dumpstate.rc +0 −3 Original line number Diff line number Diff line Loading @@ -8,7 +8,6 @@ service dumpstate /system/bin/dumpstate -s socket dumpstate stream 0660 shell log disabled oneshot capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG # dumpstatez generates a zipped bugreport but also uses a socket to print the file location once # it is finished. Loading @@ -17,11 +16,9 @@ service dumpstatez /system/bin/dumpstate -S class main disabled oneshot capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG # bugreportd starts dumpstate binder service and makes it wait for a listener to connect. service bugreportd /system/bin/dumpstate -w class main disabled oneshot capabilities CHOWN DAC_OVERRIDE DAC_READ_SEARCH FOWNER FSETID KILL NET_ADMIN NET_RAW SETGID SETUID SYS_PTRACE SYS_RESOURCE BLOCK_SUSPEND SYSLOG
cmds/servicemanager/ServiceManager.cpp +55 −36 Original line number Diff line number Diff line Loading @@ -227,6 +227,13 @@ static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::s } #endif // !VENDORSERVICEMANAGER ServiceManager::Service::~Service() { if (!hasClients) { // only expected to happen on process death LOG(WARNING) << "a service was removed when there are clients"; } } ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) { // TODO(b/151696835): reenable performance hack when we solve bug, since with // this hack and other fixes, it is unlikely we will see even an ephemeral Loading Loading @@ -293,8 +300,13 @@ sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfN } if (out) { // Setting this guarantee each time we hand out a binder ensures that the client-checking // loop knows about the event even if the client immediately drops the service // Force onClients to get sent, and then make sure the timerfd won't clear it // by setting guaranteeClient again. This logic could be simplified by using // a time-based guarantee. However, forcing onClients(true) to get sent // right here is always going to be important for processes serving multiple // lazy interfaces. service->guaranteeClient = true; CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false)); service->guaranteeClient = true; } Loading Loading @@ -384,8 +396,13 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi }; if (auto it = mNameToRegistrationCallback.find(name); it != mNameToRegistrationCallback.end()) { for (const sp<IServiceCallback>& cb : it->second) { // See also getService - handles case where client never gets the service, // we want the service to quit. mNameToService[name].guaranteeClient = true; CHECK(handleServiceClientCallback(2 /* sm + transaction */, name, false)); mNameToService[name].guaranteeClient = true; for (const sp<IServiceCallback>& cb : it->second) { // permission checked in registerForNotifications cb->onRegistration(name, binder); } Loading Loading @@ -706,28 +723,28 @@ ssize_t ServiceManager::Service::getNodeStrongRefCount() { void ServiceManager::handleClientCallbacks() { for (const auto& [name, service] : mNameToService) { handleServiceClientCallback(name, true); handleServiceClientCallback(1 /* sm has one refcount */, name, true); } } ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName, bool ServiceManager::handleServiceClientCallback(size_t knownClients, const std::string& serviceName, bool isCalledOnInterval) { auto serviceIt = mNameToService.find(serviceName); if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) { return -1; return true; // return we do have clients a.k.a. DON'T DO ANYTHING } Service& service = serviceIt->second; ssize_t count = service.getNodeStrongRefCount(); // binder driver doesn't support this feature if (count == -1) return count; // binder driver doesn't support this feature, consider we have clients if (count == -1) return true; bool hasClients = count > 1; // this process holds a strong count bool hasKernelReportedClients = static_cast<size_t>(count) > knownClients; if (service.guaranteeClient) { // we have no record of this client if (!service.hasClients && !hasClients) { if (!service.hasClients && !hasKernelReportedClients) { sendClientCallbackNotifications(serviceName, true, "service is guaranteed to be in use"); } Loading @@ -736,21 +753,23 @@ ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceNa service.guaranteeClient = false; } // only send notifications if this was called via the interval checking workflow if (isCalledOnInterval) { if (hasClients && !service.hasClients) { // client was retrieved in some other way // Regardless of this situation, we want to give this notification as soon as possible. // This way, we have a chance of preventing further thrashing. if (hasKernelReportedClients && !service.hasClients) { sendClientCallbackNotifications(serviceName, true, "we now have a record of a client"); } // there are no more clients, but the callback has not been called yet if (!hasClients && service.hasClients) { // But limit rate of shutting down service. if (isCalledOnInterval) { if (!hasKernelReportedClients && service.hasClients) { sendClientCallbackNotifications(serviceName, false, "we now have no record of a client"); } } return count; // May be different than 'hasKernelReportedClients'. We intentionally delay // information about clients going away to reduce thrashing. return service.hasClients; } void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, Loading @@ -763,13 +782,10 @@ void ServiceManager::sendClientCallbackNotifications(const std::string& serviceN } Service& service = serviceIt->second; CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients << " so we can't tell clients again that we have client: " << hasClients << " when: " << context; CHECK_NE(hasClients, service.hasClients) << context; ALOGI("Notifying %s they %s have clients when %s", serviceName.c_str(), hasClients ? "do" : "don't", context); ALOGI("Notifying %s they %s (previously: %s) have clients when %s", serviceName.c_str(), hasClients ? "do" : "don't", service.hasClients ? "do" : "don't", context); auto ccIt = mNameToClientCallback.find(serviceName); CHECK(ccIt != mNameToClientCallback.end()) Loading Loading @@ -813,26 +829,29 @@ Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IB return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } // important because we don't have timer-based guarantees, we don't want to clear // this if (serviceIt->second.guaranteeClient) { ALOGI("Tried to unregister %s, but there is about to be a client.", name.c_str()); return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } int clients = handleServiceClientCallback(name, false); // clients < 0: feature not implemented or other error. Assume clients. // Otherwise: // - kernel driver will hold onto one refcount (during this transaction) // - servicemanager has a refcount (guaranteed by this transaction) // So, if clients > 2, then at least one other service on the system must hold a refcount. if (clients < 0 || clients > 2) { // client callbacks are either disabled or there are other clients ALOGI("Tried to unregister %s, but there are clients: %d", name.c_str(), clients); // Set this flag to ensure the clients are acknowledged in the next callback constexpr size_t kKnownClients = 2; if (handleServiceClientCallback(kKnownClients, name, false)) { ALOGI("Tried to unregister %s, but there are clients.", name.c_str()); // Since we had a failed registration attempt, and the HIDL implementation of // delaying service shutdown for multiple periods wasn't ported here... this may // help reduce thrashing, but we should be able to remove it. serviceIt->second.guaranteeClient = true; return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); } ALOGI("Unregistering %s", name.c_str()); mNameToService.erase(name); return Status::ok(); Loading
cmds/servicemanager/ServiceManager.h +5 −1 Original line number Diff line number Diff line Loading @@ -80,6 +80,8 @@ private: // the number of clients of the service, including servicemanager itself ssize_t getNodeStrongRefCount(); ~Service(); }; using ServiceCallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>; Loading @@ -91,7 +93,9 @@ private: void removeRegistrationCallback(const wp<IBinder>& who, ServiceCallbackMap::iterator* it, bool* found); ssize_t handleServiceClientCallback(const std::string& serviceName, bool isCalledOnInterval); // returns whether there are known clients in addition to the count provided bool handleServiceClientCallback(size_t knownClients, const std::string& serviceName, bool isCalledOnInterval); // Also updates mHasClients (of what the last callback was) void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients, const char* context); Loading
cmds/servicemanager/servicemanager.rc +1 −1 Original line number Diff line number Diff line Loading @@ -5,7 +5,7 @@ service servicemanager /system/bin/servicemanager critical file /dev/kmsg w onrestart setprop servicemanager.ready false onrestart restart apexd onrestart restart --only-if-running apexd onrestart restart audioserver onrestart restart gatekeeperd onrestart class_restart --only-enabled main Loading
include/input/RingBuffer.h 0 → 100644 +293 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <algorithm> #include <compare> #include <cstddef> #include <iterator> #include <memory> #include <type_traits> #include <utility> #include <android-base/logging.h> #include <android-base/stringprintf.h> namespace android { // A fixed-size ring buffer of elements. // // Elements can only be removed from the front/back or added to the front/back, but with O(1) // performance. Elements from the opposing side are evicted when new elements are pushed onto a full // buffer. template <typename T> class RingBuffer { public: using value_type = T; using size_type = size_t; using difference_type = ptrdiff_t; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const value_type*; template <typename U> class Iterator; using iterator = Iterator<T>; using const_iterator = Iterator<const T>; // Creates an empty ring buffer that can hold some capacity. explicit RingBuffer(size_type capacity) : mBuffer(std::allocator<value_type>().allocate(capacity)), mCapacity(capacity) {} // Creates a full ring buffer holding a fixed number of elements initialised to some value. explicit RingBuffer(size_type count, const_reference value) : RingBuffer(count) { while (count) { pushBack(value); --count; } } RingBuffer(const RingBuffer& other) : RingBuffer(other.capacity()) { for (const auto& element : other) { pushBack(element); } } RingBuffer(RingBuffer&& other) noexcept { *this = std::move(other); } ~RingBuffer() { if (mBuffer) { clear(); std::allocator<value_type>().deallocate(mBuffer, mCapacity); } } RingBuffer& operator=(const RingBuffer& other) { return *this = RingBuffer(other); } RingBuffer& operator=(RingBuffer&& other) noexcept { if (this == &other) { return *this; } if (mBuffer) { clear(); std::allocator<value_type>().deallocate(mBuffer, mCapacity); } mBuffer = std::move(other.mBuffer); mCapacity = other.mCapacity; mBegin = other.mBegin; mSize = other.mSize; other.mBuffer = nullptr; other.mCapacity = 0; other.mBegin = 0; other.mSize = 0; return *this; } iterator begin() { return {*this, 0}; } const_iterator begin() const { return {*this, 0}; } iterator end() { return {*this, mSize}; } const_iterator end() const { return {*this, mSize}; } reference operator[](size_type i) { return mBuffer[bufferIndex(i)]; } const_reference operator[](size_type i) const { return mBuffer[bufferIndex(i)]; } // Removes all elements from the buffer. void clear() { std::destroy(begin(), end()); mSize = 0; } // Removes and returns the first element from the buffer. value_type popFront() { value_type element = mBuffer[mBegin]; std::destroy_at(std::addressof(mBuffer[mBegin])); mBegin = next(mBegin); --mSize; return element; } // Removes and returns the last element from the buffer. value_type popBack() { size_type backIndex = bufferIndex(mSize - 1); value_type element = mBuffer[backIndex]; std::destroy_at(std::addressof(mBuffer[backIndex])); --mSize; return element; } // Adds an element to the front of the buffer. void pushFront(const value_type& element) { pushFront(value_type(element)); } void pushFront(value_type&& element) { mBegin = previous(mBegin); if (size() == capacity()) { mBuffer[mBegin] = std::forward<value_type>(element); } else { // The space at mBuffer[mBegin] is uninitialised. // TODO: Use std::construct_at when it becomes available. new (std::addressof(mBuffer[mBegin])) value_type(std::forward<value_type>(element)); ++mSize; } } // Adds an element to the back of the buffer. void pushBack(const value_type& element) { pushBack(value_type(element)); } void pushBack(value_type&& element) { if (size() == capacity()) { mBuffer[mBegin] = std::forward<value_type>(element); mBegin = next(mBegin); } else { // The space at mBuffer[...] is uninitialised. // TODO: Use std::construct_at when it becomes available. new (std::addressof(mBuffer[bufferIndex(mSize)])) value_type(std::forward<value_type>(element)); ++mSize; } } bool empty() const { return mSize == 0; } size_type capacity() const { return mCapacity; } size_type size() const { return mSize; } void swap(RingBuffer& other) noexcept { using std::swap; swap(mBuffer, other.mBuffer); swap(mCapacity, other.mCapacity); swap(mBegin, other.mBegin); swap(mSize, other.mSize); } friend void swap(RingBuffer& lhs, RingBuffer& rhs) noexcept { lhs.swap(rhs); } template <typename U> class Iterator { private: using ContainerType = std::conditional_t<std::is_const_v<U>, const RingBuffer, RingBuffer>; public: using iterator_category = std::random_access_iterator_tag; using size_type = ContainerType::size_type; using difference_type = ContainerType::difference_type; using value_type = std::remove_cv_t<U>; using pointer = U*; using reference = U&; Iterator(ContainerType& container, size_type index) : mContainer(container), mIndex(index) {} Iterator(const Iterator&) = default; Iterator& operator=(const Iterator&) = default; Iterator& operator++() { ++mIndex; return *this; } Iterator operator++(int) { Iterator iterator(*this); ++(*this); return iterator; } Iterator& operator--() { --mIndex; return *this; } Iterator operator--(int) { Iterator iterator(*this); --(*this); return iterator; } Iterator& operator+=(difference_type n) { mIndex += n; return *this; } Iterator operator+(difference_type n) { Iterator iterator(*this); return iterator += n; } Iterator& operator-=(difference_type n) { return *this += -n; } Iterator operator-(difference_type n) { Iterator iterator(*this); return iterator -= n; } difference_type operator-(const Iterator& other) { return mIndex - other.mIndex; } bool operator==(const Iterator& rhs) const { return mIndex == rhs.mIndex; } bool operator!=(const Iterator& rhs) const { return !(*this == rhs); } friend auto operator<=>(const Iterator& lhs, const Iterator& rhs) { return lhs.mIndex <=> rhs.mIndex; } reference operator[](difference_type n) { return *(*this + n); } reference operator*() const { return mContainer[mIndex]; } pointer operator->() const { return std::addressof(mContainer[mIndex]); } private: ContainerType& mContainer; size_type mIndex = 0; }; private: // Returns the index of the next element in mBuffer. size_type next(size_type index) const { if (index == capacity() - 1) { return 0; } else { return index + 1; } } // Returns the index of the previous element in mBuffer. size_type previous(size_type index) const { if (index == 0) { return capacity() - 1; } else { return index - 1; } } // Converts the index of an element in [0, size()] to its corresponding index in mBuffer. size_type bufferIndex(size_type elementIndex) const { CHECK_LE(elementIndex, size()); size_type index = mBegin + elementIndex; if (index >= capacity()) { index -= capacity(); } CHECK_LT(index, capacity()) << android::base::StringPrintf("Invalid index calculated for element (%zu) " "in buffer of size %zu", elementIndex, size()); return index; } pointer mBuffer = nullptr; size_type mCapacity = 0; // Total capacity of mBuffer. size_type mBegin = 0; // Index of the first initialised element in mBuffer. size_type mSize = 0; // Total number of initialised elements. }; } // namespace android