diff --git a/cocos/audio/android/AudioEngine-inl.cpp b/cocos/audio/android/AudioEngine-inl.cpp index f5dbd3a64b..2e1f2e9e14 100644 --- a/cocos/audio/android/AudioEngine-inl.cpp +++ b/cocos/audio/android/AudioEngine-inl.cpp @@ -38,6 +38,7 @@ #include "base/CCDirector.h" #include "base/CCScheduler.h" #include "platform/android/CCFileUtils-android.h" +#include "platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h" using namespace cocos2d; using namespace cocos2d::experimental; @@ -104,27 +105,31 @@ bool AudioPlayer::init(SLEngineItf engineEngine, SLObjectItf outputMixObject,con SLDataFormat_MIME format_mime = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED}; audioSrc.pFormat = &format_mime; - if (fileFullPath[0] != '/'){ - std::string relativePath = ""; - + if (fileFullPath[0] != '/') { + off_t start, length; + std::string relativePath; size_t position = fileFullPath.find("assets/"); + if (0 == position) { // "assets/" is at the beginning of the path and we don't want it - relativePath += fileFullPath.substr(strlen("assets/")); + relativePath = fileFullPath.substr(strlen("assets/")); } else { - relativePath += fileFullPath; + relativePath = fileFullPath; } - auto asset = AAssetManager_open(cocos2d::FileUtilsAndroid::getAssetManager(), relativePath.c_str(), AASSET_MODE_UNKNOWN); - - // open asset as file descriptor - off_t start, length; - _assetFd = AAsset_openFileDescriptor(asset, &start, &length); - if (_assetFd <= 0){ + if (cocos2d::FileUtilsAndroid::getObbFile() != nullptr) { + _assetFd = getObbAssetFileDescriptorJNI(relativePath.c_str(), &start, &length); + } else { + auto asset = AAssetManager_open(cocos2d::FileUtilsAndroid::getAssetManager(), relativePath.c_str(), AASSET_MODE_UNKNOWN); + // open asset as file descriptor + _assetFd = AAsset_openFileDescriptor(asset, &start, &length); AAsset_close(asset); + } + + if (_assetFd <= 0) { + CCLOGERROR("Failed to open file descriptor for '%s'", fileFullPath.c_str()); break; } - AAsset_close(asset); // configure audio source loc_fd = {SL_DATALOCATOR_ANDROIDFD, _assetFd, start, length}; diff --git a/cocos/base/ZipUtils.cpp b/cocos/base/ZipUtils.cpp index db9d35cc61..4737384b89 100644 --- a/cocos/base/ZipUtils.cpp +++ b/cocos/base/ZipUtils.cpp @@ -645,6 +645,35 @@ unsigned char *ZipFile::getFileData(const std::string &fileName, ssize_t *size) return buffer; } +bool ZipFile::getFileData(const std::string &fileName, ResizableBuffer* buffer) +{ + bool res = false; + do + { + CC_BREAK_IF(!_data->zipFile); + CC_BREAK_IF(fileName.empty()); + + ZipFilePrivate::FileListContainer::const_iterator it = _data->fileList.find(fileName); + CC_BREAK_IF(it == _data->fileList.end()); + + ZipEntryInfo fileInfo = it->second; + + int nRet = unzGoToFilePos(_data->zipFile, &fileInfo.pos); + CC_BREAK_IF(UNZ_OK != nRet); + + nRet = unzOpenCurrentFile(_data->zipFile); + CC_BREAK_IF(UNZ_OK != nRet); + + buffer->resize(fileInfo.uncompressed_size); + int CC_UNUSED nSize = unzReadCurrentFile(_data->zipFile, buffer->buffer(), static_cast(fileInfo.uncompressed_size)); + CCASSERT(nSize == 0 || nSize == (int)fileInfo.uncompressed_size, "the file size is wrong"); + unzCloseCurrentFile(_data->zipFile); + res = true; + } while (0); + + return res; +} + std::string ZipFile::getFirstFilename() { if (unzGoToFirstFile(_data->zipFile) != UNZ_OK) return emptyFilename; diff --git a/cocos/base/ZipUtils.h b/cocos/base/ZipUtils.h index a06ac9fa1f..4a6352e32f 100644 --- a/cocos/base/ZipUtils.h +++ b/cocos/base/ZipUtils.h @@ -31,6 +31,7 @@ THE SOFTWARE. #include "platform/CCPlatformConfig.h" #include "platform/CCPlatformMacros.h" #include "platform/CCPlatformDefine.h" +#include "platform/CCFileUtils.h" #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #include "platform/android/CCFileUtils-android.h" @@ -275,6 +276,14 @@ typedef struct unz_file_info_s unz_file_info; * @since v2.0.5 */ unsigned char *getFileData(const std::string &fileName, ssize_t *size); + + /** + * Get resource file data from a zip file. + * @param fileName File name + * @param[out] buffer If the file read operation succeeds, if will contain the file data. + * @return True if successful. + */ + bool getFileData(const std::string &fileName, ResizableBuffer* buffer); std::string getFirstFilename(); std::string getNextFilename(); diff --git a/cocos/platform/android/CCFileUtils-android.cpp b/cocos/platform/android/CCFileUtils-android.cpp index 5f4f3bdedd..8023aa6913 100644 --- a/cocos/platform/android/CCFileUtils-android.cpp +++ b/cocos/platform/android/CCFileUtils-android.cpp @@ -29,8 +29,10 @@ THE SOFTWARE. #include "platform/android/CCFileUtils-android.h" #include "platform/CCCommon.h" #include "platform/android/jni/JniHelper.h" +#include "platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h" #include "android/asset_manager.h" #include "android/asset_manager_jni.h" +#include "base/ZipUtils.h" #include #include @@ -42,6 +44,7 @@ using namespace std; NS_CC_BEGIN AAssetManager* FileUtilsAndroid::assetmanager = nullptr; +ZipFile* FileUtilsAndroid::obbfile = nullptr; void FileUtilsAndroid::setassetmanager(AAssetManager* a) { if (nullptr == a) { @@ -57,7 +60,7 @@ FileUtils* FileUtils::getInstance() if (s_sharedFileUtils == nullptr) { s_sharedFileUtils = new FileUtilsAndroid(); - if(!s_sharedFileUtils->init()) + if (!s_sharedFileUtils->init()) { delete s_sharedFileUtils; s_sharedFileUtils = nullptr; @@ -73,11 +76,22 @@ FileUtilsAndroid::FileUtilsAndroid() FileUtilsAndroid::~FileUtilsAndroid() { + if (obbfile) + { + delete obbfile; + obbfile = nullptr; + } } bool FileUtilsAndroid::init() { _defaultResRootPath = "assets/"; + + std::string assetsPath(getApkPath()); + if (assetsPath.find("/obb/") != std::string::npos) + { + obbfile = new ZipFile(assetsPath); + } return FileUtils::init(); } @@ -149,9 +163,14 @@ bool FileUtilsAndroid::isFileExistInternal(const std::string& strFilePath) const const char* s = strFilePath.c_str(); // Found "assets/" at the beginning of the path and we don't want it - if (strFilePath.find(_defaultResRootPath) == 0) s += strlen("assets/"); - - if (FileUtilsAndroid::assetmanager) { + if (strFilePath.find(_defaultResRootPath) == 0) s += _defaultResRootPath.length(); + + if (obbfile && obbfile->fileExists(s)) + { + bFound = true; + } + else if (FileUtilsAndroid::assetmanager) + { AAsset* aa = AAssetManager_open(FileUtilsAndroid::assetmanager, s, AASSET_MODE_UNKNOWN); if (aa) { @@ -165,7 +184,7 @@ bool FileUtilsAndroid::isFileExistInternal(const std::string& strFilePath) const else { FILE *fp = fopen(strFilePath.c_str(), "r"); - if(fp) + if (fp) { bFound = true; fclose(fp); @@ -249,6 +268,12 @@ FileUtils::Status FileUtilsAndroid::getContents(const std::string& filename, Res } else { relativePath = fullPath; } + + if (obbfile) + { + if (obbfile->getFileData(relativePath, buffer)) + return FileUtils::Status::OK; + } if (nullptr == assetmanager) { LOGD("... FileUtilsAndroid::assetmanager is nullptr"); diff --git a/cocos/platform/android/CCFileUtils-android.h b/cocos/platform/android/CCFileUtils-android.h index 6d77713ee7..023efb5af9 100644 --- a/cocos/platform/android/CCFileUtils-android.h +++ b/cocos/platform/android/CCFileUtils-android.h @@ -38,6 +38,8 @@ Copyright (c) 2013-2014 Chukong Technologies Inc. NS_CC_BEGIN +class ZipFile; + /** * @addtogroup platform * @{ @@ -57,6 +59,7 @@ public: static void setassetmanager(AAssetManager* a); static AAssetManager* getAssetManager() { return assetmanager; } + static ZipFile* getObbFile() { return obbfile; } /* override functions */ bool init() override; @@ -73,6 +76,7 @@ private: virtual bool isDirectoryExistInternal(const std::string& dirPath) const override; static AAssetManager* assetmanager; + static ZipFile* obbfile; }; // end of platform group diff --git a/cocos/platform/android/java/libs/com.android.vending.expansion.zipfile.jar b/cocos/platform/android/java/libs/com.android.vending.expansion.zipfile.jar new file mode 100644 index 0000000000..bbdeb8a5ee Binary files /dev/null and b/cocos/platform/android/java/libs/com.android.vending.expansion.zipfile.jar differ diff --git a/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java b/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java index 9cd24e82f9..6b14a5a9de 100644 --- a/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java +++ b/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java @@ -31,19 +31,31 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.net.Uri; import android.os.Build; +import android.os.Environment; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.Vibrator; import android.preference.PreferenceManager.OnActivityResultListener; import android.util.DisplayMetrics; +import android.util.Log; import android.view.Display; import android.view.WindowManager; +import com.android.vending.expansion.zipfile.APKExpansionSupport; +import com.android.vending.expansion.zipfile.ZipResourceFile; + import com.enhance.gameservice.IGameTuningService; +import java.io.IOException; +import java.io.File; import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.LinkedHashSet; import java.util.Locale; import java.util.Map; @@ -55,6 +67,7 @@ public class Cocos2dxHelper { // =========================================================== private static final String PREFS_NAME = "Cocos2dxPrefsFile"; private static final int RUNNABLES_PER_FRAME = 5; + private static final String TAG = Cocos2dxHelper.class.getSimpleName(); // =========================================================== // Fields @@ -77,6 +90,12 @@ public class Cocos2dxHelper { private static final int BOOST_TIME = 7; //Enhance API modification end + // The absolute path to the OBB if it exists, else the absolute path to the APK. + private static String sAssetsPath = ""; + + // The OBB file + private static ZipResourceFile sOBBFile = null; + // =========================================================== // Constructors // =========================================================== @@ -91,11 +110,11 @@ public class Cocos2dxHelper { Cocos2dxHelper.sCocos2dxHelperListener = (Cocos2dxHelperListener)activity; if (!sInited) { final ApplicationInfo applicationInfo = activity.getApplicationInfo(); - + Cocos2dxHelper.sPackageName = applicationInfo.packageName; Cocos2dxHelper.sFileDirectory = activity.getFilesDir().getAbsolutePath(); - Cocos2dxHelper.nativeSetApkPath(applicationInfo.sourceDir); + Cocos2dxHelper.nativeSetApkPath(Cocos2dxHelper.getAssetsPath()); Cocos2dxHelper.sCocos2dxAccelerometer = new Cocos2dxAccelerometer(activity); Cocos2dxHelper.sCocos2dMusic = new Cocos2dxMusic(activity); @@ -114,9 +133,48 @@ public class Cocos2dxHelper { serviceIntent.setPackage("com.enhance.gameservice"); boolean suc = activity.getApplicationContext().bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE); //Enhance API modification end + + int versionCode = 1; + try { + versionCode = Cocos2dxActivity.getContext().getPackageManager().getPackageInfo(Cocos2dxHelper.getCocos2dxPackageName(), 0).versionCode; + } catch (NameNotFoundException e) { + e.printStackTrace(); + } + try { + Cocos2dxHelper.sOBBFile = APKExpansionSupport.getAPKExpansionZipFile(Cocos2dxActivity.getContext(), versionCode, 0); + } catch (IOException e) { + e.printStackTrace(); + } } } + // This function returns the absolute path to the OBB if it exists, + // else it returns the absolute path to the APK. + public static String getAssetsPath() + { + if (Cocos2dxHelper.sAssetsPath == "") { + int versionCode = 1; + try { + versionCode = Cocos2dxHelper.sActivity.getPackageManager().getPackageInfo(Cocos2dxHelper.sPackageName, 0).versionCode; + } catch (NameNotFoundException e) { + e.printStackTrace(); + } + String pathToOBB = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/obb/" + Cocos2dxHelper.sPackageName + "/main." + versionCode + "." + Cocos2dxHelper.sPackageName + ".obb"; + File obbFile = new File(pathToOBB); + if (obbFile.exists()) + Cocos2dxHelper.sAssetsPath = pathToOBB; + else + Cocos2dxHelper.sAssetsPath = Cocos2dxHelper.sActivity.getApplicationInfo().sourceDir; + } + + return Cocos2dxHelper.sAssetsPath; + } + + public static ZipResourceFile getObbFile() + { + return Cocos2dxHelper.sOBBFile; + } + //Enhance API modification begin private static ServiceConnection connection = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder service) { @@ -226,6 +284,29 @@ public class Cocos2dxHelper { } return ret; } + + public static long[] getObbAssetFileDescriptor(final String path) { + long[] array = new long[3]; + if (Cocos2dxHelper.sOBBFile != null) { + AssetFileDescriptor descriptor = Cocos2dxHelper.sOBBFile.getAssetFileDescriptor(path); + if (descriptor != null) { + try { + ParcelFileDescriptor parcel = descriptor.getParcelFileDescriptor(); + Method method = parcel.getClass().getMethod("getFd", new Class[] {}); + array[0] = (Integer)method.invoke(parcel); + array[1] = descriptor.getStartOffset(); + array[2] = descriptor.getLength(); + } catch (NoSuchMethodException e) { + Log.e(Cocos2dxHelper.TAG, "Accessing file descriptor directly from the OBB is only supported from Android 3.1 (API level 12) and above."); + } catch (IllegalAccessException e) { + Log.e(Cocos2dxHelper.TAG, e.toString()); + } catch (InvocationTargetException e) { + Log.e(Cocos2dxHelper.TAG, e.toString()); + } + } + } + return array; + } public static void preloadBackgroundMusic(final String pPath) { Cocos2dxHelper.sCocos2dMusic.preloadBackgroundMusic(pPath); diff --git a/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxMusic.java b/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxMusic.java index 0e9f2e499f..8d45f12a34 100644 --- a/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxMusic.java +++ b/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxMusic.java @@ -29,6 +29,9 @@ import android.content.Context; import android.content.res.AssetFileDescriptor; import android.media.MediaPlayer; import android.util.Log; +import android.content.res.AssetFileDescriptor; +import com.android.vending.expansion.zipfile.APKExpansionSupport; +import com.android.vending.expansion.zipfile.ZipResourceFile; import java.io.FileInputStream; @@ -134,9 +137,9 @@ public class Cocos2dxMusic { public void stopBackgroundMusic() { if (this.mBackgroundMediaPlayer != null) { - mBackgroundMediaPlayer.release(); - mBackgroundMediaPlayer = createMediaplayer(mCurrentPath); - + mBackgroundMediaPlayer.release(); + mBackgroundMediaPlayer = createMediaplayer(mCurrentPath); + /** * should set the state, if not, the following sequence will be error * play -> pause -> stop -> resume @@ -163,7 +166,7 @@ public class Cocos2dxMusic { public void rewindBackgroundMusic() { if (this.mBackgroundMediaPlayer != null) { - playBackgroundMusic(mCurrentPath, mIsLoop); + playBackgroundMusic(mCurrentPath, mIsLoop); } } @@ -250,8 +253,13 @@ public class Cocos2dxMusic { mediaPlayer.setDataSource(fis.getFD()); fis.close(); } else { - final AssetFileDescriptor assetFileDescritor = this.mContext.getAssets().openFd(path); - mediaPlayer.setDataSource(assetFileDescritor.getFileDescriptor(), assetFileDescritor.getStartOffset(), assetFileDescritor.getLength()); + if (Cocos2dxHelper.getObbFile() != null) { + final AssetFileDescriptor assetFileDescriptor = Cocos2dxHelper.getObbFile().getAssetFileDescriptor(path); + mediaPlayer.setDataSource(assetFileDescriptor.getFileDescriptor(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength()); + } else { + final AssetFileDescriptor assetFileDescriptor = this.mContext.getAssets().openFd(path); + mediaPlayer.setDataSource(assetFileDescriptor.getFileDescriptor(), assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength()); + } } mediaPlayer.prepare(); diff --git a/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxSound.java b/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxSound.java index a54317020d..abbf2441cc 100644 --- a/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxSound.java +++ b/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxSound.java @@ -28,6 +28,9 @@ import android.content.Context; import android.media.AudioManager; import android.media.SoundPool; import android.util.Log; +import android.content.res.AssetFileDescriptor; +import com.android.vending.expansion.zipfile.APKExpansionSupport; +import com.android.vending.expansion.zipfile.ZipResourceFile; import java.util.ArrayList; import java.util.HashMap; @@ -273,7 +276,12 @@ public class Cocos2dxSound { if (path.startsWith("/")) { soundID = this.mSoundPool.load(path, 0); } else { - soundID = this.mSoundPool.load(this.mContext.getAssets().openFd(path), 0); + if (Cocos2dxHelper.getObbFile() != null) { + final AssetFileDescriptor assetFileDescriptor = Cocos2dxHelper.getObbFile().getAssetFileDescriptor(path); + soundID = mSoundPool.load(assetFileDescriptor, 0); + } else { + soundID = this.mSoundPool.load(this.mContext.getAssets().openFd(path), 0); + } } } catch (final Exception e) { soundID = Cocos2dxSound.INVALID_SOUND_ID; diff --git a/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.cpp b/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.cpp index 6394a470d1..391c07063f 100644 --- a/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.cpp +++ b/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.cpp @@ -87,6 +87,31 @@ std::string getPackageNameJNI() { return JniHelper::callStaticStringMethod(className, "getCocos2dxPackageName"); } +int getObbAssetFileDescriptorJNI(const char* path, long* startOffset, long* size) { + JniMethodInfo methodInfo; + int fd = 0; + + if (JniHelper::getStaticMethodInfo(methodInfo, className.c_str(), "getObbAssetFileDescriptor", "(Ljava/lang/String;)[J")) { + jstring stringArg = methodInfo.env->NewStringUTF(path); + jlongArray newArray = (jlongArray)methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID, stringArg); + jsize theArrayLen = methodInfo.env->GetArrayLength(newArray); + + if (theArrayLen == 3) { + jboolean copy = JNI_FALSE; + jlong *array = methodInfo.env->GetLongArrayElements(newArray, ©); + fd = static_cast(array[0]); + *startOffset = array[1]; + *size = array[2]; + methodInfo.env->ReleaseLongArrayElements(newArray, array, 0); + } + + methodInfo.env->DeleteLocalRef(methodInfo.classID); + methodInfo.env->DeleteLocalRef(stringArg); + } + + return fd; +} + void conversionEncodingJNI(const char* src, int byteSize, const char* fromCharset, char* dst, const char* newCharset) { JniMethodInfo methodInfo; diff --git a/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h b/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h index e32a8e79c9..e545eca032 100644 --- a/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h +++ b/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h @@ -31,6 +31,7 @@ typedef void (*EditTextCallback)(const char* text, void* ctx); extern const char * getApkPath(); extern std::string getPackageNameJNI(); +extern int getObbAssetFileDescriptorJNI(const char* path, long* startOffset, long* size); extern void conversionEncodingJNI(const char* src, int byteSize, const char* fromCharset, char* dst, const char* newCharset); #endif /* __Java_org_cocos2dx_lib_Cocos2dxHelper_H__ */ diff --git a/licenses/LICENSE_com.android.vending.expansion.zipfile.txt b/licenses/LICENSE_com.android.vending.expansion.zipfile.txt new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/licenses/LICENSE_com.android.vending.expansion.zipfile.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.