diff --git a/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/dexpatcher/DexPatchGenerator.java b/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/dexpatcher/DexPatchGenerator.java index 4752237b2..dfc9115f6 100644 --- a/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/dexpatcher/DexPatchGenerator.java +++ b/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/dexpatcher/DexPatchGenerator.java @@ -101,6 +101,34 @@ public class DexPatchGenerator { private DexSectionDiffAlgorithm encodedArraySectionDiffAlg; private DexSectionDiffAlgorithm annotationsDirectorySectionDiffAlg; private Set additionalRemovingClassPatternSet; + private Set additionalKeepingClassPatternSet; + + private static Set collectInnerClassPatternsOfChangedClasses(Dex newDex, Collection changedClassDescs) { + Set result = new HashSet<>(); + if (newDex == null || changedClassDescs == null || changedClassDescs.isEmpty()) { + return result; + } + Set outerPrefixes = new HashSet<>(); + for (String desc : changedClassDescs) { + if (desc == null || !desc.startsWith("L") || !desc.endsWith(";")) { + continue; + } + outerPrefixes.add(desc.substring(0, desc.length() - 1) + "$"); + } + if (outerPrefixes.isEmpty()) { + return result; + } + for (ClassDef classDef : newDex.classDefs()) { + String typeName = newDex.typeNames().get(classDef.typeIndex); + for (String prefix : outerPrefixes) { + if (typeName.startsWith(prefix)) { + result.add(typeName); + break; + } + } + } + return result; + } private int patchedHeaderOffset = 0; private int patchedStringIdsOffset = 0; private int patchedTypeIdsOffset = 0; diff --git a/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/dexpatcher/util/RelatedClassResolver.java b/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/dexpatcher/util/RelatedClassResolver.java new file mode 100644 index 000000000..263aa3883 --- /dev/null +++ b/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/dexpatcher/util/RelatedClassResolver.java @@ -0,0 +1,54 @@ +/* + * 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.build.dexpatcher.util; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public final class RelatedClassResolver { + + private RelatedClassResolver() { + } + + public static Set resolve(String className, Set allClassNames) { + if (className == null || className.isEmpty() || allClassNames == null || allClassNames.isEmpty()) { + return Collections.emptySet(); + } + + final String outerName = getOuterClassName(className); + final String prefix = outerName + "$"; + + final Set result = new HashSet<>(); + for (String name : allClassNames) { + if (name.equals(outerName) || name.startsWith(prefix)) { + result.add(name); + } + } + return result; + } + + private static String getOuterClassName(String className) { + final int dollarIdx = className.indexOf('$'); + if (dollarIdx < 0) { + return className; + } + return className.substring(0, dollarIdx); + } +} diff --git a/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/patch/Configuration.java b/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/patch/Configuration.java index 9db035c24..72e1cea3a 100644 --- a/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/patch/Configuration.java +++ b/tinker-build/tinker-patch-lib/src/main/java/com/tencent/tinker/build/patch/Configuration.java @@ -37,6 +37,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -98,6 +100,66 @@ public class Configuration { public boolean mIgnoreWarning; public boolean mAllowLoaderInAnyDex; public boolean mIsProtectedApp; + + private static boolean detectProtectedApk(File apkFile) throws IOException { + if (apkFile == null || !apkFile.exists()) { + return false; + } + ZipFile zipFile = null; + try { + zipFile = new ZipFile(apkFile); + final String[] protectedMarkers = new String[] { + "assets/0OO00l111l1l", + "assets/sec", + "assets/secdata20.jar", + "assets/secdata20.dex", + "libshellx-super.2019.so", + "libshella-super.2019.so", + "libshellx.so", + "libshella.so" + }; + for (String marker : protectedMarkers) { + ZipEntry entry = zipFile.getEntry(marker); + if (entry != null) { + return true; + } + entry = zipFile.getEntry("lib/armeabi/" + marker); + if (entry != null) { + return true; + } + entry = zipFile.getEntry("lib/armeabi-v7a/" + marker); + if (entry != null) { + return true; + } + entry = zipFile.getEntry("lib/arm64-v8a/" + marker); + if (entry != null) { + return true; + } + } + return false; + } finally { + IOHelper.closeQuietly(zipFile); + } + } + + private void checkIsProtectedAppFlag() throws IOException { + if (mOldApkFile == null) { + return; + } + final boolean actuallyProtected = detectProtectedApk(mOldApkFile); + if (mIsProtectedApp && !actuallyProtected) { + throw new TinkerPatchException( + "isProtectedApp is set to true, but the base apk " + mOldApkFile.getAbsolutePath() + + " does not appear to be hardened/protected. Set isProtectedApp to false or use a hardened base apk." + ); + } + if (!mIsProtectedApp && actuallyProtected) { + throw new TinkerPatchException( + "isProtectedApp is set to false, but the base apk " + mOldApkFile.getAbsolutePath() + + " appears to be hardened/protected. Set isProtectedApp to true." + ); + } + } public boolean mRemoveLoaderForAllDex; public boolean mSupportHotplugComponent; /**