Merge pull request #121 from ostajic/dev

New Root Install Method & Log code & Version Bump
This commit is contained in:
Tornike Khintibidze 2020-08-24 13:37:47 +04:00 committed by GitHub
commit 96a9950644
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 359 additions and 57 deletions

View File

@ -7,15 +7,15 @@ apply plugin: 'com.google.firebase.firebase-perf'
apply plugin: 'com.google.firebase.crashlytics'
android {
compileSdkVersion 30
buildToolsVersion "30.0.1"
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.vanced.manager"
minSdkVersion 21
targetSdkVersion 30
versionCode 12
versionName "1.2.0 (Niko)"
targetSdkVersion 29
versionCode 13
versionName "1.2.1 (Niko)"
vectorDrawables.useSupportLibrary = true
}
@ -85,6 +85,7 @@ dependencies {
implementation 'com.github.kittinunf.fuel:fuel-coroutines:2.2.3'
implementation 'com.github.kittinunf.fuel:fuel-json:2.2.3'
implementation 'com.github.topjohnwu.libsu:core:3.0.1'
implementation 'com.github.topjohnwu.libsu:io:3.0.1'
implementation 'com.google.firebase:firebase-messaging:20.2.4'
implementation 'com.google.firebase:firebase-perf:19.0.8'
implementation 'com.mindorks.android:prdownloader:0.6.0'

View File

@ -14,15 +14,15 @@ class App: Application() {
super.onCreate()
PRDownloader.initialize(this)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
//if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
Crowdin.init(this,
CrowdinConfig.Builder()
.withDistributionHash("36c51aed3180a4f43073d28j4s6")
.withNetworkType(NetworkType.WIFI)
.build()
)
}
//}
}
/*

View File

@ -57,10 +57,14 @@ class VancedDownloadService: Service() {
Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a"
else -> "armeabi_v7a"
}
val themePath = "$installUrl/apks/v$vancedVer/$variant/Theme"
val url =
when (type) {
"arch" -> "$installUrl/apks/v$vancedVer/$variant/Arch/split_config.$arch.apk"
"theme" -> "$installUrl/apks/v$vancedVer/$variant/Theme/$theme.apk"
"hash" -> "$themePath/hash.json"
"theme" -> "$themePath/$theme.apk"
"stock" -> "$themePath/stock.apk"
"dpi" -> "$themePath/dpi.apk"
"lang" -> "$installUrl/apks/v$vancedVer/$variant/Language/split_config.${lang?.get(count)}.apk"
else -> throw NotImplementedError("This type of APK is NOT valid. What the hell did you even do?")
}
@ -80,7 +84,10 @@ class VancedDownloadService: Service() {
override fun onDownloadComplete() {
when (type) {
"arch" -> downloadSplits("theme")
"theme" -> downloadSplits("lang")
"theme" -> if(variant=="root") downloadSplits("stock") else downloadSplits("lang")
"stock" -> downloadSplits("dpi")
"dpi" -> downloadSplits("hash")
"hash" -> downloadSplits("lang")
"lang" -> {
count++
if (count < lang?.count()!!)
@ -88,6 +95,7 @@ class VancedDownloadService: Service() {
else
prepareInstall(variant!!)
}
}
}

View File

@ -2,36 +2,104 @@ package com.vanced.manager.core.installer
import android.app.Service
import android.content.Intent
import android.content.pm.PackageInfo
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.annotation.Nullable
import androidx.annotation.WorkerThread
import androidx.core.net.toUri
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.beust.klaxon.JsonObject
import com.beust.klaxon.Parser
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.io.SuFile
import com.vanced.manager.BuildConfig
import com.vanced.manager.ui.fragments.HomeFragment
import com.vanced.manager.utils.AppUtils.sendFailure
import com.vanced.manager.utils.FileInfo
import com.vanced.manager.utils.InternetTools.getJsonInt
import com.vanced.manager.utils.PackageHelper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.*
import java.util.regex.Pattern
import kotlin.collections.ArrayList
class RootSplitInstallerService: Service() {
private var hashjson: FileInfo? = null
private var vancedVersionCode: Int = 0
val yPkg = "com.google.android.youtube"
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) }
suspend fun getVer()
{
vancedVersionCode = getJsonInt("vanced.json","versionCode", application)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Shell.enableVerboseLogging = BuildConfig.DEBUG
Shell.setDefaultBuilder(
Shell.Builder.create()
.setFlags(Shell.FLAG_REDIRECT_STDERR)
.setTimeout(10)
)
runBlocking { getVer() }
Shell.getShell {
CoroutineScope(Dispatchers.IO).launch {
var job = CoroutineScope(Dispatchers.IO).launch{
val apkFilesPath = getExternalFilesDir("apks")?.path
val fileInfoList = apkFilesPath?.let { it1 -> getFileInfoList(it1) }
if (fileInfoList != null) {
installSplitApkFiles(fileInfoList)
var modApk: FileInfo? = null
for (fil in fileInfoList)
{
if(fil.name == "dark.apk" || fil.name == "black.apk")
{
modApk = fil
}
if(fil.name == "hash.json")
{
hashjson = fil
}
}
if (modApk != null && hashjson != null) {
val hash = parseJson(modApk.name.split(".")[0], hashjson!!)
if(overwriteBase(modApk, fileInfoList, vancedVersionCode,hash))
{
with(localBroadcastManager) {
sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
sendBroadcast(Intent(HomeFragment.VANCED_INSTALLED))
}
}
else
{
sendFailure(listOf("Install Failed").toMutableList(), applicationContext)
}
}
else
{
sendFailure(listOf("modApk Is Null Missing (dark.apk/black.apk) In apks Folder").toMutableList(), applicationContext)
}
//installSplitApkFiles(fileInfoList)
}
else
{
sendFailure(listOf("Files are missing, Failed Download?").toMutableList(), applicationContext)
}
}
@ -40,8 +108,16 @@ class RootSplitInstallerService: Service() {
return START_NOT_STICKY
}
private fun parseJson(s: String, hashjson: FileInfo): String
{
val jsonData = SuFile.open(hashjson.file!!.absolutePath).readText(Charsets.UTF_8)
val jsonObject = Parser.default().parse(StringBuilder(jsonData)) as JsonObject
return jsonObject.string(s)!!
}
@WorkerThread
private fun installSplitApkFiles(apkFiles: ArrayList<FileInfo>) {
private fun installSplitApkFiles(apkFiles: ArrayList<FileInfo>) : Boolean {
var sessionId: Int?
Log.d("AppLog", "installing split apk files:$apkFiles")
run {
@ -52,31 +128,32 @@ class RootSplitInstallerService: Service() {
sessionId = Integer.parseInt(sessionIdMatcher.group(1)!!)
}
apkFiles.forEach { apkFile ->
Log.d("AppLog", "installing APK : ${apkFile.name} ${apkFile.fileSize} ")
val command = arrayOf("su", "-c", "pm", "install-write", "-S", "${apkFile.fileSize}", "$sessionId", apkFile.name)
val process: Process = Runtime.getRuntime().exec(command)
val inputPipe = apkFile.getInputStream()
try {
process.outputStream.use { outputStream -> inputPipe.copyTo(outputStream) }
} catch (e: Exception) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
process.destroyForcibly()
else
process.destroy()
if(apkFile.name != "black.apk" && apkFile.name != "dark.apk" && apkFile.name != "hash.json")
{
Log.d("AppLog", "installing APK : ${apkFile.name} ${apkFile.fileSize} ")
val command = arrayOf("su", "-c", "pm", "install-write", "-S", "${apkFile.fileSize}", "$sessionId", apkFile.name)
val process: Process = Runtime.getRuntime().exec(command)
val inputPipe = apkFile.getInputStream()
try {
process.outputStream.use { outputStream -> inputPipe.copyTo(outputStream) }
} catch (e: Exception) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
process.destroyForcibly()
else
process.destroy()
throw RuntimeException(e)
throw RuntimeException(e)
}
process.waitFor()
}
process.waitFor()
}
Log.d("AppLog", "committing...")
val installResult = Shell.su("pm install-commit $sessionId").exec()
if (installResult.isSuccess) {
with(localBroadcastManager) {
sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
sendBroadcast(Intent(HomeFragment.VANCED_INSTALLED))
}
return true
} else
sendFailure(installResult.out, this)
return false
}
private fun SimpleDateFormat.tryParse(str: String) = try {
@ -128,4 +205,227 @@ class RootSplitInstallerService: Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
//install Vanced
private fun overwriteBase(apkFile: FileInfo,baseApkFiles: ArrayList<FileInfo>, versionCode: Int,hash: String): Boolean
{
if(checkVersion(versionCode,baseApkFiles))
{
val path = getVPath()
apkFile.file?.let {
val apath = it.absolutePath
if(sha256Check(apath,hash))
{
if(path?.let { it1 -> moveAPK(apath, it1) }!!)
{
val fpath = SuFile.open(path).parent!!
return chConV(path)
}
}
else
{
sendFailure(listOf("Download Went Corrupt, Retry or clear VanM Data").toMutableList(), applicationContext)
}
}
}
return false
}
//do sha256 check on downloaded apk
private fun sha256Check(apath: String, hash: String): Boolean {
val sfile = SuFile.open(apath)
return checkSHA256(hash,sfile)
}
//check version and perform action based on result
private fun checkVersion(versionCode: Int, baseApkFiles: ArrayList<FileInfo>): Boolean {
val path = getVPath()
if (path != null) {
if(path.contains("/data/app/"))
{
when(getPkgVerCode(yPkg)?.let { compareVersion(it,versionCode) })
{
1 -> {return fixHigherVer(baseApkFiles) }
-1 -> {return fixLowerVer(baseApkFiles) }
}
return true
}
else
{
return fixNoInstall(baseApkFiles)
}
}
return fixNoInstall(baseApkFiles)
}
private fun getPkgVerCode(pkg: String): Int? {
val pm = packageManager
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
pm.getPackageInfo(pkg, 0)?.longVersionCode?.and(0xFFFFFFFF)?.toInt()
else
pm.getPackageInfo(pkg, 0)?.versionCode
}
private fun getPkgInfo(pkg: String): PackageInfo?
{
return try {
val m = packageManager
val info = m.getPackageInfo(pkg, 0)
info
} catch (e:Exception) {
null
}
}
private fun compareVersion(pkgVerCode: Int, versionCode: Int): Int
{
return if(pkgVerCode > versionCode)
1
else if (pkgVerCode < versionCode)
-1
else
0
}
//uninstall current update and install base that works with patch
private fun fixHigherVer(apkFiles: ArrayList<FileInfo>) : Boolean {
if(PackageHelper.uninstallApk(yPkg, applicationContext))
{
return installSplitApkFiles(apkFiles)
}
with(localBroadcastManager) {sendFailure(listOf("Failed Uninstall Of Installed Version, Try Manually").toMutableList(), applicationContext)}
return false
}
//install newer stock youtube
private fun fixLowerVer(apkFiles: ArrayList<FileInfo>): Boolean {
return installSplitApkFiles(apkFiles)
}
//install stock youtube since no install was found
private fun fixNoInstall(baseApkFiles: ArrayList<FileInfo>): Boolean {
return installSplitApkFiles(baseApkFiles)
}
//set chcon to apk_data_file
private fun chConV(path: String): Boolean {
val response = Shell.su("chcon -R u:object_r:apk_data_file:s0 $path").exec()
//val response = Shell.su("chcon -R u:object_r:system_file:s0 $path").exec()
return if(response.isSuccess) {
true
} else {
sendFailure(response.out, applicationContext)
false
}
}
//move patch to data/app
private fun moveAPK(apkFile: String, path: String) : Boolean {
val apkinF = SuFile.open(apkFile)
val apkoutF = SuFile.open(path)
if(apkinF.exists())
{
try {
Shell.su("am force-stop $yPkg").exec()
//Shell.su("rm -r SuFile.open(path).parent")
copy(apkinF,apkoutF)
Shell.su("chmod 644 $path").exec().isSuccess
return if(Shell.su("chown system:system $path").exec().isSuccess) {
true
} else {
sendFailure(listOf("Failed To Chown, Try Again").toMutableList(), applicationContext)
false
}
}
catch (e: IOException)
{
sendFailure(listOf("${e.message}").toMutableList(), applicationContext)
return false
}
}
else {
sendFailure(listOf("Input File Missing").toMutableList(), applicationContext)
return false
}
}
@Throws(IOException::class)
fun copy(src: File?, dst: File?) {
val cmd = Shell.su("mv ${src!!.absolutePath} ${dst!!.absolutePath}").exec().isSuccess
Log.d("ZLog", cmd.toString())
}
//get path of the installed youtube
private fun getVPath(): String? {
return try {
val p = getPkgInfo(yPkg)
p?.applicationInfo?.sourceDir
} catch (e: Exception) {
null
}
}
private fun checkSHA256(sha256: String, updateFile: File?): Boolean {
try {
// get the raw file data of the photo
val mInputPFD = contentResolver.openFileDescriptor(updateFile!!.toUri() , "r")
val mContentFileDescriptor = mInputPFD!!.fileDescriptor
val fIS = FileInputStream(mContentFileDescriptor)
val mGraphicBuffer = ByteArrayOutputStream()
val buf = ByteArray(1024)
while (true) {
val readNum = fIS.read(buf)
if (readNum == -1) break
mGraphicBuffer.write(buf, 0, readNum)
}
// Generate the checksum
val sum = generateChecksum(mGraphicBuffer)
return sum == sha256
} catch (e: Exception) {
e.printStackTrace()
return false
}
}
@Throws(IOException::class)
private fun generateChecksum(data: ByteArrayOutputStream): String {
try {
val digest: MessageDigest = MessageDigest.getInstance("SHA-256")
val hash: ByteArray = digest.digest(data.toByteArray())
return printableHexString(hash)
} catch (e: Exception) {
e.printStackTrace()
}
return ""
}
private fun printableHexString(data: ByteArray): String {
// Create Hex String
val hexString: StringBuilder = StringBuilder()
for (aMessageDigest:Byte in data) {
var h: String = Integer.toHexString(0xFF and aMessageDigest.toInt())
while (h.length < 2)
h = "0$h"
hexString.append(h)
}
return hexString.toString()
}
}

View File

@ -124,8 +124,6 @@ class MainActivity : AppCompatActivity() {
}
!prefs.getBoolean("statement", true) -> DialogContainer.statementFalse(this)
variant == "root" -> {
if (showRootDialog)
DialogContainer.showRootDialog(this)
if (PackageHelper.getPackageVersionName(
"com.google.android.youtube",

View File

@ -73,28 +73,6 @@ object DialogContainer {
}
}
fun showRootDialog(activity: Activity) {
MaterialAlertDialogBuilder(activity).apply {
setTitle(activity.getString(R.string.hold_on))
setMessage(activity.getString(R.string.disable_signature))
setNeutralButton(activity.getString(R.string.button_dismiss)) { dialog, _ ->
dialog.dismiss()
}
setPositiveButton(activity.getString(R.string.guide)) { _, _ ->
openUrl(
"https://lmgtfy.com/?q=andnixsh+apk+verification+disable",
R.color.Twitter,
activity
)
}
setCancelable(false)
create()
show()
}
PreferenceManager.getDefaultSharedPreferences(activity).edit()
.putBoolean("show_root_dialog", false).apply()
}
//Easter Egg
fun statementFalse(context: Context) {
MaterialAlertDialogBuilder(context).apply {

View File

@ -24,7 +24,7 @@ class ManagerChangelogFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
CoroutineScope(Dispatchers.Main).launch {
val changelog = InternetTools.getObjectFromJson("https://x1nto.github.io/VancedFiles/manager.json", "changelog")
val changelog = InternetTools.getObjectFromJson("https://ytvanced.github.io/VancedBackend/manager.json", "changelog")
view.findViewById<TextView>(R.id.manager_changelog).text = changelog
}
}

View File

@ -65,7 +65,7 @@ open class HomeViewModel(application: Application): AndroidViewModel(application
fun fetchData() {
CoroutineScope(Dispatchers.IO).launch {
fetching.set(true)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
//if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
Crowdin.forceUpdate(getApplication())
vancedVersion.set(getJsonString("vanced.json", "version", getApplication()))
microgVersion.set(getJsonString("microg.json", "version", getApplication()))

View File

@ -67,7 +67,7 @@ object InternetTools {
suspend fun isUpdateAvailable(): Boolean {
val result = try {
JsonHelper.getJson("https://x1nto.github.io/VancedFiles/manager.json").int("versionCode") ?: 0
JsonHelper.getJson("https://ytvanced.github.io/VancedBackend/manager.json").int("versionCode") ?: 0
} catch (e: Exception) {
0
}

View File

@ -2,9 +2,11 @@ package com.vanced.manager.utils
import android.app.Activity
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import com.vanced.manager.core.installer.AppUninstallerService
import java.lang.Exception
object PackageHelper {
@ -30,4 +32,19 @@ object PackageHelper {
val pendingIntent = PendingIntent.getService(activity.applicationContext, 0, callbackIntent, 0)
activity.packageManager.packageInstaller.uninstall(pkg, pendingIntent.intentSender)
}
fun uninstallApk(pkg: String, applicationContext: Context): Boolean {
val callbackIntent = Intent(applicationContext, AppUninstallerService::class.java)
callbackIntent.putExtra("pkg", pkg)
val pendingIntent = PendingIntent.getService(applicationContext, 0, callbackIntent, 0)
try {
applicationContext.packageManager.packageInstaller.uninstall(pkg, pendingIntent.intentSender)
return true
}
catch (e: Exception)
{
e.printStackTrace()
return false;
}
}
}