1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2012 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include <array>
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "src/sfnt/SkOTUtils.h"
11*c8dee2aaSAndroid Build Coastguard Worker
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkEndian.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkAdvancedTypefaceMetrics.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sfnt/SkOTTableTypes.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sfnt/SkOTTable_head.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sfnt/SkOTTable_name.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sfnt/SkSFNTHeader.h"
21*c8dee2aaSAndroid Build Coastguard Worker
CalcTableChecksum(SK_OT_ULONG * data,size_t length)22*c8dee2aaSAndroid Build Coastguard Worker uint32_t SkOTUtils::CalcTableChecksum(SK_OT_ULONG *data, size_t length) {
23*c8dee2aaSAndroid Build Coastguard Worker uint32_t sum = 0;
24*c8dee2aaSAndroid Build Coastguard Worker SK_OT_ULONG *dataEnd = data + ((length + 3) & ~3) / sizeof(SK_OT_ULONG);
25*c8dee2aaSAndroid Build Coastguard Worker for (; data < dataEnd; ++data) {
26*c8dee2aaSAndroid Build Coastguard Worker sum += SkEndian_SwapBE32(*data);
27*c8dee2aaSAndroid Build Coastguard Worker }
28*c8dee2aaSAndroid Build Coastguard Worker return sum;
29*c8dee2aaSAndroid Build Coastguard Worker }
30*c8dee2aaSAndroid Build Coastguard Worker
RenameFont(SkStreamAsset * fontData,const char * fontName,int fontNameLen)31*c8dee2aaSAndroid Build Coastguard Worker SkData* SkOTUtils::RenameFont(SkStreamAsset* fontData, const char* fontName, int fontNameLen) {
32*c8dee2aaSAndroid Build Coastguard Worker
33*c8dee2aaSAndroid Build Coastguard Worker // Get the sfnt header.
34*c8dee2aaSAndroid Build Coastguard Worker SkSFNTHeader sfntHeader;
35*c8dee2aaSAndroid Build Coastguard Worker if (fontData->read(&sfntHeader, sizeof(sfntHeader)) < sizeof(sfntHeader)) {
36*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
37*c8dee2aaSAndroid Build Coastguard Worker }
38*c8dee2aaSAndroid Build Coastguard Worker
39*c8dee2aaSAndroid Build Coastguard Worker // Find the existing 'name' table.
40*c8dee2aaSAndroid Build Coastguard Worker int tableIndex;
41*c8dee2aaSAndroid Build Coastguard Worker SkSFNTHeader::TableDirectoryEntry tableEntry;
42*c8dee2aaSAndroid Build Coastguard Worker int numTables = SkEndian_SwapBE16(sfntHeader.numTables);
43*c8dee2aaSAndroid Build Coastguard Worker for (tableIndex = 0; tableIndex < numTables; ++tableIndex) {
44*c8dee2aaSAndroid Build Coastguard Worker if (fontData->read(&tableEntry, sizeof(tableEntry)) < sizeof(tableEntry)) {
45*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
46*c8dee2aaSAndroid Build Coastguard Worker }
47*c8dee2aaSAndroid Build Coastguard Worker if (SkOTTableName::TAG == tableEntry.tag) {
48*c8dee2aaSAndroid Build Coastguard Worker break;
49*c8dee2aaSAndroid Build Coastguard Worker }
50*c8dee2aaSAndroid Build Coastguard Worker }
51*c8dee2aaSAndroid Build Coastguard Worker if (tableIndex == numTables) {
52*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
53*c8dee2aaSAndroid Build Coastguard Worker }
54*c8dee2aaSAndroid Build Coastguard Worker
55*c8dee2aaSAndroid Build Coastguard Worker if (!fontData->rewind()) {
56*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
57*c8dee2aaSAndroid Build Coastguard Worker }
58*c8dee2aaSAndroid Build Coastguard Worker
59*c8dee2aaSAndroid Build Coastguard Worker // The required 'name' record types: Family, Style, Unique, Full and PostScript.
60*c8dee2aaSAndroid Build Coastguard Worker static constexpr std::array<SkOTTableName::Record::NameID::Predefined::Value, 5> names{{
61*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName::Record::NameID::Predefined::FontFamilyName,
62*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName::Record::NameID::Predefined::FontSubfamilyName,
63*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName::Record::NameID::Predefined::UniqueFontIdentifier,
64*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName::Record::NameID::Predefined::FullFontName,
65*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName::Record::NameID::Predefined::PostscriptName,
66*c8dee2aaSAndroid Build Coastguard Worker }};
67*c8dee2aaSAndroid Build Coastguard Worker
68*c8dee2aaSAndroid Build Coastguard Worker // GDI will not use a Symbol cmap table if there is no Symbol encoded name.
69*c8dee2aaSAndroid Build Coastguard Worker static constexpr std::array<SkOTTableName::Record::EncodingID::Windows::Value, 2> encodings{{
70*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName::Record::EncodingID::Windows::Symbol,
71*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2,
72*c8dee2aaSAndroid Build Coastguard Worker }};
73*c8dee2aaSAndroid Build Coastguard Worker
74*c8dee2aaSAndroid Build Coastguard Worker // Copy the data, leaving out the old name table.
75*c8dee2aaSAndroid Build Coastguard Worker // In theory, we could also remove the DSIG table if it exists.
76*c8dee2aaSAndroid Build Coastguard Worker size_t nameTableLogicalSize = sizeof(SkOTTableName)
77*c8dee2aaSAndroid Build Coastguard Worker + (encodings.size() * names.size() * sizeof(SkOTTableName::Record))
78*c8dee2aaSAndroid Build Coastguard Worker + (fontNameLen * sizeof(SK_OT_USHORT));
79*c8dee2aaSAndroid Build Coastguard Worker size_t nameTablePhysicalSize = (nameTableLogicalSize + 3) & ~3; // Rounded up to a multiple of 4.
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker size_t oldNameTablePhysicalSize = (SkEndian_SwapBE32(tableEntry.logicalLength) + 3) & ~3; // Rounded up to a multiple of 4.
82*c8dee2aaSAndroid Build Coastguard Worker size_t oldNameTableOffset = SkEndian_SwapBE32(tableEntry.offset);
83*c8dee2aaSAndroid Build Coastguard Worker
84*c8dee2aaSAndroid Build Coastguard Worker //originalDataSize is the size of the original data without the name table.
85*c8dee2aaSAndroid Build Coastguard Worker size_t originalDataSize = fontData->getLength() - oldNameTablePhysicalSize;
86*c8dee2aaSAndroid Build Coastguard Worker size_t newDataSize = originalDataSize + nameTablePhysicalSize;
87*c8dee2aaSAndroid Build Coastguard Worker
88*c8dee2aaSAndroid Build Coastguard Worker auto rewrittenFontData = SkData::MakeUninitialized(newDataSize);
89*c8dee2aaSAndroid Build Coastguard Worker SK_OT_BYTE* data = static_cast<SK_OT_BYTE*>(rewrittenFontData->writable_data());
90*c8dee2aaSAndroid Build Coastguard Worker
91*c8dee2aaSAndroid Build Coastguard Worker if (fontData->read(data, oldNameTableOffset) < oldNameTableOffset) {
92*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
93*c8dee2aaSAndroid Build Coastguard Worker }
94*c8dee2aaSAndroid Build Coastguard Worker if (fontData->skip(oldNameTablePhysicalSize) < oldNameTablePhysicalSize) {
95*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
96*c8dee2aaSAndroid Build Coastguard Worker }
97*c8dee2aaSAndroid Build Coastguard Worker if (fontData->read(data + oldNameTableOffset, originalDataSize - oldNameTableOffset) < originalDataSize - oldNameTableOffset) {
98*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
99*c8dee2aaSAndroid Build Coastguard Worker }
100*c8dee2aaSAndroid Build Coastguard Worker
101*c8dee2aaSAndroid Build Coastguard Worker //Fix up the offsets of the directory entries after the old 'name' table entry.
102*c8dee2aaSAndroid Build Coastguard Worker SkSFNTHeader::TableDirectoryEntry* currentEntry = reinterpret_cast<SkSFNTHeader::TableDirectoryEntry*>(data + sizeof(SkSFNTHeader));
103*c8dee2aaSAndroid Build Coastguard Worker SkSFNTHeader::TableDirectoryEntry* endEntry = currentEntry + numTables;
104*c8dee2aaSAndroid Build Coastguard Worker SkSFNTHeader::TableDirectoryEntry* headTableEntry = nullptr;
105*c8dee2aaSAndroid Build Coastguard Worker for (; currentEntry < endEntry; ++currentEntry) {
106*c8dee2aaSAndroid Build Coastguard Worker uint32_t oldOffset = SkEndian_SwapBE32(currentEntry->offset);
107*c8dee2aaSAndroid Build Coastguard Worker if (oldOffset > oldNameTableOffset) {
108*c8dee2aaSAndroid Build Coastguard Worker currentEntry->offset = SkEndian_SwapBE32(SkToU32(oldOffset - oldNameTablePhysicalSize));
109*c8dee2aaSAndroid Build Coastguard Worker }
110*c8dee2aaSAndroid Build Coastguard Worker
111*c8dee2aaSAndroid Build Coastguard Worker if (SkOTTableHead::TAG == currentEntry->tag) {
112*c8dee2aaSAndroid Build Coastguard Worker headTableEntry = currentEntry;
113*c8dee2aaSAndroid Build Coastguard Worker }
114*c8dee2aaSAndroid Build Coastguard Worker }
115*c8dee2aaSAndroid Build Coastguard Worker
116*c8dee2aaSAndroid Build Coastguard Worker // Make the table directory entry point to the new 'name' table.
117*c8dee2aaSAndroid Build Coastguard Worker SkSFNTHeader::TableDirectoryEntry* nameTableEntry = reinterpret_cast<SkSFNTHeader::TableDirectoryEntry*>(data + sizeof(SkSFNTHeader)) + tableIndex;
118*c8dee2aaSAndroid Build Coastguard Worker nameTableEntry->logicalLength = SkEndian_SwapBE32(SkToU32(nameTableLogicalSize));
119*c8dee2aaSAndroid Build Coastguard Worker nameTableEntry->offset = SkEndian_SwapBE32(SkToU32(originalDataSize));
120*c8dee2aaSAndroid Build Coastguard Worker
121*c8dee2aaSAndroid Build Coastguard Worker // Write the new 'name' table after the original font data.
122*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName* nameTable = reinterpret_cast<SkOTTableName*>(data + originalDataSize);
123*c8dee2aaSAndroid Build Coastguard Worker unsigned short stringOffset = sizeof(SkOTTableName) + (encodings.size() * names.size() * sizeof(SkOTTableName::Record));
124*c8dee2aaSAndroid Build Coastguard Worker nameTable->format = SkOTTableName::format_0;
125*c8dee2aaSAndroid Build Coastguard Worker nameTable->count = SkEndian_SwapBE16(encodings.size() * names.size());
126*c8dee2aaSAndroid Build Coastguard Worker nameTable->stringOffset = SkEndian_SwapBE16(stringOffset);
127*c8dee2aaSAndroid Build Coastguard Worker
128*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName::Record* nameRecord = reinterpret_cast<SkOTTableName::Record*>(data + originalDataSize + sizeof(SkOTTableName));
129*c8dee2aaSAndroid Build Coastguard Worker for (const auto& encoding : encodings) {
130*c8dee2aaSAndroid Build Coastguard Worker for (const auto& name : names) {
131*c8dee2aaSAndroid Build Coastguard Worker nameRecord->platformID.value = SkOTTableName::Record::PlatformID::Windows;
132*c8dee2aaSAndroid Build Coastguard Worker nameRecord->encodingID.windows.value = encoding;
133*c8dee2aaSAndroid Build Coastguard Worker nameRecord->languageID.windows.value = SkOTTableName::Record::LanguageID::Windows::English_UnitedStates;
134*c8dee2aaSAndroid Build Coastguard Worker nameRecord->nameID.predefined.value = name;
135*c8dee2aaSAndroid Build Coastguard Worker nameRecord->offset = SkEndian_SwapBE16(0);
136*c8dee2aaSAndroid Build Coastguard Worker nameRecord->length = SkEndian_SwapBE16(SkToU16(fontNameLen * sizeof(SK_OT_USHORT)));
137*c8dee2aaSAndroid Build Coastguard Worker ++nameRecord;
138*c8dee2aaSAndroid Build Coastguard Worker }
139*c8dee2aaSAndroid Build Coastguard Worker }
140*c8dee2aaSAndroid Build Coastguard Worker
141*c8dee2aaSAndroid Build Coastguard Worker SK_OT_USHORT* nameString = reinterpret_cast<SK_OT_USHORT*>(data + originalDataSize + stringOffset);
142*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < fontNameLen; ++i) {
143*c8dee2aaSAndroid Build Coastguard Worker nameString[i] = SkEndian_SwapBE16(fontName[i]);
144*c8dee2aaSAndroid Build Coastguard Worker }
145*c8dee2aaSAndroid Build Coastguard Worker
146*c8dee2aaSAndroid Build Coastguard Worker unsigned char* logical = data + originalDataSize + nameTableLogicalSize;
147*c8dee2aaSAndroid Build Coastguard Worker unsigned char* physical = data + originalDataSize + nameTablePhysicalSize;
148*c8dee2aaSAndroid Build Coastguard Worker for (; logical < physical; ++logical) {
149*c8dee2aaSAndroid Build Coastguard Worker *logical = 0;
150*c8dee2aaSAndroid Build Coastguard Worker }
151*c8dee2aaSAndroid Build Coastguard Worker
152*c8dee2aaSAndroid Build Coastguard Worker // Update the table checksum in the directory entry.
153*c8dee2aaSAndroid Build Coastguard Worker nameTableEntry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum(reinterpret_cast<SK_OT_ULONG*>(nameTable), nameTableLogicalSize));
154*c8dee2aaSAndroid Build Coastguard Worker
155*c8dee2aaSAndroid Build Coastguard Worker // Update the checksum adjustment in the head table.
156*c8dee2aaSAndroid Build Coastguard Worker if (headTableEntry) {
157*c8dee2aaSAndroid Build Coastguard Worker size_t headTableOffset = SkEndian_SwapBE32(headTableEntry->offset);
158*c8dee2aaSAndroid Build Coastguard Worker if (headTableOffset + sizeof(SkOTTableHead) < originalDataSize) {
159*c8dee2aaSAndroid Build Coastguard Worker SkOTTableHead* headTable = reinterpret_cast<SkOTTableHead*>(data + headTableOffset);
160*c8dee2aaSAndroid Build Coastguard Worker headTable->checksumAdjustment = SkEndian_SwapBE32(0);
161*c8dee2aaSAndroid Build Coastguard Worker uint32_t unadjustedFontChecksum = SkOTUtils::CalcTableChecksum(reinterpret_cast<SK_OT_ULONG*>(data), originalDataSize + nameTablePhysicalSize);
162*c8dee2aaSAndroid Build Coastguard Worker headTable->checksumAdjustment = SkEndian_SwapBE32(SkOTTableHead::fontChecksum - unadjustedFontChecksum);
163*c8dee2aaSAndroid Build Coastguard Worker }
164*c8dee2aaSAndroid Build Coastguard Worker }
165*c8dee2aaSAndroid Build Coastguard Worker
166*c8dee2aaSAndroid Build Coastguard Worker return rewrittenFontData.release();
167*c8dee2aaSAndroid Build Coastguard Worker }
168*c8dee2aaSAndroid Build Coastguard Worker
169*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkOTUtils::LocalizedStrings_NameTable>
Make(const SkTypeface & typeface,SK_OT_USHORT types[],int typesCount)170*c8dee2aaSAndroid Build Coastguard Worker SkOTUtils::LocalizedStrings_NameTable::Make(const SkTypeface& typeface,
171*c8dee2aaSAndroid Build Coastguard Worker SK_OT_USHORT types[],
172*c8dee2aaSAndroid Build Coastguard Worker int typesCount)
173*c8dee2aaSAndroid Build Coastguard Worker {
174*c8dee2aaSAndroid Build Coastguard Worker static const SkFontTableTag nameTag = SkSetFourByteTag('n','a','m','e');
175*c8dee2aaSAndroid Build Coastguard Worker size_t nameTableSize = typeface.getTableSize(nameTag);
176*c8dee2aaSAndroid Build Coastguard Worker if (0 == nameTableSize) {
177*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
178*c8dee2aaSAndroid Build Coastguard Worker }
179*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<uint8_t[]> nameTableData(new uint8_t[nameTableSize]);
180*c8dee2aaSAndroid Build Coastguard Worker size_t copied = typeface.getTableData(nameTag, 0, nameTableSize, nameTableData.get());
181*c8dee2aaSAndroid Build Coastguard Worker if (copied != nameTableSize) {
182*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
183*c8dee2aaSAndroid Build Coastguard Worker }
184*c8dee2aaSAndroid Build Coastguard Worker
185*c8dee2aaSAndroid Build Coastguard Worker return sk_sp<SkOTUtils::LocalizedStrings_NameTable>(
186*c8dee2aaSAndroid Build Coastguard Worker new SkOTUtils::LocalizedStrings_NameTable(std::move(nameTableData), nameTableSize,
187*c8dee2aaSAndroid Build Coastguard Worker types, typesCount));
188*c8dee2aaSAndroid Build Coastguard Worker }
189*c8dee2aaSAndroid Build Coastguard Worker
190*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkOTUtils::LocalizedStrings_NameTable>
MakeForFamilyNames(const SkTypeface & typeface)191*c8dee2aaSAndroid Build Coastguard Worker SkOTUtils::LocalizedStrings_NameTable::MakeForFamilyNames(const SkTypeface& typeface) {
192*c8dee2aaSAndroid Build Coastguard Worker return Make(typeface,
193*c8dee2aaSAndroid Build Coastguard Worker SkOTUtils::LocalizedStrings_NameTable::familyNameTypes,
194*c8dee2aaSAndroid Build Coastguard Worker std::size(SkOTUtils::LocalizedStrings_NameTable::familyNameTypes));
195*c8dee2aaSAndroid Build Coastguard Worker }
196*c8dee2aaSAndroid Build Coastguard Worker
next(SkTypeface::LocalizedString * localizedString)197*c8dee2aaSAndroid Build Coastguard Worker bool SkOTUtils::LocalizedStrings_NameTable::next(SkTypeface::LocalizedString* localizedString) {
198*c8dee2aaSAndroid Build Coastguard Worker do {
199*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName::Iterator::Record record;
200*c8dee2aaSAndroid Build Coastguard Worker if (fFamilyNameIter.next(record)) {
201*c8dee2aaSAndroid Build Coastguard Worker localizedString->fString = record.name;
202*c8dee2aaSAndroid Build Coastguard Worker localizedString->fLanguage = record.language;
203*c8dee2aaSAndroid Build Coastguard Worker return true;
204*c8dee2aaSAndroid Build Coastguard Worker }
205*c8dee2aaSAndroid Build Coastguard Worker if (fTypesCount == fTypesIndex + 1) {
206*c8dee2aaSAndroid Build Coastguard Worker return false;
207*c8dee2aaSAndroid Build Coastguard Worker }
208*c8dee2aaSAndroid Build Coastguard Worker ++fTypesIndex;
209*c8dee2aaSAndroid Build Coastguard Worker fFamilyNameIter.reset(fTypes[fTypesIndex]);
210*c8dee2aaSAndroid Build Coastguard Worker } while (true);
211*c8dee2aaSAndroid Build Coastguard Worker }
212*c8dee2aaSAndroid Build Coastguard Worker
213*c8dee2aaSAndroid Build Coastguard Worker SK_OT_USHORT SkOTUtils::LocalizedStrings_NameTable::familyNameTypes[3] = {
214*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName::Record::NameID::Predefined::FontFamilyName,
215*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName::Record::NameID::Predefined::PreferredFamily,
216*c8dee2aaSAndroid Build Coastguard Worker SkOTTableName::Record::NameID::Predefined::WWSFamilyName,
217*c8dee2aaSAndroid Build Coastguard Worker };
218*c8dee2aaSAndroid Build Coastguard Worker
SetAdvancedTypefaceFlags(SkOTTableOS2_V4::Type fsType,SkAdvancedTypefaceMetrics * info)219*c8dee2aaSAndroid Build Coastguard Worker void SkOTUtils::SetAdvancedTypefaceFlags(SkOTTableOS2_V4::Type fsType,
220*c8dee2aaSAndroid Build Coastguard Worker SkAdvancedTypefaceMetrics* info) {
221*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(info);
222*c8dee2aaSAndroid Build Coastguard Worker // The logic should be identical to SkTypeface_FreeType::onGetAdvancedMetrics().
223*c8dee2aaSAndroid Build Coastguard Worker if (fsType.raw.value != 0) {
224*c8dee2aaSAndroid Build Coastguard Worker if (SkToBool(fsType.field.Restricted) || SkToBool(fsType.field.Bitmap)) {
225*c8dee2aaSAndroid Build Coastguard Worker info->fFlags |= SkAdvancedTypefaceMetrics::kNotEmbeddable_FontFlag;
226*c8dee2aaSAndroid Build Coastguard Worker }
227*c8dee2aaSAndroid Build Coastguard Worker if (SkToBool(fsType.field.NoSubsetting)) {
228*c8dee2aaSAndroid Build Coastguard Worker info->fFlags |= SkAdvancedTypefaceMetrics::kNotSubsettable_FontFlag;
229*c8dee2aaSAndroid Build Coastguard Worker }
230*c8dee2aaSAndroid Build Coastguard Worker }
231*c8dee2aaSAndroid Build Coastguard Worker }
232