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