NativeString* AssetManagerGlue::getResourceName(int resid) { ResTable::resource_name name; if (!getResources().getResourceName(resid, &name)) { return NULL; } String16 str; if (name.package != NULL) { str.setTo(name.package, name.packageLen); } if (name.type != NULL) { if (str.size() > 0) { char16_t div = ':'; str.append(&div, 1); } str.append(name.type, name.typeLen); } if (name.name != NULL) { if (str.size() > 0) { char16_t div = '/'; str.append(&div, 1); } str.append(name.name, name.nameLen); } return new NativeString(str); }
ssize_t StringPool::add(const String16& ident, const String16& value, bool mergeDuplicates) { if (ident.size() > 0) { ssize_t idx = mIdents.valueFor(ident); if (idx >= 0) { fprintf(stderr, "ERROR: Duplicate string identifier %s\n", String8(mEntries[idx].value).string()); return UNKNOWN_ERROR; } } ssize_t vidx = mValues.indexOfKey(value); ssize_t pos = vidx >= 0 ? mValues.valueAt(vidx) : -1; ssize_t eidx = pos >= 0 ? mEntryArray.itemAt(pos) : -1; if (eidx < 0) { eidx = mEntries.add(entry(value)); if (eidx < 0) { fprintf(stderr, "Failure adding string %s\n", String8(value).string()); return eidx; } } const bool first = vidx < 0; if (first || !mergeDuplicates) { pos = mEntryArray.add(eidx); if (first) { vidx = mValues.add(value, pos); const size_t N = mEntryArrayToValues.size(); for (size_t i=0; i<N; i++) { size_t& e = mEntryArrayToValues.editItemAt(i); if ((ssize_t)e >= vidx) { e++; } } } mEntryArrayToValues.add(vidx); if (!mSorted) { entry& ent = mEntries.editItemAt(eidx); ent.indices.add(pos); } } if (ident.size() > 0) { mIdents.add(ident, vidx); } NOISY(printf("Adding string %s to pool: pos=%d eidx=%d vidx=%d\n", String8(value).string(), pos, eidx, vidx)); return pos; }
size_t Buffer::writeString16(String16 _src, size_t _offset) { if(_offset != npos) _pf_writeOffset = _offset; else _pf_writeOffset = _pf_buffer.size(); writeInt16(_src.size(), _pf_writeOffset); for(int i = 0; i < _src.size(); ++i) writeInt16(_src[i], _offset); return _pf_writeOffset; }
TEST(OpenCVEngineTest, GetPathForUnExistVersion) { sp<IOpenCVEngine> Engine = InitConnect(); EXPECT_FALSE(NULL == Engine.get()); String16 result = Engine->GetLibPathByVersion(String16("2.5")); EXPECT_EQ(0, result.size()); }
status_t ChatSession::Say(const String16& statement) { HRESULT hr = S_OK; String16 userName; int callerPid = GetCallerPID(); _TRACE("ChatSession::Say(%s) >>",DISPLAY_STRING16(statement)); { Mutex::Autolock _lock(m_csAdviseLock); sp<IBinder> binder = m_Listeners.valueFor(callerPid); sp<IChatSessionEvents> eventSink = interface_cast<IChatSessionEvents>(binder); if(eventSink != NULL){ _TRACE("**source(cse:%p binder:%p) ",eventSink.get(),binder.get()); userName = eventSink->GetUserName(); } } if (userName.size() && CheckAccess(userName)) { { Mutex::Autolock _lock(m_csStatementLock); m_statements.add(userName + String16("\t:\t") +statement); } //Fire_OnNewStatement(userName, statement); ChatWorker::getInstance().Defer_OnNewStatement(this,userName, statement); } else hr = E_ACCESSDENIED; _TRACE("ChatSession::Say() <<hr:%x",hr); return hr; }
void ChatSession::Advise(const sp<IChatSessionEvents>& eventSink) { int callerPid = GetCallerPID(); _TRACE("ChatSession::Advise >>(%s,pid:%d)",DISPLAY_STRING16(m_wszSessionName),callerPid); if (eventSink == NULL){ _TRACE("ChatSession::Advise << E_INVALIDARG"); return; } String16 userName = eventSink->GetUserName(); if(userName.size()&& !m_bAllowAnonymousAccess){ _TRACE("ChatSession::Advise << E_ACCESSDENIED"); return; } sp<IBinder> binder = eventSink->asBinder(); binder->linkToDeath(this); _TRACE("**ADD(cse:%p binder:%p) ",eventSink.get(),binder.get()); _TRACE("add user %s",DISPLAY_STRING16(userName)); Fire_OnNewUser(userName); //ChatWorker::getInstance().Defer_OnNewUser(this,userName); Mutex::Autolock _lock(m_csAdviseLock); m_Listeners.add(callerPid,binder); _TRACE("ChatSession::Advise << S_OK"); return; }
TEST(OpenCVEngineTest, GetPathForExecHWNewVersion) { sp<IOpenCVEngine> Engine = InitConnect(); Starter.PackageManager->InstalledPackages.clear(); Starter.PackageManager->InstallVersion("241", PLATFORM_UNKNOWN, ARCH_X86); EXPECT_FALSE(NULL == Engine.get()); String16 result = Engine->GetLibPathByVersion(String16("2.4.2")); EXPECT_EQ(0, result.size()); }
TEST(OpenCVEngineTest, GetPathForInCompatiblePackage1) { sp<IOpenCVEngine> Engine = InitConnect(); Starter.PackageManager->InstalledPackages.clear(); Starter.PackageManager->InstallVersion("242", PLATFORM_UNKNOWN, ARCH_ARMv7); EXPECT_FALSE(NULL == Engine.get()); String16 result = Engine->GetLibPathByVersion(String16("2.4")); EXPECT_EQ(0, result.size()); }
TEST(OpenCVEngineTest, GetPathForExecHWNewVersion) { sp<IOpenCVEngine> Engine = InitConnect(); Starter.PackageManager->InstalledPackages.clear(); Starter.PackageManager->InstallVersion(2040100, PLATFORM_TEGRA3, ARCH_ARMv7 | FEATURES_HAS_NEON); EXPECT_FALSE(NULL == Engine.get()); String16 result = Engine->GetLibPathByVersion(String16("2.4.2")); EXPECT_EQ(0, result.size()); }
TEST(OpenCVEngineTest, GetPathFor2_4_5) { sp<IOpenCVEngine> Engine = InitConnect(); Starter.PackageManager->InstalledPackages.clear(); Starter.PackageManager->InstallVersion(2040500, PLATFORM_UNKNOWN, ARCH_ARMv7); EXPECT_FALSE(NULL == Engine.get()); String16 result = Engine->GetLibPathByVersion(String16("2.4.5")); EXPECT_EQ(0, result.size()); // 2.4.5 is not published yet }
String16 pseudo_generate_expansion(const unsigned int length) { String16 result = k_expansion_string; const char16_t* s = result.string(); if (result.size() < length) { result += String16(" "); result += pseudo_generate_expansion(length - result.size()); } else { int ext = 0; // Should contain only whole words, so looking for a space for (unsigned int i = length + 1; i < result.size(); ++i) { ++ext; if (s[i] == ' ') { break; } } result.remove(length + ext, 0); } return result; }
static String8 good_old_string(const String16& src) { String8 name8; char ch8[2]; ch8[1] = 0; for (unsigned j = 0; j < src.size(); j++) { char16_t ch = src[j]; if (ch < 128) ch8[0] = (char)ch; name8.append(ch8); } return name8; }
SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) { Mutex::Autolock _l(sLock); SensorManager* sensorManager; std::map<String16, SensorManager*>::iterator iterator = sPackageInstances.find(packageName); if (iterator != sPackageInstances.end()) { sensorManager = iterator->second; } else { String16 opPackageName = packageName; // It is possible that the calling code has no access to the package name. // In this case we will get the packages for the calling UID and pick the // first one for attributing the app op. This will work correctly for // runtime permissions as for legacy apps we will toggle the app op for // all packages in the UID. The caveat is that the operation may be attributed // to the wrong package and stats based on app ops may be slightly off. if (opPackageName.size() <= 0) { sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); if (binder != 0) { const uid_t uid = IPCThreadState::self()->getCallingUid(); Vector<String16> packages; interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages); if (!packages.isEmpty()) { opPackageName = packages[0]; } else { ALOGE("No packages for calling UID"); } } else { ALOGE("Cannot get permission service"); } } sensorManager = new SensorManager(opPackageName); // If we had no package name, we looked it up from the UID and the sensor // manager instance we created should also be mapped to the empty package // name, to avoid looking up the packages for a UID and get the same result. if (packageName.size() <= 0) { sPackageInstances.insert(std::make_pair(String16(), sensorManager)); } // Stash the per package sensor manager. sPackageInstances.insert(std::make_pair(opPackageName, sensorManager)); } return *sensorManager; }
/* BnHelloWorld */ status_t BnHelloWorld::onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { LOGE("OnTransact(%u,%u)", code, flags); CHECK_INTERFACE(IHelloWorld, data, reply); switch(code) { case HW_HELLOTHERE: { /** * Checking permissions is always a good idea. * * Note that the native client will also be granted these permissions in two cases * 1) you run the client code as root or system user. * 2) you run the client code as user who was granted this permission. * @see http://github.com/keesj/gomo/wiki/AndroidSecurity for more information **/ if (checkCallingPermission(String16("org.credil.helloworldservice.permissions.CALL_HELLOTHERE")) == false){ return PERMISSION_DENIED; } String16 str = data.readString16(); hellothere(String8(str).string()); ///reply->writeString16(str); return NO_ERROR; } break; case RETURN_INT_SHANQUAN:{ if (checkCallingPermission(String16("org.credil.helloworldservice.permissions.CALL_HELLOTHERE")) == false){ return PERMISSION_DENIED; } String16 str = data.readString16(); helloint(String8(str).string()); int tmp= str.size(); printf("servie str.size():%i\n",tmp); status_t status = reply->writeInt32(tmp); if( status == NO_ERROR) printf("file:%s,line:%i,no error\n",__FILE__,__LINE__); else printf("file:%s,line:%i, error\n",__FILE__,__LINE__); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } return NO_ERROR; }
// Send a command to the supplicant, and return the reply as a String. static jstring doStringCommand(JNIEnv* env, jstring javaCommand) { char reply[REPLY_BUF_SIZE]; ScopedUtfChars command(env, javaCommand); if (!doCommand(env, javaCommand, reply, sizeof(reply))) { return NULL; } if (DBG) ALOGD("cmd = %s, reply: %s", command.c_str(), reply); String16 str; if (strstr(command.c_str(),"BSS RANGE=")) { parseScanResults(str,reply); } else if (strstr(command.c_str(),"GET_NETWORK") && strstr(command.c_str(),"ssid") && !strstr(command.c_str(),"bssid") && !strstr(command.c_str(),"scan_ssid")){ constructSsid(str, reply); } else { str += String16((char *)reply); } return env->NewString((const jchar *)str.string(), str.size()); }
status_t String16::setTo(const String16& other, size_t len, size_t begin) { const size_t N = other.size(); if (begin >= N) { SharedBuffer::bufferFromData(mString)->release(); mString = getEmptyString(); return NO_ERROR; } if ((begin+len) > N) len = N-begin; if (begin == 0 && len == N) { setTo(other); return NO_ERROR; } if (&other == this) { LOG_ALWAYS_FATAL("Not implemented"); } return setTo(other.string()+begin, len); }
status_t String16::append(const String16& other) { const size_t myLen = size(); const size_t otherLen = other.size(); if (myLen == 0) { setTo(other); return NO_ERROR; } else if (otherLen == 0) { return NO_ERROR; } SharedBuffer* buf = SharedBuffer::bufferFromData(mString) ->editResize((myLen+otherLen+1)*sizeof(char16_t)); if (buf) { char16_t* str = (char16_t*)buf->data(); memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t)); mString = str; return NO_ERROR; } return NO_MEMORY; }
String16 getNamespaceResourcePackage(String16 namespaceUri, bool* outIsPublic) { //printf("%s starts with %s?\n", String8(namespaceUri).string(), // String8(RESOURCES_PREFIX).string()); size_t prefixSize; bool isPublic = true; if (namespaceUri.startsWith(RESOURCES_PREFIX)) { prefixSize = RESOURCES_PREFIX.size(); } else if (namespaceUri.startsWith(RESOURCES_PRV_PREFIX)) { isPublic = false; prefixSize = RESOURCES_PRV_PREFIX.size(); } else { if (outIsPublic) *outIsPublic = isPublic; // = true return String16(); } //printf("YES!\n"); //printf("namespace: %s\n", String8(String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize)).string()); if (outIsPublic) *outIsPublic = isPublic; return String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize); }
String16 pseudobidi_string(const String16& source) { const char16_t* s = source.string(); String16 result; result += k_rlm; result += k_rlo; for (size_t i=0; i<source.size(); i++) { char16_t c = s[i]; switch(c) { case ' ': result += k_pdf; result += k_rlm; result.append(&c, 1); result += k_rlm; result += k_rlo; break; default: result.append(&c, 1); break; } } result += k_pdf; result += k_rlm; return result; }
status_t ChatSession::Unadvise() { String16 userName; int callerPid = GetCallerPID(); _TRACE("ChatSession::Unadvise(%s,pid:%d)",DISPLAY_STRING16(m_wszSessionName),callerPid); { Mutex::Autolock _lock(m_csAdviseLock); sp<IBinder> binder = m_Listeners.valueFor(callerPid); sp<IChatSessionEvents> eventSink = interface_cast<IChatSessionEvents>(binder); if(eventSink != NULL){ m_Listeners.removeItem(callerPid); userName = eventSink->GetUserName(); _TRACE("**MOV(cse:%p binder:%p) ",eventSink.get(),binder.get()); } } if(userName.size()){ //ChatWorker::getInstance().Defer_OnUserLeft(this,userName); Fire_OnUserLeft(userName); } return NO_ERROR; }
/** * Converts characters so they look like they've been localized. * * Note: This leaves escape sequences untouched so they can later be * processed by ResTable::collectString in the normal way. */ String16 pseudolocalize_string(const String16& source) { const char16_t* s = source.string(); String16 result; const size_t I = source.size(); for (size_t i=0; i<I; i++) { char16_t c = s[i]; if (c == '\\') { // Escape syntax, no need to pseudolocalize if (i<I-1) { result += String16("\\"); i++; c = s[i]; switch (c) { case 'u': // this one takes up 5 chars result += String16(s+i, 5); i += 4; break; case 't': case 'n': case '#': case '@': case '?': case '"': case '\'': case '\\': default: result.append(&c, 1); break; } } else { result.append(&c, 1); } } else if (c == '%') { // Placeholder syntax, no need to pseudolocalize result += k_placeholder_open; bool end = false; result.append(&c, 1); while (!end && i < I) { ++i; c = s[i]; result.append(&c, 1); if (is_possible_normal_placeholder_end(c)) { end = true; } else if (c == 't') { ++i; c = s[i]; result.append(&c, 1); end = true; } } result += k_placeholder_close; } else if (c == '<' || c == '&') { // html syntax, no need to pseudolocalize bool tag_closed = false; while (!tag_closed && i < I) { if (c == '&') { String16 escape_text; escape_text.append(&c, 1); bool end = false; size_t htmlCodePos = i; while (!end && htmlCodePos < I) { ++htmlCodePos; c = s[htmlCodePos]; escape_text.append(&c, 1); // Valid html code if (c == ';') { end = true; i = htmlCodePos; } // Wrong html code else if (!((c == '#' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))) { end = true; } } result += escape_text; if (escape_text != String16("<")) { tag_closed = true; } continue; } if (c == '>') { tag_closed = true; result.append(&c, 1); continue; } result.append(&c, 1); i++; c = s[i]; } } else { // This is a pure text that should be pseudolocalized const char* p = pseudolocalize_char(c); if (p != NULL) { result += String16(p); } else { result.append(&c, 1); } } } return result; }
bool String16::startsWith(const String16& prefix) const { const size_t ps = prefix.size(); if (ps > size()) return false; return strzcmp16(mString, ps, prefix.string(), ps) == 0; }
jstring string16ToJstring(JNIEnv* env, String16 string) { const char16_t* str = string.string(); size_t len = string.size(); return env->NewString(reinterpret_cast<const jchar*>(str), len); }
String8::String8(const String16& o) : mString(allocFromUTF16(o.string(), o.size())) { }
status_t parseStyledString(Bundle* bundle, const char* fileName, ResXMLTree* inXml, const String16& endTag, String16* outString, Vector<StringPool::entry_style_span>* outSpans, bool pseudolocalize) { Vector<StringPool::entry_style_span> spanStack; String16 curString; String16 rawString; const char* errorMsg; int xliffDepth = 0; bool firstTime = true; size_t len; ResXMLTree::event_code_t code; while ((code=inXml->next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::TEXT) { String16 text(inXml->getText(&len)); if (firstTime && text.size() > 0) { firstTime = false; if (text.string()[0] == '@') { // If this is a resource reference, don't do the pseudoloc. pseudolocalize = false; } } if (xliffDepth == 0 && pseudolocalize) { std::string orig(String8(text).string()); std::string pseudo = pseudolocalize_string(orig); curString.append(String16(String8(pseudo.c_str()))); } else { curString.append(text); } } else if (code == ResXMLTree::START_TAG) { const String16 element16(inXml->getElementName(&len)); const String8 element8(element16); size_t nslen; const uint16_t* ns = inXml->getElementNamespace(&nslen); if (ns == NULL) { ns = (const uint16_t*)"\0\0"; nslen = 0; } const String8 nspace(String16(ns, nslen)); if (nspace == XLIFF_XMLNS) { const int N = sizeof(ALLOWED_XLIFF_ELEMENTS)/sizeof(ALLOWED_XLIFF_ELEMENTS[0]); for (int i=0; i<N; i++) { if (element8 == ALLOWED_XLIFF_ELEMENTS[i]) { xliffDepth++; // in this case, treat it like it was just text, in other words, do nothing // here and silently drop this element goto moveon; } } { SourcePos(String8(fileName), inXml->getLineNumber()).error( "Found unsupported XLIFF tag <%s>\n", element8.string()); return UNKNOWN_ERROR; } moveon: continue; } if (outSpans == NULL) { SourcePos(String8(fileName), inXml->getLineNumber()).error( "Found style tag <%s> where styles are not allowed\n", element8.string()); return UNKNOWN_ERROR; } if (!ResTable::collectString(outString, curString.string(), curString.size(), false, &errorMsg, true)) { SourcePos(String8(fileName), inXml->getLineNumber()).error("%s (in %s)\n", errorMsg, String8(curString).string()); return UNKNOWN_ERROR; } rawString.append(curString); curString = String16(); StringPool::entry_style_span span; span.name = element16; for (size_t ai=0; ai<inXml->getAttributeCount(); ai++) { span.name.append(String16(";")); const char16_t* str = inXml->getAttributeName(ai, &len); span.name.append(str, len); span.name.append(String16("=")); str = inXml->getAttributeStringValue(ai, &len); span.name.append(str, len); } //printf("Span: %s\n", String8(span.name).string()); span.span.firstChar = span.span.lastChar = outString->size(); spanStack.push(span); } else if (code == ResXMLTree::END_TAG) { size_t nslen; const uint16_t* ns = inXml->getElementNamespace(&nslen); if (ns == NULL) { ns = (const uint16_t*)"\0\0"; nslen = 0; } const String8 nspace(String16(ns, nslen)); if (nspace == XLIFF_XMLNS) { xliffDepth--; continue; } if (!ResTable::collectString(outString, curString.string(), curString.size(), false, &errorMsg, true)) { SourcePos(String8(fileName), inXml->getLineNumber()).error("%s (in %s)\n", errorMsg, String8(curString).string()); return UNKNOWN_ERROR; } rawString.append(curString); curString = String16(); if (spanStack.size() == 0) { if (strcmp16(inXml->getElementName(&len), endTag.string()) != 0) { SourcePos(String8(fileName), inXml->getLineNumber()).error( "Found tag %s where <%s> close is expected\n", String8(inXml->getElementName(&len)).string(), String8(endTag).string()); return UNKNOWN_ERROR; } break; } StringPool::entry_style_span span = spanStack.top(); String16 spanTag; ssize_t semi = span.name.findFirst(';'); if (semi >= 0) { spanTag.setTo(span.name.string(), semi); } else { spanTag.setTo(span.name); } if (strcmp16(inXml->getElementName(&len), spanTag.string()) != 0) { SourcePos(String8(fileName), inXml->getLineNumber()).error( "Found close tag %s where close tag %s is expected\n", String8(inXml->getElementName(&len)).string(), String8(spanTag).string()); return UNKNOWN_ERROR; } bool empty = true; if (outString->size() > 0) { span.span.lastChar = outString->size()-1; if (span.span.lastChar >= span.span.firstChar) { empty = false; outSpans->add(span); } } spanStack.pop(); /* * This warning seems to be just an irritation to most people, * since it is typically introduced by translators who then never * see the warning. */ if (0 && empty) { fprintf(stderr, "%s:%d: warning: empty '%s' span found in text '%s'\n", fileName, inXml->getLineNumber(), String8(spanTag).string(), String8(*outString).string()); } } else if (code == ResXMLTree::START_NAMESPACE) { // nothing } } if (code == ResXMLTree::BAD_DOCUMENT) { SourcePos(String8(fileName), inXml->getLineNumber()).error( "Error parsing XML\n"); } if (outSpans != NULL && outSpans->size() > 0) { if (curString.size() > 0) { if (!ResTable::collectString(outString, curString.string(), curString.size(), false, &errorMsg, true)) { SourcePos(String8(fileName), inXml->getLineNumber()).error( "%s (in %s)\n", errorMsg, String8(curString).string()); return UNKNOWN_ERROR; } } } else { // There is no style information, so string processing will happen // later as part of the overall type conversion. Return to the // client the raw unprocessed text. rawString.append(curString); outString->setTo(rawString); } return NO_ERROR; }
static bool getAppInfo(const String8& path, AppInfo& outInfo) { memset(&outInfo, 0, sizeof(outInfo)); AssetManager assetManager; int32_t cookie = 0; if (!assetManager.addAssetPath(path, &cookie)) { return false; } Asset* asset = assetManager.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_BUFFER); if (asset == NULL) { return false; } ResXMLTree xml; if (xml.setTo(asset->getBuffer(true), asset->getLength(), false) != NO_ERROR) { delete asset; return false; } const String16 kAndroidNamespace("http://schemas.android.com/apk/res/android"); const String16 kManifestTag("manifest"); const String16 kApplicationTag("application"); const String16 kUsesSdkTag("uses-sdk"); const String16 kVersionCodeAttr("versionCode"); const String16 kMultiArchAttr("multiArch"); const String16 kMinSdkVersionAttr("minSdkVersion"); ResXMLParser::event_code_t event; while ((event = xml.next()) != ResXMLParser::BAD_DOCUMENT && event != ResXMLParser::END_DOCUMENT) { if (event != ResXMLParser::START_TAG) { continue; } size_t len; const char16_t* name = xml.getElementName(&len); String16 name16(name, len); if (name16 == kManifestTag) { ssize_t idx = xml.indexOfAttribute( kAndroidNamespace.string(), kAndroidNamespace.size(), kVersionCodeAttr.string(), kVersionCodeAttr.size()); if (idx >= 0) { outInfo.versionCode = xml.getAttributeData(idx); } } else if (name16 == kApplicationTag) { ssize_t idx = xml.indexOfAttribute( kAndroidNamespace.string(), kAndroidNamespace.size(), kMultiArchAttr.string(), kMultiArchAttr.size()); if (idx >= 0) { outInfo.multiArch = xml.getAttributeData(idx) != 0; } } else if (name16 == kUsesSdkTag) { ssize_t idx = xml.indexOfAttribute( kAndroidNamespace.string(), kAndroidNamespace.size(), kMinSdkVersionAttr.string(), kMinSdkVersionAttr.size()); if (idx >= 0) { uint16_t type = xml.getAttributeDataType(idx); if (type >= Res_value::TYPE_FIRST_INT && type <= Res_value::TYPE_LAST_INT) { outInfo.minSdkVersion = xml.getAttributeData(idx); } else if (type == Res_value::TYPE_STRING) { String8 minSdk8(xml.getStrings().string8ObjectAt(idx)); char* endPtr; int minSdk = strtol(minSdk8.string(), &endPtr, 10); if (endPtr != minSdk8.string() + minSdk8.size()) { fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n", minSdk8.string()); } else { outInfo.minSdkVersion = minSdk; } } else { fprintf(stderr, "warning: unrecognized value for android:minSdkVersion.\n"); } } } } delete asset; return true; }