[dev_opt_2.15.0] patch升级代码提交
This commit is contained in:
1
core/function-impl/mogo-core-function-patch/.gitignore
vendored
Normal file
1
core/function-impl/mogo-core-function-patch/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
180
core/function-impl/mogo-core-function-patch/build.gradle
Normal file
180
core/function-impl/mogo-core-function-patch/build.gradle
Normal file
@@ -0,0 +1,180 @@
|
||||
import org.gradle.api.execution.*
|
||||
import com.android.build.gradle.tasks.*
|
||||
import com.android.build.gradle.*
|
||||
import org.gradle.process.*
|
||||
import org.gradle.api.invocation.*
|
||||
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'com.alibaba.arouter'
|
||||
id 'kotlin-kapt'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.android.compileSdkVersion
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.android.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.android.targetSdkVersion
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
|
||||
//ARouter apt 参数
|
||||
kapt {
|
||||
useBuildCache = false
|
||||
arguments {
|
||||
arg("AROUTER_MODULE_NAME", project.getName())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs.srcDirs = ['jniLibs']
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation rootProject.ext.dependencies.androidxccorektx
|
||||
implementation rootProject.ext.dependencies.androidxappcompat
|
||||
implementation rootProject.ext.dependencies.material
|
||||
androidTestImplementation rootProject.ext.dependencies.androidxjunit
|
||||
androidTestImplementation rootProject.ext.dependencies.junit
|
||||
implementation rootProject.ext.dependencies.lancetx_runtime
|
||||
implementation rootProject.ext.dependencies.arouter
|
||||
kapt rootProject.ext.dependencies.aroutercompiler
|
||||
if (Boolean.valueOf(USE_MAVEN_PACKAGE)) {
|
||||
implementation rootProject.ext.dependencies.moduleservice
|
||||
implementation rootProject.ext.dependencies.mogo_core_data
|
||||
implementation rootProject.ext.dependencies.mogo_core_utils
|
||||
implementation rootProject.ext.dependencies.mogo_core_function_api
|
||||
implementation rootProject.ext.dependencies.mogo_core_res
|
||||
} else {
|
||||
implementation project(':core:mogo-core-data')
|
||||
implementation project(':core:mogo-core-utils')
|
||||
implementation project(':core:mogo-core-function-api')
|
||||
implementation project(':core:mogo-core-res')
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.gradle.taskGraph.whenReady { TaskExecutionGraph graph ->
|
||||
def tasks = graph.allTasks.findAll { t0 ->
|
||||
t0 instanceof PackageApplication
|
||||
}
|
||||
def app = rootProject.subprojects.find {
|
||||
it.plugins.hasPlugin("com.android.application")
|
||||
}
|
||||
if (app != null) {
|
||||
def android = (AppExtension)app.extensions.findByName("android")
|
||||
def signConfig = android.signingConfigs.getByName("release")
|
||||
if (signConfig != null && isOnlineReleaseBuild(gradle)) {
|
||||
if (tasks != null) {
|
||||
tasks.forEach { t1 ->
|
||||
t1.doLast { t2 ->
|
||||
def packageTask = (PackageApplication) t2
|
||||
def outDir = packageTask.outputDirectory
|
||||
def apkNames = packageTask.apkNames
|
||||
if (apkNames != null && apkNames.size() > 0) {
|
||||
apkNames.forEach { apkName ->
|
||||
def apk = new File(outDir, apkName)
|
||||
def lp = new File(rootProject.rootDir, "local.properties")
|
||||
if (apk.exists() && lp.exists()) {
|
||||
def p = new Properties()
|
||||
lp.withInputStream { instr ->
|
||||
p.load(instr)
|
||||
}
|
||||
def sdkDir = p.getProperty('sdk.dir')
|
||||
def apkSigner = new File(sdkDir, isWindows() ? "build-tools${File.separator}${android.buildToolsVersion}${File.separator}apksigner.bat" : "build-tools${File.separator}${android.buildToolsVersion}${File.separator}apksigner")
|
||||
|
||||
def newApk = new File(outDir, "normalized_" + apkName)
|
||||
def outApk = new File(outDir, "signed_" + apkName)
|
||||
def workDir = new File(projectDir, "shell")
|
||||
def shell = isMac() ? ".${File.separator}macos${File.separator}ApkNormalized" : (isLinux() ? ".${File.separator}linux64${File.separator}ApkNormalized" : ".${File.separator}windows64${File.separator}ApkNormalized.exe")
|
||||
println "***** apk:${apk.name} -> 开始执行apk文件格式对齐 *****"
|
||||
ExecResult result = exec {
|
||||
workingDir workDir
|
||||
commandLine shell, "${apk.absolutePath}", "${newApk.absolutePath}"
|
||||
}
|
||||
def code = result.exitValue
|
||||
if (code == 0) {
|
||||
println "***** apk:${apk.name} -> apk文件格式对齐完成 *****"
|
||||
}
|
||||
|
||||
println "***** apk:${newApk.name} -> 对文件格式对齐后的apk重新签名 *****"
|
||||
result = exec {
|
||||
workingDir apkSigner.parentFile
|
||||
commandLine ".${File.separator}${apkSigner.name}", "sign", "-v",
|
||||
"--out", "${outApk.absolutePath}",
|
||||
"--ks", "${signConfig.storeFile.absolutePath}",
|
||||
"--ks-key-alias", "${signConfig.keyAlias}",
|
||||
"--ks-pass", "pass:${signConfig.storePassword}",
|
||||
"--key-pass", "pass:${signConfig.keyPassword}",
|
||||
"--pass-encoding", "utf-8",
|
||||
"${newApk.absolutePath}"
|
||||
}
|
||||
code = result.exitValue
|
||||
if (code == 0) {
|
||||
println "***** apk:${newApk.name} -> 签名成功 *****"
|
||||
}
|
||||
println "***** 正在删除临时文件 *****"
|
||||
try {
|
||||
newApk.delete()
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace()
|
||||
throw t
|
||||
}
|
||||
try {
|
||||
outApk.renameTo(apk)
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace()
|
||||
throw t
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isWindows() {
|
||||
return System.getProperty("os.name").toLowerCase().contains('windows')
|
||||
}
|
||||
|
||||
static boolean isLinux() {
|
||||
return System.getProperty("os.name").toLowerCase().contains('linux')
|
||||
}
|
||||
|
||||
static boolean isMac() {
|
||||
return System.getProperty("os.name").toLowerCase().contains('mac')
|
||||
}
|
||||
|
||||
static boolean isOnlineReleaseBuild(Gradle g) {
|
||||
StringBuilder sb = new StringBuilder()
|
||||
for (String s : g.startParameter.taskNames) {
|
||||
sb.append(s)
|
||||
}
|
||||
String buildScript = sb.toString().toLowerCase()
|
||||
return buildScript.endsWith("release") && buildScript.contains("online")
|
||||
}
|
||||
|
||||
|
||||
BIN
core/function-impl/mogo-core-function-patch/jniLibs/arm64-v8a/libapkpatch.so
Executable file
BIN
core/function-impl/mogo-core-function-patch/jniLibs/arm64-v8a/libapkpatch.so
Executable file
Binary file not shown.
BIN
core/function-impl/mogo-core-function-patch/jniLibs/armeabi-v7a/libapkpatch.so
Executable file
BIN
core/function-impl/mogo-core-function-patch/jniLibs/armeabi-v7a/libapkpatch.so
Executable file
Binary file not shown.
BIN
core/function-impl/mogo-core-function-patch/jniLibs/x86/libapkpatch.so
Executable file
BIN
core/function-impl/mogo-core-function-patch/jniLibs/x86/libapkpatch.so
Executable file
Binary file not shown.
BIN
core/function-impl/mogo-core-function-patch/jniLibs/x86_64/libapkpatch.so
Executable file
BIN
core/function-impl/mogo-core-function-patch/jniLibs/x86_64/libapkpatch.so
Executable file
Binary file not shown.
21
core/function-impl/mogo-core-function-patch/proguard-rules.pro
vendored
Normal file
21
core/function-impl/mogo-core-function-patch/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
BIN
core/function-impl/mogo-core-function-patch/shell/linux64/ApkNormalized
Executable file
BIN
core/function-impl/mogo-core-function-patch/shell/linux64/ApkNormalized
Executable file
Binary file not shown.
BIN
core/function-impl/mogo-core-function-patch/shell/macos/ApkNormalized
Executable file
BIN
core/function-impl/mogo-core-function-patch/shell/macos/ApkNormalized
Executable file
Binary file not shown.
BIN
core/function-impl/mogo-core-function-patch/shell/windows64/ApkNormalized.exe
Executable file
BIN
core/function-impl/mogo-core-function-patch/shell/windows64/ApkNormalized.exe
Executable file
Binary file not shown.
@@ -0,0 +1,21 @@
|
||||
package com.mogo.launcer.patch
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class) class ExampleInstrumentedTest {
|
||||
@Test fun useAppContext() { // Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.mogo.launcer.patch.test", appContext.packageName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mogo.launcher.patch">
|
||||
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
||||
<application>
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileProvider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
<meta-data android:name="SUPPORT_PATCH" android:value="1" />
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.github.sisong;
|
||||
|
||||
public class ApkPatch{
|
||||
|
||||
static {
|
||||
System.loadLibrary("apkpatch");
|
||||
}
|
||||
|
||||
// return TPatchResult, 0 is ok; patchFilePath file created by ZipDiff;
|
||||
public static native int patch(String oldApkPath,String patchFilePath,String outNewApkPath,
|
||||
long maxUncompressMemory,String tempUncompressFilePath,int threadNum);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.mogo.launcher.patch
|
||||
|
||||
import android.content.*
|
||||
import android.text.TextUtils
|
||||
import android.util.*
|
||||
import com.github.sisong.*
|
||||
import com.mogo.eagle.core.utilcode.util.AppUtils
|
||||
import com.mogo.launcher.patch.utils.*
|
||||
import java.io.File
|
||||
|
||||
internal object PatchManager {
|
||||
|
||||
private const val TAG = "PatchManager"
|
||||
fun isPatchAccept(context: Context, expectSourceMd5: String, patchSize: Long): Boolean {
|
||||
val apkMd5 = AppUtils.getAppApkMd5()
|
||||
if (TextUtils.isEmpty(apkMd5)) {
|
||||
return false
|
||||
}
|
||||
if (apkMd5 != expectSourceMd5) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun applyPatch(ctx: Context, patch: File, newApk: File): Boolean {
|
||||
val oldApkPath = ctx.packageManager.getPackageInfo(ctx.packageName, 0).applicationInfo.sourceDir
|
||||
if (TextUtils.isEmpty(oldApkPath)) {
|
||||
throw AssertionError("旧的apk文件的文件路径为空.")
|
||||
}
|
||||
val oldApk = File(oldApkPath)
|
||||
Log.d(TAG, "applyPatch -- 1 --:oldApk -> ${oldApk.absolutePath}, patch: -> ${patch.absolutePath}, newApk: ${newApk.absolutePath}")
|
||||
if (!oldApk.exists()) {
|
||||
throw AssertionError("旧的apk文件不存在, 文件所在路径:${oldApk.absolutePath}")
|
||||
}
|
||||
Log.d(TAG, "applyPatch -- 2 --")
|
||||
|
||||
if (!patch.exists()) {
|
||||
throw AssertionError("差分包文件不存在,文件所在路径:${patch.absolutePath}")
|
||||
}
|
||||
Log.d(TAG, "applyPatch -- 3 --")
|
||||
val newApkParent = newApk.parentFile
|
||||
if (newApkParent != null && !newApkParent.exists()) {
|
||||
val ret = newApkParent.mkdirs()
|
||||
if (!ret) {
|
||||
throw AssertionError("新包创建父目录失败")
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "applyPatch -- 4 --")
|
||||
if (newApk.exists()) {
|
||||
newApk.delete()
|
||||
}
|
||||
if (newApkParent == null) {
|
||||
throw AssertionError("新包父目录为空")
|
||||
}
|
||||
Log.d(TAG, "applyPatch -- 5 --")
|
||||
val tempFilePath = File(ctx.getExternalFilesDir(null), "temp/temp.patch")
|
||||
|
||||
if (tempFilePath.exists()) {
|
||||
tempFilePath.delete()
|
||||
}
|
||||
tempFilePath.parentFile?.takeIf { !it.exists() }?.mkdirs()
|
||||
val result = ApkPatch.patch(oldApk.absolutePath, patch.absolutePath, newApk.absolutePath, 4 * 1024 * 1024, "", 4)
|
||||
Log.d(TAG, "applyPatch -- end ---: result: $result")
|
||||
if (result != 0) {
|
||||
throw AssertionError("文件合成失败")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun checkMd5ForMergedApk(mergedApk: File, expectNewApkMd5: String): Boolean {
|
||||
if (!mergedApk.exists()) {
|
||||
return false
|
||||
}
|
||||
val md5 = Md5Util.getMd5FromFile(mergedApk)
|
||||
if (md5 != expectNewApkMd5) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.mogo.launcher.patch.provider
|
||||
|
||||
import android.content.*
|
||||
import com.alibaba.android.arouter.facade.annotation.Route
|
||||
import com.mogo.eagle.core.data.constants.MogoServicePaths
|
||||
import com.mogo.eagle.core.function.api.patch.*
|
||||
import com.mogo.launcher.patch.*
|
||||
import java.io.*
|
||||
|
||||
|
||||
@Route(path = MogoServicePaths.PATH_PATCH_UPGRADE)
|
||||
class MoGoPatchProviderImpl : IMoGoPatchProvider {
|
||||
|
||||
|
||||
override fun isPatchAccept(context: Context, expectOldApkMd5: String, patchSize: Long): Boolean {
|
||||
return PatchManager.isPatchAccept(context, expectOldApkMd5, patchSize)
|
||||
}
|
||||
|
||||
override fun applyPatch(context: Context, patch: File, newApk: File): Boolean {
|
||||
return try {
|
||||
PatchManager.applyPatch(context, patch, newApk)
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun checkMd5ForMergedApk(context: Context, mergedApk: File, expectNewApkMd5: String): Boolean {
|
||||
return PatchManager.checkMd5ForMergedApk(mergedApk, expectNewApkMd5)
|
||||
}
|
||||
|
||||
override fun init(context: Context?) { }
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.mogo.launcher.patch.utils
|
||||
|
||||
import java.io.*
|
||||
import java.nio.channels.FileChannel.MapMode
|
||||
import java.security.*
|
||||
|
||||
class Md5Util {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* 获取单个文件的MD5值!
|
||||
* @param file
|
||||
* @return
|
||||
* 解决首位0被省略问题
|
||||
*/
|
||||
fun getMd5FromFile(file: File): String? {
|
||||
var stringbuffer: StringBuffer? = null
|
||||
try {
|
||||
val hexDigits = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f')
|
||||
val input = FileInputStream(file)
|
||||
val ch = input.channel
|
||||
val byteBuffer = ch.map(MapMode.READ_ONLY, 0, file.length())
|
||||
val digest = MessageDigest.getInstance("MD5")
|
||||
digest.update(byteBuffer)
|
||||
val bytes = digest.digest()
|
||||
val n = bytes.size
|
||||
stringbuffer = StringBuffer(2 * n)
|
||||
for (l in 0 until n) {
|
||||
val bt = bytes[l]
|
||||
val c0 = hexDigits[bt.toInt() and 0xf0 shr 4]
|
||||
val c1 = hexDigits[bt.toInt() and 0xf]
|
||||
stringbuffer.append(c0)
|
||||
stringbuffer.append(c1)
|
||||
}
|
||||
} catch (e: java.lang.Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return stringbuffer?.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mogo.launcer.patch
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user