Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ public class TinkerResourceExtension {
*/
int largeModSize

/**
* On case insensitive os like macOS, when unzip apk, if res filename in apk is minified,
* minified res filenames may be conflict, eg: aBc.xml and Abc.xml. but they represent the same file
* on macOS
* In this case, you cannot generate the correct patch file because some res files are overwritten
*
* default false
* false: has no effect on the final patch. print log if conflict files are detected
* true: detect conflict files and rename them to temp unique filename (except the first one).
* the temp unique filenames will remain in some files ( old/new unzip dir, tinker_result,
* res_log.txt, log.txt), but will not remain in the final patch file.
*/
boolean caseInsensitiveCompat

public TinkerResourceExtension() {
pattern = []
ignoreChange = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ public class TinkerPatchSchemaTask extends DefaultTask {
.setConfigFields(packageConfigFields)
.setSevenZipPath(configuration.sevenZip.path)
.setUseSign(configuration.useSign)
.setCaseInsensitiveCompat(configuration.res.caseInsensitiveCompat)
.setArkHotPath(configuration.arkHot.path)
.setArkHotName(configuration.arkHot.name)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.tencent.tinker.build.apkparser.AndroidParser;
import com.tencent.tinker.build.info.InfoWriter;
import com.tencent.tinker.build.patch.Configuration;
import com.tencent.tinker.build.util.CaseSensitive;
import com.tencent.tinker.build.util.DiffFactory;
import com.tencent.tinker.build.util.FileOperation;
import com.tencent.tinker.build.util.CustomDiff;
Expand Down Expand Up @@ -125,6 +126,8 @@ private boolean checkLargeModFile(File file) {
@Override
public void onAllPatchesStart() throws IOException, TinkerPatchException {
newApkParser.parseResourceTable();
CaseSensitive.caseInsensitiveCompat = config.mCaseInsensitiveCompat;

final Map<String, ResourcePackage> newApkResPkgNameMap = newApkParser.getResourceTable().getPackageNameMap();
do {
if (newApkResPkgNameMap == null) {
Expand Down Expand Up @@ -502,6 +505,7 @@ private void writeMetaFile(ArrayList<String> set, int mode) {
}
metaWriter.writeLineToInfoFile(title);
for (String name : set) {
name = CaseSensitive.getOriginalEntryName(name);
String line = name;
if (mode == TypedValue.LARGE_MOD) {
LargeModeInfo info = largeModifiedMap.get(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ public class Configuration {
protected static final String ATTR_IGNORE_CHANGE = "ignoreChange";
protected static final String ATTR_IGNORE_CHANGE_WARNING = "ignoreChangeWarning";
protected static final String ATTR_RES_LARGE_MOD = "largeModSize";
protected static final String ATTR_RES_CASE_SENSITIVE = "caseInsensitiveCompat";


protected static final String ATTR_ARKHOT_PATH = "path";
protected static final String ATTR_ARKHOT_NAME = "name";
Expand Down Expand Up @@ -120,6 +122,7 @@ public class Configuration {
public HashSet<Pattern> mResIgnoreChangeWarningPattern;
public HashSet<String> mResRawPattern;
public int mLargeModSize;
public boolean mCaseInsensitiveCompat;
/**
* only gradle have the param
*/
Expand Down Expand Up @@ -237,6 +240,7 @@ public Configuration(InputParam param) throws IOException, TinkerPatchException
mLargeModSize = param.largeModSize;
//only gradle have the param
mUseApplyResource = param.useApplyResource;
mCaseInsensitiveCompat = param.caseInsensitiveCompat;

mDexLoaderPattern.addAll(param.dexLoaderPattern);
mDexIgnoreWarningLoaderPattern.addAll(param.dexIgnoreWarningLoaderPattern);
Expand Down Expand Up @@ -267,6 +271,7 @@ public Configuration(InputParam param) throws IOException, TinkerPatchException
mPackageFields = param.configFields;

mUseSignAPk = param.useSign;

mCustomDiffPath = param.customDiffPath;
mCustomDiffPathArgs = param.customDiffPathArgs;
setSignData(param.signFile, param.keypass, param.storealias, param.storepass);
Expand Down Expand Up @@ -642,6 +647,8 @@ private void readResPatternsFromXml(Node node) throws IOException {
}
} else if (tagName.equals(ATTR_RES_LARGE_MOD)) {
mLargeModSize = Integer.valueOf(value);
} else if (tagName.equals(ATTR_RES_CASE_SENSITIVE)) {
mCaseInsensitiveCompat = Boolean.valueOf(value);
} else {
System.err.println("unknown dex tag " + tagName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class InputParam {
public final boolean isProtectedApp;
public final boolean supportHotplugComponent;
public final boolean useSign;
public final boolean caseInsensitiveCompat;

/**
* tinkerPatch.dex
Expand Down Expand Up @@ -106,6 +107,7 @@ private InputParam(
boolean isProtectedApp,
boolean supportHotplugComponent,
boolean useSign,
boolean caseInsensitiveCompat,

ArrayList<String> dexFilePattern,
ArrayList<String> dexLoaderPattern,
Expand Down Expand Up @@ -139,6 +141,7 @@ private InputParam(
this.isProtectedApp = isProtectedApp;
this.supportHotplugComponent = supportHotplugComponent;
this.useSign = useSign;
this.caseInsensitiveCompat = caseInsensitiveCompat;

this.dexFilePattern = dexFilePattern;
this.dexLoaderPattern = dexLoaderPattern;
Expand Down Expand Up @@ -178,6 +181,7 @@ public static class Builder {
private boolean isProtectedApp;
private boolean isComponentHotplugSupported;
private boolean useSign;
private boolean caseInsensitiveCompat;

/**
* tinkerPatch.dex
Expand Down Expand Up @@ -365,6 +369,11 @@ public Builder setUseSign(boolean useSign) {
return this;
}

public Builder setCaseInsensitiveCompat(boolean caseInsensitiveCompat) {
this.caseInsensitiveCompat = caseInsensitiveCompat;
return this;
}

public Builder setArkHotPath(String path) {
this.arkHotPatchPath = path;
return this;
Expand Down Expand Up @@ -392,6 +401,7 @@ public InputParam create() {
isProtectedApp,
isComponentHotplugSupported,
useSign,
caseInsensitiveCompat,
dexFilePattern,
dexLoaderPattern,
dexIgnoreWarningLoaderPattern,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.tencent.tinker.build.util;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class CaseSensitive {

public static boolean caseInsensitiveCompat = false;
private static final String PREFIX = "CS";
private static final String UNDERLINE = "_";
private static long seq = System.currentTimeMillis();

// <Original Filename, Wrapped Filename>
private static final Map<String, String> wrappedFileMap = new HashMap<>();

/**
* return original file if no conflict files detected, otherwise return a wrapped file with a unique filename.
*
* "conflict files" means 2 different filenames point to a same File in case insensitive os (like macOS)
*
* eg: File aBc.xml exists, input: abC.xml, return: CS12345678_abC.xml
*
* @param target original target file
*/
public static File wrap(File target) {
if (!target.exists()) {
return target;
}
File parentFile = target.getParentFile();
if (parentFile == null || !parentFile.exists()) {
return target;
}
File[] list = parentFile.listFiles();
if (list == null || list.length == 0) {
return target;
}

String filename = target.getName();
String oldName = "";
for (File child : list) {
if (child.getName().equalsIgnoreCase(filename)) {
oldName = child.getName();
break;
}
}
if (oldName.isEmpty() || oldName.equals(filename)) {
return target;
}
if (!caseInsensitiveCompat) {
Logger.e("find conflict files " + oldName + " and " + filename);
return target;
}
if (wrappedFileMap.containsKey(filename)) {
return new File(parentFile, wrappedFileMap.get(filename));
}

File wrappedFile = new File(parentFile, PREFIX + (seq++) + UNDERLINE + filename);
wrappedFileMap.put(filename, wrappedFile.getName());
Logger.d("find conflict file exists:" + oldName + ", wrapped:" + wrappedFile);
return wrappedFile;
}

/**
* eg:
* input: CS12345678_abc.xml
* return: abc.xml
*/
public static String getOriginalFileName(String name) {
if (!caseInsensitiveCompat
|| name == null
|| name.isEmpty()
|| !name.startsWith(PREFIX)
|| !wrappedFileMap.containsValue(name)) {
return name;
}
int index = name.indexOf(UNDERLINE);
if (index < 0) {
return name;
}
try {
long verifyLong = Long.parseLong(name.substring(2, index));
} catch (NumberFormatException e) {
return name;
}
return name.substring(index + 1);
}

/**
* eg:
* input: res/CS12345678_abc.xml
* return: res/abc.xml
*/
public static String getOriginalEntryName(String entryName) {
if (!caseInsensitiveCompat
|| entryName == null
|| entryName.isEmpty()) {
return entryName;
}
int lastSeparatorIndex = entryName.lastIndexOf(File.separator);
if (lastSeparatorIndex < 0) {
return getOriginalFileName(entryName);
}
String path = entryName.substring(0, lastSeparatorIndex);
String name = entryName.substring(lastSeparatorIndex + 1);
name = getOriginalFileName(name);
return path + File.separator + name;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ public static void unZipAPk(String fileName, String filePath) throws IOException

File file = new File(filePath + File.separator + entry.getName());

file = CaseSensitive.wrap(file);

File parentFile = file.getParentFile();
if (parentFile != null && (!parentFile.exists())) {
parentFile.mkdirs();
Expand Down Expand Up @@ -250,7 +252,11 @@ public static void zipFiles(Collection<File> resFileList, File zipFile, String c
}

private static void zipFile(File resFile, ZipOutputStream zipout, String rootpath) throws IOException {
rootpath = rootpath + (rootpath.trim().length() == 0 ? "" : File.separator) + resFile.getName();
String resName = resFile.getName();
if (resFile.isFile()) {
resName = CaseSensitive.getOriginalFileName(resName);
}
rootpath = rootpath + (rootpath.trim().length() == 0 ? "" : File.separator) + resName;
if (resFile.isDirectory()) {
File[] fileList = resFile.listFiles();
for (File file : fileList) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ public static String genResOutputFile(File output, File newZipFile, Configuratio
);
}
String name = zipEntry.getName();
name = CaseSensitive.getOriginalEntryName(name);
if (!TinkerZipUtil.validateZipEntryName(output.getParentFile(), name)) {
throw new IOException("Bad ZipEntry name: " + name);
}
Expand All @@ -162,6 +163,7 @@ public static String genResOutputFile(File output, File newZipFile, Configuratio
TinkerZipUtil.extractTinkerEntry(oldApk, manifestZipEntry, out);

for (String name : largeModifiedSet) {
name = CaseSensitive.getOriginalEntryName(name);
TinkerZipEntry largeZipEntry = oldApk.getEntry(name);
if (largeZipEntry == null) {
throw new TinkerPatchException(
Expand All @@ -173,6 +175,7 @@ public static String genResOutputFile(File output, File newZipFile, Configuratio
}

for (String name : addedSet) {
name = CaseSensitive.getOriginalEntryName(name);
TinkerZipEntry addZipEntry = newApk.getEntry(name);
if (addZipEntry == null) {
throw new TinkerPatchException(
Expand All @@ -183,6 +186,7 @@ public static String genResOutputFile(File output, File newZipFile, Configuratio
}

for (String name : modifiedSet) {
name = CaseSensitive.getOriginalEntryName(name);
TinkerZipEntry modZipEntry = newApk.getEntry(name);
if (modZipEntry == null) {
throw new TinkerPatchException(
Expand Down