Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f108eaec authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 9576141 from 1afa3021 to udc-release

Change-Id: I732db610a996d37e16876aed3835bacb16da8175
parents e9944fd7 1afa3021
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -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.
@@ -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
+55 −36
Original line number Diff line number Diff line
@@ -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
@@ -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;
    }

@@ -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);
        }
@@ -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");
        }
@@ -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,
@@ -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())
@@ -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();
+5 −1
Original line number Diff line number Diff line
@@ -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>>>;
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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
+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