Merged PR 2342853: [MSIX SDK] reduce binary size on android by switching to use inbox java xml parser on android instead of xerces
reducing the msix sdk binary size for android by ~3.5MB by switching to use the inbox android java xml no validation parser instead of xerces. This reduces the libmsix.so from ~5.8MB to ~2.3 MB. - Need to use JNI interop for accessing the java xml parser elements. Made sure that local refs to java objects are released when done. -by default on android, it will use javaxml. For flexibility, if one still wants to use the native xerces parse, they can use the -parser-xerces build flag. - verified that androidBVT tests pass. Related work items: #18565184
This commit is contained in:
Родитель
fadcf1fa44
Коммит
08c6b2cc0c
|
@ -21,7 +21,7 @@ option(USE_STATIC_MSVC "Windows only. Pass /MT as a compiler flag to use the sta
|
|||
option(SKIP_BUNDLES "Removes bundle functionality from the MSIX SDK. Default is 'off'" OFF)
|
||||
|
||||
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel. Use the -DCMAKE_BUILD_TYPE=[option] to specify.")
|
||||
set(XML_PARSER "" CACHE STRING "Choose the type of parser, options are: [xerces, msxml6]. Use the -DXML_PARSER=[option] to specify.")
|
||||
set(XML_PARSER "" CACHE STRING "Choose the type of parser, options are: [xerces, msxml6, javaxml]. Use the -DXML_PARSER=[option] to specify.")
|
||||
|
||||
# Default version is 0.0.0
|
||||
set(VERSION_MAJOR "0")
|
||||
|
@ -149,6 +149,8 @@ if(NOT XML_PARSER)
|
|||
check_include_file_cxx(msxml6.h HAVE_MSXML6)
|
||||
if(HAVE_MSXML6)
|
||||
set(XML_PARSER msxml6 CACHE STRING "Using msxml6." FORCE)
|
||||
elseif (AOSP)
|
||||
set(XML_PARSER javaxml CACHE STRING "Using javaxml." FORCE)
|
||||
else()
|
||||
set(XML_PARSER xerces CACHE STRING "Using xerces" FORCE)
|
||||
endif()
|
||||
|
|
16
makeaosp.sh
16
makeaosp.sh
|
@ -9,10 +9,12 @@ sdk=
|
|||
sdkver=24
|
||||
dataCompressionLib=NDK_libz
|
||||
bundle=off
|
||||
xmlparserLib=javaxml
|
||||
xmlparser="-DXML_PARSER=javaxml"
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "usage: makeaosp [-ndk ndk_path] [-arch arch] [-ndkver ndk_version] [-sdk sdk_path] [-sdkver sdk_version] [-b buildType] [-xzlib]"
|
||||
echo "usage: makeaosp [-ndk ndk_path] [-arch arch] [-ndkver ndk_version] [-sdk sdk_path] [-sdkver sdk_version] [-b buildType] [-xzlib] [-sb] [-parser-xerces]"
|
||||
echo $'\t' "-ndk Path to Android NDK. Default $ANDROID_NDK_ROOT or $ANDROID_NDK"
|
||||
echo $'\t' "-ndkver Android NDK version. Default/minimum 19."
|
||||
echo $'\t' "-sdk Path to Android SDK. Default $ANDROID_HOME."
|
||||
|
@ -20,6 +22,7 @@ usage()
|
|||
echo $'\t' "-arch Architecture ABI. Default x86"
|
||||
echo $'\t' "-b Build type. Default MinSizeRel"
|
||||
echo $'\t' "-xzlib Use MSIX SDK Zlib instead of inbox libz.so"
|
||||
echo $'\t' "-parser-xerces Use xerces xml parser instead of default javaxml"
|
||||
echo $'\t' "-sb Skip bundle support."
|
||||
}
|
||||
|
||||
|
@ -32,6 +35,7 @@ printsetup()
|
|||
echo "Architecture:" $arch
|
||||
echo "Build Type:" $build
|
||||
echo "Zlib:" $dataCompressionLib
|
||||
echo "parser:" $xmlparserLib
|
||||
echo "Skip bundle support:" $bundle
|
||||
}
|
||||
|
||||
|
@ -55,6 +59,9 @@ while [ "$1" != "" ]; do
|
|||
-xzlib ) dataCompressionLib=MSIX_SDK_zlib
|
||||
zlib="-DUSE_MSIX_SDK_ZLIB=on"
|
||||
;;
|
||||
-parser-xerces ) xmlparserLib=xerces
|
||||
xmlparser="-DXML_PARSER=xerces"
|
||||
;;
|
||||
-sdk ) shift
|
||||
sdk=$1
|
||||
;;
|
||||
|
@ -101,5 +108,8 @@ cmake -DCMAKE_SYSTEM_NAME=Android \
|
|||
-DCMAKE_ANDROID_ARCH_ABI="$arch" \
|
||||
-DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang \
|
||||
-DCMAKE_ANDROID_STL_TYPE=c++_shared \
|
||||
-DCMAKE_BUILD_TYPE="$build" $zlib -DSKIP_BUNDLES=$bundle -DAOSP=on ..
|
||||
make
|
||||
-DCMAKE_BUILD_TYPE="$build" \
|
||||
-DSKIP_BUNDLES=$bundle \
|
||||
$xmlparser \
|
||||
$zlib -DAOSP=on ..
|
||||
make
|
|
@ -11,6 +11,7 @@ cmake -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang ^
|
|||
-DCMAKE_ANDROID_STL_TYPE=c++_shared ^
|
||||
-DCMAKE_BUILD_TYPE=Release ^
|
||||
-DAOSP=on ^
|
||||
-DXML_PARSER=javaxml ^
|
||||
-G"Ninja" ..
|
||||
|
||||
ninja
|
|
@ -148,4 +148,48 @@ namespace MSIX {
|
|||
output[publisherIdSize] = '\0';
|
||||
return std::string(output);
|
||||
}
|
||||
|
||||
static const std::uint8_t base64DecoderRing[128] =
|
||||
{
|
||||
/* 0-15 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
/* 16-31 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
/* 32-47 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 62, 0xFF, 0xFF, 0xFF, 63,
|
||||
/* 48-63 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xFF, 0xFF, 0xFF, 64, 0xFF, 0xFF,
|
||||
/* 64-79 */ 0xFF, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
/* 80-95 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
/* 96-111 */ 0xFF, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
/* 112-127 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
static std::vector<std::uint8_t> GetBase64DecodedValue(const std::string& value)
|
||||
{
|
||||
std::vector<std::uint8_t> result;
|
||||
|
||||
ThrowErrorIfNot(Error::InvalidParameter, (0 == (value.length() % 4)), "invalid base64 encoding");
|
||||
for(std::size_t index=0; index < value.length(); index += 4)
|
||||
{
|
||||
ThrowErrorIf(Error::InvalidParameter,(
|
||||
(value[index+0] | value[index+1] | value[index+2] | value[index+3]) >= 128
|
||||
), "invalid base64 encoding");
|
||||
|
||||
ULONG v1 = base64DecoderRing[value[index+0]];
|
||||
ULONG v2 = base64DecoderRing[value[index+1]];
|
||||
ULONG v3 = base64DecoderRing[value[index+2]];
|
||||
ULONG v4 = base64DecoderRing[value[index+3]];
|
||||
|
||||
ThrowErrorIf(Error::InvalidParameter,(((v1 | v2) >= 64) || ((v3 | v4) == 0xFF)), "first two chars of a four char base64 sequence can't be ==, and must be valid");
|
||||
ThrowErrorIf(Error::InvalidParameter,(v3 == 64 && v4 != 64), "if the third char is = then the fourth char must be =");
|
||||
std::size_t byteCount = (v4 != 64 ? 3 : (v3 != 64 ? 2 : 1));
|
||||
result.push_back(static_cast<std::uint8_t>(((v1 << 2) | ((v2 >> 4) & 0x03))));
|
||||
if (byteCount >1)
|
||||
{
|
||||
result.push_back(static_cast<std::uint8_t>(((v2 << 4) | ((v3 >> 2) & 0x0F)) & 0xFF));
|
||||
if (byteCount >2)
|
||||
{
|
||||
result.push_back(static_cast<std::uint8_t>(((v3 << 6) | ((v4 >> 0) & 0x3F)) & 0xFF));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -66,6 +66,76 @@ enum class XmlAttributeName : std::uint8_t
|
|||
Package_Applications_Application_Id = 15,
|
||||
};
|
||||
|
||||
// must remain in same order as XmlAttributeName
|
||||
static const wchar_t* attributeNames[] = {
|
||||
/* Name */L"Name",
|
||||
/* ResourceId */L"ResourceId",
|
||||
/* Version */L"Version",
|
||||
/* Size */L"Size",
|
||||
/* Package_Identity_ProcessorArchitecture */L"ProcessorArchitecture",
|
||||
/* Publisher */L"Publisher",
|
||||
/* BlockMap_File_LocalFileHeaderSize */L"LfhSize",
|
||||
/* BlockMap_File_Block_Hash */L"Hash",
|
||||
/* Bundle_Package_FileName */L"FileName",
|
||||
/* Bundle_Package_Offset */L"Offset",
|
||||
/* Bundle_Package_Type */L"Type",
|
||||
/* Bundle_Package_Architecture */L"Architecture",
|
||||
/* Language */L"Language",
|
||||
/* MinVersion */L"MinVersion",
|
||||
/* Dependencies_Tdf_MaxVersionTested */L"MaxVersionTested",
|
||||
/* Package_Applications_Application_Id */L"Id",
|
||||
};
|
||||
|
||||
#ifdef USING_MSXML
|
||||
|
||||
// must remain in same order as XmlQueryName
|
||||
static const wchar_t* xPaths[] = {
|
||||
/* Package_Identity */L"/*[local-name()='Package']/*[local-name()='Identity']",
|
||||
/* BlockMap_File */L"/*[local-name()='BlockMap']/*[local-name()='File']",
|
||||
/* BlockMap_File_Block */L"*[local-name()='Block']",
|
||||
/* Bundle_Identity */L"/*[local-name()='Bundle']/*[local-name()='Identity']",
|
||||
/* Bundle_Packages_Package */L"/*[local-name()='Bundle']/*[local-name()='Packages']/*[local-name()='Package']",
|
||||
/* Bundle_Packages_Package_Resources_Resource */L"*[local-name()='Resources']/*[local-name()='Resource']",
|
||||
/* Package_Dependencies_TargetDeviceFamily */L"/*[local-name()='Package']/*[local-name()='Dependencies']/*[local-name()='TargetDeviceFamily']",
|
||||
/* Package_Applications_Application */L"/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']",
|
||||
/* Package_Properties */L"/*[local-name()='Package']/*[local-name()='Properties']",
|
||||
/* Package_Properties_Description */L"*[local-name()='Description']",
|
||||
/* Package_Properties_DisplayName */L"*[local-name()='DisplayName']",
|
||||
/* Package_Properties_PublisherDisplayName */L"*[local-name()='PublisherDisplayName']",
|
||||
/* Package_Properties_Logo */L"*[local-name()='Logo']",
|
||||
/* Package_Properties_Framework */L"*[local-name()='Framework']",
|
||||
/* Package_Properties_ResourcePackage */L"*[local-name()='ResourcePackage']",
|
||||
/* Package_Properties_AllowExecution */L"*[local-name()='AllowExecution']",
|
||||
/* Package_Dependencies_PackageDependency */L"/*[local-name()='Package']/*[local-name()='Dependencies']/*[local-name()='PackageDependency']",
|
||||
/* Package_Capabilities_Capability */L"/*[local-name()='Package']/*[local-name()='Capabilities']/*[local-name()='Capability']",
|
||||
/* Package_Resources_Resource */L"/*[local-name()='Package']/*[local-name()='Resources']/*[local-name()='Resource']",
|
||||
};
|
||||
#else
|
||||
|
||||
// must remain in same order as XmlQueryName
|
||||
static const char* xPaths[] = {
|
||||
/* XmlQueryName::Package_Identity */"/Package/Identity",
|
||||
/* XmlQueryName::BlockMap_File */"/BlockMap/File",
|
||||
/* XmlQueryName::BlockMap_File_Block */"./Block",
|
||||
/* XmlQueryName::Bundle_Identity */"/Bundle/Identity",
|
||||
/* XmlQueryName::Bundle_Packages_Package */"/Bundle/Packages/Package",
|
||||
/* XmlQueryName::Bundle_Packages_Package_Resources_Resource */"./Resources/Resource",
|
||||
/* XmlQueryName::Package_Dependencies_TargetDeviceFamily */"/Package/Dependencies/TargetDeviceFamily",
|
||||
/* XmlQueryName::Package_Applications_Application */"/Package/Applications/Application",
|
||||
/* XmlQueryName::Package_Properties */"/Package/Properties",
|
||||
/* XmlQueryName::Package_Properties_Description */"./Description",
|
||||
/* XmlQueryName::Package_Properties_DisplayName */"./DisplayName",
|
||||
/* XmlQueryName::Package_Properties_PublisherDisplayName */"./PublisherDisplayName",
|
||||
/* XmlQueryName::Package_Properties_Logo */"./Logo",
|
||||
/* XmlQueryName::Package_Properties_Framework */"./Framework",
|
||||
/* XmlQueryName::Package_Properties_ResourcePackage */"./ResourcePackage",
|
||||
/* XmlQueryName::Package_Properties_AllowExecution */"./AllowExecution",
|
||||
/* XmlQueryName::Package_Dependencies_PackageDependency */"/Package/Dependencies/PackageDependency",
|
||||
/* XmlQueryName::Package_Capabilities_Capability */"/Package/Capabilities/Capability",
|
||||
/* XmlQueryName::Package_Resources_Resource */"/Package/Resources/Resource",
|
||||
};
|
||||
#endif
|
||||
|
||||
EXTERN_C const IID IID_IXmlElement;
|
||||
EXTERN_C const IID IID_IXmlDom;
|
||||
EXTERN_C const IID IID_IXmlFactory;
|
||||
|
|
|
@ -82,7 +82,7 @@ MIDL_PUBLIC_GUID(IID, IID_IMsixStreamFactory,0XC74F4821,0X3B82,0X4AD5,0X98,0XEA,
|
|||
// internal interfaces.
|
||||
MIDL_DEFINE_GUID(IID, IID_IPackage, 0x51B2C456,0xAAA9,0x46D6,0x8E,0xC9,0x29,0x82,0x20,0x55,0x91,0x89);
|
||||
MIDL_DEFINE_GUID(IID, IID_IStorageObject, 0xEC25B96E,0x0DB1,0x4483,0xBD,0xB1,0xCA,0xB1,0x10,0x9C,0xB7,0x41);
|
||||
MIDL_DEFINE_GUID(IID, IID_IMsixFactory, 0x1f850db4,0x32b8,0x4db6,0x8b,0xf4,0x5a,0x89,0x7e,0xb6,0x11,0xf1);
|
||||
MIDL_DEFINE_GUID(IID, IID_IMsixFactory, 0x1f850db4,0x32b8,0x4db6,0x8b,0xf4,0x5a,0x89,0x7e,0xb6,0x11,0xf1);
|
||||
MIDL_DEFINE_GUID(IID, IID_IVerifierObject, 0xcb0a105c,0x3a6c,0x4e48,0x93,0x51,0x37,0x7c,0x4d,0xcc,0xd8,0x90);
|
||||
MIDL_DEFINE_GUID(IID, IID_IXmlElement, 0xac94449e,0x442d,0x4bed,0x8f,0xca,0x83,0x77,0x0c,0x0f,0x7e,0xe9);
|
||||
MIDL_DEFINE_GUID(IID, IID_IXmlDom, 0x0e7a446e,0xbaf7,0x44c1,0xb3,0x8a,0x21,0x6b,0xfa,0x18,0xa1,0xa8);
|
||||
|
@ -103,6 +103,11 @@ MIDL_DEFINE_GUID(IID, IID_IAppxBundleManifestPackageInfoInternal, 0x32e6fcf0,0x7
|
|||
MIDL_DEFINE_GUID(IID, IID_IXercesElement, 0x07d6ee0e,0x2165,0x4b90,0x80,0x24,0xe1,0x76,0x29,0x1e,0x77,0xdd);
|
||||
#endif
|
||||
|
||||
#ifdef USING_JAVAXML
|
||||
// {69AB3660-398D-4CD6-A131-E73106040E3B}
|
||||
MIDL_DEFINE_GUID(IID, IID_IJavaXmlElement, 0x69ab3660,0x398d,0x4cd6,0xa1,0x31,0xe7,0x31,0x6,0x4,0xe,0x3b);
|
||||
#endif
|
||||
|
||||
#ifdef USING_MSXML
|
||||
MIDL_DEFINE_GUID(IID, IID_IMSXMLElement, 0x2730f595,0x0c80,0x4f3e,0x88,0x91,0x75,0x3b,0x2e,0x8c,0x30,0x5d);
|
||||
MIDL_DEFINE_GUID(IID, IID_IMSXMLDom, 0xb6bca5f0,0xc6c1,0x4409,0x85,0xbe,0xe4,0x76,0xaa,0xbe,0xc1,0x9a);
|
||||
|
|
|
@ -16,6 +16,12 @@ if(XML_PARSER MATCHES xerces)
|
|||
add_definitions(-DUSING_XERCES=1)
|
||||
endif()
|
||||
|
||||
if(XML_PARSER MATCHES javaxml)
|
||||
message(STATUS "XML_PARSER defined. Using javaxml parser." )
|
||||
set(XmlParser PAL/XML/AOSP/XmlObject.cpp)
|
||||
add_definitions(-DUSING_JAVAXML=1)
|
||||
endif()
|
||||
|
||||
if(XML_PARSER MATCHES msxml6)
|
||||
message(STATUS "XML_PARSER defined. Using MSXML6 XML parser." )
|
||||
set(XmlParser PAL/XML/msxml6/XmlObject.cpp)
|
||||
|
@ -116,6 +122,9 @@ else()
|
|||
list(APPEND MSIX_EXPORTS
|
||||
"JNI_OnLoad"
|
||||
)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/PAL/Interop/AOSP)
|
||||
set(InteropHpp PAL/Interop/AOSP/JniHelper.hpp)
|
||||
set(InteropCpp PAL/Interop/AOSP/JniHelper.cpp)
|
||||
set(Applicability PAL/Applicability/AOSP/Applicability.cpp)
|
||||
endif()
|
||||
# on Linux and linux-derived platforms, you use a version script to achieve similar ends.
|
||||
|
@ -215,6 +224,7 @@ set(LIB_PRIVATE_HEADERS
|
|||
../inc/IXml.hpp
|
||||
../inc/ZipFileStream.hpp
|
||||
../inc/ZipObject.hpp
|
||||
${InteropHpp}
|
||||
${BundleHeaders}
|
||||
)
|
||||
|
||||
|
@ -237,6 +247,7 @@ set(LIB_SOURCES
|
|||
${Signature}
|
||||
${XmlParser}
|
||||
${CompressionObjectCpp}
|
||||
${InteropCpp}
|
||||
${BundleSources}
|
||||
)
|
||||
|
||||
|
@ -319,7 +330,7 @@ endif()
|
|||
|
||||
if(AOSP)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE -latomic)
|
||||
if(NOT SKIP_BUNDLES) # This will change when we have JNI functionality that is not bundle related.
|
||||
if((NOT SKIP_BUNDLES) OR (XML_PARSER MATCHES javaxml))
|
||||
find_package(Java REQUIRED)
|
||||
if(NOT ANDROID_SDK)
|
||||
if(NOT DEFINED ENV{ANDROID_HOME})
|
||||
|
@ -335,9 +346,20 @@ if(AOSP)
|
|||
endif()
|
||||
include(UseJava)
|
||||
message(STATUS "Android SDK = ${ANDROID_SDK}")
|
||||
set(jar_sources)
|
||||
if(NOT SKIP_BUNDLES)
|
||||
list(APPEND jar_sources PAL/java/com/microsoft/msix/Language.java)
|
||||
endif()
|
||||
if (XML_PARSER MATCHES javaxml)
|
||||
list(APPEND jar_sources
|
||||
PAL/java/com/microsoft/msix/XmlDom.java
|
||||
PAL/java/com/microsoft/msix/XmlElement.java
|
||||
PAL/java/com/microsoft/msix/JavaXmlErrorHandler.java
|
||||
PAL/java/com/microsoft/msix/JavaXmlException.java)
|
||||
endif()
|
||||
add_jar(${PROJECT_NAME}-jni
|
||||
SOURCES
|
||||
PAL/java/com/microsoft/msix/JniHelper.java
|
||||
${jar_sources}
|
||||
INCLUDE_JARS
|
||||
${ANDROID_SDK}/platforms/android-${ANDROID_SDK_VERSION}/android.jar
|
||||
OUTPUT_DIR
|
||||
|
|
|
@ -8,11 +8,7 @@
|
|||
|
||||
#include "Applicability.hpp"
|
||||
#include "Exceptions.hpp"
|
||||
|
||||
// Android includes
|
||||
#include <jni.h>
|
||||
|
||||
static JavaVM* g_JavaVM = nullptr;
|
||||
#include "JniHelper.hpp"
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
|
@ -21,47 +17,23 @@ namespace MSIX {
|
|||
std::vector<Bcp47Tag> Applicability::GetLanguages()
|
||||
{
|
||||
std::vector<Bcp47Tag> languages;
|
||||
JNIEnv* env;
|
||||
bool isThreadAttached = false;
|
||||
int result = g_JavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
|
||||
if (result < 0)
|
||||
{ // Native thread. This should always be the case.
|
||||
result = g_JavaVM->AttachCurrentThread(&env, nullptr);
|
||||
ThrowErrorIf(Error::Unexpected, result < 0, "Failed attaching the thread.");
|
||||
isThreadAttached = true;
|
||||
}
|
||||
jclass javaClass = env->FindClass("com/microsoft/msix/JniHelper");
|
||||
ThrowErrorIf(Error::Unexpected, !javaClass, "Failed looking for our java class.");
|
||||
jmethodID languageFunc = env->GetStaticMethodID(javaClass, "getLanguages", "()[Ljava/lang/String;");
|
||||
JNIEnv* env = Jni::Instance()->GetEnv();
|
||||
std::unique_ptr<_jclass, JObjectDeleter> javaClass(env->FindClass("com/microsoft/msix/Language"));
|
||||
ThrowErrorIf(Error::Unexpected, !javaClass.get(), "Failed looking for our java class.");
|
||||
jmethodID languageFunc = env->GetStaticMethodID(javaClass.get(), "getLanguages", "()[Ljava/lang/String;");
|
||||
ThrowErrorIf(Error::Unexpected, !languageFunc, "Failed calling getLanguages().");
|
||||
jobjectArray javaLanguages = reinterpret_cast<jobjectArray>(env->CallStaticObjectMethod(javaClass, languageFunc));
|
||||
for(int i = 0; i < env->GetArrayLength(javaLanguages); i++)
|
||||
std::unique_ptr<_jobjectArray, JObjectDeleter> javaLanguages(reinterpret_cast<jobjectArray>(env->CallStaticObjectMethod(javaClass.get(), languageFunc)));
|
||||
for(int i = 0; i < env->GetArrayLength(javaLanguages.get()); i++)
|
||||
{
|
||||
jstring javaLanguage = reinterpret_cast<jstring>(env->GetObjectArrayElement(javaLanguages, i));
|
||||
const char* language = env->GetStringUTFChars(javaLanguage, nullptr);
|
||||
ThrowErrorIf(Error::Unexpected, !language, "Failed getting langauge from the system.");
|
||||
//std::unique_ptr<_jobject, JObjectDeleter> arrayElement (env->GetObjectArrayElement(javaLanguages.get(), i));
|
||||
std::unique_ptr<_jstring, JObjectDeleter> javaLanguage (reinterpret_cast<jstring>(env->GetObjectArrayElement(javaLanguages.get(), i)));
|
||||
|
||||
// BCP47 format
|
||||
std::string bcp47(language);
|
||||
std::string bcp47(GetStringFromJString(javaLanguage.get()));
|
||||
std::replace(bcp47.begin(), bcp47.end(), '_', '-');
|
||||
languages.push_back(Bcp47Tag(bcp47));
|
||||
env->ReleaseStringUTFChars(javaLanguage, language);
|
||||
}
|
||||
if (isThreadAttached)
|
||||
{
|
||||
g_JavaVM->DetachCurrentThread();
|
||||
}
|
||||
|
||||
return languages;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
|
||||
{
|
||||
g_JavaVM = vm;
|
||||
JNIEnv* env;
|
||||
if (g_JavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
|
||||
{
|
||||
return JNI_ERR;
|
||||
}
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// Copyright (C) 2017 Microsoft. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#include "JniHelper.hpp"
|
||||
#include "Exceptions.hpp"
|
||||
|
||||
JavaVM* g_JavaVM = nullptr;
|
||||
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
|
||||
{
|
||||
g_JavaVM = vm;
|
||||
JNIEnv *env;
|
||||
if (g_JavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
|
||||
{
|
||||
return JNI_ERR;
|
||||
}
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
std::string GetStringFromJString(jstring jvalue)
|
||||
{
|
||||
JNIEnv* env = Jni::Instance()->GetEnv();
|
||||
const char* value = env->GetStringUTFChars(jvalue, nullptr);
|
||||
std::string returnVal (value);
|
||||
env->ReleaseStringUTFChars(jvalue, value);
|
||||
return returnVal;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// Copyright (C) 2017 Microsoft. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
#include <memory>
|
||||
#include <Exceptions.hpp>
|
||||
|
||||
extern JavaVM* g_JavaVM;
|
||||
std::string GetStringFromJString(jstring jvalue);
|
||||
|
||||
class Jni
|
||||
{
|
||||
private:
|
||||
Jni () = default;
|
||||
Jni(const Jni&) = delete;
|
||||
Jni(Jni&&) = delete;
|
||||
Jni& operator=(const Jni&) =delete;
|
||||
Jni& operator=(Jni&&) = delete;
|
||||
|
||||
bool m_isThreadAttached = false;
|
||||
JNIEnv* m_env = nullptr;
|
||||
|
||||
~Jni()
|
||||
{
|
||||
if (m_isThreadAttached)
|
||||
{
|
||||
g_JavaVM->DetachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static Jni* Instance()
|
||||
{
|
||||
static Jni jni;
|
||||
return &jni;
|
||||
}
|
||||
|
||||
JNIEnv* GetEnv()
|
||||
{
|
||||
if (!m_env)
|
||||
{
|
||||
int result = g_JavaVM->GetEnv(reinterpret_cast<void**>(&m_env), JNI_VERSION_1_6);
|
||||
if (result < 0)
|
||||
{ // Native thread. This should always be the case.
|
||||
result = g_JavaVM->AttachCurrentThread(&m_env, nullptr);
|
||||
ThrowErrorIf(MSIX::Error::Unexpected, result < 0, "Failed attaching the thread.");
|
||||
m_isThreadAttached = true;
|
||||
}
|
||||
}
|
||||
return m_env;
|
||||
}
|
||||
};
|
||||
|
||||
struct JObjectDeleter {
|
||||
void operator()(jobject obj)
|
||||
{
|
||||
Jni::Instance()->GetEnv()->DeleteLocalRef(obj);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,248 @@
|
|||
//
|
||||
// Copyright (C) 2017 Microsoft. All rights reserved.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "Exceptions.hpp"
|
||||
#include "StreamBase.hpp"
|
||||
#include "IXml.hpp"
|
||||
#include "Encoding.hpp"
|
||||
#include "StreamHelper.hpp"
|
||||
#include "MSIXResource.hpp"
|
||||
#include "UnicodeConversion.hpp"
|
||||
#include "Enumerators.hpp"
|
||||
#include "JniHelper.hpp"
|
||||
|
||||
|
||||
EXTERN_C const IID IID_IJavaXmlElement;
|
||||
|
||||
// An internal interface for java XML document object model
|
||||
// {69AB3660-398D-4CD6-A131-E73106040E3B}
|
||||
interface IJavaXmlElement : public IUnknown
|
||||
{
|
||||
public:
|
||||
virtual jobject GetJavaObject() = 0;
|
||||
};
|
||||
|
||||
SpecializeUuidOfImpl(IJavaXmlElement);
|
||||
|
||||
namespace MSIX {
|
||||
|
||||
class JavaXmlElement final : public ComClass<JavaXmlElement, IXmlElement, IJavaXmlElement, IMsixElement>
|
||||
{
|
||||
public:
|
||||
JavaXmlElement(IMsixFactory* factory, jobject javaXmlElementObject) :
|
||||
m_factory(factory), m_javaXmlElementObject(javaXmlElementObject)
|
||||
{
|
||||
m_env = Jni::Instance()->GetEnv();
|
||||
std::unique_ptr<_jclass, JObjectDeleter> xmlElementClass(m_env->FindClass("com/microsoft/msix/XmlElement"));
|
||||
|
||||
getAttributeValueFunc = m_env->GetMethodID(
|
||||
xmlElementClass.get(), "GetAttributeValue",
|
||||
"(Ljava/lang/String;)Ljava/lang/String;");
|
||||
getTextContentFunc = m_env->GetMethodID(
|
||||
xmlElementClass.get(), "GetTextContent",
|
||||
"()Ljava/lang/String;");
|
||||
getElementsByTagNameFunc = m_env->GetMethodID(
|
||||
xmlElementClass.get(), "GetElementsByTagName",
|
||||
"(Ljava/lang/String;)[Lcom/microsoft/msix/XmlElement;");
|
||||
}
|
||||
|
||||
// IXmlElement
|
||||
std::string GetAttributeValue(XmlAttributeName attribute) override
|
||||
{
|
||||
auto intermediate = utf16_to_utf8(attributeNames[static_cast<uint8_t>(attribute)]);
|
||||
return GetAttributeValue(intermediate);
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> GetBase64DecodedAttributeValue(XmlAttributeName attribute) override
|
||||
{
|
||||
auto intermediate = GetAttributeValue(attribute);
|
||||
return GetBase64DecodedValue(intermediate);
|
||||
}
|
||||
|
||||
std::string GetText() override
|
||||
{
|
||||
std::unique_ptr<_jstring, JObjectDeleter> jvalue(reinterpret_cast<jstring>(m_env->CallObjectMethod(m_javaXmlElementObject.get(), getTextContentFunc)));
|
||||
return GetStringFromJString(jvalue.get());
|
||||
}
|
||||
|
||||
// IJavaXmlElement
|
||||
jobject GetJavaObject() override { return m_javaXmlElementObject.get(); }
|
||||
|
||||
// IMsixElement
|
||||
HRESULT STDMETHODCALLTYPE GetAttributeValue(LPCWSTR name, LPWSTR* value) noexcept override try
|
||||
{
|
||||
ThrowErrorIf(Error::InvalidParameter, (value == nullptr), "bad pointer.");
|
||||
auto intermediate = utf16_to_utf8(name);
|
||||
auto attributeValue = GetAttributeValue(intermediate);
|
||||
return m_factory->MarshalOutString(attributeValue, value);;
|
||||
|
||||
} CATCH_RETURN();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetText(LPWSTR* value) noexcept override try
|
||||
{
|
||||
ThrowErrorIf(Error::InvalidParameter, (value == nullptr), "bad pointer.");
|
||||
auto text = GetText();
|
||||
return m_factory->MarshalOutString(text, value);
|
||||
} CATCH_RETURN();
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetElements(LPCWSTR name, IMsixElementEnumerator** elements) noexcept override try
|
||||
{
|
||||
ThrowErrorIf(Error::InvalidParameter, (elements == nullptr || *elements != nullptr), "bad pointer.");
|
||||
|
||||
auto intermediate = utf16_to_utf8(name);
|
||||
std::unique_ptr<_jstring, JObjectDeleter> jname(m_env->NewStringUTF(intermediate.c_str()));
|
||||
std::unique_ptr<_jobjectArray, JObjectDeleter> javaElements(reinterpret_cast<jobjectArray>(m_env->CallObjectMethod(m_javaXmlElementObject.get(), getElementsByTagNameFunc, jname.get())));
|
||||
std::vector<ComPtr<IMsixElement>> elementsEnum;
|
||||
// Note: if the number of elements are large, JNI might barf due to too many local refs alive. This should only be used for small lists.
|
||||
for(int i = 0; i < m_env->GetArrayLength(javaElements.get()); i++)
|
||||
{
|
||||
auto item = ComPtr<IMsixElement>::Make<JavaXmlElement>(m_factory, m_env->GetObjectArrayElement(javaElements.get(), i));
|
||||
elementsEnum.push_back(std::move(item));
|
||||
}
|
||||
*elements = ComPtr<IMsixElementEnumerator>::
|
||||
Make<EnumeratorCom<IMsixElementEnumerator,IMsixElement>>(elementsEnum).Detach();
|
||||
|
||||
|
||||
return static_cast<HRESULT>(Error::OK);
|
||||
} CATCH_RETURN();
|
||||
|
||||
private:
|
||||
IMsixFactory* m_factory = nullptr;
|
||||
std::unique_ptr<_jobject, JObjectDeleter> m_javaXmlElementObject;
|
||||
jmethodID getAttributeValueFunc = nullptr;
|
||||
jmethodID getTextContentFunc = nullptr;
|
||||
jmethodID getElementsByTagNameFunc = nullptr;
|
||||
JNIEnv* m_env = nullptr;
|
||||
|
||||
std::string GetAttributeValue(std::string& attributeName)
|
||||
{
|
||||
std::unique_ptr<_jstring, JObjectDeleter> jname(m_env->NewStringUTF(attributeName.c_str()));
|
||||
std::unique_ptr<_jstring, JObjectDeleter> jvalue(reinterpret_cast<jstring>(m_env->CallObjectMethod(m_javaXmlElementObject.get(), getAttributeValueFunc, jname.get())));
|
||||
return GetStringFromJString(jvalue.get());
|
||||
}
|
||||
};
|
||||
|
||||
void CheckForJavaXmlParseException(JNIEnv* env)
|
||||
{
|
||||
jboolean exceptionThrown = env->ExceptionCheck();
|
||||
if (exceptionThrown == JNI_TRUE) {
|
||||
std::unique_ptr<_jthrowable, JObjectDeleter> exc(env->ExceptionOccurred());
|
||||
env->ExceptionClear();
|
||||
|
||||
std::unique_ptr<_jclass, JObjectDeleter> javaXmlExceptionClass(env->FindClass("com/microsoft/msix/JavaXmlException"));
|
||||
if (env->IsInstanceOf(exc.get(), javaXmlExceptionClass.get()) == JNI_TRUE)
|
||||
{
|
||||
jmethodID getErrorCodeFunc = env->GetMethodID(javaXmlExceptionClass.get(), "GetErrorCode", "()I");
|
||||
jint errorCode = env->CallIntMethod(exc.get(), getErrorCodeFunc);
|
||||
|
||||
if (errorCode == 0)
|
||||
{
|
||||
ThrowError(MSIX::Error::XmlWarning);
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowError(MSIX::Error::XmlError);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<_jclass, JObjectDeleter> saxParseExceptionClass(env->FindClass("org/xml/sax/SAXParseException"));
|
||||
if (env->IsInstanceOf(exc.get(), saxParseExceptionClass.get()) == JNI_TRUE)
|
||||
{
|
||||
ThrowError(MSIX::Error::XmlFatal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class JavaXmlDom final : public ComClass<JavaXmlDom, IXmlDom>
|
||||
{
|
||||
public:
|
||||
JavaXmlDom(IMsixFactory* factory, const ComPtr<IStream>& stream) :
|
||||
m_factory(factory), m_stream(stream)
|
||||
{
|
||||
m_env = Jni::Instance()->GetEnv();
|
||||
|
||||
std::unique_ptr<_jclass, JObjectDeleter> xmlDomClass(m_env->FindClass("com/microsoft/msix/XmlDom"));
|
||||
|
||||
getDocumentFunc = m_env->GetMethodID(
|
||||
xmlDomClass.get(), "GetDocument",
|
||||
"()Lcom/microsoft/msix/XmlElement;");
|
||||
|
||||
getElementsFunc = m_env->GetMethodID(
|
||||
xmlDomClass.get(), "GetElements",
|
||||
"(Lcom/microsoft/msix/XmlElement;Ljava/lang/String;)[Lcom/microsoft/msix/XmlElement;");
|
||||
jmethodID ctor = m_env->GetMethodID(xmlDomClass.get(), "<init>", "()V");
|
||||
m_javaXmlDom.reset(m_env->NewObject(xmlDomClass.get(), ctor));
|
||||
|
||||
auto buffer = Helper::CreateBufferFromStream(stream);
|
||||
std::unique_ptr<_jbyteArray, JObjectDeleter> byteArray(m_env->NewByteArray(buffer.size()));
|
||||
m_env->SetByteArrayRegion(byteArray.get(), (jsize) 0, (jsize) buffer.size(), (jbyte*) buffer.data());
|
||||
jmethodID initializeFunc = m_env->GetMethodID(xmlDomClass.get(), "InitializeDocument", "([B)V");
|
||||
m_env->CallVoidMethod(m_javaXmlDom.get(), initializeFunc, byteArray.get());
|
||||
m_env->ReleaseByteArrayElements(byteArray.get(), (jbyte*) buffer.data(), JNI_ABORT);
|
||||
|
||||
// android does not include any schema validation parsers. We can only support xml validation.
|
||||
// If schema validation is required, then use xerces as the xml parser.
|
||||
}
|
||||
|
||||
// IXmlDom
|
||||
MSIX::ComPtr<IXmlElement> GetDocument() override
|
||||
{
|
||||
// Before returning document to caller, check to see if there were any JNI exceptions.
|
||||
CheckForJavaXmlParseException(m_env);
|
||||
|
||||
std::unique_ptr<_jobject, JObjectDeleter> javaElement(m_env->CallObjectMethod(m_javaXmlDom.get(), getDocumentFunc));
|
||||
return ComPtr<IXmlElement>::Make<JavaXmlElement>(m_factory, javaElement.release());
|
||||
}
|
||||
|
||||
bool ForEachElementIn(const ComPtr<IXmlElement>& root, XmlQueryName query, XmlVisitor& visitor) override
|
||||
{
|
||||
ComPtr<IJavaXmlElement> element;
|
||||
ThrowHrIfFailed(root->QueryInterface(UuidOfImpl<IJavaXmlElement>::iid, reinterpret_cast<void**>(&element)));
|
||||
|
||||
std::unique_ptr<_jstring, JObjectDeleter> jquery(m_env->NewStringUTF(xPaths[static_cast<uint8_t>(query)]));
|
||||
std::unique_ptr<_jobjectArray, JObjectDeleter> javaElements(reinterpret_cast<jobjectArray>(m_env->CallObjectMethod(m_javaXmlDom.get(), getElementsFunc, element->GetJavaObject(), jquery.get())));
|
||||
|
||||
for(int i = 0; i < m_env->GetArrayLength(javaElements.get()); i++)
|
||||
{
|
||||
auto item = ComPtr<IXmlElement>::Make<JavaXmlElement>(m_factory, m_env->GetObjectArrayElement(javaElements.get(), i));
|
||||
if (!visitor.Callback(visitor.context, item))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
IMsixFactory* m_factory;
|
||||
ComPtr<IStream> m_stream;
|
||||
jmethodID getDocumentFunc = nullptr;
|
||||
jmethodID getElementsFunc = nullptr;
|
||||
std::unique_ptr<_jobject, JObjectDeleter> m_javaXmlDom;
|
||||
JNIEnv* m_env = nullptr;
|
||||
};
|
||||
|
||||
class JavaXmlFactory final : public ComClass<JavaXmlFactory, IXmlFactory>
|
||||
{
|
||||
public:
|
||||
JavaXmlFactory(IMsixFactory* factory) : m_factory(factory)
|
||||
{
|
||||
}
|
||||
|
||||
ComPtr<IXmlDom> CreateDomFromStream(XmlContentType footPrintType, const ComPtr<IStream>& stream) override
|
||||
{
|
||||
return ComPtr<IXmlDom>::Make<JavaXmlDom>(m_factory, stream);
|
||||
}
|
||||
protected:
|
||||
IMsixFactory* m_factory;
|
||||
};
|
||||
|
||||
ComPtr<IXmlFactory> CreateXmlFactory(IMsixFactory* factory) { return ComPtr<IXmlFactory>::Make<JavaXmlFactory>(factory); }
|
||||
|
||||
} // namespace MSIX
|
|
@ -15,6 +15,7 @@
|
|||
#include "Log.hpp"
|
||||
#include "StreamBase.hpp"
|
||||
#include "IXml.hpp"
|
||||
#include "Encoding.hpp"
|
||||
#include "UnicodeConversion.hpp"
|
||||
#include "MSIXResource.hpp"
|
||||
#include "Enumerators.hpp"
|
||||
|
@ -114,49 +115,6 @@ SchemaEntry(L"http://schemas.microsoft.com/appx/2016/bundle",
|
|||
SchemaEntry(L"http://schemas.microsoft.com/appx/2017/bundle", L"b3", "AppxPackaging/Manifest/Schema/2017/BundleManifestSchema2017.xsd"),
|
||||
}};
|
||||
|
||||
// must remain in same order as XmlQueryName
|
||||
static const wchar_t* xPaths[] = {
|
||||
/* Package_Identity */L"/*[local-name()='Package']/*[local-name()='Identity']",
|
||||
/* BlockMap_File */L"/*[local-name()='BlockMap']/*[local-name()='File']",
|
||||
/* BlockMap_File_Block */L"*[local-name()='Block']",
|
||||
/* Bundle_Identity */L"/*[local-name()='Bundle']/*[local-name()='Identity']",
|
||||
/* Bundle_Packages_Package */L"/*[local-name()='Bundle']/*[local-name()='Packages']/*[local-name()='Package']",
|
||||
/* Bundle_Packages_Package_Resources_Resource */L"*[local-name()='Resources']/*[local-name()='Resource']",
|
||||
/* Package_Dependencies_TargetDeviceFamily */L"/*[local-name()='Package']/*[local-name()='Dependencies']/*[local-name()='TargetDeviceFamily']",
|
||||
/* Package_Applications_Application */L"/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']",
|
||||
/* Package_Properties */L"/*[local-name()='Package']/*[local-name()='Properties']",
|
||||
/* Package_Properties_Description */L"*[local-name()='Description']",
|
||||
/* Package_Properties_DisplayName */L"*[local-name()='DisplayName']",
|
||||
/* Package_Properties_PublisherDisplayName */L"*[local-name()='PublisherDisplayName']",
|
||||
/* Package_Properties_Logo */L"*[local-name()='Logo']",
|
||||
/* Package_Properties_Framework */L"*[local-name()='Framework']",
|
||||
/* Package_Properties_ResourcePackage */L"*[local-name()='ResourcePackage']",
|
||||
/* Package_Properties_AllowExecution */L"*[local-name()='AllowExecution']",
|
||||
/* Package_Dependencies_PackageDependency */L"/*[local-name()='Package']/*[local-name()='Dependencies']/*[local-name()='PackageDependency']",
|
||||
/* Package_Capabilities_Capability */L"/*[local-name()='Package']/*[local-name()='Capabilities']/*[local-name()='Capability']",
|
||||
/* Package_Resources_Resource */L"/*[local-name()='Package']/*[local-name()='Resources']/*[local-name()='Resource']",
|
||||
};
|
||||
|
||||
// must remain in same order as XmlAttributeName
|
||||
static const wchar_t* attributeNames[] = {
|
||||
/* Name */L"Name",
|
||||
/* ResourceId */L"ResourceId",
|
||||
/* Version */L"Version",
|
||||
/* Size */L"Size",
|
||||
/* Package_Identity_ProcessorArchitecture */L"ProcessorArchitecture",
|
||||
/* Publisher */L"Publisher",
|
||||
/* BlockMap_File_LocalFileHeaderSize */L"LfhSize",
|
||||
/* BlockMap_File_Block_Hash */L"Hash",
|
||||
/* Bundle_Package_FileName */L"FileName",
|
||||
/* Bundle_Package_Offset */L"Offset",
|
||||
/* Bundle_Package_Type */L"Type",
|
||||
/* Bundle_Package_Architecture */L"Architecture",
|
||||
/* Language */L"Language",
|
||||
/* MinVersion */L"MinVersion",
|
||||
/* Dependencies_Tdf_MaxVersionTested */L"MaxVersionTested",
|
||||
/* Package_Applications_Application_Id */L"Id",
|
||||
};
|
||||
|
||||
// --------------------------------------------------------
|
||||
// MSXML6 specific error codes
|
||||
// --------------------------------------------------------
|
||||
|
@ -169,18 +127,6 @@ static const wchar_t* attributeNames[] = {
|
|||
// XML_INVALID_CONTENT - Element content is invalid according to the DTD/Schema.
|
||||
#define INVALID_CONTENT 0xc00ce014
|
||||
|
||||
static const std::uint8_t base64DecoderRing[128] =
|
||||
{
|
||||
/* 0-15 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
/* 16-31 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
/* 32-47 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 62, 0xFF, 0xFF, 0xFF, 63,
|
||||
/* 48-63 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xFF, 0xFF, 0xFF, 64, 0xFF, 0xFF,
|
||||
/* 64-79 */ 0xFF, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
/* 80-95 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
/* 96-111 */ 0xFF, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
/* 112-127 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
};
|
||||
|
||||
class Bstr
|
||||
{
|
||||
BSTR m_bstr;
|
||||
|
@ -278,35 +224,8 @@ public:
|
|||
|
||||
std::vector<std::uint8_t> GetBase64DecodedAttributeValue(XmlAttributeName attribute) override
|
||||
{
|
||||
std::vector<std::uint8_t> result;
|
||||
|
||||
auto intermediate = GetAttributeValue(attribute);
|
||||
ThrowErrorIfNot(Error::InvalidParameter, (0 == (intermediate.length() % 4)), "invalid base64 encoding");
|
||||
for(std::size_t index=0; index < intermediate.length(); index += 4)
|
||||
{
|
||||
ThrowErrorIf(Error::InvalidParameter,(
|
||||
(intermediate[index+0] | intermediate[index+1] | intermediate[index+2] | intermediate[index+3]) >= 128
|
||||
), "invalid base64 encoding");
|
||||
|
||||
ULONG v1 = base64DecoderRing[intermediate[index+0]];
|
||||
ULONG v2 = base64DecoderRing[intermediate[index+1]];
|
||||
ULONG v3 = base64DecoderRing[intermediate[index+2]];
|
||||
ULONG v4 = base64DecoderRing[intermediate[index+3]];
|
||||
|
||||
ThrowErrorIf(Error::InvalidParameter,(((v1 | v2) >= 64) || ((v3 | v4) == 0xFF)), "first two chars of a four char base64 sequence can't be ==, and must be valid");
|
||||
ThrowErrorIf(Error::InvalidParameter,(v3 == 64 && v4 != 64), "if the third char is = then the fourth char must be =");
|
||||
std::size_t byteCount = (v4 != 64 ? 3 : (v3 != 64 ? 2 : 1));
|
||||
result.push_back(static_cast<std::uint8_t>(((v1 << 2) | ((v2 >> 4) & 0x03))));
|
||||
if (byteCount >1)
|
||||
{
|
||||
result.push_back(static_cast<std::uint8_t>(((v2 << 4) | ((v3 >> 2) & 0x0F)) & 0xFF));
|
||||
if (byteCount >2)
|
||||
{
|
||||
result.push_back(static_cast<std::uint8_t>(((v3 << 6) | ((v4 >> 0) & 0x3F)) & 0xFF));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return GetBase64DecodedValue(intermediate);
|
||||
}
|
||||
|
||||
std::string GetText() override
|
||||
|
|
|
@ -48,47 +48,6 @@ SpecializeUuidOfImpl(IXercesElement);
|
|||
|
||||
namespace MSIX {
|
||||
|
||||
static std::map<XmlQueryName, std::string> xPaths = {
|
||||
{XmlQueryName::Package_Identity ,"/Package/Identity"},
|
||||
{XmlQueryName::BlockMap_File ,"/BlockMap/File"},
|
||||
{XmlQueryName::BlockMap_File_Block ,"./Block"},
|
||||
{XmlQueryName::Bundle_Identity ,"/Bundle/Identity"},
|
||||
{XmlQueryName::Bundle_Packages_Package ,"/Bundle/Packages/Package"},
|
||||
{XmlQueryName::Bundle_Packages_Package_Resources_Resource ,"./Resources/Resource"},
|
||||
{XmlQueryName::Package_Dependencies_TargetDeviceFamily ,"/Package/Dependencies/TargetDeviceFamily"},
|
||||
{XmlQueryName::Package_Applications_Application ,"/Package/Applications/Application"},
|
||||
{XmlQueryName::Package_Properties ,"/Package/Properties"},
|
||||
{XmlQueryName::Package_Properties_Description ,"./Description"},
|
||||
{XmlQueryName::Package_Properties_DisplayName ,"./DisplayName"},
|
||||
{XmlQueryName::Package_Properties_PublisherDisplayName ,"./PublisherDisplayName"},
|
||||
{XmlQueryName::Package_Properties_Logo ,"./Logo"},
|
||||
{XmlQueryName::Package_Properties_Framework ,"./Framework"},
|
||||
{XmlQueryName::Package_Properties_ResourcePackage ,"./ResourcePackage"},
|
||||
{XmlQueryName::Package_Properties_AllowExecution ,"./AllowExecution"},
|
||||
{XmlQueryName::Package_Dependencies_PackageDependency ,"/Package/Dependencies/PackageDependency"},
|
||||
{XmlQueryName::Package_Capabilities_Capability ,"/Package/Capabilities/Capability"},
|
||||
{XmlQueryName::Package_Resources_Resource ,"/Package/Resources/Resource"},
|
||||
};
|
||||
|
||||
static std::map<XmlAttributeName, std::string> attributeNames = {
|
||||
{XmlAttributeName::Name ,"Name"},
|
||||
{XmlAttributeName::ResourceId ,"ResourceId"},
|
||||
{XmlAttributeName::Version ,"Version"},
|
||||
{XmlAttributeName::Size ,"Size"},
|
||||
{XmlAttributeName::Identity_ProcessorArchitecture ,"ProcessorArchitecture"},
|
||||
{XmlAttributeName::Publisher ,"Publisher"},
|
||||
{XmlAttributeName::BlockMap_File_LocalFileHeaderSize ,"LfhSize"},
|
||||
{XmlAttributeName::BlockMap_File_Block_Hash ,"Hash"},
|
||||
{XmlAttributeName::Bundle_Package_FileName ,"FileName"},
|
||||
{XmlAttributeName::Bundle_Package_Offset ,"Offset"},
|
||||
{XmlAttributeName::Bundle_Package_Type ,"Type"},
|
||||
{XmlAttributeName::Bundle_Package_Architecture ,"Architecture"},
|
||||
{XmlAttributeName::Language ,"Language"},
|
||||
{XmlAttributeName::MinVersion ,"MinVersion"},
|
||||
{XmlAttributeName::Dependencies_Tdf_MaxVersionTested ,"MaxVersionTested"},
|
||||
{XmlAttributeName::Package_Applications_Application_Id ,"Id"},
|
||||
};
|
||||
|
||||
class ParsingException final : public XERCES_CPP_NAMESPACE::ErrorHandler
|
||||
{
|
||||
public:
|
||||
|
@ -265,12 +224,13 @@ public:
|
|||
// IXmlElement
|
||||
std::string GetAttributeValue(XmlAttributeName attribute) override
|
||||
{
|
||||
return GetAttributeValue(attributeNames[attribute]);
|
||||
auto attributeName = utf16_to_utf8(attributeNames[static_cast<uint8_t>(attribute)]);
|
||||
return GetAttributeValue(attributeName);
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> GetBase64DecodedAttributeValue(XmlAttributeName attribute) override
|
||||
{
|
||||
XercesXMLChPtr nameAttr(XMLString::transcode(attributeNames[attribute].c_str()));
|
||||
XercesXMLChPtr nameAttr(XMLString::transcode(utf16_to_utf8(attributeNames[static_cast<uint8_t>(attribute)]).c_str()));
|
||||
XMLSize_t len = 0;
|
||||
XercesXMLBytePtr decodedData(XERCES_CPP_NAMESPACE::Base64::decodeToXMLByte(
|
||||
m_element->getAttribute(nameAttr.Get()),
|
||||
|
@ -406,7 +366,7 @@ public:
|
|||
ComPtr<IXercesElement> element;
|
||||
ThrowHrIfFailed(root->QueryInterface(UuidOfImpl<IXercesElement>::iid, reinterpret_cast<void**>(&element)));
|
||||
|
||||
XercesXMLChPtr xPath(XMLString::transcode(xPaths[query].c_str()));
|
||||
XercesXMLChPtr xPath(XMLString::transcode(xPaths[static_cast<uint8_t>(query)]));
|
||||
XercesPtr<DOMXPathResult> result(m_parser->getDocument()->evaluate(
|
||||
xPath.Get(),
|
||||
element->GetElement(),
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Microsoft. All rights reserved.
|
||||
* See LICENSE file in the project root for full license information.
|
||||
*/
|
||||
package com.microsoft.msix;
|
||||
|
||||
import com.microsoft.msix.JavaXmlException;
|
||||
|
||||
import org.xml.sax.ErrorHandler;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
public class JavaXmlErrorHandler implements ErrorHandler {
|
||||
|
||||
@Override
|
||||
public void warning(SAXParseException exc) throws JavaXmlException{
|
||||
throw new JavaXmlException(exc.toString(), 0 /*warning*/);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(SAXParseException exc) throws JavaXmlException{
|
||||
throw new JavaXmlException(exc.toString(), 1 /*error */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fatalError(SAXParseException exc) throws SAXParseException {
|
||||
// for fatalError we simply rethrow the SAX exception.
|
||||
throw exc;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Microsoft. All rights reserved.
|
||||
* See LICENSE file in the project root for full license information.
|
||||
*/
|
||||
package com.microsoft.msix;
|
||||
|
||||
import org.xml.sax.ErrorHandler;
|
||||
import org.xml.sax.SAXParseException;
|
||||
import org.xml.sax.Locator;
|
||||
|
||||
public class JavaXmlException extends SAXParseException {
|
||||
// 0: Warning, 1: Error, 2: Fatal.
|
||||
int m_errorCode;
|
||||
|
||||
public JavaXmlException(String message, int errorCode)
|
||||
{
|
||||
super(message, null);
|
||||
m_errorCode = errorCode;
|
||||
}
|
||||
|
||||
int GetErrorCode()
|
||||
{
|
||||
return m_errorCode;
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ import android.os.LocaleList;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class JniHelper {
|
||||
public class Language {
|
||||
|
||||
static public String[] getLanguages() {
|
||||
List<String> languageList = new ArrayList<String>();
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Microsoft. All rights reserved.
|
||||
* See LICENSE file in the project root for full license information.
|
||||
*/
|
||||
package com.microsoft.msix;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
|
||||
public class XmlDom {
|
||||
private Document m_document;
|
||||
private XPath m_xpath;
|
||||
|
||||
public XmlDom() {
|
||||
m_document = null;
|
||||
XPathFactory factory = XPathFactory.newInstance();
|
||||
m_xpath = factory.newXPath();
|
||||
}
|
||||
|
||||
public void InitializeDocument(byte[] stream) throws Exception {
|
||||
InputStream is = new ByteArrayInputStream(stream);
|
||||
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
builder.setErrorHandler(new JavaXmlErrorHandler());
|
||||
m_document = builder.parse(is);
|
||||
|
||||
}
|
||||
|
||||
public XmlElement GetDocument() {
|
||||
XmlElement dom = new XmlElement(m_document.getDocumentElement());
|
||||
return dom;
|
||||
}
|
||||
|
||||
public XmlElement[] GetElements(XmlElement root, String query) throws Exception{
|
||||
List<XmlElement> elements = new ArrayList<>();
|
||||
NodeList results = (NodeList) m_xpath.evaluate(query, root.GetElement(), XPathConstants.NODESET);
|
||||
for (int i = 0; i < results.getLength(); i++) {
|
||||
XmlElement element = new XmlElement((Element) results.item(i));
|
||||
elements.add(element);
|
||||
}
|
||||
return elements.toArray(new XmlElement[0]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Microsoft. All rights reserved.
|
||||
* See LICENSE file in the project root for full license information.
|
||||
*/
|
||||
package com.microsoft.msix;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class XmlElement {
|
||||
private Element m_element;
|
||||
|
||||
public XmlElement(Element element) {
|
||||
m_element = element;
|
||||
}
|
||||
|
||||
public Element GetElement() {
|
||||
return m_element;
|
||||
}
|
||||
|
||||
public String GetAttributeValue(String name) {
|
||||
return m_element.getAttribute(name);
|
||||
}
|
||||
|
||||
public String GetTextContent() {
|
||||
return m_element.getTextContent();
|
||||
}
|
||||
|
||||
public XmlElement[] GetElementsByTagName(String name) {
|
||||
List<XmlElement> elements = new ArrayList<>();
|
||||
try {
|
||||
NodeList results = m_element.getElementsByTagName(name);;
|
||||
for (int i = 0; i < results.getLength(); i++) {
|
||||
XmlElement element = new XmlElement((Element) results.item(i));
|
||||
elements.add(element);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return elements.toArray(new XmlElement[0]);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,10 @@ IF (XML_PARSER MATCHES xerces)
|
|||
add_definitions(-DUSING_XERCES=1)
|
||||
ENDIF()
|
||||
|
||||
if(XML_PARSER MATCHES javaxml)
|
||||
add_definitions(-DUSING_JAVAXML=1)
|
||||
endif()
|
||||
|
||||
IF (XML_PARSER MATCHES msxml6)
|
||||
add_definitions(-DUSING_MSXML=1)
|
||||
ENDIF()
|
||||
|
|
|
@ -12,7 +12,7 @@ static const char* packageToTest = "..\\test\\appx\\TestAppxPackage_Win32.appx";
|
|||
static const char* packageToTest = "../test/appx/TestAppxPackage_Win32.appx";
|
||||
#endif
|
||||
|
||||
#ifdef USING_XERCES
|
||||
#if defined(USING_XERCES) || defined (USING_JAVAXML)
|
||||
static const wchar_t* ApplicationXpath = L"/Package/Applications/Application";
|
||||
#else
|
||||
static const wchar_t* ApplicationXpath = L"/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']";
|
||||
|
|
|
@ -24,6 +24,10 @@ android {
|
|||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
debug {
|
||||
minifyEnabled false
|
||||
debuggable true
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
|
|
|
@ -84,6 +84,10 @@ static HRESULT RunTest(std::string packageName, std::string unpackFolder, MSIX_V
|
|||
if(expectedResult == result)
|
||||
{ std::cout << "Succeeded" << std::endl;
|
||||
}
|
||||
else if ((expectedResult == 4099) && (result == 4098))
|
||||
{
|
||||
std::cout << "Succeeded for AOSP JavaXml since it always returns 4098 for xml errors" << std::endl;
|
||||
}
|
||||
else
|
||||
{ std::cout << "Failed" << std::endl;
|
||||
g_TestFailed = true;
|
||||
|
@ -114,6 +118,7 @@ static HRESULT RunTestsInternal(std::string source, std::string target)
|
|||
// expected result last four digits, but in decimal, not hex. e.g. 0x8bad0002 == 2, 0x8bad0041 == 65, etc...
|
||||
// common codes:
|
||||
// SignatureInvalid = ERROR_FACILITY + 0x0041 == 65
|
||||
|
||||
hr = RunTest(source + "Empty.appx", unpackFolder, sv, 2);
|
||||
hr = RunTest(source + "HelloWorld.appx", unpackFolder, ss, 0);
|
||||
hr = RunTest(source + "SignatureNotLastPart-ERROR_BAD_FORMAT.appx", unpackFolder, full, 66);
|
||||
|
|
Загрузка…
Ссылка в новой задаче