/*
* Tencent is pleased to support the open source community by making Tinker available.
*
* Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* 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.
*/
package com.tencent.tinker.loader;
import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import com.tencent.tinker.loader.app.TinkerApplication;
import com.tencent.tinker.loader.shareutil.ShareConstants;
import com.tencent.tinker.loader.shareutil.ShareDexDiffPatchInfo;
import com.tencent.tinker.loader.shareutil.ShareIntentUtil;
import com.tencent.tinker.loader.shareutil.ShareOatUtil;
import com.tencent.tinker.loader.shareutil.SharePatchFileUtil;
import com.tencent.tinker.loader.shareutil.ShareSecurityCheck;
import com.tencent.tinker.loader.shareutil.ShareTinkerInternals;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import dalvik.system.PathClassLoader;
/**
* Created by zhangshaowen on 16/3/8.
* check the complete of the dex files
* pre-load patch dex files
*/
public class TinkerDexLoader {
private static final String TAG = "Tinker.TinkerDexLoader";
private static final String DEX_MEAT_FILE = ShareConstants.DEX_META_FILE;
private static final String DEX_PATH = ShareConstants.DEX_PATH;
private static final String DEFAULT_DEX_OPTIMIZE_PATH = ShareConstants.DEFAULT_DEX_OPTIMIZE_PATH;
private static final String INTERPRET_DEX_OPTIMIZE_PATH = ShareConstants.INTERPRET_DEX_OPTIMIZE_PATH;
private static final ArrayList<ShareDexDiffPatchInfo> dexList = new ArrayList<>();
private static File testOptDexFile;
private TinkerDexLoader() {
}
/**
* Load tinker JARs and add them to
* the Application ClassLoader.
*
* @param application The application.
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static boolean loadTinkerJars(final TinkerApplication application, String directory, String oatDir, Intent intentResult, boolean isSystemOTA) {
if (dexList.isEmpty()) {
Log.w(TAG, "there is no dex to load");
return true;
}
PathClassLoader classLoader = (PathClassLoader) TinkerDexLoader.class.getClassLoader();
if (classLoader != null) {
Log.i(TAG, "classloader: " + classLoader.toString());
} else {
Log.e(TAG, "classloader is null");
ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_CLASSLOADER_NULL);
return false;
}
String dexPath = directory + "/" + DEX_PATH + "/";
// Log.i(TAG, "loadTinkerJars: dex path: " + dexPath);
// Log.i(TAG, "loadTinkerJars: opt path: " + optimizeDir.getAbsolutePath());
ArrayList<File> legalFiles = new ArrayList<>();
final boolean isArtPlatForm = ShareTinkerInternals.isVmArt();
for (ShareDexDiffPatchInfo info : dexList) {
//for dalvik, ignore art support dex
if (isJustArtSupportDex(info)) {
continue;
}
String path = dexPath + info.realName;
File file = new File(path);
if (application.isTinkerLoadVerifyFlag()) {
long start = System.currentTimeMillis();
String checkMd5 = isArtPlatForm ? info.destMd5InArt : info.destMd5InDvm;
if (!SharePatchFileUtil.verifyDexFileMd5(file, checkMd5)) {
//it is good to delete the mismatch file
ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_MD5_MISMATCH);
intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_MISMATCH_DEX_PATH,
file.getAbsolutePath());
return false;
}
Log.i(TAG, "verify dex file:" + file.getPath() + " md5, use time: " + (System.currentTimeMillis() - start));
}
legalFiles.add(file);
}
File optimizeDir = new File(directory + "/" + oatDir);
if (isSystemOTA) {
final boolean[] parallelOTAResult = {true};
final Throwable[] parallelOTAThrowable = new Throwable[1];
String targetISA;
try {
targetISA = ShareOatUtil.getOatFileInstructionSet(testOptDexFile);
} catch (Throwable e) {
// don't ota on the front
deleteOutOfDateOATFile(directory);
intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_INTERPRET_EXCEPTION, e);
ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_GET_OTA_INSTRUCTION_SET_EXCEPTION);
return false;
}
deleteOutOfDateOATFile(directory);
Log.w(TAG, "systemOTA, try parallel oat dexes, targetISA:" + targetISA);
// change dir
optimizeDir = new File(directory + "/" + INTERPRET_DEX_OPTIMIZE_PATH);
TinkerParallelDexOptimizer.optimizeAll(
legalFiles, optimizeDir, true, targetISA,
new TinkerParallelDexOptimizer.ResultCallback() {
long start;
@Override
public void onStart(File dexFile, File optimizedDir) {
start = System.currentTimeMillis();
Log.i(TAG, "start to optimize dex:" + dexFile.getPath());
}
@Override
public void onSuccess(File dexFile, File optimizedDir, File optimizedFile) {
// Do nothing.
Log.i(TAG, "success to optimize dex " + dexFile.getPath() + ", use time " + (System.currentTimeMillis() - start));
}
@Override
public void onFailed(File dexFile, File optimizedDir, Throwable thr) {
parallelOTAResult[0] = false;
parallelOTAThrowable[0] = thr;
Log.i(TAG, "fail to optimize dex " + dexFile.getPath() + ", use time " + (System.currentTimeMillis() - start));
}
}
);
if (!parallelOTAResult[0]) {
Log.e(TAG, "parallel oat dexes failed");
intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_INTERPRET_EXCEPTION, parallelOTAThrowable);
ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_OTA_INTERPRET_ONLY_EXCEPTION);
return false;
}
}
try {
SystemClassLoaderAdder.installDexes(application, classLoader, optimizeDir, legalFiles);
} catch (Throwable e) {
Log.e(TAG, "install dexes failed");
// e.printStackTrace();
intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_EXCEPTION, e);
ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_LOAD_EXCEPTION);
return false;
}
return true;
}
/**
* all the dex files in meta file exist?
* fast check, only check whether exist
*
* @param directory
* @return boolean
*/
public static boolean checkComplete(String directory, ShareSecurityCheck securityCheck, String oatDir, Intent intentResult) {
String meta = securityCheck.getMetaContentMap().get(DEX_MEAT_FILE);
//not found dex
if (meta == null) {
return true;
}
dexList.clear();
ShareDexDiffPatchInfo.parseDexDiffPatchInfo(meta, dexList);
if (dexList.isEmpty()) {
return true;
}
HashMap<String, String> dexes = new HashMap<>();
for (ShareDexDiffPatchInfo info : dexList) {
//for dalvik, ignore art support dex
if (isJustArtSupportDex(info)) {
continue;
}
if (!ShareDexDiffPatchInfo.checkDexDiffPatchInfo(info)) {
intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_PACKAGE_PATCH_CHECK, ShareConstants.ERROR_PACKAGE_CHECK_DEX_META_CORRUPTED);
ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_PACKAGE_CHECK_FAIL);
return false;
}
dexes.put(info.realName, info.destMd5InDvm);
}
//tinker/patch.info/patch-641e634c/dex
String dexDirectory = directory + "/" + DEX_PATH + "/";
File dexDir = new File(dexDirectory);
if (!dexDir.exists() || !dexDir.isDirectory()) {
ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_DIRECTORY_NOT_EXIST);
return false;
}
String optimizeDexDirectory = directory + "/" + oatDir + "/";
File optimizeDexDirectoryFile = new File(optimizeDexDirectory);
//fast check whether there is any dex files missing
for (String name : dexes.keySet()) {
File dexFile = new File(dexDirectory + name);
if (!SharePatchFileUtil.isLegalFile(dexFile)) {
intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_MISSING_DEX_PATH, dexFile.getAbsolutePath());
ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_FILE_NOT_EXIST);
return false;
}
//check dex opt whether complete also
File dexOptFile = new File(SharePatchFileUtil.optimizedPathFor(dexFile, optimizeDexDirectoryFile));
if (!SharePatchFileUtil.isLegalFile(dexOptFile)) {
intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_MISSING_DEX_PATH, dexOptFile.getAbsolutePath());
ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_OPT_FILE_NOT_EXIST);
return false;
}
// find test dex
if (dexOptFile.getName().startsWith(ShareConstants.TEST_DEX_NAME)) {
testOptDexFile = dexOptFile;
}
}
//if is ok, add to result intent
intentResult.putExtra(ShareIntentUtil.INTENT_PATCH_DEXES_PATH, dexes);
return true;
}
private static void deleteOutOfDateOATFile(String directory) {
String optimizeDexDirectory = directory + "/" + DEFAULT_DEX_OPTIMIZE_PATH + "/";
SharePatchFileUtil.deleteDir(optimizeDexDirectory);
}
private static boolean isJustArtSupportDex(ShareDexDiffPatchInfo dexDiffPatchInfo) {
if (ShareTinkerInternals.isVmArt()) {
return false;
}
String destMd5InDvm = dexDiffPatchInfo.destMd5InDvm;
if (destMd5InDvm.equals("0")) {
return true;
}
return false;
}
}