18e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project/*
28e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Copyright 2009, The Android Open Source Project
38e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
48e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
58e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Redistribution and use in source and binary forms, with or without
68e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * modification, are permitted provided that the following conditions
78e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * are met:
8e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block *  * Redistributions of source code must retain the above copyright
9e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block *    notice, this list of conditions and the following disclaimer.
10e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block *  * Redistributions in binary form must reproduce the above copyright
11e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block *    notice, this list of conditions and the following disclaimer in the
12e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block *    documentation and/or other materials provided with the distribution.
138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
14e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
15e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24e03118fd5d9b776df29de3f96232ed6b5395c845Steve Block * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project */
268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "config.h"
288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "FontCache.h"
29775c244a94159b3e986a8691f1a5f092f50af9d0Russell Brenner
308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "Font.h"
31775c244a94159b3e986a8691f1a5f092f50af9d0Russell Brenner#include "FontPlatformData.h"
328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "NotImplemented.h"
338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "SimpleFontData.h"
348e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "SkPaint.h"
358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "SkTypeface.h"
368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "SkUtils.h"
378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectnamespace WebCore {
398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
40cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brennerstatic const char* getFallbackFontName(const FontDescription& fontDescription)
41cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner{
42cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    switch (fontDescription.genericFamily()) {
43cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    case FontDescription::StandardFamily:
44cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    case FontDescription::SerifFamily:
45cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        return "serif";
46cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    case FontDescription::SansSerifFamily:
47cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        return "sans-serif";
48cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    case FontDescription::MonospaceFamily:
49cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        return "monospace";
50cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    case FontDescription::CursiveFamily:
51cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        return "cursive";
52cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    case FontDescription::FantasyFamily:
53cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        return "fantasy";
54cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    case FontDescription::NoFamily:
55cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    default:
56cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        return "";
57cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    }
58cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner}
59cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner
60cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brennerstatic bool isFallbackFamily(String family)
61cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner{
62cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    return family.startsWith("-webkit-")
63cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        || equalIgnoringCase(family, "serif")
64cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        || equalIgnoringCase(family, "sans-serif")
65cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        || equalIgnoringCase(family, "sans")
66cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        || equalIgnoringCase(family, "monospace")
67e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "times")   // skia aliases for serif
68e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "times new roman")
69e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "palatino")
70e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "georgia")
71e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "baskerville")
72e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "goudy")
73cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        || equalIgnoringCase(family, "cursive")
74e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "fantasy")
75e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "ITC Stone Serif")
76e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "arial")   // skia aliases for sans-serif
77e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "helvetica")
78e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "tahoma")
79e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "verdana")
80e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "courier") // skia aliases for monospace
81e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "courier new")
82e34da8b408683ed4f0b8859ce243fb89dd9ccf83Russell Brenner        || equalIgnoringCase(family, "monaco");
83cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner}
84cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner
85cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brennerstatic char* AtomicStringToUTF8String(const AtomicString& utf16)
86cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner{
87cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    SkASSERT(sizeof(uint16_t) == sizeof(utf16.characters()[0]));
88cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    const uint16_t* uni = (uint16_t*)utf16.characters();
89cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner
90cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    size_t bytes = SkUTF16_ToUTF8(uni, utf16.length(), 0);
91cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    char* utf8 = (char*)sk_malloc_throw(bytes + 1);
92cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner
93cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    (void)SkUTF16_ToUTF8(uni, utf16.length(), utf8);
94cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    utf8[bytes] = 0;
95cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    return utf8;
96cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner}
97cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner
98cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner
998e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectvoid FontCache::platformInit()
1008e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1018e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1028e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1038e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectconst SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length)
1048e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1058e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // since all of our fonts logically map to the fallback, we can always claim
1068e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // that each font supports all characters.
1078e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return font.primaryFont();
1088e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1098e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
110477d5d728b7ab5d2a4fbefc981a5b5d41e15f187Steve BlockSimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font)
1118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return 0;
1138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
115cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell BrennerSimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& description)
1167f3023a5515317c8d9ed577bbc141f3fefa64fadRussell Brenner{
117cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    static const AtomicString sansStr("sans-serif");
118cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    static const AtomicString serifStr("serif");
119cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    static const AtomicString monospaceStr("monospace");
120cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner
121cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    FontPlatformData* fontPlatformData = 0;
122cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    switch (description.genericFamily()) {
123cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    case FontDescription::SerifFamily:
124cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        fontPlatformData = getCachedFontPlatformData(description, serifStr);
125cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        break;
126cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    case FontDescription::MonospaceFamily:
127cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        fontPlatformData = getCachedFontPlatformData(description, monospaceStr);
128cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        break;
129cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    case FontDescription::SansSerifFamily:
130cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    default:
131cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        fontPlatformData = getCachedFontPlatformData(description, sansStr);
132cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        break;
133cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    }
1347f3023a5515317c8d9ed577bbc141f3fefa64fadRussell Brenner
135cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    ASSERT(fontPlatformData);
136cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    return getCachedFontData(fontPlatformData);
1378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectFontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
1408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
141cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    char* storage = 0;
1428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    const char* name = 0;
143cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    FontPlatformData* result = 0;
1447f3023a5515317c8d9ed577bbc141f3fefa64fadRussell Brenner
145cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    if (family.length()) {
1468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        storage = AtomicStringToUTF8String(family);
1478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        name = storage;
148cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    } else
149cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        name = getFallbackFontName(fontDescription);
1507f3023a5515317c8d9ed577bbc141f3fefa64fadRussell Brenner
1518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    int style = SkTypeface::kNormal;
1528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (fontDescription.weight() >= FontWeightBold)
1538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        style |= SkTypeface::kBold;
1548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (fontDescription.italic())
1558e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        style |= SkTypeface::kItalic;
1568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
157cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    // CreateFromName always returns a typeface, falling back to a default font
158cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    // if the one requested is not found. Calling Equal() with a null pointer
159cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    // serves to compare the returned font against the default, with the caveat
160cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    // that the default is always of normal style. If we detect the default, we
161cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    // ignore it and allow WebCore to give us the next font on the CSS fallback
162cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    // list. The only exception is if the family name is a commonly used generic
163cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    // family, as when called by getSimilarFontPlatformData() and
164cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    // getLastResortFallbackFont(). In this case, the default font is an
165cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    // acceptable result.
166cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner
167cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    SkTypeface* tf = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
168cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner
169cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    if (!SkTypeface::Equal(tf, 0) || isFallbackFamily(family.string())) {
170cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        // We had to use normal styling to see if this was a default font. If
171cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        // we need bold or italic, replace with the corrected typeface.
172cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        if (style != SkTypeface::kNormal) {
173cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner            tf->unref();
174cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner            tf = SkTypeface::CreateFromName(name, (SkTypeface::Style)style);
175cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        }
176cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner
177cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner        result = new FontPlatformData(tf, fontDescription.computedSize(),
1782622fb80bac785bc80c698cc7397e160b95db1e7Victoria Lease                            (style & SkTypeface::kBold),
179295fd960c28a38cfa7a28d4b8f68f474394f7fb0claireho                            (style & SkTypeface::kItalic) && !tf->isItalic(),
180295fd960c28a38cfa7a28d4b8f68f474394f7fb0claireho                            fontDescription.orientation(),
181295fd960c28a38cfa7a28d4b8f68f474394f7fb0claireho                            fontDescription.textOrientation());
182cbcc214ef5f642d9b14ee5a542cc573441e6fb43Russell Brenner    }
183fd0bf63b4addb1bcb90de078bbd4700e2545a69fRussell Brenner
1848e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    tf->unref();
1858e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    sk_free(storage);
1868e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return result;
1878e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1888e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1898e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // new as of SVN change 36269, Sept 8, 2008
1908e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectvoid FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks)
1918e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1928e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // Don't understand this yet, but it seems safe to leave unimplemented
1938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1948e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1958e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
196