1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #undef LOG_TAG
19 #define LOG_TAG "ClientCache"
20 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
21 
22 #include <cinttypes>
23 
24 #include <android-base/stringprintf.h>
25 #include <gui/TraceUtils.h>
26 #include <renderengine/impl/ExternalTexture.h>
27 
28 #include "ClientCache.h"
29 
30 namespace android {
31 
32 ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache);
33 
ClientCache()34 ClientCache::ClientCache() : mDeathRecipient(sp<CacheDeathRecipient>::make()) {}
35 
getBuffer(const client_cache_t & cacheId,ClientCacheBuffer ** outClientCacheBuffer)36 bool ClientCache::getBuffer(const client_cache_t& cacheId,
37                             ClientCacheBuffer** outClientCacheBuffer) {
38     auto& [processToken, id] = cacheId;
39     if (processToken == nullptr) {
40         ALOGE_AND_TRACE("ClientCache::getBuffer - invalid (nullptr) process token");
41         return false;
42     }
43     auto it = mBuffers.find(processToken);
44     if (it == mBuffers.end()) {
45         ALOGE_AND_TRACE("ClientCache::getBuffer - invalid process token");
46         return false;
47     }
48 
49     auto& processBuffers = it->second.second;
50 
51     auto bufItr = processBuffers.find(id);
52     if (bufItr == processBuffers.end()) {
53         ALOGE_AND_TRACE("ClientCache::getBuffer - invalid buffer id");
54         return false;
55     }
56 
57     ClientCacheBuffer& buf = bufItr->second;
58     *outClientCacheBuffer = &buf;
59     return true;
60 }
61 
62 base::expected<std::shared_ptr<renderengine::ExternalTexture>, ClientCache::AddError>
add(const client_cache_t & cacheId,const sp<GraphicBuffer> & buffer)63 ClientCache::add(const client_cache_t& cacheId, const sp<GraphicBuffer>& buffer) {
64     auto& [processToken, id] = cacheId;
65     if (processToken == nullptr) {
66         ALOGE_AND_TRACE("ClientCache::add - invalid (nullptr) process token");
67         return base::unexpected(AddError::Unspecified);
68     }
69 
70     if (!buffer) {
71         ALOGE_AND_TRACE("ClientCache::add - invalid (nullptr) buffer");
72         return base::unexpected(AddError::Unspecified);
73     }
74 
75     std::lock_guard lock(mMutex);
76     sp<IBinder> token;
77 
78     // If this is a new process token, set a death recipient. If the client process dies, we will
79     // get a callback through binderDied.
80     auto it = mBuffers.find(processToken);
81     if (it == mBuffers.end()) {
82         token = processToken.promote();
83         if (!token) {
84             ALOGE_AND_TRACE("ClientCache::add - invalid token");
85             return base::unexpected(AddError::Unspecified);
86         }
87 
88         // Only call linkToDeath if not a local binder
89         if (token->localBinder() == nullptr) {
90             status_t err = token->linkToDeath(mDeathRecipient);
91             if (err != NO_ERROR) {
92                 ALOGE_AND_TRACE("ClientCache::add - could not link to death");
93                 return base::unexpected(AddError::Unspecified);
94             }
95         }
96         auto [itr, success] =
97                 mBuffers.emplace(processToken,
98                                  std::make_pair(token,
99                                                 std::unordered_map<uint64_t, ClientCacheBuffer>()));
100         LOG_ALWAYS_FATAL_IF(!success, "failed to insert new process into client cache");
101         it = itr;
102     }
103 
104     auto& processBuffers = it->second.second;
105 
106     if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
107         ALOGE_AND_TRACE("ClientCache::add - cache is full");
108         return base::unexpected(AddError::CacheFull);
109     }
110 
111     LOG_ALWAYS_FATAL_IF(mRenderEngine == nullptr,
112                         "Attempted to build the ClientCache before a RenderEngine instance was "
113                         "ready!");
114 
115     return (processBuffers[id].buffer = std::make_shared<
116                     renderengine::impl::ExternalTexture>(buffer, *mRenderEngine,
117                                                          renderengine::impl::ExternalTexture::
118                                                                  Usage::READABLE));
119 }
120 
erase(const client_cache_t & cacheId)121 sp<GraphicBuffer> ClientCache::erase(const client_cache_t& cacheId) {
122     sp<GraphicBuffer> buffer;
123     auto& [processToken, id] = cacheId;
124     std::vector<sp<ErasedRecipient>> pendingErase;
125     {
126         std::lock_guard lock(mMutex);
127         ClientCacheBuffer* buf = nullptr;
128         if (!getBuffer(cacheId, &buf)) {
129             ALOGE("failed to erase buffer, could not retrieve buffer");
130             return nullptr;
131         }
132 
133         buffer = buf->buffer->getBuffer();
134 
135         for (auto& recipient : buf->recipients) {
136             sp<ErasedRecipient> erasedRecipient = recipient.promote();
137             if (erasedRecipient) {
138                 pendingErase.push_back(erasedRecipient);
139             }
140         }
141 
142         mBuffers[processToken].second.erase(id);
143     }
144 
145     for (auto& recipient : pendingErase) {
146         recipient->bufferErased(cacheId);
147     }
148     return buffer;
149 }
150 
get(const client_cache_t & cacheId)151 std::shared_ptr<renderengine::ExternalTexture> ClientCache::get(const client_cache_t& cacheId) {
152     std::lock_guard lock(mMutex);
153 
154     ClientCacheBuffer* buf = nullptr;
155     if (!getBuffer(cacheId, &buf)) {
156         ALOGE("failed to get buffer, could not retrieve buffer");
157         return nullptr;
158     }
159 
160     return buf->buffer;
161 }
162 
registerErasedRecipient(const client_cache_t & cacheId,const wp<ErasedRecipient> & recipient)163 bool ClientCache::registerErasedRecipient(const client_cache_t& cacheId,
164                                           const wp<ErasedRecipient>& recipient) {
165     std::lock_guard lock(mMutex);
166 
167     ClientCacheBuffer* buf = nullptr;
168     if (!getBuffer(cacheId, &buf)) {
169         ALOGV("failed to register erased recipient, could not retrieve buffer");
170         return false;
171     }
172     buf->recipients.insert(recipient);
173     return true;
174 }
175 
unregisterErasedRecipient(const client_cache_t & cacheId,const wp<ErasedRecipient> & recipient)176 void ClientCache::unregisterErasedRecipient(const client_cache_t& cacheId,
177                                             const wp<ErasedRecipient>& recipient) {
178     std::lock_guard lock(mMutex);
179 
180     ClientCacheBuffer* buf = nullptr;
181     if (!getBuffer(cacheId, &buf)) {
182         ALOGE("failed to unregister erased recipient");
183         return;
184     }
185 
186     buf->recipients.erase(recipient);
187 }
188 
removeProcess(const wp<IBinder> & processToken)189 void ClientCache::removeProcess(const wp<IBinder>& processToken) {
190     std::vector<std::pair<sp<ErasedRecipient>, client_cache_t>> pendingErase;
191     {
192         if (processToken == nullptr) {
193             ALOGE("failed to remove process, invalid (nullptr) process token");
194             return;
195         }
196         std::lock_guard lock(mMutex);
197         auto itr = mBuffers.find(processToken);
198         if (itr == mBuffers.end()) {
199             ALOGE("failed to remove process, could not find process");
200             return;
201         }
202 
203         for (auto& [id, clientCacheBuffer] : itr->second.second) {
204             client_cache_t cacheId = {processToken, id};
205             for (auto& recipient : clientCacheBuffer.recipients) {
206                 sp<ErasedRecipient> erasedRecipient = recipient.promote();
207                 if (erasedRecipient) {
208                     pendingErase.emplace_back(erasedRecipient, cacheId);
209                 }
210             }
211         }
212         mBuffers.erase(itr);
213     }
214 
215     for (auto& [recipient, cacheId] : pendingErase) {
216         recipient->bufferErased(cacheId);
217     }
218 }
219 
binderDied(const wp<IBinder> & who)220 void ClientCache::CacheDeathRecipient::binderDied(const wp<IBinder>& who) {
221     ClientCache::getInstance().removeProcess(who);
222 }
223 
dump(std::string & result)224 void ClientCache::dump(std::string& result) {
225     std::lock_guard lock(mMutex);
226     for (const auto& [_, cache] : mBuffers) {
227         base::StringAppendF(&result, " Cache owner: %p\n", cache.first.get());
228 
229         for (const auto& [id, entry] : cache.second) {
230             const auto& buffer = entry.buffer->getBuffer();
231             base::StringAppendF(&result, "\tID: %" PRIu64 ", size: %ux%u\n", id, buffer->getWidth(),
232                                 buffer->getHeight());
233         }
234     }
235 }
236 
237 } // namespace android
238