1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/android/resources/resource_manager_impl.h"
6 
7 #include <inttypes.h>
8 #include <stddef.h>
9 
10 #include <utility>
11 #include <vector>
12 
13 #include "base/android/jni_array.h"
14 #include "base/android/jni_string.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/threading/thread_task_runner_handle.h"
18 #include "base/trace_event/memory_dump_manager.h"
19 #include "base/trace_event/memory_usage_estimator.h"
20 #include "base/trace_event/process_memory_dump.h"
21 #include "base/trace_event/trace_event.h"
22 #include "cc/resources/scoped_ui_resource.h"
23 #include "cc/resources/ui_resource_manager.h"
24 #include "third_party/skia/include/core/SkBitmap.h"
25 #include "third_party/skia/include/core/SkCanvas.h"
26 #include "third_party/skia/include/core/SkColorFilter.h"
27 #include "ui/android/resources/ui_resource_provider.h"
28 #include "ui/android/ui_android_jni_headers/ResourceManager_jni.h"
29 #include "ui/android/window_android.h"
30 #include "ui/gfx/android/java_bitmap.h"
31 #include "ui/gfx/geometry/rect.h"
32 
33 using base::android::JavaArrayOfIntArrayToIntVector;
34 using base::android::JavaParamRef;
35 using base::android::JavaRef;
36 
37 namespace {
38 
CreateMemoryDump(const std::string & name,size_t memory_usage,base::trace_event::ProcessMemoryDump * pmd)39 base::trace_event::MemoryAllocatorDump* CreateMemoryDump(
40     const std::string& name,
41     size_t memory_usage,
42     base::trace_event::ProcessMemoryDump* pmd) {
43   base::trace_event::MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(name);
44   dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
45                   base::trace_event::MemoryAllocatorDump::kUnitsBytes,
46                   memory_usage);
47 
48   static const char* system_allocator_name =
49       base::trace_event::MemoryDumpManager::GetInstance()
50           ->system_allocator_pool_name();
51   if (system_allocator_name)
52     pmd->AddSuballocation(dump->guid(), system_allocator_name);
53   return dump;
54 }
55 
56 }  // namespace
57 
58 namespace ui {
59 
60 // static
FromJavaObject(const JavaRef<jobject> & jobj)61 ResourceManagerImpl* ResourceManagerImpl::FromJavaObject(
62     const JavaRef<jobject>& jobj) {
63   return reinterpret_cast<ResourceManagerImpl*>(
64       Java_ResourceManager_getNativePtr(base::android::AttachCurrentThread(),
65                                         jobj));
66 }
67 
ResourceManagerImpl(gfx::NativeWindow native_window)68 ResourceManagerImpl::ResourceManagerImpl(gfx::NativeWindow native_window)
69     : ui_resource_manager_(nullptr) {
70   JNIEnv* env = base::android::AttachCurrentThread();
71   java_obj_.Reset(
72       env, Java_ResourceManager_create(env, native_window->GetJavaObject(),
73                                        reinterpret_cast<intptr_t>(this))
74                .obj());
75   DCHECK(!java_obj_.is_null());
76   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
77       this, "android::ResourceManagerImpl",
78       base::ThreadTaskRunnerHandle::Get());
79 }
80 
~ResourceManagerImpl()81 ResourceManagerImpl::~ResourceManagerImpl() {
82   base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
83       this);
84   Java_ResourceManager_destroy(base::android::AttachCurrentThread(), java_obj_);
85 }
86 
Init(cc::UIResourceManager * ui_resource_manager)87 void ResourceManagerImpl::Init(cc::UIResourceManager* ui_resource_manager) {
88   DCHECK(!ui_resource_manager_);
89   DCHECK(ui_resource_manager);
90   ui_resource_manager_ = ui_resource_manager;
91 }
92 
93 base::android::ScopedJavaLocalRef<jobject>
GetJavaObject()94 ResourceManagerImpl::GetJavaObject() {
95   return base::android::ScopedJavaLocalRef<jobject>(java_obj_);
96 }
97 
GetResource(AndroidResourceType res_type,int res_id)98 Resource* ResourceManagerImpl::GetResource(AndroidResourceType res_type,
99                                            int res_id) {
100   DCHECK_GE(res_type, ANDROID_RESOURCE_TYPE_FIRST);
101   DCHECK_LE(res_type, ANDROID_RESOURCE_TYPE_LAST);
102 
103   std::unordered_map<int, std::unique_ptr<Resource>>::iterator item =
104       resources_[res_type].find(res_id);
105 
106   if (item == resources_[res_type].end() ||
107       res_type == ANDROID_RESOURCE_TYPE_DYNAMIC ||
108       res_type == ANDROID_RESOURCE_TYPE_DYNAMIC_BITMAP) {
109     RequestResourceFromJava(res_type, res_id);
110 
111     // Check if the resource has been added (some dynamic may not have been).
112     item = resources_[res_type].find(res_id);
113     if (item == resources_[res_type].end())
114       return nullptr;
115   }
116 
117   return item->second.get();
118 }
119 
RemoveUnusedTints()120 void ResourceManagerImpl::RemoveUnusedTints() {
121   // Iterate over the currently cached tints and remove ones that were not
122   // used as defined in |used_tints|.
123   for (auto it = tinted_resources_.cbegin(); it != tinted_resources_.cend();) {
124     if (used_tints_.find(it->first) == used_tints_.end()) {
125       it = tinted_resources_.erase(it);
126     } else {
127       ++it;
128     }
129   }
130 }
131 
OnFrameUpdatesFinished()132 void ResourceManagerImpl::OnFrameUpdatesFinished() {
133   RemoveUnusedTints();
134   used_tints_.clear();
135 }
136 
GetStaticResourceWithTint(int res_id,SkColor tint_color)137 Resource* ResourceManagerImpl::GetStaticResourceWithTint(int res_id,
138                                                          SkColor tint_color) {
139   if (tinted_resources_.find(tint_color) == tinted_resources_.end()) {
140     tinted_resources_[tint_color] = std::make_unique<ResourceMap>();
141   }
142 
143   used_tints_.insert(tint_color);
144   ResourceMap* resource_map = tinted_resources_[tint_color].get();
145 
146   // If the resource is already cached, use it.
147   std::unordered_map<int, std::unique_ptr<Resource>>::iterator item =
148       resource_map->find(res_id);
149   if (item != resource_map->end())
150     return item->second.get();
151 
152   Resource* base_image = GetResource(ANDROID_RESOURCE_TYPE_STATIC, res_id);
153   DCHECK(base_image);
154 
155   std::unique_ptr<Resource> tinted_resource = base_image->CreateForCopy();
156 
157   TRACE_EVENT0("browser", "ResourceManagerImpl::GetStaticResourceWithTint");
158   SkBitmap tinted_bitmap;
159   tinted_bitmap.allocPixels(SkImageInfo::MakeN32Premul(
160       base_image->size().width(), base_image->size().height()));
161 
162   SkCanvas canvas(tinted_bitmap);
163   canvas.clear(SK_ColorTRANSPARENT);
164 
165   // Build a color filter to use on the base resource. This filter multiplies
166   // the RGB components by the components of the new color but retains the
167   // alpha of the original image.
168   float color_matrix[20] = {0, 0, 0, 0, SkColorGetR(tint_color) * (1.0f / 255),
169                             0, 0, 0, 0, SkColorGetG(tint_color) * (1.0f / 255),
170                             0, 0, 0, 0, SkColorGetB(tint_color) * (1.0f / 255),
171                             0, 0, 0, 1, 0};
172   SkPaint color_filter;
173   color_filter.setColorFilter(SkColorFilters::Matrix(color_matrix));
174 
175   // Draw the resource and make it immutable.
176   base_image->ui_resource()
177       ->GetBitmap(base_image->ui_resource()->id(), false)
178       .DrawToCanvas(&canvas, &color_filter);
179   tinted_bitmap.setImmutable();
180 
181   // Create a UI resource from the new bitmap.
182   tinted_resource->SetUIResource(
183       cc::ScopedUIResource::Create(ui_resource_manager_,
184                                    cc::UIResourceBitmap(tinted_bitmap)),
185       base_image->size());
186 
187   (*resource_map)[res_id].swap(tinted_resource);
188 
189   return (*resource_map)[res_id].get();
190 }
191 
ClearTintedResourceCache(JNIEnv * env,const JavaRef<jobject> & jobj)192 void ResourceManagerImpl::ClearTintedResourceCache(JNIEnv* env,
193     const JavaRef<jobject>& jobj) {
194   tinted_resources_.clear();
195 }
196 
PreloadResource(AndroidResourceType res_type,int res_id)197 void ResourceManagerImpl::PreloadResource(AndroidResourceType res_type,
198                                           int res_id) {
199   DCHECK_GE(res_type, ANDROID_RESOURCE_TYPE_FIRST);
200   DCHECK_LE(res_type, ANDROID_RESOURCE_TYPE_LAST);
201 
202   // Don't send out a query if the resource is already loaded.
203   if (resources_[res_type].find(res_id) != resources_[res_type].end())
204     return;
205 
206   PreloadResourceFromJava(res_type, res_id);
207 }
208 
OnResourceReady(JNIEnv * env,const JavaRef<jobject> & jobj,jint res_type,jint res_id,const JavaRef<jobject> & bitmap,jint width,jint height,jlong native_resource)209 void ResourceManagerImpl::OnResourceReady(JNIEnv* env,
210                                           const JavaRef<jobject>& jobj,
211                                           jint res_type,
212                                           jint res_id,
213                                           const JavaRef<jobject>& bitmap,
214                                           jint width,
215                                           jint height,
216                                           jlong native_resource) {
217   DCHECK_GE(res_type, ANDROID_RESOURCE_TYPE_FIRST);
218   DCHECK_LE(res_type, ANDROID_RESOURCE_TYPE_LAST);
219   TRACE_EVENT2("ui", "ResourceManagerImpl::OnResourceReady",
220                "resource_type", res_type,
221                "resource_id", res_id);
222 
223   resources_[res_type][res_id] =
224       base::WrapUnique(reinterpret_cast<Resource*>(native_resource));
225   Resource* resource = resources_[res_type][res_id].get();
226 
227   gfx::JavaBitmap jbitmap(bitmap);
228   SkBitmap skbitmap = gfx::CreateSkBitmapFromJavaBitmap(jbitmap);
229   skbitmap.setImmutable();
230   resource->SetUIResource(
231       cc::ScopedUIResource::Create(ui_resource_manager_,
232                                    cc::UIResourceBitmap(skbitmap)),
233       gfx::Size(width, height));
234 }
235 
RemoveResource(JNIEnv * env,const base::android::JavaRef<jobject> & jobj,jint res_type,jint res_id)236 void ResourceManagerImpl::RemoveResource(
237     JNIEnv* env,
238     const base::android::JavaRef<jobject>& jobj,
239     jint res_type,
240     jint res_id) {
241   resources_[res_type].erase(res_id);
242 }
243 
OnMemoryDump(const base::trace_event::MemoryDumpArgs & args,base::trace_event::ProcessMemoryDump * pmd)244 bool ResourceManagerImpl::OnMemoryDump(
245     const base::trace_event::MemoryDumpArgs& args,
246     base::trace_event::ProcessMemoryDump* pmd) {
247   std::string prefix = base::StringPrintf("ui/resource_manager_0x%" PRIXPTR,
248                                           reinterpret_cast<uintptr_t>(this));
249   for (uint32_t type = static_cast<uint32_t>(ANDROID_RESOURCE_TYPE_FIRST);
250        type <= static_cast<uint32_t>(ANDROID_RESOURCE_TYPE_LAST); ++type) {
251     size_t usage = base::trace_event::EstimateMemoryUsage(resources_[type]);
252     auto* dump = CreateMemoryDump(
253         prefix + base::StringPrintf("/default_resource/0x%u",
254                                     static_cast<uint32_t>(type)),
255         usage, pmd);
256     dump->AddScalar("resource_count", "objects", resources_[type].size());
257   }
258 
259   size_t tinted_resource_usage =
260       base::trace_event::EstimateMemoryUsage(tinted_resources_);
261   CreateMemoryDump(prefix + "/tinted_resource", tinted_resource_usage, pmd);
262   return true;
263 }
264 
PreloadResourceFromJava(AndroidResourceType res_type,int res_id)265 void ResourceManagerImpl::PreloadResourceFromJava(AndroidResourceType res_type,
266                                                   int res_id) {
267   TRACE_EVENT2("ui", "ResourceManagerImpl::PreloadResourceFromJava",
268                "resource_type", res_type,
269                "resource_id", res_id);
270   Java_ResourceManager_preloadResource(base::android::AttachCurrentThread(),
271                                        java_obj_, res_type, res_id);
272 }
273 
RequestResourceFromJava(AndroidResourceType res_type,int res_id)274 void ResourceManagerImpl::RequestResourceFromJava(AndroidResourceType res_type,
275                                                   int res_id) {
276   TRACE_EVENT2("ui", "ResourceManagerImpl::RequestResourceFromJava",
277                "resource_type", res_type,
278                "resource_id", res_id);
279   Java_ResourceManager_resourceRequested(base::android::AttachCurrentThread(),
280                                          java_obj_, res_type, res_id);
281 }
282 
283 }  // namespace ui
284