Fix TextDropShadowCache infinite loop

Bug: 26862239

Switch TextDropCacheShadow to use the tracked objectSize()
instead of the optional bitmapSize. A mismatch here
results in ::get() infinite looping trying to free space in
the cache since the LRU removal callback would always
decrement mSize by 0 since bitmapSize was not being set.

Also prevent the infinite loop in the future by crashing if
this scenario happens again.

Change-Id: Ib4e9fbe1c8327af2335ad650fd694a1627d9824f
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index fc40554..587a366 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -248,7 +248,8 @@
     tests/unit/VectorDrawableTests.cpp \
     tests/unit/OffscreenBufferPoolTests.cpp \
     tests/unit/StringUtilsTests.cpp \
-    tests/unit/BufferPoolTests.cpp
+    tests/unit/BufferPoolTests.cpp \
+    tests/unit/TextDropShadowCacheTests.cpp
 
 ifeq (true, $(HWUI_NEW_OPS))
     LOCAL_SRC_FILES += \
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 62a20fc..1707468 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -148,7 +148,7 @@
 
 void TextDropShadowCache::operator()(ShadowText&, ShadowTexture*& texture) {
     if (texture) {
-        mSize -= texture->bitmapSize;
+        mSize -= texture->objectSize();
 
         if (mDebugEnabled) {
             ALOGD("Shadow texture deleted, size = %d", texture->bitmapSize);
@@ -195,7 +195,9 @@
         // Don't even try to cache a bitmap that's bigger than the cache
         if (size < mMaxSize) {
             while (mSize + size > mMaxSize) {
-                mCache.removeOldest();
+                LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(),
+                        "Failed to remove oldest from cache. mSize = %"
+                        PRIu32 ", mCache.size() = %zu", mSize, mCache.size());
             }
         }
 
@@ -212,7 +214,7 @@
 
             entry.copyTextLocally();
 
-            mSize += size;
+            mSize += texture->objectSize();
             mCache.put(entry, texture);
         } else {
             texture->cleanup = true;
diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
new file mode 100644
index 0000000..c54f2c3
--- /dev/null
+++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "GammaFontRenderer.h"
+#include "TextDropShadowCache.h"
+#include "utils/Blur.h"
+#include "tests/common/TestUtils.h"
+
+#include <SkBlurDrawLooper.h>
+#include <SkPaint.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+RENDERTHREAD_TEST(TextDropShadowCache, addRemove) {
+    GammaFontRenderer gammaFontRenderer;
+    FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer();
+    TextDropShadowCache cache(5000);
+    cache.setFontRenderer(fontRenderer);
+
+    SkPaint paint;
+    paint.setLooper(SkBlurDrawLooper::Create((SkColor)0xFFFFFFFF,
+            Blur::convertRadiusToSigma(10), 10, 10))->unref();
+    std::string msg("This is a test");
+    std::unique_ptr<float[]> positions(new float[msg.length()]);
+    for (size_t i = 0; i < msg.length(); i++) {
+        positions[i] = i * 10.0f;
+    }
+    fontRenderer.setFont(&paint, SkMatrix::I());
+    ShadowTexture* texture = cache.get(&paint, msg.c_str(), msg.length(),
+            10.0f, positions.get());
+    ASSERT_TRUE(texture);
+    ASSERT_FALSE(texture->cleanup);
+    ASSERT_EQ((uint32_t) texture->objectSize(), cache.getSize());
+    ASSERT_TRUE(cache.getSize());
+    cache.clear();
+    ASSERT_EQ(cache.getSize(), 0u);
+}