use of com.tencent.tinker.build.util.TinkerPatchException in project tinker by Tencent.
the class CliMain method loadConfigFromXml.
private void loadConfigFromXml(File configFile, File outputFile, File oldApkFile, File newApkFile) {
if (configFile == null) {
configFile = new File(mRunningLocation + File.separator + TypedValue.FILE_CONFIG);
if (!configFile.exists()) {
System.err.printf("the config file %s does not exit\n", configFile.getAbsolutePath());
printUsage(System.err);
System.exit(ERRNO_USAGE);
}
}
try {
config = new Configuration(configFile, outputFile, oldApkFile, newApkFile);
} catch (IOException | ParserConfigurationException | SAXException e) {
e.printStackTrace();
goToError();
} catch (TinkerPatchException e) {
e.printStackTrace();
goToError();
}
}
use of com.tencent.tinker.build.util.TinkerPatchException in project tinker by Tencent.
the class DexDiffDecoder method collectAddedOrDeletedClasses.
/**
* Before starting real diff works, we collect added class descriptor
* and deleted class descriptor for further analysing in {@code checkCrossDexMovingClasses}.
*/
private void collectAddedOrDeletedClasses(File oldFile, File newFile) throws IOException {
Dex oldDex = new Dex(oldFile);
Dex newDex = new Dex(newFile);
Set<String> oldClassDescs = new HashSet<>();
for (ClassDef oldClassDef : oldDex.classDefs()) {
oldClassDescs.add(oldDex.typeNames().get(oldClassDef.typeIndex));
}
Set<String> newClassDescs = new HashSet<>();
for (ClassDef newClassDef : newDex.classDefs()) {
newClassDescs.add(newDex.typeNames().get(newClassDef.typeIndex));
}
Set<String> addedClassDescs = new HashSet<>(newClassDescs);
addedClassDescs.removeAll(oldClassDescs);
Set<String> deletedClassDescs = new HashSet<>(oldClassDescs);
deletedClassDescs.removeAll(newClassDescs);
for (String addedClassDesc : addedClassDescs) {
if (addedClassDescToDexNameMap.containsKey(addedClassDesc)) {
throw new TinkerPatchException(String.format("Class Duplicate. Class [%s] is added in both new dex: [%s] and [%s]. Please check your newly apk.", addedClassDesc, addedClassDescToDexNameMap.get(addedClassDesc), newFile.toString()));
} else {
addedClassDescToDexNameMap.put(addedClassDesc, newFile.toString());
}
}
for (String deletedClassDesc : deletedClassDescs) {
if (deletedClassDescToDexNameMap.containsKey(deletedClassDesc)) {
throw new TinkerPatchException(String.format("Class Duplicate. Class [%s] is deleted in both old dex: [%s] and [%s]. Please check your base apk.", deletedClassDesc, addedClassDescToDexNameMap.get(deletedClassDesc), oldFile.toString()));
} else {
deletedClassDescToDexNameMap.put(deletedClassDesc, newFile.toString());
}
}
}
use of com.tencent.tinker.build.util.TinkerPatchException in project tinker by Tencent.
the class DexDiffDecoder method getRawOrWrappedDexMD5.
private String getRawOrWrappedDexMD5(File dexOrJarFile) {
final String name = dexOrJarFile.getName();
if (name.endsWith(".dex")) {
return MD5.getMD5(dexOrJarFile);
} else {
JarFile dexJar = null;
try {
dexJar = new JarFile(dexOrJarFile);
ZipEntry classesDex = dexJar.getEntry(DexFormat.DEX_IN_JAR_NAME);
// no code
if (classesDex == null) {
throw new TinkerPatchException(String.format("Jar file %s do not contain 'classes.dex', it is not a correct dex jar file!", dexOrJarFile.getAbsolutePath()));
}
return MD5.getMD5(dexJar.getInputStream(classesDex), 1024 * 100);
} catch (IOException e) {
throw new TinkerPatchException(String.format("File %s is not end with '.dex', but it is not a correct dex jar file !", dexOrJarFile.getAbsolutePath()), e);
} finally {
if (dexJar != null) {
try {
dexJar.close();
} catch (Exception e) {
// Ignored.
}
}
}
}
}
use of com.tencent.tinker.build.util.TinkerPatchException in project tinker by Tencent.
the class DexDiffDecoder method checkDexChange.
private void checkDexChange(Dex originDex, Dex newDex) {
DexClassesComparator classesCmptor = new DexClassesComparator("*");
classesCmptor.setIgnoredRemovedClassDescPattern(config.mDexLoaderPattern);
classesCmptor.startCheck(originDex, newDex);
List<DexClassInfo> addedClassInfos = classesCmptor.getAddedClassInfos();
boolean isNoClassesAdded = addedClassInfos.isEmpty();
Map<String, DexClassInfo[]> changedClassDescToClassInfosMap;
boolean isNoClassesChanged;
if (isNoClassesAdded) {
changedClassDescToClassInfosMap = classesCmptor.getChangedClassDescToInfosMap();
isNoClassesChanged = changedClassDescToClassInfosMap.isEmpty();
} else {
throw new TinkerPatchException("some classes was unexpectedly added in patched new dex, check if there's any bugs in " + "patch algorithm. Related classes: " + Utils.collectionToString(addedClassInfos));
}
if (isNoClassesChanged) {
List<DexClassInfo> deletedClassInfos = classesCmptor.getDeletedClassInfos();
if (!deletedClassInfos.isEmpty()) {
throw new TinkerPatchException("some classes that are not matched to loader class pattern " + "was unexpectedly deleted in patched new dex, check if there's any bugs in " + "patch algorithm. Related classes: " + Utils.collectionToString(deletedClassInfos));
}
} else {
throw new TinkerPatchException("some classes was unexpectedly changed in patched new dex, check if there's any bugs in " + "patch algorithm. Related classes: " + Utils.collectionToString(changedClassDescToClassInfosMap.keySet()));
}
}
use of com.tencent.tinker.build.util.TinkerPatchException in project tinker by Tencent.
the class ResDiffDecoder method onAllPatchesEnd.
@Override
public void onAllPatchesEnd() throws IOException, TinkerPatchException {
//only there is only deleted set, we just ignore
if (addedSet.isEmpty() && modifiedSet.isEmpty() && largeModifiedSet.isEmpty()) {
return;
}
if (!config.mResRawPattern.contains(TypedValue.RES_ARSC)) {
throw new TinkerPatchException("resource must contain resources.arsc pattern");
}
if (!config.mResRawPattern.contains(TypedValue.RES_MANIFEST)) {
throw new TinkerPatchException("resource must contain AndroidManifest.xml pattern");
}
//check gradle build
if (config.mUsingGradle) {
final boolean ignoreWarning = config.mIgnoreWarning;
final boolean resourceArscChanged = modifiedSet.contains(TypedValue.RES_ARSC) || largeModifiedSet.contains(TypedValue.RES_ARSC);
if (resourceArscChanged && !config.mUseApplyResource) {
if (ignoreWarning) {
//ignoreWarning, just log
Logger.e("Warning:ignoreWarning is true, but resources.arsc is changed, you should use applyResourceMapping mode to build the new apk, otherwise, it may be crash at some times");
} else {
Logger.e("Warning:ignoreWarning is false, but resources.arsc is changed, you should use applyResourceMapping mode to build the new apk, otherwise, it may be crash at some times");
throw new TinkerPatchException(String.format("ignoreWarning is false, but resources.arsc is changed, you should use applyResourceMapping mode to build the new apk, otherwise, it may be crash at some times"));
}
}
/*else if (config.mUseApplyResource) {
int totalChangeSize = addedSet.size() + modifiedSet.size() + largeModifiedSet.size();
if (totalChangeSize == 1 && resourceArscChanged) {
Logger.e("Warning: we are using applyResourceMapping mode to build the new apk, but there is only resources.arsc changed, you should ensure there is actually resource changed!");
}
}*/
}
//add delete set
deletedSet.addAll(getDeletedResource(config.mTempUnzipOldDir, config.mTempUnzipNewDir));
//we can't modify AndroidManifest file
addedSet.remove(TypedValue.RES_MANIFEST);
deletedSet.remove(TypedValue.RES_MANIFEST);
modifiedSet.remove(TypedValue.RES_MANIFEST);
largeModifiedSet.remove(TypedValue.RES_MANIFEST);
//remove add, delete or modified if they are in ignore change pattern also
removeIgnoreChangeFile(modifiedSet);
removeIgnoreChangeFile(deletedSet);
removeIgnoreChangeFile(addedSet);
removeIgnoreChangeFile(largeModifiedSet);
// last add test res in assets for user cannot ignore it;
addAssetsFileForTestResource();
File tempResZip = new File(config.mOutFolder + File.separator + TEMP_RES_ZIP);
final File tempResFiles = config.mTempResultDir;
//gen zip resources_out.zip
FileOperation.zipInputDir(tempResFiles, tempResZip);
File extractToZip = new File(config.mOutFolder + File.separator + TypedValue.RES_OUT);
String resZipMd5 = Utils.genResOutputFile(extractToZip, tempResZip, config, addedSet, modifiedSet, deletedSet, largeModifiedSet, largeModifiedMap);
Logger.e("Final normal zip resource: %s, size=%d, md5=%s", extractToZip.getName(), extractToZip.length(), resZipMd5);
logWriter.writeLineToInfoFile(String.format("Final normal zip resource: %s, size=%d, md5=%s", extractToZip.getName(), extractToZip.length(), resZipMd5));
//delete temp file
FileOperation.deleteFile(tempResZip);
//gen zip resources_out_7z.zip
File extractTo7Zip = new File(config.mOutFolder + File.separator + TypedValue.RES_OUT_7ZIP);
File tempRes7Zip = new File(config.mOutFolder + File.separator + TEMP_RES_7ZIP);
//ensure 7zip is enable
if (FileOperation.sevenZipInputDir(tempResFiles, tempRes7Zip, config)) {
//7zip whether actual exist
if (tempRes7Zip.exists()) {
String res7zipMd5 = Utils.genResOutputFile(extractTo7Zip, tempRes7Zip, config, addedSet, modifiedSet, deletedSet, largeModifiedSet, largeModifiedMap);
//delete temp file
FileOperation.deleteFile(tempRes7Zip);
Logger.e("Final 7zip resource: %s, size=%d, md5=%s", extractTo7Zip.getName(), extractTo7Zip.length(), res7zipMd5);
logWriter.writeLineToInfoFile(String.format("Final 7zip resource: %s, size=%d, md5=%s", extractTo7Zip.getName(), extractTo7Zip.length(), res7zipMd5));
}
}
//first, write resource meta first
//use resources.arsc's base crc to identify base.apk
String arscBaseCrc = FileOperation.getZipEntryCrc(config.mOldApkFile, TypedValue.RES_ARSC);
String arscMd5 = FileOperation.getZipEntryMd5(extractToZip, TypedValue.RES_ARSC);
if (arscBaseCrc == null || arscMd5 == null) {
throw new TinkerPatchException("can't find resources.arsc's base crc or md5");
}
String resourceMeta = Utils.getResourceMeta(arscBaseCrc, arscMd5);
writeMetaFile(resourceMeta);
//pattern
String patternMeta = TypedValue.PATTERN_TITLE;
HashSet<String> patterns = new HashSet<>(config.mResRawPattern);
//we will process them separate
patterns.remove(TypedValue.RES_MANIFEST);
writeMetaFile(patternMeta + patterns.size());
//write pattern
for (String item : patterns) {
writeMetaFile(item);
}
//write meta file, write large modify first
writeMetaFile(largeModifiedSet, TypedValue.LARGE_MOD);
writeMetaFile(modifiedSet, TypedValue.MOD);
writeMetaFile(addedSet, TypedValue.ADD);
writeMetaFile(deletedSet, TypedValue.DEL);
}
Aggregations