From 5734a01260c36f01705143db9285f625077d0f9f Mon Sep 17 00:00:00 2001 From: ZaneYork Date: Tue, 24 Mar 2020 11:04:57 +0800 Subject: [PATCH] 1.Fix compatibility for Android M 2.SMAPI 3.4.0 3.Galaxy Store compat --- app/build.gradle | 3 +- .../apksig/DefaultApkSignerEngine.java | 8 +-- .../internal/apk/ApkSigningBlockUtils.java | 10 +-- .../internal/apk/v1/V1SchemeSigner.java | 10 +-- .../internal/apk/v1/V1SchemeVerifier.java | 25 ++++---- .../apksig/internal/asn1/Asn1BerParser.java | 22 ++++--- .../apksig/internal/asn1/Asn1DerEncoder.java | 11 ++-- .../internal/util/ChainedDataSource.java | 8 ++- .../util/DelegatingX509Certificate.java | 6 +- .../internal/util/VerityTreeBuilder.java | 3 +- .../internal/util/X509CertificateUtils.java | 5 +- .../smapiinstaller/constant/Constants.java | 4 ++ .../zane/smapiinstaller/logic/ApkPatcher.java | 2 +- .../smapiinstaller/logic/GameLauncher.java | 10 ++- .../zane/smapiinstaller/utils/MathUtils.java | 63 +++++++++++++++++++ .../smapiinstaller/utils/ReflectionUtils.java | 34 ++++++++++ .../java/net/fornwall/apksigner/Base64.java | 30 +++++++++ 17 files changed, 208 insertions(+), 46 deletions(-) create mode 100644 app/src/main/java/com/zane/smapiinstaller/utils/MathUtils.java create mode 100644 app/src/main/java/com/zane/smapiinstaller/utils/ReflectionUtils.java create mode 100644 app/src/main/java/net/fornwall/apksigner/Base64.java diff --git a/app/build.gradle b/app/build.gradle index 1b8939c..27c2d27 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,7 +10,7 @@ android { applicationId "com.zane.smapiinstaller" minSdkVersion 19 targetSdkVersion 28 - versionCode 21 + versionCode 23 versionName "1.3.6" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -86,6 +86,7 @@ dependencies { implementation 'com.github.didikee:AndroidDonate:0.1.0' implementation 'com.hjq:language:3.0' implementation 'org.greenrobot:greendao:3.2.2' + implementation 'net.sourceforge.streamsupport:android-retrostreams:1.7.1' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' diff --git a/app/src/main/java/com/android/apksig/DefaultApkSignerEngine.java b/app/src/main/java/com/android/apksig/DefaultApkSignerEngine.java index c88239e..fcd0c70 100644 --- a/app/src/main/java/com/android/apksig/DefaultApkSignerEngine.java +++ b/app/src/main/java/com/android/apksig/DefaultApkSignerEngine.java @@ -51,8 +51,9 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; +import java9.util.Optional; import java.util.Set; +import java9.util.stream.StreamSupport; /** * Default implementation of {@link ApkSignerEngine}. @@ -437,9 +438,8 @@ public class DefaultApkSignerEngine implements ApkSignerEngine { isDebuggable(entryName)) { Optional extractedDigest = - V1SchemeVerifier.getDigestsToVerify( - entry.getValue(), "-Digest", mMinSdkVersion, Integer.MAX_VALUE) - .stream() + StreamSupport.stream(V1SchemeVerifier.getDigestsToVerify( + entry.getValue(), "-Digest", mMinSdkVersion, Integer.MAX_VALUE)) .filter(d -> d.jcaDigestAlgorithm == alg) .findFirst(); diff --git a/app/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java b/app/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java index cc69af3..4b9b0c6 100644 --- a/app/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java +++ b/app/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java @@ -63,8 +63,10 @@ import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collectors; +import java9.util.function.Supplier; +import java9.util.stream.Collectors; + +import java9.util.stream.StreamSupport; public class ApkSigningBlockUtils { @@ -415,7 +417,7 @@ public class ApkSigningBlockUtils { DataSource centralDir, DataSource eocd) throws IOException, NoSuchAlgorithmException, DigestException { Map contentDigests = new HashMap<>(); - Set oneMbChunkBasedAlgorithm = digestAlgorithms.stream() + Set oneMbChunkBasedAlgorithm = StreamSupport.stream(digestAlgorithms) .filter(a -> a == ContentDigestAlgorithm.CHUNKED_SHA256 || a == ContentDigestAlgorithm.CHUNKED_SHA512) .collect(Collectors.toSet()); @@ -1100,7 +1102,7 @@ public class ApkSigningBlockUtils { if (bestSigAlgorithmOnSdkVersion.isEmpty()) { throw new NoSupportedSignaturesException("No supported signature"); } - return bestSigAlgorithmOnSdkVersion.values().stream() + return StreamSupport.stream(bestSigAlgorithmOnSdkVersion.values()) .sorted((sig1, sig2) -> Integer.compare( sig1.algorithm.getId(), sig2.algorithm.getId())) .collect(Collectors.toList()); diff --git a/app/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java b/app/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java index f900211..b782c59 100644 --- a/app/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java +++ b/app/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeSigner.java @@ -32,6 +32,9 @@ import com.android.apksig.internal.pkcs7.SignedData; import com.android.apksig.internal.pkcs7.SignerIdentifier; import com.android.apksig.internal.pkcs7.SignerInfo; import com.android.apksig.internal.util.Pair; + +import net.fornwall.apksigner.Base64; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -46,7 +49,6 @@ import java.security.SignatureException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Base64; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -375,7 +377,7 @@ public abstract class V1SchemeSigner { Attributes entryAttrs = new Attributes(); entryAttrs.putValue( entryDigestAttributeName, - Base64.getEncoder().encodeToString(entryDigest)); + Base64.encode(entryDigest)); ByteArrayOutputStream sectionOut = new ByteArrayOutputStream(); byte[] sectionBytes; try { @@ -448,7 +450,7 @@ public abstract class V1SchemeSigner { MessageDigest md = getMessageDigestInstance(manifestDigestAlgorithm); mainAttrs.putValue( getManifestDigestAttributeName(manifestDigestAlgorithm), - Base64.getEncoder().encodeToString(md.digest(manifest.contents))); + Base64.encode(md.digest(manifest.contents))); ByteArrayOutputStream out = new ByteArrayOutputStream(); try { SignatureFileWriter.writeMainSection(out, mainAttrs); @@ -464,7 +466,7 @@ public abstract class V1SchemeSigner { Attributes attrs = new Attributes(); attrs.putValue( entryDigestAttributeName, - Base64.getEncoder().encodeToString(sectionDigest)); + Base64.encode(sectionDigest)); try { SignatureFileWriter.writeIndividualSection(out, sectionName, attrs); diff --git a/app/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java b/app/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java index 47d5b01..a55bcaa 100644 --- a/app/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java +++ b/app/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeVerifier.java @@ -47,6 +47,8 @@ import com.android.apksig.util.DataSinks; import com.android.apksig.util.DataSource; import com.android.apksig.zip.ZipFormatException; +import net.fornwall.apksigner.Base64; + import java.io.IOException; import java.math.BigInteger; import java.nio.ByteBuffer; @@ -61,8 +63,6 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; -import java.util.Base64; -import java.util.Base64.Decoder; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -1464,8 +1464,8 @@ public abstract class V1SchemeVerifier { V1SchemeSigner.MANIFEST_ENTRY_NAME, jcaDigestAlgorithm, mSignatureFileEntry.getName(), - Base64.getEncoder().encodeToString(actual), - Base64.getEncoder().encodeToString(expected)); + Base64.encode(actual), + Base64.encode(expected)); verified = false; } } @@ -1506,8 +1506,8 @@ public abstract class V1SchemeVerifier { Issue.JAR_SIG_MANIFEST_MAIN_SECTION_DIGEST_DID_NOT_VERIFY, jcaDigestAlgorithm, mSignatureFileEntry.getName(), - Base64.getEncoder().encodeToString(actual), - Base64.getEncoder().encodeToString(expected)); + Base64.encode(actual), + Base64.encode(expected)); } } } @@ -1559,8 +1559,8 @@ public abstract class V1SchemeVerifier { entryName, jcaDigestAlgorithm, mSignatureFileEntry.getName(), - Base64.getEncoder().encodeToString(actual), - Base64.getEncoder().encodeToString(expected)); + Base64.encode(actual), + Base64.encode(expected)); } } } @@ -1635,7 +1635,6 @@ public abstract class V1SchemeVerifier { String digestAttrSuffix, int minSdkVersion, int maxSdkVersion) { - Decoder base64Decoder = Base64.getDecoder(); List result = new ArrayList<>(1); if (minSdkVersion < AndroidSdkVersion.JELLY_BEAN_MR2) { // Prior to JB MR2, Android platform's logic for picking a digest algorithm to verify is @@ -1664,7 +1663,7 @@ public abstract class V1SchemeVerifier { continue; } // Supported digest algorithm - result.add(new NamedDigest(alg, base64Decoder.decode(digestBase64))); + result.add(new NamedDigest(alg, Base64.decode(digestBase64))); break; } // No supported digests found -- this will fail to verify on pre-JB MR2 Androids. @@ -1683,7 +1682,7 @@ public abstract class V1SchemeVerifier { // Attribute not found continue; } - byte[] digest = base64Decoder.decode(digestBase64); + byte[] digest = Base64.decode(digestBase64); byte[] digestInResult = getDigest(result, alg); if ((digestInResult == null) || (!Arrays.equals(digestInResult, digest))) { result.add(new NamedDigest(alg, digest)); @@ -1902,8 +1901,8 @@ public abstract class V1SchemeVerifier { entryName, expectedDigest.jcaDigestAlgorithm, V1SchemeSigner.MANIFEST_ENTRY_NAME, - Base64.getEncoder().encodeToString(actualDigest), - Base64.getEncoder().encodeToString(expectedDigest.digest)); + Base64.encode(actualDigest), + Base64.encode(expectedDigest.digest)); } } } diff --git a/app/src/main/java/com/android/apksig/internal/asn1/Asn1BerParser.java b/app/src/main/java/com/android/apksig/internal/asn1/Asn1BerParser.java index fdbd6eb..805e918 100644 --- a/app/src/main/java/com/android/apksig/internal/asn1/Asn1BerParser.java +++ b/app/src/main/java/com/android/apksig/internal/asn1/Asn1BerParser.java @@ -22,6 +22,8 @@ import com.android.apksig.internal.asn1.ber.BerDataValueReader; import com.android.apksig.internal.asn1.ber.BerEncoding; import com.android.apksig.internal.asn1.ber.ByteBufferBerDataValueReader; import com.android.apksig.internal.util.ByteBufferUtils; +import com.zane.smapiinstaller.utils.MathUtils; +import com.zane.smapiinstaller.utils.ReflectionUtils; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -311,7 +313,7 @@ public final class Asn1BerParser { private static Asn1Type getContainerAsn1Type(Class containerClass) throws Asn1DecodingException { - Asn1Class containerAnnotation = containerClass.getDeclaredAnnotation(Asn1Class.class); + Asn1Class containerAnnotation = ReflectionUtils.getDeclaredAnnotation(containerClass, Asn1Class.class); if (containerAnnotation == null) { throw new Asn1DecodingException( containerClass.getName() + " is not annotated with " @@ -332,7 +334,13 @@ public final class Asn1BerParser { private static Class getElementType(Field field) throws Asn1DecodingException, ClassNotFoundException { - String type = field.getGenericType().getTypeName(); + String type; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { + type = field.getGenericType().getTypeName(); + } + else { + type = field.getGenericType().toString(); + } int delimiterIndex = type.indexOf('<'); if (delimiterIndex == -1) { throw new Asn1DecodingException("Not a container type: " + field.getGenericType()); @@ -508,7 +516,7 @@ public final class Asn1BerParser { private static int integerToInt(ByteBuffer encoded) throws Asn1DecodingException { BigInteger value = integerToBigInteger(encoded); try { - return value.intValue(); + return MathUtils.intValueExact(value); } catch (ArithmeticException e) { throw new Asn1DecodingException( String.format("INTEGER cannot be represented as int: %1$d (0x%1$x)", value), e); @@ -518,7 +526,7 @@ public final class Asn1BerParser { private static long integerToLong(ByteBuffer encoded) throws Asn1DecodingException { BigInteger value = integerToBigInteger(encoded); try { - return value.longValue(); + return MathUtils.longValueExact(value); } catch (ArithmeticException e) { throw new Asn1DecodingException( String.format("INTEGER cannot be represented as long: %1$d (0x%1$x)", value), @@ -531,7 +539,7 @@ public final class Asn1BerParser { Field[] declaredFields = containerClass.getDeclaredFields(); List result = new ArrayList<>(declaredFields.length); for (Field field : declaredFields) { - Asn1Field annotation = field.getDeclaredAnnotation(Asn1Field.class); + Asn1Field annotation = ReflectionUtils.getDeclaredAnnotation(field, Asn1Field.class); if (annotation == null) { continue; } @@ -646,7 +654,7 @@ public final class Asn1BerParser { case SEQUENCE: { Asn1Class containerAnnotation = - targetType.getDeclaredAnnotation(Asn1Class.class); + ReflectionUtils.getDeclaredAnnotation(targetType, Asn1Class.class); if ((containerAnnotation != null) && (containerAnnotation.type() == Asn1Type.SEQUENCE)) { return parseSequence(dataValue, targetType); @@ -656,7 +664,7 @@ public final class Asn1BerParser { case CHOICE: { Asn1Class containerAnnotation = - targetType.getDeclaredAnnotation(Asn1Class.class); + ReflectionUtils.getDeclaredAnnotation(targetType, Asn1Class.class); if ((containerAnnotation != null) && (containerAnnotation.type() == Asn1Type.CHOICE)) { return parseChoice(dataValue, targetType); diff --git a/app/src/main/java/com/android/apksig/internal/asn1/Asn1DerEncoder.java b/app/src/main/java/com/android/apksig/internal/asn1/Asn1DerEncoder.java index 22a432f..56601c1 100644 --- a/app/src/main/java/com/android/apksig/internal/asn1/Asn1DerEncoder.java +++ b/app/src/main/java/com/android/apksig/internal/asn1/Asn1DerEncoder.java @@ -17,8 +17,11 @@ package com.android.apksig.internal.asn1; import com.android.apksig.internal.asn1.ber.BerEncoding; +import com.google.common.collect.Iterables; +import com.zane.smapiinstaller.utils.ReflectionUtils; import java.io.ByteArrayOutputStream; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.math.BigInteger; @@ -53,7 +56,7 @@ public final class Asn1DerEncoder { */ public static byte[] encode(Object container) throws Asn1EncodingException { Class containerClass = container.getClass(); - Asn1Class containerAnnotation = containerClass.getDeclaredAnnotation(Asn1Class.class); + Asn1Class containerAnnotation = ReflectionUtils.getDeclaredAnnotation(containerClass, Asn1Class.class); if (containerAnnotation == null) { throw new Asn1EncodingException( containerClass.getName() + " not annotated with " + Asn1Class.class.getName()); @@ -216,7 +219,7 @@ public final class Asn1DerEncoder { Field[] declaredFields = containerClass.getDeclaredFields(); List result = new ArrayList<>(declaredFields.length); for (Field field : declaredFields) { - Asn1Field annotation = field.getDeclaredAnnotation(Asn1Field.class); + Asn1Field annotation = ReflectionUtils.getDeclaredAnnotation(field, Asn1Field.class); if (annotation == null) { continue; } @@ -561,7 +564,7 @@ public final class Asn1DerEncoder { case SEQUENCE: { Asn1Class containerAnnotation = - sourceType.getDeclaredAnnotation(Asn1Class.class); + ReflectionUtils.getDeclaredAnnotation(sourceType, Asn1Class.class); if ((containerAnnotation != null) && (containerAnnotation.type() == Asn1Type.SEQUENCE)) { return toSequence(source); @@ -571,7 +574,7 @@ public final class Asn1DerEncoder { case CHOICE: { Asn1Class containerAnnotation = - sourceType.getDeclaredAnnotation(Asn1Class.class); + ReflectionUtils.getDeclaredAnnotation(sourceType, Asn1Class.class); if ((containerAnnotation != null) && (containerAnnotation.type() == Asn1Type.CHOICE)) { return toChoice(source); diff --git a/app/src/main/java/com/android/apksig/internal/util/ChainedDataSource.java b/app/src/main/java/com/android/apksig/internal/util/ChainedDataSource.java index a0baf1a..b89ced0 100644 --- a/app/src/main/java/com/android/apksig/internal/util/ChainedDataSource.java +++ b/app/src/main/java/com/android/apksig/internal/util/ChainedDataSource.java @@ -18,11 +18,15 @@ package com.android.apksig.internal.util; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSource; +import com.zane.smapiinstaller.utils.MathUtils; + import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; +import java9.util.stream.Stream; + /** Pseudo {@link DataSource} that chains the given {@link DataSource} as a continuous one. */ public class ChainedDataSource implements DataSource { @@ -31,7 +35,7 @@ public class ChainedDataSource implements DataSource { public ChainedDataSource(DataSource... sources) { mSources = sources; - mTotalSize = Arrays.stream(sources).mapToLong(src -> src.size()).sum(); + mTotalSize = Stream.of(sources).mapToLong(src -> src.size()).sum(); } @Override @@ -86,7 +90,7 @@ public class ChainedDataSource implements DataSource { ByteBuffer buffer = ByteBuffer.allocate(size); for (; i < mSources.length && buffer.hasRemaining(); i++) { long sizeToCopy = Math.min(mSources[i].size() - offset, buffer.remaining()); - mSources[i].copyTo(offset, Math.toIntExact(sizeToCopy), buffer); + mSources[i].copyTo(offset, MathUtils.toIntExact(sizeToCopy), buffer); offset = 0; // may not be zero for the first source, but reset after that. } buffer.rewind(); diff --git a/app/src/main/java/com/android/apksig/internal/util/DelegatingX509Certificate.java b/app/src/main/java/com/android/apksig/internal/util/DelegatingX509Certificate.java index 8f9e1fd..3f39e2c 100644 --- a/app/src/main/java/com/android/apksig/internal/util/DelegatingX509Certificate.java +++ b/app/src/main/java/com/android/apksig/internal/util/DelegatingX509Certificate.java @@ -16,6 +16,8 @@ package com.android.apksig.internal.util; +import android.os.Build; + import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -212,6 +214,8 @@ public class DelegatingX509Certificate extends X509Certificate { @Override public void verify(PublicKey key, Provider sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { - mDelegate.verify(key, sigProvider); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + mDelegate.verify(key, sigProvider); + } } } diff --git a/app/src/main/java/com/android/apksig/internal/util/VerityTreeBuilder.java b/app/src/main/java/com/android/apksig/internal/util/VerityTreeBuilder.java index 75497a7..0635df8 100644 --- a/app/src/main/java/com/android/apksig/internal/util/VerityTreeBuilder.java +++ b/app/src/main/java/com/android/apksig/internal/util/VerityTreeBuilder.java @@ -20,6 +20,7 @@ import com.android.apksig.internal.zip.ZipUtils; import com.android.apksig.util.DataSink; import com.android.apksig.util.DataSource; import com.android.apksig.util.DataSources; +import com.zane.smapiinstaller.utils.MathUtils; import java.io.IOException; import java.nio.ByteBuffer; @@ -155,7 +156,7 @@ public class VerityTreeBuilder { levelOffset[0] = 0; for (int i = 0; i < levelSize.size(); i++) { // We don't support verity tree if it is larger then Integer.MAX_VALUE. - levelOffset[i + 1] = levelOffset[i] + Math.toIntExact( + levelOffset[i + 1] = levelOffset[i] + MathUtils.toIntExact( levelSize.get(levelSize.size() - i - 1)); } return levelOffset; diff --git a/app/src/main/java/com/android/apksig/internal/util/X509CertificateUtils.java b/app/src/main/java/com/android/apksig/internal/util/X509CertificateUtils.java index 9a266f2..4741164 100644 --- a/app/src/main/java/com/android/apksig/internal/util/X509CertificateUtils.java +++ b/app/src/main/java/com/android/apksig/internal/util/X509CertificateUtils.java @@ -22,6 +22,8 @@ import com.android.apksig.internal.asn1.Asn1DerEncoder; import com.android.apksig.internal.asn1.Asn1EncodingException; import com.android.apksig.internal.x509.Certificate; +import net.fornwall.apksigner.Base64; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -30,7 +32,6 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Base64; import java.util.Collection; /** @@ -261,7 +262,7 @@ public class X509CertificateUtils { + "valid certificate footer"); } } - byte[] derEncoding = Base64.getDecoder().decode(pemEncoding.toString()); + byte[] derEncoding = Base64.decode(pemEncoding.toString()); // consume any trailing whitespace in the byte buffer int nextEncodedChar = certificateBuffer.position(); while (certificateBuffer.hasRemaining()) { diff --git a/app/src/main/java/com/zane/smapiinstaller/constant/Constants.java b/app/src/main/java/com/zane/smapiinstaller/constant/Constants.java index f6375e0..bd72c4c 100644 --- a/app/src/main/java/com/zane/smapiinstaller/constant/Constants.java +++ b/app/src/main/java/com/zane/smapiinstaller/constant/Constants.java @@ -21,6 +21,10 @@ public class Constants { * 安装包目标包名 */ public static final String TARGET_PACKAGE_NAME = "com.zane.stardewvalley"; + /** + * 安装包目标包名 + */ + public static final String TARGET_PACKAGE_NAME_SAMSUNG = "com.zane.stardewvalleysamsung"; /** * DLC下载路径 */ diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java b/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java index 05928ac..dd28d5d 100644 --- a/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java +++ b/app/src/main/java/com/zane/smapiinstaller/logic/ApkPatcher.java @@ -161,7 +161,7 @@ public class ApkPatcher { case "package": if (packageName.get() == null) { packageName.set(strObj); - attr.obj = Constants.TARGET_PACKAGE_NAME; + attr.obj = strObj.replace(ManifestPatchConstants.APP_PACKAGE_NAME, Constants.TARGET_PACKAGE_NAME); } break; case "label": diff --git a/app/src/main/java/com/zane/smapiinstaller/logic/GameLauncher.java b/app/src/main/java/com/zane/smapiinstaller/logic/GameLauncher.java index 4b0ef11..4b1299d 100644 --- a/app/src/main/java/com/zane/smapiinstaller/logic/GameLauncher.java +++ b/app/src/main/java/com/zane/smapiinstaller/logic/GameLauncher.java @@ -29,15 +29,21 @@ public class GameLauncher { Activity context = CommonLogic.getActivityFromView(root); PackageManager packageManager = context.getPackageManager(); try { - PackageInfo packageInfo = packageManager.getPackageInfo(Constants.TARGET_PACKAGE_NAME, 0); + PackageInfo packageInfo; + try { + packageInfo = packageManager.getPackageInfo(Constants.TARGET_PACKAGE_NAME, 0); + } catch (PackageManager.NameNotFoundException ignored) { + packageInfo = packageManager.getPackageInfo(Constants.TARGET_PACKAGE_NAME_SAMSUNG, 0); + } if(!CommonLogic.unpackSmapiFiles(context, packageInfo.applicationInfo.publicSourceDir, true)) { DialogUtils.showAlertDialog(root, R.string.error, R.string.error_failed_to_repair); return; } ModAssetsManager modAssetsManager = new ModAssetsManager(root); + PackageInfo finalPackageInfo = packageInfo; modAssetsManager.checkModEnvironment((isConfirm) -> { if(isConfirm) { - Intent intent = packageManager.getLaunchIntentForPackage(Constants.TARGET_PACKAGE_NAME); + Intent intent = packageManager.getLaunchIntentForPackage(finalPackageInfo.packageName); context.startActivity(intent); } }); diff --git a/app/src/main/java/com/zane/smapiinstaller/utils/MathUtils.java b/app/src/main/java/com/zane/smapiinstaller/utils/MathUtils.java new file mode 100644 index 0000000..04f278e --- /dev/null +++ b/app/src/main/java/com/zane/smapiinstaller/utils/MathUtils.java @@ -0,0 +1,63 @@ +package com.zane.smapiinstaller.utils; + +import java.math.BigDecimal; +import java.math.BigInteger; + +public class MathUtils { + + /** + * Returns the value of the {@code long} argument; + * throwing an exception if the value overflows an {@code int}. + * + * @param value the long value + * @return the argument as an int + * @throws ArithmeticException if the {@code argument} overflows an int + * @since 1.8 + */ + public static int toIntExact(long value) { + if ((int)value != value) { + throw new ArithmeticException("integer overflow"); + } + return (int)value; + } + + /** + * Converts this {@code BigInteger} to an {@code int}, checking + * for lost information. If the value of this {@code BigInteger} + * is out of the range of the {@code int} type, then an + * {@code ArithmeticException} is thrown. + * + * @return this {@code BigInteger} converted to an {@code int}. + * @throws ArithmeticException if the value of {@code this} will + * not exactly fit in a {@code int}. + * @see BigInteger#intValue + * @since 1.8 + */ + public static int intValueExact(BigInteger value) { + if (value.bitLength() <= 31) { + return value.intValue(); + } else { + throw new ArithmeticException("BigInteger out of int range"); + } + } + + /** + * Converts this {@code BigInteger} to a {@code long}, checking + * for lost information. If the value of this {@code BigInteger} + * is out of the range of the {@code long} type, then an + * {@code ArithmeticException} is thrown. + * + * @return this {@code BigInteger} converted to a {@code long}. + * @throws ArithmeticException if the value of {@code this} will + * not exactly fit in a {@code long}. + * @see BigInteger#longValue + * @since 1.8 + */ + public static long longValueExact(BigInteger value) { + if (value.bitLength() <= 63) { + return value.longValue(); + } else { + throw new ArithmeticException("BigInteger out of long range"); + } + } +} diff --git a/app/src/main/java/com/zane/smapiinstaller/utils/ReflectionUtils.java b/app/src/main/java/com/zane/smapiinstaller/utils/ReflectionUtils.java new file mode 100644 index 0000000..50efeb3 --- /dev/null +++ b/app/src/main/java/com/zane/smapiinstaller/utils/ReflectionUtils.java @@ -0,0 +1,34 @@ +package com.zane.smapiinstaller.utils; + +import android.os.Build; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +public class ReflectionUtils { + public static T getDeclaredAnnotation(Field targetField, Class targetAnnotation) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return targetField.getDeclaredAnnotation(targetAnnotation); + } + Annotation[] declaredAnnotations = targetField.getDeclaredAnnotations(); + for (Annotation annotation : declaredAnnotations) { + if(targetAnnotation.isAssignableFrom(annotation.getClass())) { + return (T) annotation; + } + } + return null; + } + + public static T getDeclaredAnnotation(Class targetClass, Class targetAnnotation) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return targetClass.getDeclaredAnnotation(targetAnnotation); + } + Annotation[] declaredAnnotations = targetClass.getDeclaredAnnotations(); + for (Annotation annotation : declaredAnnotations) { + if(targetAnnotation.isAssignableFrom(annotation.getClass())) { + return (T) annotation; + } + } + return null; + } +} diff --git a/app/src/main/java/net/fornwall/apksigner/Base64.java b/app/src/main/java/net/fornwall/apksigner/Base64.java new file mode 100644 index 0000000..ca283e5 --- /dev/null +++ b/app/src/main/java/net/fornwall/apksigner/Base64.java @@ -0,0 +1,30 @@ +package net.fornwall.apksigner; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.spongycastle.util.encoders.Base64Encoder; + +/** Base64 encoding handling in a portable way across Android and JSE. */ +public class Base64 { + + public static String encode(byte[] data) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + new Base64Encoder().encode(data, 0, data.length, baos); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new String(baos.toByteArray()); + } + + public static byte[] decode(String data) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + new Base64Encoder().decode(data, baos); + } catch (IOException e) { + throw new RuntimeException(e); + } + return baos.toByteArray(); + } +} \ No newline at end of file