blob: e1e334836cd5ae62364f46fd49a57657a55329a1 [file] [log] [blame]
Andreas Huberbfb9fb12009-12-03 11:31:19 -08001/*
2 * Copyright (C) 2009 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#include <media/mediascanner.h>
18
19#include <utils/StringArray.h>
20
21#include "autodetect.h"
22#include "unicode/ucnv.h"
23#include "unicode/ustring.h"
24
25namespace android {
26
27MediaScannerClient::MediaScannerClient()
28 : mNames(NULL),
29 mValues(NULL),
30 mLocaleEncoding(kEncodingNone)
31{
32}
33
34MediaScannerClient::~MediaScannerClient()
35{
36 delete mNames;
37 delete mValues;
38}
39
40void MediaScannerClient::setLocale(const char* locale)
41{
42 if (!locale) return;
43
44 if (!strncmp(locale, "ja", 2))
45 mLocaleEncoding = kEncodingShiftJIS;
46 else if (!strncmp(locale, "ko", 2))
47 mLocaleEncoding = kEncodingEUCKR;
48 else if (!strncmp(locale, "zh", 2)) {
49 if (!strcmp(locale, "zh_CN")) {
50 // simplified chinese for mainland China
51 mLocaleEncoding = kEncodingGBK;
52 } else {
53 // assume traditional for non-mainland Chinese locales (Taiwan, Hong Kong, Singapore)
54 mLocaleEncoding = kEncodingBig5;
55 }
56 }
57}
58
59void MediaScannerClient::beginFile()
60{
61 mNames = new StringArray;
62 mValues = new StringArray;
63}
64
Jeff Brown2c70d4a2011-07-20 16:38:43 -070065status_t MediaScannerClient::addStringTag(const char* name, const char* value)
Andreas Huberbfb9fb12009-12-03 11:31:19 -080066{
Marco Nelissendea009a2010-03-11 13:37:46 -080067 if (mLocaleEncoding != kEncodingNone) {
68 // don't bother caching strings that are all ASCII.
69 // call handleStringTag directly instead.
70 // check to see if value (which should be utf8) has any non-ASCII characters
71 bool nonAscii = false;
72 const char* chp = value;
73 char ch;
74 while ((ch = *chp++)) {
75 if (ch & 0x80) {
76 nonAscii = true;
77 break;
78 }
Andreas Huberbfb9fb12009-12-03 11:31:19 -080079 }
Andreas Huberbfb9fb12009-12-03 11:31:19 -080080
Marco Nelissendea009a2010-03-11 13:37:46 -080081 if (nonAscii) {
82 // save the strings for later so they can be used for native encoding detection
83 mNames->push_back(name);
84 mValues->push_back(value);
Marco Nelissen1cad1522011-11-18 13:10:56 -080085 return OK;
Marco Nelissendea009a2010-03-11 13:37:46 -080086 }
87 // else fall through
Marco Nelissenb45b0842010-02-10 09:56:05 -080088 }
Marco Nelissenb45b0842010-02-10 09:56:05 -080089
Andreas Huberbfb9fb12009-12-03 11:31:19 -080090 // autodetection is not necessary, so no need to cache the values
91 // pass directly to the client instead
92 return handleStringTag(name, value);
93}
94
95static uint32_t possibleEncodings(const char* s)
96{
97 uint32_t result = kEncodingAll;
98 // if s contains a native encoding, then it was mistakenly encoded in utf8 as if it were latin-1
99 // so we need to reverse the latin-1 -> utf8 conversion to get the native chars back
100 uint8_t ch1, ch2;
101 uint8_t* chp = (uint8_t *)s;
102
103 while ((ch1 = *chp++)) {
104 if (ch1 & 0x80) {
105 ch2 = *chp++;
106 ch1 = ((ch1 << 6) & 0xC0) | (ch2 & 0x3F);
107 // ch1 is now the first byte of the potential native char
108
109 ch2 = *chp++;
110 if (ch2 & 0x80)
111 ch2 = ((ch2 << 6) & 0xC0) | (*chp++ & 0x3F);
112 // ch2 is now the second byte of the potential native char
113 int ch = (int)ch1 << 8 | (int)ch2;
114 result &= findPossibleEncodings(ch);
115 }
116 // else ASCII character, which could be anything
117 }
118
119 return result;
120}
121
122void MediaScannerClient::convertValues(uint32_t encoding)
123{
124 const char* enc = NULL;
125 switch (encoding) {
126 case kEncodingShiftJIS:
127 enc = "shift-jis";
128 break;
129 case kEncodingGBK:
130 enc = "gbk";
131 break;
132 case kEncodingBig5:
133 enc = "Big5";
134 break;
135 case kEncodingEUCKR:
136 enc = "EUC-KR";
137 break;
138 }
139
140 if (enc) {
141 UErrorCode status = U_ZERO_ERROR;
142
143 UConverter *conv = ucnv_open(enc, &status);
144 if (U_FAILURE(status)) {
Glenn Kasten0765c442012-01-27 15:24:38 -0800145 ALOGE("could not create UConverter for %s", enc);
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800146 return;
147 }
148 UConverter *utf8Conv = ucnv_open("UTF-8", &status);
149 if (U_FAILURE(status)) {
Glenn Kasten0765c442012-01-27 15:24:38 -0800150 ALOGE("could not create UConverter for UTF-8");
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800151 ucnv_close(conv);
152 return;
153 }
154
155 // for each value string, convert from native encoding to UTF-8
156 for (int i = 0; i < mNames->size(); i++) {
157 // first we need to untangle the utf8 and convert it back to the original bytes
158 // since we are reducing the length of the string, we can do this in place
159 uint8_t* src = (uint8_t *)mValues->getEntry(i);
160 int len = strlen((char *)src);
161 uint8_t* dest = src;
162
163 uint8_t uch;
164 while ((uch = *src++)) {
165 if (uch & 0x80)
166 *dest++ = ((uch << 6) & 0xC0) | (*src++ & 0x3F);
167 else
168 *dest++ = uch;
169 }
170 *dest = 0;
171
172 // now convert from native encoding to UTF-8
173 const char* source = mValues->getEntry(i);
174 int targetLength = len * 3 + 1;
175 char* buffer = new char[targetLength];
Glenn Kasten80f3fc22012-01-04 12:38:13 -0800176 // don't normally check for NULL, but in this case targetLength may be large
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800177 if (!buffer)
178 break;
179 char* target = buffer;
180
181 ucnv_convertEx(utf8Conv, conv, &target, target + targetLength,
182 &source, (const char *)dest, NULL, NULL, NULL, NULL, TRUE, TRUE, &status);
183 if (U_FAILURE(status)) {
Glenn Kasten0765c442012-01-27 15:24:38 -0800184 ALOGE("ucnv_convertEx failed: %d", status);
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800185 mValues->setEntry(i, "???");
186 } else {
187 // zero terminate
188 *target = 0;
189 mValues->setEntry(i, buffer);
190 }
191
192 delete[] buffer;
193 }
194
195 ucnv_close(conv);
196 ucnv_close(utf8Conv);
197 }
198}
199
200void MediaScannerClient::endFile()
201{
Marco Nelissendea009a2010-03-11 13:37:46 -0800202 if (mLocaleEncoding != kEncodingNone) {
203 int size = mNames->size();
204 uint32_t encoding = kEncodingAll;
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800205
Marco Nelissendea009a2010-03-11 13:37:46 -0800206 // compute a bit mask containing all possible encodings
207 for (int i = 0; i < mNames->size(); i++)
208 encoding &= possibleEncodings(mValues->getEntry(i));
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800209
Marco Nelissendea009a2010-03-11 13:37:46 -0800210 // if the locale encoding matches, then assume we have a native encoding.
211 if (encoding & mLocaleEncoding)
212 convertValues(mLocaleEncoding);
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800213
Marco Nelissendea009a2010-03-11 13:37:46 -0800214 // finally, push all name/value pairs to the client
215 for (int i = 0; i < mNames->size(); i++) {
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700216 status_t status = handleStringTag(mNames->getEntry(i), mValues->getEntry(i));
217 if (status) {
Marco Nelissendea009a2010-03-11 13:37:46 -0800218 break;
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700219 }
Marco Nelissendea009a2010-03-11 13:37:46 -0800220 }
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800221 }
222 // else addStringTag() has done all the work so we have nothing to do
223
224 delete mNames;
225 delete mValues;
226 mNames = NULL;
227 mValues = NULL;
228}
229
230} // namespace android