From fdf04f77f23b2afd02f700ed46d9a590a9204272 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 30 Oct 2019 01:02:53 -0400 Subject: [PATCH 01/74] Send bitmap to notifications and shortcuts On API 23+, the platform unifies the way to handle drawable resources across processes: all drawables can be passed via Icon. This allows us to send raw bitmap to the system without the need to specify a resource ID. This means that we are allowed to NOT include these drawable resources within our stub APK, since our full APK can draw the images programmatically and send raw bitmaps to the system. --- .../main/java/com/topjohnwu/magisk/Hacks.kt | 11 --- .../topjohnwu/magisk/extensions/XAndroid.kt | 24 +++++ .../magisk/model/download/DownloadService.kt | 14 +-- .../model/download/NotificationService.kt | 18 ++-- .../model/download/RemoteFileService.kt | 8 +- .../topjohnwu/magisk/view/Notifications.kt | 95 ++++++++++++------- .../com/topjohnwu/magisk/view/Shortcuts.kt | 67 +++++++++---- .../drawable-anydpi-v21/ic_magisk_outline.xml | 0 .../res/drawable-v26/sc_cloud_download.xml | 0 .../main/res/drawable-v26/sc_extension.xml | 0 .../main/res/drawable-v26/sc_magiskhide.xml | 0 .../main/res/drawable-v26/sc_superuser.xml | 0 .../main/res/drawable/ic_cloud_download.xml | 0 .../src/main/res/drawable/ic_extension.xml | 0 .../src/main/res/drawable/ic_magiskhide.xml | 0 .../src/main/res/drawable/ic_superuser.xml | 0 .../java/com/topjohnwu/magisk/DynAPK.java | 15 +-- .../topjohnwu/magisk/obfuscate/Mapping.java | 10 +- 18 files changed, 152 insertions(+), 110 deletions(-) rename {shared => app}/src/main/res/drawable-anydpi-v21/ic_magisk_outline.xml (100%) rename {shared => app}/src/main/res/drawable-v26/sc_cloud_download.xml (100%) rename {shared => app}/src/main/res/drawable-v26/sc_extension.xml (100%) rename {shared => app}/src/main/res/drawable-v26/sc_magiskhide.xml (100%) rename {shared => app}/src/main/res/drawable-v26/sc_superuser.xml (100%) rename {shared => app}/src/main/res/drawable/ic_cloud_download.xml (100%) rename {shared => app}/src/main/res/drawable/ic_extension.xml (100%) rename {shared => app}/src/main/res/drawable/ic_magiskhide.xml (100%) rename {shared => app}/src/main/res/drawable/ic_superuser.xml (100%) diff --git a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt index 0245f9591..dbac949b9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt @@ -73,17 +73,6 @@ fun Context.intent(c: Class<*>): Intent { } ?: Intent(this, cls) } -fun resolveRes(idx: Int): Int { - return Info.stub?.resourceMap?.get(idx) ?: when(idx) { - DynAPK.NOTIFICATION -> R.drawable.ic_magisk_outline - DynAPK.DOWNLOAD -> R.drawable.sc_cloud_download - DynAPK.SUPERUSER -> R.drawable.sc_superuser - DynAPK.MODULES -> R.drawable.sc_extension - DynAPK.MAGISKHIDE -> R.drawable.sc_magiskhide - else -> -1 - } -} - private open class GlobalResContext(base: Context) : ContextWrapper(base) { open val mRes: Resources get() = ResourceMgr.resource private val loader by lazy { javaClass.classLoader!! } diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt index 5bd64ce20..5266230fb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt @@ -12,12 +12,19 @@ import android.content.pm.PackageManager.* import android.content.res.Configuration import android.content.res.Resources import android.database.Cursor +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.drawable.AdaptiveIconDrawable +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.LayerDrawable import android.net.Uri import android.os.Build +import android.os.Build.VERSION.SDK_INT import android.provider.OpenableColumns import android.view.View import androidx.annotation.ColorRes import androidx.annotation.DrawableRes +import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.ContextCompat import androidx.core.net.toUri import com.topjohnwu.magisk.Const @@ -97,6 +104,23 @@ fun Context.rawResource(id: Int) = resources.openRawResource(id) fun Context.readUri(uri: Uri) = contentResolver.openInputStream(uri) ?: throw FileNotFoundException() +fun Context.getBitmap(id: Int): Bitmap { + var drawable = AppCompatResources.getDrawable(this, id)!! + if (drawable is BitmapDrawable) + return drawable.bitmap + if (SDK_INT >= 26 && drawable is AdaptiveIconDrawable) { + drawable = LayerDrawable(arrayOf(drawable.background, drawable.foreground)) + } + val bitmap = Bitmap.createBitmap( + drawable.intrinsicWidth, drawable.intrinsicHeight, + Bitmap.Config.ARGB_8888 + ) + val canvas = Canvas(bitmap) + drawable.setBounds(0, 0, canvas.width, canvas.height) + drawable.draw(canvas) + return bitmap +} + fun Intent.startActivity(context: Context) = context.startActivity(this) fun Intent.startActivityWithRoot() { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt index 3f0eca392..dbf3e07d4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt @@ -1,12 +1,12 @@ package com.topjohnwu.magisk.model.download import android.annotation.SuppressLint +import android.app.Notification import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.Build import android.webkit.MimeTypeMap -import androidx.core.app.NotificationCompat import com.topjohnwu.magisk.R import com.topjohnwu.magisk.extensions.chooser import com.topjohnwu.magisk.extensions.exists @@ -69,14 +69,14 @@ open class DownloadService : RemoteFileService() { // --- - override fun NotificationCompat.Builder.addActions(subject: DownloadSubject) + override fun Notification.Builder.addActions(subject: DownloadSubject) = when (subject) { is Magisk -> addActionsInternal(subject) is Module -> addActionsInternal(subject) is Manager -> addActionsInternal(subject) } - private fun NotificationCompat.Builder.addActionsInternal(subject: Magisk) + private fun Notification.Builder.addActionsInternal(subject: Magisk) = when (val conf = subject.configuration) { Download -> this.apply { fileIntent(subject.file.parentFile!!) @@ -92,7 +92,7 @@ open class DownloadService : RemoteFileService() { else -> this } - private fun NotificationCompat.Builder.addActionsInternal(subject: Module) + private fun Notification.Builder.addActionsInternal(subject: Module) = when (subject.configuration) { Download -> this.apply { fileIntent(subject.file.parentFile!!) @@ -106,19 +106,19 @@ open class DownloadService : RemoteFileService() { else -> this } - private fun NotificationCompat.Builder.addActionsInternal(subject: Manager) + private fun Notification.Builder.addActionsInternal(subject: Manager) = when (subject.configuration) { APK.Upgrade -> setContentIntent(APKInstall.installIntent(context, subject.file)) else -> this } @Suppress("ReplaceSingleLineLet") - private fun NotificationCompat.Builder.setContentIntent(intent: Intent) = + private fun Notification.Builder.setContentIntent(intent: Intent) = PendingIntent.getActivity(context, nextInt(), intent, PendingIntent.FLAG_ONE_SHOT) .let { setContentIntent(it) } @Suppress("ReplaceSingleLineLet") - private fun NotificationCompat.Builder.addAction(icon: Int, title: Int, intent: Intent) = + private fun Notification.Builder.addAction(icon: Int, title: Int, intent: Intent) = PendingIntent.getActivity(context, nextInt(), intent, PendingIntent.FLAG_ONE_SHOT) .let { addAction(icon, getString(title), it) } diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/NotificationService.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/NotificationService.kt index f558265e3..fc8cb48a9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/NotificationService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/NotificationService.kt @@ -3,22 +3,20 @@ package com.topjohnwu.magisk.model.download import android.app.Notification import android.content.Intent import android.os.IBinder -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat import com.topjohnwu.magisk.base.BaseService +import com.topjohnwu.magisk.view.Notifications import org.koin.core.KoinComponent import java.util.* import kotlin.random.Random.Default.nextInt abstract class NotificationService : BaseService(), KoinComponent { - abstract val defaultNotification: NotificationCompat.Builder + abstract val defaultNotification: Notification.Builder - private val manager by lazy { NotificationManagerCompat.from(this) } private val hasNotifications get() = notifications.isNotEmpty() private val notifications = - Collections.synchronizedMap(mutableMapOf()) + Collections.synchronizedMap(mutableMapOf()) override fun onTaskRemoved(rootIntent: Intent?) { super.onTaskRemoved(rootIntent) @@ -30,7 +28,7 @@ abstract class NotificationService : BaseService(), KoinComponent { fun update( id: Int, - body: (NotificationCompat.Builder) -> Unit = {} + body: (Notification.Builder) -> Unit = {} ) { val notification = notifications.getOrPut(id) { defaultNotification } @@ -43,7 +41,7 @@ abstract class NotificationService : BaseService(), KoinComponent { protected fun finishNotify( id: Int, - editBody: (NotificationCompat.Builder) -> NotificationCompat.Builder? = { null } + editBody: (Notification.Builder) -> Notification.Builder? = { null } ) : Int { val currentNotification = remove(id)?.run(editBody) @@ -62,11 +60,11 @@ abstract class NotificationService : BaseService(), KoinComponent { // --- private fun notify(id: Int, notification: Notification) { - manager.notify(id, notification) + Notifications.mgr.notify(id, notification) } private fun cancel(id: Int) { - manager.cancel(id) + Notifications.mgr.cancel(id) } protected fun remove(id: Int) = notifications.remove(id).also { @@ -84,4 +82,4 @@ abstract class NotificationService : BaseService(), KoinComponent { // -- override fun onBind(p0: Intent?): IBinder? = null -} \ No newline at end of file +} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt index 418a4d3de..eacc44b33 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/RemoteFileService.kt @@ -1,8 +1,8 @@ package com.topjohnwu.magisk.model.download import android.app.Activity +import android.app.Notification import android.content.Intent -import androidx.core.app.NotificationCompat import com.topjohnwu.magisk.R import com.topjohnwu.magisk.data.network.GithubRawServices import com.topjohnwu.magisk.di.NullActivity @@ -24,7 +24,7 @@ abstract class RemoteFileService : NotificationService() { val service: GithubRawServices by inject() - override val defaultNotification: NotificationCompat.Builder + override val defaultNotification get() = Notifications.progress(this, "") override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -108,8 +108,8 @@ abstract class RemoteFileService : NotificationService() { @Throws(Throwable::class) protected abstract fun onFinished(subject: DownloadSubject, id: Int) - protected abstract fun NotificationCompat.Builder.addActions(subject: DownloadSubject) - : NotificationCompat.Builder + protected abstract fun Notification.Builder.addActions(subject: DownloadSubject) + : Notification.Builder companion object { const val ARG_URL = "arg_url" diff --git a/app/src/main/java/com/topjohnwu/magisk/view/Notifications.kt b/app/src/main/java/com/topjohnwu/magisk/view/Notifications.kt index ef8d5e026..5efa6c789 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/Notifications.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/Notifications.kt @@ -1,35 +1,51 @@ package com.topjohnwu.magisk.view +import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context -import android.os.Build -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat +import android.os.Build.VERSION.SDK_INT import androidx.core.app.TaskStackBuilder +import androidx.core.content.getSystemService +import androidx.core.graphics.drawable.toIcon import com.topjohnwu.magisk.* +import com.topjohnwu.magisk.Const.ID.PROGRESS_NOTIFICATION_CHANNEL +import com.topjohnwu.magisk.Const.ID.UPDATE_NOTIFICATION_CHANNEL import com.topjohnwu.magisk.extensions.get +import com.topjohnwu.magisk.extensions.getBitmap import com.topjohnwu.magisk.model.receiver.GeneralReceiver import com.topjohnwu.magisk.ui.SplashActivity object Notifications { - val mgr by lazy { NotificationManagerCompat.from(get()) } - private val icon by lazy { resolveRes(DynAPK.NOTIFICATION) } + val mgr by lazy { get().getSystemService()!! } fun setup(context: Context) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - mgr.deleteNotificationChannel("magisk_notification") - var channel = NotificationChannel(Const.ID.UPDATE_NOTIFICATION_CHANNEL, + if (SDK_INT >= 26) { + var channel = NotificationChannel(UPDATE_NOTIFICATION_CHANNEL, context.getString(R.string.update_channel), NotificationManager.IMPORTANCE_DEFAULT) mgr.createNotificationChannel(channel) - channel = NotificationChannel(Const.ID.PROGRESS_NOTIFICATION_CHANNEL, + channel = NotificationChannel(PROGRESS_NOTIFICATION_CHANNEL, context.getString(R.string.progress_channel), NotificationManager.IMPORTANCE_LOW) mgr.createNotificationChannel(channel) } } + private fun updateBuilder(context: Context): Notification.Builder { + return Notification.Builder(context).apply { + val bitmap = context.getBitmap(R.drawable.ic_magisk_outline) + setLargeIcon(bitmap) + if (SDK_INT >= 26) { + setSmallIcon(bitmap.toIcon()) + setChannelId(UPDATE_NOTIFICATION_CHANNEL) + } else { + setSmallIcon(R.drawable.ic_magisk_outline) + setVibrate(longArrayOf(0, 100, 100, 100)) + } + } + } + fun magiskUpdate(context: Context) { val intent = context.intent(SplashActivity::class.java) .putExtra(Const.Key.OPEN_SECTION, "magisk") @@ -39,13 +55,11 @@ object Notifications { val pendingIntent = stackBuilder.getPendingIntent(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, PendingIntent.FLAG_UPDATE_CURRENT) - val builder = NotificationCompat.Builder(context, Const.ID.UPDATE_NOTIFICATION_CHANNEL) - builder.setSmallIcon(icon) - .setContentTitle(context.getString(R.string.magisk_update_title)) - .setContentText(context.getString(R.string.manager_download_install)) - .setVibrate(longArrayOf(0, 100, 100, 100)) - .setAutoCancel(true) - .setContentIntent(pendingIntent) + val builder = updateBuilder(context) + .setContentTitle(context.getString(R.string.magisk_update_title)) + .setContentText(context.getString(R.string.manager_download_install)) + .setAutoCancel(true) + .setContentIntent(pendingIntent) mgr.notify(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, builder.build()) } @@ -58,13 +72,11 @@ object Notifications { val pendingIntent = PendingIntent.getBroadcast(context, Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) - val builder = NotificationCompat.Builder(context, Const.ID.UPDATE_NOTIFICATION_CHANNEL) - builder.setSmallIcon(icon) - .setContentTitle(context.getString(R.string.manager_update_title)) - .setContentText(context.getString(R.string.manager_download_install)) - .setVibrate(longArrayOf(0, 100, 100, 100)) - .setAutoCancel(true) - .setContentIntent(pendingIntent) + val builder = updateBuilder(context) + .setContentTitle(context.getString(R.string.manager_update_title)) + .setContentText(context.getString(R.string.manager_download_install)) + .setAutoCancel(true) + .setContentIntent(pendingIntent) mgr.notify(Const.ID.APK_UPDATE_NOTIFICATION_ID, builder.build()) } @@ -75,23 +87,34 @@ object Notifications { val pendingIntent = PendingIntent.getBroadcast(context, Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) - val builder = NotificationCompat.Builder(context, Const.ID.UPDATE_NOTIFICATION_CHANNEL) - builder.setSmallIcon(icon) - .setContentTitle(context.getString(R.string.dtbo_patched_title)) - .setContentText(context.getString(R.string.dtbo_patched_reboot)) - .setVibrate(longArrayOf(0, 100, 100, 100)) - .addAction(R.drawable.ic_refresh, context.getString(R.string.reboot), pendingIntent) + val builder = updateBuilder(context) + .setContentTitle(context.getString(R.string.dtbo_patched_title)) + .setContentText(context.getString(R.string.dtbo_patched_reboot)) + + if (SDK_INT >= 23) { + val action = Notification.Action.Builder( + context.getBitmap(R.drawable.ic_refresh).toIcon(), + context.getString(R.string.reboot), pendingIntent).build() + builder.addAction(action) + } else { + builder.addAction( + R.drawable.ic_refresh, + context.getString(R.string.reboot), pendingIntent) + } mgr.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build()) } - fun progress(context: Context, title: CharSequence): NotificationCompat.Builder { - val builder = NotificationCompat.Builder(context, Const.ID.PROGRESS_NOTIFICATION_CHANNEL) - builder.setPriority(NotificationCompat.PRIORITY_LOW) - .setSmallIcon(android.R.drawable.stat_sys_download) - .setContentTitle(title) - .setProgress(0, 0, true) - .setOngoing(true) + fun progress(context: Context, title: CharSequence): Notification.Builder { + val builder = if (SDK_INT >= 26) { + Notification.Builder(context, PROGRESS_NOTIFICATION_CHANNEL) + } else { + Notification.Builder(context).setPriority(Notification.PRIORITY_LOW) + } + builder.setSmallIcon(android.R.drawable.stat_sys_download) + .setContentTitle(title) + .setProgress(0, 0, true) + .setOngoing(true) return builder } } diff --git a/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt b/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt index 61af35fda..548b4fa42 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt @@ -7,7 +7,10 @@ import android.content.pm.ShortcutManager import android.graphics.drawable.Icon import android.os.Build import androidx.annotation.RequiresApi +import androidx.core.graphics.drawable.toAdaptiveIcon +import androidx.core.graphics.drawable.toIcon import com.topjohnwu.magisk.* +import com.topjohnwu.magisk.extensions.getBitmap import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.superuser.Shell @@ -26,47 +29,71 @@ object Shortcuts { val shortCuts = mutableListOf() val root = Shell.rootAccess() val intent = context.intent(SplashActivity::class.java) + + fun getIcon(id: Int): Icon { + return if (Build.VERSION.SDK_INT >= 26) + context.getBitmap(id).toAdaptiveIcon() + else + context.getBitmap(id).toIcon() + } + if (Utils.showSuperUser()) { - shortCuts.add(ShortcutInfo.Builder(context, "superuser") + shortCuts.add( + ShortcutInfo.Builder(context, "superuser") .setShortLabel(context.getString(R.string.superuser)) - .setIntent(Intent(intent) + .setIntent( + Intent(intent) .putExtra(Const.Key.OPEN_SECTION, "superuser") .setAction(Intent.ACTION_VIEW) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) - .setIcon(Icon.createWithResource(context, resolveRes(DynAPK.SUPERUSER))) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + ) + .setIcon(getIcon(R.drawable.sc_superuser)) .setRank(0) - .build()) + .build() + ) } if (root && Info.env.magiskHide) { - shortCuts.add(ShortcutInfo.Builder(context, "magiskhide") + shortCuts.add( + ShortcutInfo.Builder(context, "magiskhide") .setShortLabel(context.getString(R.string.magiskhide)) - .setIntent(Intent(intent) + .setIntent( + Intent(intent) .putExtra(Const.Key.OPEN_SECTION, "magiskhide") .setAction(Intent.ACTION_VIEW) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) - .setIcon(Icon.createWithResource(context, resolveRes(DynAPK.MAGISKHIDE))) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + ) + .setIcon(getIcon(R.drawable.sc_magiskhide)) .setRank(1) - .build()) + .build() + ) } if (!Config.coreOnly && root && Info.env.magiskVersionCode >= 0) { - shortCuts.add(ShortcutInfo.Builder(context, "modules") + shortCuts.add( + ShortcutInfo.Builder(context, "modules") .setShortLabel(context.getString(R.string.modules)) - .setIntent(Intent(intent) + .setIntent( + Intent(intent) .putExtra(Const.Key.OPEN_SECTION, "modules") .setAction(Intent.ACTION_VIEW) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) - .setIcon(Icon.createWithResource(context, resolveRes(DynAPK.MODULES))) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + ) + .setIcon(getIcon(R.drawable.sc_extension)) .setRank(3) - .build()) - shortCuts.add(ShortcutInfo.Builder(context, "downloads") + .build() + ) + shortCuts.add( + ShortcutInfo.Builder(context, "downloads") .setShortLabel(context.getString(R.string.downloads)) - .setIntent(Intent(intent) + .setIntent( + Intent(intent) .putExtra(Const.Key.OPEN_SECTION, "downloads") .setAction(Intent.ACTION_VIEW) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)) - .setIcon(Icon.createWithResource(context, resolveRes(DynAPK.DOWNLOAD))) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + ) + .setIcon(getIcon(R.drawable.sc_cloud_download)) .setRank(2) - .build()) + .build() + ) } return shortCuts } diff --git a/shared/src/main/res/drawable-anydpi-v21/ic_magisk_outline.xml b/app/src/main/res/drawable-anydpi-v21/ic_magisk_outline.xml similarity index 100% rename from shared/src/main/res/drawable-anydpi-v21/ic_magisk_outline.xml rename to app/src/main/res/drawable-anydpi-v21/ic_magisk_outline.xml diff --git a/shared/src/main/res/drawable-v26/sc_cloud_download.xml b/app/src/main/res/drawable-v26/sc_cloud_download.xml similarity index 100% rename from shared/src/main/res/drawable-v26/sc_cloud_download.xml rename to app/src/main/res/drawable-v26/sc_cloud_download.xml diff --git a/shared/src/main/res/drawable-v26/sc_extension.xml b/app/src/main/res/drawable-v26/sc_extension.xml similarity index 100% rename from shared/src/main/res/drawable-v26/sc_extension.xml rename to app/src/main/res/drawable-v26/sc_extension.xml diff --git a/shared/src/main/res/drawable-v26/sc_magiskhide.xml b/app/src/main/res/drawable-v26/sc_magiskhide.xml similarity index 100% rename from shared/src/main/res/drawable-v26/sc_magiskhide.xml rename to app/src/main/res/drawable-v26/sc_magiskhide.xml diff --git a/shared/src/main/res/drawable-v26/sc_superuser.xml b/app/src/main/res/drawable-v26/sc_superuser.xml similarity index 100% rename from shared/src/main/res/drawable-v26/sc_superuser.xml rename to app/src/main/res/drawable-v26/sc_superuser.xml diff --git a/shared/src/main/res/drawable/ic_cloud_download.xml b/app/src/main/res/drawable/ic_cloud_download.xml similarity index 100% rename from shared/src/main/res/drawable/ic_cloud_download.xml rename to app/src/main/res/drawable/ic_cloud_download.xml diff --git a/shared/src/main/res/drawable/ic_extension.xml b/app/src/main/res/drawable/ic_extension.xml similarity index 100% rename from shared/src/main/res/drawable/ic_extension.xml rename to app/src/main/res/drawable/ic_extension.xml diff --git a/shared/src/main/res/drawable/ic_magiskhide.xml b/app/src/main/res/drawable/ic_magiskhide.xml similarity index 100% rename from shared/src/main/res/drawable/ic_magiskhide.xml rename to app/src/main/res/drawable/ic_magiskhide.xml diff --git a/shared/src/main/res/drawable/ic_superuser.xml b/app/src/main/res/drawable/ic_superuser.xml similarity index 100% rename from shared/src/main/res/drawable/ic_superuser.xml rename to app/src/main/res/drawable/ic_superuser.xml diff --git a/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java b/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java index 8dc790c81..e7b69633a 100644 --- a/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java +++ b/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java @@ -11,19 +11,11 @@ import static android.os.Build.VERSION.SDK_INT; public class DynAPK { - private static final int STUB_VERSION = 2; + private static final int STUB_VERSION = 3; // Indices of the object array private static final int STUB_VERSION_ENTRY = 0; private static final int COMPONENT_MAP = 1; - private static final int RESOURCE_MAP = 2; - - // Indices of the resource map - public static final int NOTIFICATION = 0; - public static final int DOWNLOAD = 1; - public static final int SUPERUSER = 2; - public static final int MODULES = 3; - public static final int MAGISKHIDE = 4; private static File dynDir; private static Method addAssetPath; @@ -53,15 +45,13 @@ public class DynAPK { Data data = new Data(); data.version = (int) arr[STUB_VERSION_ENTRY]; data.componentMap = (Map) arr[COMPONENT_MAP]; - data.resourceMap = (int[]) arr[RESOURCE_MAP]; return data; } public static Object pack(Data data) { - Object[] arr = new Object[3]; + Object[] arr = new Object[2]; arr[STUB_VERSION_ENTRY] = STUB_VERSION; arr[COMPONENT_MAP] = data.componentMap; - arr[RESOURCE_MAP] = data.resourceMap; return arr; } @@ -76,6 +66,5 @@ public class DynAPK { public static class Data { public int version; public Map componentMap; - public int[] resourceMap; } } diff --git a/stub/src/main/java/com/topjohnwu/magisk/obfuscate/Mapping.java b/stub/src/main/java/com/topjohnwu/magisk/obfuscate/Mapping.java index 65e7eec82..2630453ee 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/obfuscate/Mapping.java +++ b/stub/src/main/java/com/topjohnwu/magisk/obfuscate/Mapping.java @@ -3,8 +3,7 @@ package com.topjohnwu.magisk.obfuscate; import java.util.HashMap; import java.util.Map; -import static com.topjohnwu.magisk.DynAPK.*; -import static com.topjohnwu.magisk.R.drawable.*; +import static com.topjohnwu.magisk.DynAPK.Data; public class Mapping { private static Map map = new HashMap<>(); @@ -25,13 +24,6 @@ public class Mapping { for (Map.Entry e : map.entrySet()) { data.componentMap.put(e.getValue(), e.getKey()); } - int[] res = new int[5]; - res[NOTIFICATION] = ic_magisk_outline; - res[SUPERUSER] = sc_superuser; - res[MAGISKHIDE] = sc_magiskhide; - res[DOWNLOAD] = sc_cloud_download; - res[MODULES] = sc_extension; - data.resourceMap = res; } public static String get(String name) { From dc9f69bab0a8d5d4cc8ca0f673d968c4b313240c Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 30 Oct 2019 04:15:53 -0400 Subject: [PATCH 02/74] Minor changes --- shared/proguard-rules.pro | 2 +- .../java/com/topjohnwu/magisk/DynAPK.java | 4 +-- stub/src/main/AndroidManifest.xml | 18 ++++++------- stub/src/main/java/a/{x.java => a.java} | 2 +- stub/src/main/java/a/{z.java => c.java} | 2 +- stub/src/main/java/a/{y.java => e.java} | 2 +- stub/src/main/java/a/{w.java => h.java} | 2 +- .../topjohnwu/magisk/DelegateApplication.java | 6 ++--- .../topjohnwu/magisk/obfuscate/Mapping.java | 26 ++++++++----------- 9 files changed, 30 insertions(+), 34 deletions(-) rename stub/src/main/java/a/{x.java => a.java} (57%) rename stub/src/main/java/a/{z.java => c.java} (59%) rename stub/src/main/java/a/{y.java => e.java} (58%) rename stub/src/main/java/a/{w.java => h.java} (62%) diff --git a/shared/proguard-rules.pro b/shared/proguard-rules.pro index e0a7aa840..708dc21be 100644 --- a/shared/proguard-rules.pro +++ b/shared/proguard-rules.pro @@ -20,6 +20,6 @@ # hide the original source file name. #-renamesourcefileattribute SourceFile --keepclassmembers class * implements javax.net.ssl.SSLSocketFactory { +-keepclassmembers class * extends javax.net.ssl.SSLSocketFactory { ** delegate; } diff --git a/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java b/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java index e7b69633a..648e054e1 100644 --- a/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java +++ b/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java @@ -50,7 +50,7 @@ public class DynAPK { public static Object pack(Data data) { Object[] arr = new Object[2]; - arr[STUB_VERSION_ENTRY] = STUB_VERSION; + arr[STUB_VERSION_ENTRY] = data.version; arr[COMPONENT_MAP] = data.componentMap; return arr; } @@ -64,7 +64,7 @@ public class DynAPK { } public static class Data { - public int version; + public int version = STUB_VERSION; public Map componentMap; } } diff --git a/stub/src/main/AndroidManifest.xml b/stub/src/main/AndroidManifest.xml index 729d39a19..6d3dcb4bd 100644 --- a/stub/src/main/AndroidManifest.xml +++ b/stub/src/main/AndroidManifest.xml @@ -16,8 +16,8 @@ @@ -25,7 +25,7 @@ @@ -37,24 +37,24 @@ @@ -70,7 +70,7 @@ map = new HashMap<>(); - // This mapping will be sent into the guest app - public static Data data = new Data(); - static { - map.put(a.z.class.getName(), "a.c"); - map.put("a.x", "a.f"); - map.put("a.o", "a.b"); - map.put("a.g", "a.m"); - map.put(a.w.class.getName(), "a.h"); - map.put("a.v", "a.j"); - map.put("a.j", "androidx.work.impl.background.systemjob.SystemJobService"); - - data.componentMap = new HashMap<>(map.size()); - for (Map.Entry e : map.entrySet()) { - data.componentMap.put(e.getValue(), e.getKey()); - } + map.put("a.x", "androidx.work.impl.background.systemjob.SystemJobService"); } public static String get(String name) { @@ -31,4 +17,14 @@ public class Mapping { return n != null ? n : name; } + public static Data data() { + Map componentMap = new HashMap<>(map.size()); + for (Map.Entry e : map.entrySet()) { + componentMap.put(e.getValue(), e.getKey()); + } + Data data = new Data(); + data.componentMap = componentMap; + return data; + } + } From 490e4d31808bdcf4fa413d1c146191b01ffb8025 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 30 Oct 2019 05:00:52 -0400 Subject: [PATCH 03/74] Target the proper channel in stub --- stub/build.gradle | 2 +- .../java/com/topjohnwu/magisk/DownloadActivity.java | 9 +++++---- .../java/com/topjohnwu/magisk/obfuscate/RawData.java | 10 +++++++--- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/stub/build.gradle b/stub/build.gradle index c43fcc5a8..932b547fc 100644 --- a/stub/build.gradle +++ b/stub/build.gradle @@ -4,7 +4,7 @@ android { defaultConfig { applicationId 'com.topjohnwu.magisk' versionCode 1 - versionName "1.0" + versionName props['appVersion'] buildConfigField 'String', 'DEV_CHANNEL', props['DEV_CHANNEL'] ?: 'null' } diff --git a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java index 38e1e9c5e..231a24d54 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java +++ b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java @@ -24,9 +24,10 @@ import static com.topjohnwu.magisk.DelegateApplication.MANAGER_APK; public class DownloadActivity extends Activity { + private static final boolean CANARY = BuildConfig.VERSION_NAME.contains("-"); private static final String URL = - BuildConfig.DEV_CHANNEL != null ? BuildConfig.DEV_CHANNEL : - RawData.urlBase() + (BuildConfig.DEBUG ? RawData.canary() : RawData.stable()); + BuildConfig.DEV_CHANNEL != null ? BuildConfig.DEV_CHANNEL : RawData.urlBase() + + (BuildConfig.DEBUG ? RawData.debug() : (CANARY ? RawData.canary() : RawData.stable())); private String apkLink; private ErrorHandler err = (conn, e) -> { @@ -71,7 +72,7 @@ public class DownloadActivity extends Activity { new AlertDialog.Builder(this) .setCancelable(false) .setTitle(RawData.appName()) - .setMessage(RawData.no_internet_msg()) + .setMessage(RawData.networkError()) .setNegativeButton(ok, (d, w) -> finish()) .show(); } @@ -87,7 +88,7 @@ public class DownloadActivity extends Activity { new AlertDialog.Builder(DownloadActivity.this) .setCancelable(false) .setTitle(RawData.appName()) - .setMessage(RawData.upgrade_msg()) + .setMessage(RawData.upgradeMsg()) .setPositiveButton(yes, (d, w) -> dlAPK()) .setNegativeButton(no, (d, w) -> finish()) .show(); diff --git a/stub/src/main/java/com/topjohnwu/magisk/obfuscate/RawData.java b/stub/src/main/java/com/topjohnwu/magisk/obfuscate/RawData.java index da3dabd4e..27ce8cf92 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/obfuscate/RawData.java +++ b/stub/src/main/java/com/topjohnwu/magisk/obfuscate/RawData.java @@ -16,19 +16,23 @@ public class RawData { return "https://raw.githubusercontent.com/topjohnwu/magisk_files/"; } - public static String canary() { + public static String debug() { return "canary/debug.json"; } + public static String canary() { + return "canary/release.json"; + } + public static String stable() { return "master/stable.json"; } - public static String no_internet_msg() { + public static String networkError() { return res.getString(R.string.no_internet_msg); } - public static String upgrade_msg() { + public static String upgradeMsg() { return res.getString(R.string.upgrade_msg); } From 31e003bda5e94fdf3b233370751c2bc190187ce6 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 30 Oct 2019 05:24:22 -0400 Subject: [PATCH 04/74] Fix bug in version detection --- .../java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt index ee86ab652..899632f7f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt @@ -215,8 +215,8 @@ class HomeViewModel( private fun updateSelf() { magiskState.value = when (Info.env.magiskVersionCode) { - in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED - in 1 until (Info.remote.magisk.versionCode - 1) -> MagiskState.OBSOLETE + in Int.MIN_VALUE .. 0 -> MagiskState.NOT_INSTALLED + in 1 until Info.remote.magisk.versionCode -> MagiskState.OBSOLETE else -> MagiskState.UP_TO_DATE } @@ -224,10 +224,10 @@ class HomeViewModel( VERSION_FMT.format(Info.remote.magisk.version, Info.remote.magisk.versionCode) _managerState.value = when (Info.remote.app.versionCode) { - in Int.MIN_VALUE until 0 -> MagiskState.NOT_INSTALLED //wrong update channel - in (BuildConfig.VERSION_CODE + 1) until Int.MAX_VALUE -> MagiskState.OBSOLETE + in Int.MIN_VALUE .. 0 -> MagiskState.NOT_INSTALLED //wrong update channel + in (BuildConfig.VERSION_CODE + 1) .. Int.MAX_VALUE -> MagiskState.OBSOLETE else -> { - if (isRunningAsStub && Info.stub!!.version < Info.remote.stub.versionCode) + if (Info.stub?.version ?: Int.MAX_VALUE < Info.remote.stub.versionCode) MagiskState.OBSOLETE else MagiskState.UP_TO_DATE From e7155837d79177c1ac77288924073b78b9c7b16a Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 31 Oct 2019 01:57:47 -0400 Subject: [PATCH 05/74] Make sure magisk daemon won't get killed by init According to this comment in #1880: https://github.com/topjohnwu/Magisk/issues/1880#issuecomment-546657588 If Linux recycled our PPID, and coincidentally the process that reused the PPID is root, AND init wants to kill the whole process group, magiskd will get killed as a result. There is no real way to block a SIGKILL signal, so we simply make sure our daemon PID is the process group leader by renaming the directory. Close #1880 --- native/jni/core/daemon.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/native/jni/core/daemon.cpp b/native/jni/core/daemon.cpp index f13f05d3e..0964b23d6 100644 --- a/native/jni/core/daemon.cpp +++ b/native/jni/core/daemon.cpp @@ -210,9 +210,17 @@ int connect_daemon(bool create) { exit(1); } + int ppid = getpid(); LOGD("client: launching new main daemon process\n"); if (fork_dont_care() == 0) { close(fd); + + // Make sure ppid is not in acct + char src[64], dest[64]; + sprintf(src, "/acct/uid_0/pid_%d", ppid); + sprintf(dest, "/acct/uid_0/pid_%d", getpid()); + rename(src, dest); + main_daemon(); } From 0a89edf3b02cbeaed52fc8695c8462fd88e1f45a Mon Sep 17 00:00:00 2001 From: Rom Date: Thu, 31 Oct 2019 07:04:00 +0100 Subject: [PATCH 06/74] Update French translation --- app/src/main/res/values-fr/strings.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 2fcb32888..95f6e9e26 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -145,7 +145,10 @@ Fichier hosts sans système Prise en charge du fichier hosts sans système pour les applications de type AdBlock. Ajout d’un module hosts sans système - + Taper le nom de l'application désirée + Nouveau nom + L'application sera réempacter sous ce nom + Format invalide Applications et ADB Applications uniquement ADB uniquement From 353c3c7d817381054032166b2b31ecf67fa101c5 Mon Sep 17 00:00:00 2001 From: osm0sis Date: Tue, 29 Oct 2019 15:48:58 -0300 Subject: [PATCH 07/74] magiskboot: add unpack -n to help with repack validity tests - support unpack without decompression to allow easy testing of magiskboot's header, structure and hashing handling by comparing repack checksum versus origbootimg - make -n first to match repack --- native/jni/magiskboot/bootimg.cpp | 6 +++--- native/jni/magiskboot/magiskboot.h | 2 +- native/jni/magiskboot/main.cpp | 28 +++++++++++++++++++++++----- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/native/jni/magiskboot/bootimg.cpp b/native/jni/magiskboot/bootimg.cpp index 9b7a7cbe3..98e935194 100644 --- a/native/jni/magiskboot/bootimg.cpp +++ b/native/jni/magiskboot/bootimg.cpp @@ -312,14 +312,14 @@ void boot_img::find_kernel_dtb() { } } -int unpack(const char *image, bool hdr) { +int unpack(const char *image, bool nodecomp, bool hdr) { boot_img boot(image); if (hdr) boot.hdr->dump_hdr_file(); // Dump kernel - if (COMPRESSED(boot.k_fmt)) { + if (COMPRESSED(boot.k_fmt) && !nodecomp) { int fd = creat(KERNEL_FILE, 0644); decompress(boot.k_fmt, fd, boot.kernel, boot.hdr->kernel_size()); close(fd); @@ -331,7 +331,7 @@ int unpack(const char *image, bool hdr) { dump(boot.kernel_dtb, boot.kernel_dt_size, KER_DTB_FILE); // Dump ramdisk - if (COMPRESSED(boot.r_fmt)) { + if (COMPRESSED(boot.r_fmt) && !nodecomp) { int fd = creat(RAMDISK_FILE, 0644); decompress(boot.r_fmt, fd, boot.ramdisk, boot.hdr->ramdisk_size()); close(fd); diff --git a/native/jni/magiskboot/magiskboot.h b/native/jni/magiskboot/magiskboot.h index 6fb23cd09..580018e49 100644 --- a/native/jni/magiskboot/magiskboot.h +++ b/native/jni/magiskboot/magiskboot.h @@ -12,7 +12,7 @@ #define DTB_FILE "dtb" #define NEW_BOOT "new-boot.img" -int unpack(const char *image, bool hdr = false); +int unpack(const char *image, bool nodecomp = false, bool hdr = false); void repack(const char* src_img, const char* out_img, bool nocomp = false); int hexpatch(const char *image, const char *from, const char *to); int cpio_commands(int argc, char *argv[]); diff --git a/native/jni/magiskboot/main.cpp b/native/jni/magiskboot/main.cpp index e848f2442..9efa10d49 100644 --- a/native/jni/magiskboot/main.cpp +++ b/native/jni/magiskboot/main.cpp @@ -21,9 +21,11 @@ FULL_VER(MagiskBoot) R"EOF( - Boot Image Modification Tool Usage: %s [args...] Supported actions: - unpack [-h] + unpack [-n] [-h] Unpack to, if available, kernel, kernel_dtb, ramdisk.cpio, second, dtb, extra, and recovery_dtbo into current directory. + If '-n' is provided, it will not attempt to decompress kernel or + ramdisk.cpio from their original formats. If '-h' is provided, it will dump header info to 'header', which will be parsed when repacking. Return values: @@ -146,10 +148,26 @@ int main(int argc, char *argv[]) { printf("\n"); munmap(buf, size); } else if (argc > 2 && action == "unpack") { - if (argv[2] == "-h"sv) { - if (argc == 3) - usage(argv[0]); - return unpack(argv[3], true); + if (argv[2] == "-n"sv) { + if (argv[3] == "-h"sv) { + if (argc == 4) + usage(argv[0]); + return unpack(argv[4], true, true); + } else { + if (argc == 3) + usage(argv[0]); + return unpack(argv[3], true); + } + } else if (argv[2] == "-h"sv) { + if (argv[3] == "-n"sv) { + if (argc == 4) + usage(argv[0]); + return unpack(argv[4], true, true); + } else { + if (argc == 3) + usage(argv[0]); + return unpack(argv[3], false, true); + } } else { return unpack(argv[2]); } From 0f219e5ae6eb13d92d2f3dda22c23bf502cf0d4a Mon Sep 17 00:00:00 2001 From: John Wu Date: Thu, 31 Oct 2019 02:37:24 -0400 Subject: [PATCH 08/74] Better argument parsing logic --- native/jni/magiskboot/main.cpp | 36 +++++++++++++++------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/native/jni/magiskboot/main.cpp b/native/jni/magiskboot/main.cpp index 9efa10d49..2c5a89c5d 100644 --- a/native/jni/magiskboot/main.cpp +++ b/native/jni/magiskboot/main.cpp @@ -148,29 +148,25 @@ int main(int argc, char *argv[]) { printf("\n"); munmap(buf, size); } else if (argc > 2 && action == "unpack") { - if (argv[2] == "-n"sv) { - if (argv[3] == "-h"sv) { - if (argc == 4) + int idx = 2; + bool nodecomp = false; + bool hdr = false; + for (;;) { + if (idx >= argc) + usage(argv[0]); + if (argv[idx][0] != '-') + break; + for (char *flag = &argv[idx][1]; *flag; ++flag) { + if (*flag == 'n') + nodecomp = true; + else if (*flag == 'h') + hdr = true; + else usage(argv[0]); - return unpack(argv[4], true, true); - } else { - if (argc == 3) - usage(argv[0]); - return unpack(argv[3], true); } - } else if (argv[2] == "-h"sv) { - if (argv[3] == "-n"sv) { - if (argc == 4) - usage(argv[0]); - return unpack(argv[4], true, true); - } else { - if (argc == 3) - usage(argv[0]); - return unpack(argv[3], false, true); - } - } else { - return unpack(argv[2]); + ++idx; } + return unpack(argv[idx], nodecomp, hdr); } else if (argc > 2 && action == "repack") { if (argv[2] == "-n"sv) { if (argc == 3) From 17a77e2577d9e85b0791a2aa08207fdccdd91061 Mon Sep 17 00:00:00 2001 From: John Wu Date: Thu, 31 Oct 2019 02:43:59 -0400 Subject: [PATCH 09/74] Shortcut booleans --- native/jni/magiskboot/bootimg.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/native/jni/magiskboot/bootimg.cpp b/native/jni/magiskboot/bootimg.cpp index 98e935194..78c0347c9 100644 --- a/native/jni/magiskboot/bootimg.cpp +++ b/native/jni/magiskboot/bootimg.cpp @@ -319,7 +319,7 @@ int unpack(const char *image, bool nodecomp, bool hdr) { boot.hdr->dump_hdr_file(); // Dump kernel - if (COMPRESSED(boot.k_fmt) && !nodecomp) { + if (!nodecomp && COMPRESSED(boot.k_fmt)) { int fd = creat(KERNEL_FILE, 0644); decompress(boot.k_fmt, fd, boot.kernel, boot.hdr->kernel_size()); close(fd); @@ -331,7 +331,7 @@ int unpack(const char *image, bool nodecomp, bool hdr) { dump(boot.kernel_dtb, boot.kernel_dt_size, KER_DTB_FILE); // Dump ramdisk - if (COMPRESSED(boot.r_fmt) && !nodecomp) { + if (!nodecomp && COMPRESSED(boot.r_fmt)) { int fd = creat(RAMDISK_FILE, 0644); decompress(boot.r_fmt, fd, boot.ramdisk, boot.hdr->ramdisk_size()); close(fd); @@ -436,7 +436,7 @@ void repack(const char* src_img, const char* out_img, bool nocomp) { size_t raw_size; void *raw_buf; mmap_ro(RAMDISK_FILE, raw_buf, raw_size); - if (!COMPRESSED_ANY(check_fmt(raw_buf, raw_size)) && COMPRESSED(boot.r_fmt) && !nocomp) { + if (!nocomp && !COMPRESSED_ANY(check_fmt(raw_buf, raw_size)) && COMPRESSED(boot.r_fmt)) { boot.hdr->ramdisk_size() = compress(boot.r_fmt, fd, raw_buf, raw_size); } else { boot.hdr->ramdisk_size() = xwrite(fd, raw_buf, raw_size); From 34c65e13bc11aa61358ff8cc3c4f8a1466f15974 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 31 Oct 2019 12:39:54 -0400 Subject: [PATCH 10/74] Fix strings Close #2012 --- app/src/main/res/values-fr/strings.xml | 4 ++-- native/jni/magiskhide/hide_utils.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 95f6e9e26..13708f5b5 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -145,9 +145,9 @@ Fichier hosts sans système Prise en charge du fichier hosts sans système pour les applications de type AdBlock. Ajout d’un module hosts sans système - Taper le nom de l'application désirée + Taper le nom de l\'application désirée Nouveau nom - L'application sera réempacter sous ce nom + L\'application sera réempacter sous ce nom Format invalide Applications et ADB Applications uniquement diff --git a/native/jni/magiskhide/hide_utils.cpp b/native/jni/magiskhide/hide_utils.cpp index e12b75780..a5bdcda33 100644 --- a/native/jni/magiskhide/hide_utils.cpp +++ b/native/jni/magiskhide/hide_utils.cpp @@ -283,7 +283,7 @@ void auto_start_magiskhide() { db_settings dbs; get_db_settings(dbs, HIDE_CONFIG); if (dbs[HIDE_CONFIG]) { - new_daemon_thread([]() -> void { launch_magiskhide(-1); }); + new_daemon_thread([]{ launch_magiskhide(-1); }); } } From 0f34457a109e6650d360cebe5b02c4d289b2cd06 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 31 Oct 2019 15:33:13 -0400 Subject: [PATCH 11/74] Directly store strings in viewmodel --- .../com/topjohnwu/magisk/ui/home/HomeViewModel.kt | 12 ++++++------ app/src/main/res/layout/fragment_magisk.xml | 4 +--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt index 899632f7f..a84b5c2fe 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt @@ -77,7 +77,7 @@ class HomeViewModel( "" } - val safetyNetTitle = KObservableField(R.string.safetyNet_check_text) + val safetyNetTitle = KObservableField(R.string.safetyNet_check_text.res()) val ctsState = KObservableField(SafetyNetState.IDLE) val basicIntegrityState = KObservableField(SafetyNetState.IDLE) val safetyNetState = Observer(ctsState, basicIntegrityState) { @@ -135,7 +135,7 @@ class HomeViewModel( fun safetyNetPressed() { ctsState.value = SafetyNetState.LOADING basicIntegrityState.value = SafetyNetState.LOADING - safetyNetTitle.value = R.string.checking_safetyNet_status + safetyNetTitle.value = R.string.checking_safetyNet_status.res() UpdateSafetyNetEvent().publish() } @@ -144,7 +144,7 @@ class HomeViewModel( response and 0x0F == 0 -> { val hasCtsPassed = response and SafetyNetHelper.CTS_PASS != 0 val hasBasicIntegrityPassed = response and SafetyNetHelper.BASIC_PASS != 0 - safetyNetTitle.value = R.string.safetyNet_check_success + safetyNetTitle.value = R.string.safetyNet_check_success.res() ctsState.value = if (hasCtsPassed) { SafetyNetState.PASS } else { @@ -164,8 +164,8 @@ class HomeViewModel( ctsState.value = SafetyNetState.IDLE basicIntegrityState.value = SafetyNetState.IDLE safetyNetTitle.value = when (response) { - SafetyNetHelper.RESPONSE_ERR -> R.string.safetyNet_res_invalid - else -> R.string.safetyNet_api_error + SafetyNetHelper.RESPONSE_ERR -> R.string.safetyNet_res_invalid.res() + else -> R.string.safetyNet_api_error.res() } } } @@ -192,7 +192,7 @@ class HomeViewModel( _managerState.value = MagiskState.LOADING ctsState.value = SafetyNetState.IDLE basicIntegrityState.value = SafetyNetState.IDLE - safetyNetTitle.value = R.string.safetyNet_check_text + safetyNetTitle.value = R.string.safetyNet_check_text.res() }.subscribeK { updateSelf() ensureEnv() diff --git a/app/src/main/res/layout/fragment_magisk.xml b/app/src/main/res/layout/fragment_magisk.xml index 399aa75a2..366867ead 100644 --- a/app/src/main/res/layout/fragment_magisk.xml +++ b/app/src/main/res/layout/fragment_magisk.xml @@ -15,8 +15,6 @@ - - @@ -239,7 +237,7 @@ android:layout_marginRight="@dimen/margin_generic" android:gravity="center" android:maxLines="1" - android:text="@{XStringKt.res(viewModel.safetyNetTitle)}" + android:text="@{viewModel.safetyNetTitle}" android:textStyle="bold" app:autoSizeMaxTextSize="14sp" app:autoSizeTextType="uniform" From 10ce11d671cf5c29fc2a237477666c20548fcce8 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 31 Oct 2019 17:13:06 -0400 Subject: [PATCH 12/74] Fix config/locale issues Close #1977 --- app/src/main/java/com/topjohnwu/magisk/App.kt | 11 +-- .../main/java/com/topjohnwu/magisk/Hacks.kt | 42 ++++------- .../magisk/ui/settings/SettingsFragment.kt | 20 +----- .../com/topjohnwu/magisk/utils/Locales.kt | 70 +++++++++++++++---- 4 files changed, 77 insertions(+), 66 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/App.kt b/app/src/main/java/com/topjohnwu/magisk/App.kt index 0569ac57a..391c2563f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/App.kt +++ b/app/src/main/java/com/topjohnwu/magisk/App.kt @@ -16,6 +16,7 @@ import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.unwrap import com.topjohnwu.magisk.utils.RootInit +import com.topjohnwu.magisk.utils.updateConfig import com.topjohnwu.superuser.Shell import org.koin.android.ext.koin.androidContext import org.koin.core.context.startKoin @@ -58,15 +59,15 @@ open class App() : Application() { app = this impl = base } - ResourceMgr.init(impl) - super.attachBaseContext(impl.wrap()) + val wrapped = impl.wrap() + super.attachBaseContext(wrapped) // Normal startup startKoin { - androidContext(baseContext) + androidContext(wrapped) modules(koinModules) } - ResourceMgr.reload() + ResourceMgr.init(impl) app.registerActivityLifecycleCallbacks(get()) WorkManager.initialize(impl.wrapJob(), androidx.work.Configuration.Builder().build()) } @@ -77,7 +78,7 @@ open class App() : Application() { } override fun onConfigurationChanged(newConfig: Configuration) { - ResourceMgr.reload(newConfig) + resources.updateConfig(newConfig) if (!isRunningAsStub) super.onConfigurationChanged(newConfig) } diff --git a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt index dbac949b9..272d299ae 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt @@ -25,6 +25,8 @@ import com.topjohnwu.magisk.ui.flash.FlashActivity import com.topjohnwu.magisk.ui.surequest.SuRequestActivity import com.topjohnwu.magisk.utils.currentLocale import com.topjohnwu.magisk.utils.defaultLocale +import com.topjohnwu.magisk.utils.refreshLocale +import com.topjohnwu.magisk.utils.updateConfig import java.util.* fun AssetManager.addAssetPath(path: String) { @@ -51,15 +53,6 @@ fun Context.wrapJob(): Context = object : GlobalResContext(this) { } } -// Override locale and inject resources from dynamic APK -private fun Resources.patch(config: Configuration = Configuration(configuration)): Resources { - config.setLocale(currentLocale) - updateConfiguration(config, displayMetrics) - if (isRunningAsStub) - assets.addAssetPath(ResourceMgr.resApk) - return this -} - fun Class<*>.cmp(pkg: String = BuildConfig.APPLICATION_ID): ComponentName { val name = ClassMap[this].name return ComponentName(pkg, Info.stub?.componentMap?.get(name) ?: name) @@ -92,35 +85,28 @@ private open class GlobalResContext(base: Context) : ContextWrapper(base) { private class ResContext(base: Context) : GlobalResContext(base) { override val mRes by lazy { base.resources.patch() } + + private fun Resources.patch(): Resources { + updateConfig() + if (isRunningAsStub) + assets.addAssetPath(ResourceMgr.resApk) + return this + } } object ResourceMgr { - internal lateinit var resource: Resources - internal lateinit var resApk: String + lateinit var resource: Resources + lateinit var resApk: String fun init(context: Context) { resource = context.resources - if (isRunningAsStub) + refreshLocale() + if (isRunningAsStub) { resApk = DynAPK.current(context).path - } - - fun reload(config: Configuration = Configuration(resource.configuration)) { - val localeConfig = Config.locale - currentLocale = when { - localeConfig.isEmpty() -> defaultLocale - else -> localeConfig.langTagToLocale() + resource.assets.addAssetPath(resApk) } - Locale.setDefault(currentLocale) - resource.patch(config) } - - fun getString(locale: Locale, @StringRes id: Int): String { - val config = Configuration() - config.setLocale(locale) - return Resources(resource.assets, resource.displayMetrics, config).getString(id) - } - } @RequiresApi(api = 28) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt index 81fba3fff..fdcac23a5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt @@ -19,7 +19,6 @@ import com.topjohnwu.magisk.data.database.RepoDao import com.topjohnwu.magisk.databinding.CustomDownloadDialogBinding import com.topjohnwu.magisk.databinding.DialogCustomNameBinding import com.topjohnwu.magisk.extensions.subscribeK -import com.topjohnwu.magisk.extensions.toLangTag import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.DownloadSubject @@ -199,7 +198,7 @@ class SettingsFragment : BasePreferenceFragment() { Shell.su("magiskhide --disable").submit() } Config.Key.LOCALE -> { - ResourceMgr.reload() + refreshLocale() activity.recreate() } Config.Key.CHECK_UPDATES -> Utils.scheduleUpdateCheck(activity) @@ -223,22 +222,7 @@ class SettingsFragment : BasePreferenceFragment() { private fun setLocalePreference(lp: ListPreference) { lp.isEnabled = false - availableLocales.map { - val names = mutableListOf() - val values = mutableListOf() - - names.add( - ResourceMgr.getString(defaultLocale, R.string.system_default) - ) - values.add("") - - it.forEach { locale -> - names.add(locale.getDisplayName(locale)) - values.add(locale.toLangTag()) - } - - Pair(names.toTypedArray(), values.toTypedArray()) - }.subscribeK { (names, values) -> + availableLocales.subscribeK { (names, values) -> lp.isEnabled = true lp.entries = names lp.entryValues = values diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt b/app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt index f5fe0c8ea..f08bdb3d3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Locales.kt @@ -1,24 +1,32 @@ +@file:Suppress("DEPRECATION") + package com.topjohnwu.magisk.utils import android.annotation.SuppressLint import android.content.res.Configuration import android.content.res.Resources +import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R import com.topjohnwu.magisk.ResourceMgr import com.topjohnwu.magisk.extensions.langTagToLocale +import com.topjohnwu.magisk.extensions.toLangTag import io.reactivex.Single import java.util.* import kotlin.Comparator +import kotlin.collections.ArrayList var currentLocale: Locale = Locale.getDefault() @SuppressLint("ConstantLocale") val defaultLocale: Locale = Locale.getDefault() -@Suppress("DEPRECATION") val availableLocales = Single.fromCallable { val compareId = R.string.app_changelog - mutableListOf().apply { + val config = ResourceMgr.resource.configuration + val metrics = ResourceMgr.resource.displayMetrics + val res = Resources(ResourceMgr.resource.assets, metrics, config) + + val locales = mutableListOf().apply { // Add default locale add(Locale.ENGLISH) @@ -26,24 +34,56 @@ val availableLocales = Single.fromCallable { add(Locale.TAIWAN) add(Locale("pt", "BR")) - val config = Configuration() - val metrics = ResourceMgr.resource.displayMetrics - val res = Resources(ResourceMgr.resource.assets, metrics, config) - // Other locales val otherLocales = ResourceMgr.resource.assets.locales - .map { it.langTagToLocale() } - .distinctBy { - config.setLocale(it) - res.updateConfiguration(config, metrics) - res.getString(compareId) - } - - listOf("", "").toTypedArray() + .map { it.langTagToLocale() } + .distinctBy { + config.setLocale(it) + res.updateConfiguration(config, metrics) + res.getString(compareId) + } addAll(otherLocales) }.sortedWith(Comparator { a, b -> a.getDisplayName(a).toLowerCase(a) - .compareTo(b.getDisplayName(b).toLowerCase(b)) + .compareTo(b.getDisplayName(b).toLowerCase(b)) }) + + config.setLocale(defaultLocale) + res.updateConfiguration(config, metrics) + val defName = res.getString(R.string.system_default) + + // Restore back to current locale + config.setLocale(currentLocale) + res.updateConfiguration(config, metrics) + + Pair(locales, defName) +}.map { (locales, defName) -> + val names = ArrayList(locales.size + 1) + val values = ArrayList(locales.size + 1) + + names.add(defName) + values.add("") + + locales.forEach { locale -> + names.add(locale.getDisplayName(locale)) + values.add(locale.toLangTag()) + } + + Pair(names.toTypedArray(), values.toTypedArray()) }.cache()!! + +fun Resources.updateConfig(config: Configuration = configuration) { + config.setLocale(currentLocale) + updateConfiguration(config, displayMetrics) +} + +fun refreshLocale() { + val localeConfig = Config.locale + currentLocale = when { + localeConfig.isEmpty() -> defaultLocale + else -> localeConfig.langTagToLocale() + } + Locale.setDefault(currentLocale) + ResourceMgr.resource.updateConfig() +} From f4299fbea8ef3cbdb1b53bf3cb5af687f71399b9 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 31 Oct 2019 18:11:10 -0400 Subject: [PATCH 13/74] Update BusyBox to 1.31.1 --- native/jni/external/busybox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/jni/external/busybox b/native/jni/external/busybox index b7cba59ff..99cca1408 160000 --- a/native/jni/external/busybox +++ b/native/jni/external/busybox @@ -1 +1 @@ -Subproject commit b7cba59ffb2720b09a0bd81bf40f2a85db64153e +Subproject commit 99cca1408cfe177221b04811aa2b6fe172eb7587 From 493068c073b8fb9621fd731d7debd01d1f1e3117 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 1 Nov 2019 02:12:28 -0400 Subject: [PATCH 14/74] Attempt to rescan zygote multiple times Close #1654 --- native/jni/core/bootstages.cpp | 2 ++ native/jni/magiskhide/hide_utils.cpp | 14 +++++++++----- native/jni/magiskhide/magiskhide.h | 1 + native/jni/magiskhide/proc_monitor.cpp | 6 ++++++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index fb80491ae..f3fa51a9b 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -757,6 +757,8 @@ void boot_complete(int client) { if (!pfs_done) return; + auto_start_magiskhide(); + if (access(MANAGERAPK, F_OK) == 0) { // Install Magisk Manager if exists rename(MANAGERAPK, "/data/magisk.apk"); diff --git a/native/jni/magiskhide/hide_utils.cpp b/native/jni/magiskhide/hide_utils.cpp index a5bdcda33..c08a8eacc 100644 --- a/native/jni/magiskhide/hide_utils.cpp +++ b/native/jni/magiskhide/hide_utils.cpp @@ -217,7 +217,7 @@ static void set_hide_config() { db_err(err); } -static inline void launch_err(int client, int code = DAEMON_ERROR) { +[[noreturn]] static void launch_err(int client, int code = DAEMON_ERROR) { if (code != HIDE_IS_ENABLED) hide_enabled = false; if (client >= 0) { @@ -280,10 +280,14 @@ int stop_magiskhide() { } void auto_start_magiskhide() { - db_settings dbs; - get_db_settings(dbs, HIDE_CONFIG); - if (dbs[HIDE_CONFIG]) { - new_daemon_thread([]{ launch_magiskhide(-1); }); + if (hide_enabled) { + pthread_kill(proc_monitor_thread, SIGZYGOTE); + } else if (SDK_INT >= 19) { + db_settings dbs; + get_db_settings(dbs, HIDE_CONFIG); + if (dbs[HIDE_CONFIG]) { + new_daemon_thread([]{ launch_magiskhide(-1); }); + } } } diff --git a/native/jni/magiskhide/magiskhide.h b/native/jni/magiskhide/magiskhide.h index aeb87c380..832d9e8e9 100644 --- a/native/jni/magiskhide/magiskhide.h +++ b/native/jni/magiskhide/magiskhide.h @@ -13,6 +13,7 @@ #include #define SIGTERMTHRD SIGUSR1 +#define SIGZYGOTE SIGUSR2 #define SAFETYNET_COMPONENT "com.google.android.gms/.droidguard.DroidGuardService" #define SAFETYNET_PROCESS "com.google.android.gms.unstable" diff --git a/native/jni/magiskhide/proc_monitor.cpp b/native/jni/magiskhide/proc_monitor.cpp index 28da53872..c9d92fa2e 100644 --- a/native/jni/magiskhide/proc_monitor.cpp +++ b/native/jni/magiskhide/proc_monitor.cpp @@ -155,6 +155,10 @@ static void inotify_event(int) { check_zygote(); } +static void check_zygote(int) { + check_zygote(); +} + // Workaround for the lack of pthread_cancel static void term_thread(int) { LOGD("proc_monitor: cleaning up\n"); @@ -312,6 +316,8 @@ void proc_monitor() { sigaction(SIGTERMTHRD, &act, nullptr); act.sa_handler = inotify_event; sigaction(SIGIO, &act, nullptr); + act.sa_handler = check_zygote; + sigaction(SIGZYGOTE, &act, nullptr); setup_inotify(); From 8277896ca188e2f2c49c512ab540c51be4b851f3 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 1 Nov 2019 03:07:12 -0400 Subject: [PATCH 15/74] Make sure uninstall.sh is executed on remove --- native/jni/core/bootstages.cpp | 27 +++++++++++++++++++++++++++ native/jni/core/daemon.cpp | 28 ---------------------------- native/jni/include/daemon.h | 7 +++---- native/jni/utils/misc.cpp | 14 ++++++++++++++ native/jni/utils/misc.h | 3 ++- 5 files changed, 46 insertions(+), 33 deletions(-) diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index f3fa51a9b..9c57c776e 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -449,6 +449,33 @@ static void prepare_modules() { chmod(SECURE_DIR, 0700); } +static void reboot() { + if (RECOVERY_MODE) + exec_command_sync("/system/bin/reboot", "recovery"); + else + exec_command_sync("/system/bin/reboot"); +} + +void remove_modules() { + LOGI("* Remove all modules and reboot"); + chdir(MODULEROOT); + rm_rf("lost+found"); + DIR *dir = xopendir("."); + struct dirent *entry; + while ((entry = xreaddir(dir))) { + if (entry->d_type == DT_DIR) { + if (entry->d_name == "."sv || entry->d_name == ".."sv || entry->d_name == ".core"sv) + continue; + chdir(entry->d_name); + close(creat("remove", 0644)); + chdir(".."); + } + } + closedir(dir); + chdir("/"); + reboot(); +} + static void collect_modules() { chdir(MODULEROOT); rm_rf("lost+found"); diff --git a/native/jni/core/daemon.cpp b/native/jni/core/daemon.cpp index 0964b23d6..2948b1497 100644 --- a/native/jni/core/daemon.cpp +++ b/native/jni/core/daemon.cpp @@ -31,13 +31,6 @@ static void verify_client(int client, pid_t pid) { } } -static void remove_modules() { - LOGI("* Remove all modules and reboot"); - rm_rf(MODULEROOT); - rm_rf(MODULEUPGRADE); - reboot(); -} - static void *request_handler(void *args) { int client = reinterpret_cast(args); @@ -179,27 +172,6 @@ static void main_daemon() { } } -void reboot() { - if (RECOVERY_MODE) - exec_command_sync("/system/bin/reboot", "recovery"); - else - exec_command_sync("/system/bin/reboot"); -} - -int switch_mnt_ns(int pid) { - char mnt[32]; - snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", pid); - if (access(mnt, R_OK) == -1) return 1; // Maybe process died.. - - int fd, ret; - fd = xopen(mnt, O_RDONLY); - if (fd < 0) return 1; - // Switch to its namespace - ret = xsetns(fd, 0); - close(fd); - return ret; -} - int connect_daemon(bool create) { struct sockaddr_un sun; socklen_t len = setup_sockaddr(&sun, MAIN_SOCKET); diff --git a/native/jni/include/daemon.h b/native/jni/include/daemon.h index 0f6956b0e..6af462f43 100644 --- a/native/jni/include/daemon.h +++ b/native/jni/include/daemon.h @@ -30,13 +30,11 @@ enum { DAEMON_LAST }; -// daemon.c +// daemon.cpp int connect_daemon(bool create = false); -int switch_mnt_ns(int pid); -void reboot(); -// socket.c +// socket.cpp socklen_t setup_sockaddr(struct sockaddr_un *sun, const char *name); int create_rand_socket(struct sockaddr_un *sun); @@ -63,6 +61,7 @@ void unlock_blocks(); void post_fs_data(int client); void late_start(int client); void boot_complete(int client); +void remove_modules(); /************* * Scripting * diff --git a/native/jni/utils/misc.cpp b/native/jni/utils/misc.cpp index b01da66b1..4678fb41e 100644 --- a/native/jni/utils/misc.cpp +++ b/native/jni/utils/misc.cpp @@ -198,3 +198,17 @@ uint32_t binary_gcd(uint32_t u, uint32_t v) { } while (v != 0); return u << shift; } + +int switch_mnt_ns(int pid) { + char mnt[32]; + snprintf(mnt, sizeof(mnt), "/proc/%d/ns/mnt", pid); + if (access(mnt, R_OK) == -1) return 1; // Maybe process died.. + + int fd, ret; + fd = xopen(mnt, O_RDONLY); + if (fd < 0) return 1; + // Switch to its namespace + ret = xsetns(fd, 0); + close(fd); + return ret; +} diff --git a/native/jni/utils/misc.h b/native/jni/utils/misc.h index a55516f30..c5addb0fa 100644 --- a/native/jni/utils/misc.h +++ b/native/jni/utils/misc.h @@ -15,6 +15,7 @@ void init_argv0(int argc, char **argv); void set_nice_name(const char *name); int parse_int(const char *s); uint32_t binary_gcd(uint32_t u, uint32_t v); +int switch_mnt_ns(int pid); #ifdef __cplusplus } @@ -96,4 +97,4 @@ int exec_command_sync(Args &&...args) { bool ends_with(const std::string_view &s1, const std::string_view &s2); -#endif \ No newline at end of file +#endif From a30f5b175ff99918394f796966bffaf7a5615b89 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 1 Nov 2019 09:38:01 -0400 Subject: [PATCH 16/74] Fix busybox makefiles --- native/jni/external/busybox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/jni/external/busybox b/native/jni/external/busybox index 99cca1408..e8f5c72a7 160000 --- a/native/jni/external/busybox +++ b/native/jni/external/busybox @@ -1 +1 @@ -Subproject commit 99cca1408cfe177221b04811aa2b6fe172eb7587 +Subproject commit e8f5c72a76b627796ff47e0796ba04ad10c2efc3 From d8d72f92b3fd5c48cdff4b4c16db405c41a065f8 Mon Sep 17 00:00:00 2001 From: Viktor De Pasquale Date: Fri, 1 Nov 2019 14:47:59 +0100 Subject: [PATCH 17/74] Fixed policy toggle being impossible to cancel --- .../com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt index aa0bcfee4..a8fa02f18 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt @@ -42,6 +42,11 @@ class SuperuserViewModel( init { rxBus.register() + .filter { + val isIgnored = it.item == ignoreNext + if (isIgnored) ignoreNext = null + !isIgnored + } .subscribeK { togglePolicy(it.item, it.enable) } .add() rxBus.register() From 4cd154675f905108103d40e6e292c1e5f2ce4900 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 1 Nov 2019 18:52:37 -0400 Subject: [PATCH 18/74] Random dname --- .../java/com/topjohnwu/magisk/utils/Keygen.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Keygen.kt b/app/src/main/java/com/topjohnwu/magisk/utils/Keygen.kt index 1ba0dc982..5f937459d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Keygen.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Keygen.kt @@ -38,7 +38,7 @@ class Keygen: CertKeyProvider { private const val ALIAS = "magisk" private val PASSWORD get() = "magisk".toCharArray() private const val TESTKEY_CERT = "61ed377e85d386a8dfee6b864bd85b0bfaa5af81" - private const val DNAME = "C=US,ST=California,L=Mountain View,O=Google Inc.,OU=Android,CN=Android" + private const val ALPHANUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" private const val BASE64_FLAG = Base64.NO_PADDING or Base64.NO_WRAP } @@ -88,6 +88,17 @@ class Keygen: CertKeyProvider { } } + private fun randomString(): String { + val rand = kotlin.random.Random.Default + val len = rand.nextInt(5, 10) + val sb = StringBuilder(len) + for (i in 0..len) { + val idx = rand.nextInt(ALPHANUM.length) + sb.append(ALPHANUM[idx]) + } + return sb.toString() + } + private fun init(): KeyStore { GlobalContext.getOrNull() ?: { // Invoked externally, do some basic initialization @@ -113,7 +124,7 @@ class Keygen: CertKeyProvider { // Generate new private key and certificate val kp = KeyPairGenerator.getInstance("RSA").apply { initialize(4096) }.genKeyPair() - val dname = X500Name(DNAME) + val dname = X500Name("CN=${randomString()}") val builder = JcaX509v3CertificateBuilder(dname, BigInteger(160, Random()), start.time, end.time, dname, kp.public) val signer = JcaContentSignerBuilder("SHA256WithRSA").build(kp.private) From 6dd34aec479c5538de8acd9aade9c06b83b9e7e7 Mon Sep 17 00:00:00 2001 From: osm0sis Date: Sat, 2 Nov 2019 00:26:08 -0300 Subject: [PATCH 19/74] scripts: refactor and major addon.d fixes - remove redundant addon.d.sh script bits that were covered elsewhere ($TMPDIR in util_functions.sh, find_dtbo_image in patch_dtbo_image) - refactor addon.d.sh and flash_script.sh for simplicity and readability, and put common flashing script in util_functions.sh (as patch_boot_image), which should greatly help avoid them getting out of sync going forward and fixes compressing ramdisk support and post-patch cleanup for addon.d - add check_data to addon.d.sh since moving stock_boot* and stock_dtbo* backups depend on it and so weren't occuring with addon.d - fix find_manager_apk with working fallback for recovery addon.d execution (where `magisk --sqlite` will not work for hidden Manager), Manager DynAPK hiding, and print a useful log warning if an APK can't be found --- scripts/addon.d.sh | 81 ++++++++++++--------------------------- scripts/flash_script.sh | 39 ++----------------- scripts/util_functions.sh | 48 +++++++++++++++++++++-- 3 files changed, 73 insertions(+), 95 deletions(-) diff --git a/scripts/addon.d.sh b/scripts/addon.d.sh index 95c18d0bd..53aa676c4 100644 --- a/scripts/addon.d.sh +++ b/scripts/addon.d.sh @@ -20,9 +20,6 @@ else fi initialize() { - # This path should work in any cases - TMPDIR=/dev/tmp - mount /data 2>/dev/null MAGISKBIN=/data/adb/magisk @@ -43,71 +40,43 @@ initialize() { fi } -show_logo() { - ui_print "************************" - ui_print "* Magisk v$MAGISK_VER addon.d" - ui_print "************************" -} - -installation() { - find_manager_apk - get_flags - find_boot_image - find_dtbo_image - [ -z $BOOTIMAGE ] && abort "! Unable to detect target image" - ui_print "- Target image: $BOOTIMAGE" - [ -z $DTBOIMAGE ] || ui_print "- DTBO image: $DTBOIMAGE" - - remove_system_su - - [ -f $APK ] && eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true - $BOOTSIGNED && ui_print "- Boot image is signed with AVB 1.0" - - SOURCEDMODE=true - cd $MAGISKBIN - - # Source the boot patcher - . ./boot_patch.sh "$BOOTIMAGE" - - ui_print "- Flashing new boot image" - flash_image new-boot.img "$BOOTIMAGE" || abort "! Insufficient partition size" - rm -f new-boot.img - - if [ -f stock_boot* ]; then - rm -f /data/stock_boot* 2>/dev/null - $DATA && mv stock_boot* /data - fi - - $KEEPVERITY || patch_dtbo_image - - if [ -f stock_dtbo* ]; then - rm -f /data/stock_dtbo* 2>/dev/null - $DATA && mv stock_dtbo* /data - fi - - cd / -} - -finalize() { - ui_print "- Done" - exit 0 -} - main() { if ! $backuptool_ab; then # Wait for post addon.d-v1 processes to finish sleep 5 fi + $BOOTMODE || recovery_actions - show_logo + + ui_print "************************" + ui_print "* Magisk v$MAGISK_VER addon.d" + ui_print "************************" + mount_partitions + check_data + get_flags + if $backuptool_ab; then # Swap the slot for addon.d-v2 if [ ! -z $SLOT ]; then [ $SLOT = _a ] && SLOT=_b || SLOT=_a; fi fi - installation + + find_boot_image + + [ -z $BOOTIMAGE ] && abort "! Unable to detect target image" + ui_print "- Target image: $BOOTIMAGE" + + remove_system_su + find_manager_apk + patch_boot_image + + cd / + # Cleanups $BOOTMODE || recovery_cleanup - finalize + rm -rf $TMPDIR + + ui_print "- Done" + exit 0 } case "$1" in diff --git a/scripts/flash_script.sh b/scripts/flash_script.sh index f8f47e4cf..df92b5e48 100644 --- a/scripts/flash_script.sh +++ b/scripts/flash_script.sh @@ -1,7 +1,7 @@ #MAGISK ########################################################################################## # -# Magisk Flash Script +# Magisk Flash Script (updater-script) # by topjohnwu # # This script will detect, construct the environment for Magisk @@ -103,43 +103,10 @@ fi $BOOTMODE || recovery_actions ########################################################################################## -# Boot patching +# Boot/DTBO Patching ########################################################################################## -eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true -$BOOTSIGNED && ui_print "- Boot image is signed with AVB 1.0" - -SOURCEDMODE=true -cd $MAGISKBIN - -$IS64BIT && mv -f magiskinit64 magiskinit || rm -f magiskinit64 - -# Source the boot patcher -. ./boot_patch.sh "$BOOTIMAGE" - -ui_print "- Flashing new boot image" - -if ! flash_image new-boot.img "$BOOTIMAGE"; then - ui_print "- Compressing ramdisk to fit in partition" - ./magiskboot cpio ramdisk.cpio compress - ./magiskboot repack "$BOOTIMAGE" - flash_image new-boot.img "$BOOTIMAGE" || abort "! Insufficient partition size" -fi - -./magiskboot cleanup -rm -f new-boot.img - -if [ -f stock_boot* ]; then - rm -f /data/stock_boot* 2>/dev/null - $DATA && mv stock_boot* /data -fi - -$KEEPVERITY || patch_dtbo_image - -if [ -f stock_dtbo* ]; then - rm -f /data/stock_dtbo* 2>/dev/null - $DATA && mv stock_dtbo* /data -fi +patch_boot_image cd / # Cleanups diff --git a/scripts/util_functions.sh b/scripts/util_functions.sh index 046a483fc..a2af6444b 100644 --- a/scripts/util_functions.sh +++ b/scripts/util_functions.sh @@ -293,6 +293,45 @@ patch_dtbo_image() { return 1 } +patch_boot_image() { + # Common installation script for flash_script.sh (updater-script) and addon.d.sh + eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true + $BOOTSIGNED && ui_print "- Boot image is signed with AVB 1.0" + + SOURCEDMODE=true + cd $MAGISKBIN + + $IS64BIT && mv -f magiskinit64 magiskinit 2>/dev/null || rm -f magiskinit64 + + # Source the boot patcher + . ./boot_patch.sh "$BOOTIMAGE" + + ui_print "- Flashing new boot image" + + if ! flash_image new-boot.img "$BOOTIMAGE"; then + ui_print "- Compressing ramdisk to fit in partition" + ./magiskboot cpio ramdisk.cpio compress + ./magiskboot repack "$BOOTIMAGE" + flash_image new-boot.img "$BOOTIMAGE" || abort "! Insufficient partition size" + fi + + ./magiskboot cleanup + rm -f new-boot.img + + if [ -f stock_boot* ]; then + rm -f /data/stock_boot* 2>/dev/null + $DATA && mv stock_boot* /data + fi + + # Patch DTBO together with boot image + $KEEPVERITY || patch_dtbo_image + + if [ -f stock_dtbo* ]; then + rm -f /data/stock_dtbo* 2>/dev/null + $DATA && mv stock_dtbo* /data + fi +} + sign_chromeos() { ui_print "- Signing ChromeOS boot image" @@ -360,13 +399,16 @@ check_data() { } find_manager_apk() { - APK=/data/adb/magisk.apk + [ -z $APK ] && APK=/data/adb/magisk.apk [ -f $APK ] || APK=/data/magisk/magisk.apk [ -f $APK ] || APK=/data/app/com.topjohnwu.magisk*/*.apk if [ ! -f $APK ]; then - DBAPK=`magisk --sqlite "SELECT value FROM strings WHERE key='requester'" | cut -d= -f2` - [ -z "$DBAPK" ] || APK=/data/app/$DBAPK*/*.apk + DBAPK=`magisk --sqlite "SELECT value FROM strings WHERE key='requester'" 2>/dev/null | cut -d= -f2` + [ -z $DBAPK ] && DBAPK=`strings /data/adb/magisk.db | grep 5requester | cut -c11-` + [ -z $DBAPK ] || APK=/data/user_de/*/$DBAPK/dyn/*.apk + [ -f $APK ] || APK=/data/app/$DBAPK*/*.apk fi + [ -f $APK ] || ui_print "! Unable to detect Magisk Manager APK for BootSigner" } ################# From c85b1c56af6fc2493380f902962407dcb292a5a5 Mon Sep 17 00:00:00 2001 From: osm0sis Date: Sat, 2 Nov 2019 00:26:53 -0300 Subject: [PATCH 20/74] signing: fixes for bootimg hdr_v1 and hdr_v2 - increase SignBoot bootimg header version maximum from 4 to 8 (upstream AOSP is already at 3) and make a variable for future ease - hdr read size of 1024 bytes was too small as hdr_v1 and hdr_v2 have increased the used header page areas to 1632 and 1648 bytes, respectively, so raise this to the minimum page size of 2048 and also make a variable for future ease - do not return "not signed" for all caught exceptions, show StackTrace for future debugging then still return false for script purposes - correct "test keys" boot image signing strings (scripts and app) to "verity keys" --- .../topjohnwu/magisk/tasks/MagiskInstaller.kt | 2 +- scripts/util_functions.sh | 2 +- .../java/com/topjohnwu/signing/SignBoot.java | 19 +++++++++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt b/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt index 5357f9703..e6de79548 100644 --- a/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt +++ b/app/src/main/java/com/topjohnwu/magisk/tasks/MagiskInstaller.kt @@ -266,7 +266,7 @@ abstract class MagiskInstaller { val patched = File(installDir, "new-boot.img") if (isSigned) { - console.add("- Signing boot image with test keys") + console.add("- Signing boot image with verity keys") val signed = File(installDir, "signed.img") try { withStreams(SuFileInputStream(patched), signed.outputStream().buffered()) { diff --git a/scripts/util_functions.sh b/scripts/util_functions.sh index a2af6444b..6482e114a 100644 --- a/scripts/util_functions.sh +++ b/scripts/util_functions.sh @@ -256,7 +256,7 @@ flash_image() { esac if $BOOTSIGNED; then CMD2="$BOOTSIGNER -sign" - ui_print "- Sign image with test keys" + ui_print "- Sign image with verity keys" else CMD2="cat -" fi diff --git a/signing/src/main/java/com/topjohnwu/signing/SignBoot.java b/signing/src/main/java/com/topjohnwu/signing/SignBoot.java index ce1494ff5..c7347b2b7 100644 --- a/signing/src/main/java/com/topjohnwu/signing/SignBoot.java +++ b/signing/src/main/java/com/topjohnwu/signing/SignBoot.java @@ -33,6 +33,12 @@ public class SignBoot { private static final int BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET = 1632; private static final int BOOT_IMAGE_HEADER_V2_DTB_SIZE_OFFSET = 1648; + /* Arbitrary maximum header version value; when greater assume the field is dt/extra size */ + private static final int BOOT_IMAGE_HEADER_VERSION_MAXIMUM = 8; + + /* Maximum header size byte value to read (bootimg minimum page size) */ + private static final int BOOT_IMAGE_HEADER_SIZE_MAXIMUM = 2048; + private static class PushBackRWStream extends FilterInputStream { private OutputStream out; private int pos = 0; @@ -82,7 +88,7 @@ public class SignBoot { InputStream cert, InputStream key) { try { PushBackRWStream in = new PushBackRWStream(imgIn, imgOut); - byte[] hdr = new byte[1024]; + byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM]; // First read the header in.read(hdr); int signableSize = getSignableImageSize(hdr); @@ -113,7 +119,7 @@ public class SignBoot { public static boolean verifySignature(InputStream imgIn, InputStream certIn) { try { // Read the header for size - byte[] hdr = new byte[1024]; + byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM]; if (imgIn.read(hdr) != hdr.length) return false; int signableSize = getSignableImageSize(hdr); @@ -141,7 +147,8 @@ public class SignBoot { System.err.println("Signature is INVALID"); } } catch (Exception e) { - System.err.println("Invalid image: not signed"); + e.printStackTrace(); + return false; } return false; } @@ -165,8 +172,8 @@ public class SignBoot { + ((kernelSize + pageSize - 1) / pageSize) * pageSize + ((ramdskSize + pageSize - 1) / pageSize) * pageSize + ((secondSize + pageSize - 1) / pageSize) * pageSize; - int headerVersion = image.getInt(); // boot image header version or extra size - if (headerVersion > 0 && headerVersion < 4) { + int headerVersion = image.getInt(); // boot image header version or dt/extra size + if (headerVersion > 0 && headerVersion < BOOT_IMAGE_HEADER_VERSION_MAXIMUM) { image.position(BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET); int recoveryDtboLength = image.getInt(); length += ((recoveryDtboLength + pageSize - 1) / pageSize) * pageSize; @@ -183,7 +190,7 @@ public class SignBoot { "Invalid image header: invalid header length"); } } else { - // headerVersion is 0 or actually extra size in this case + // headerVersion is 0 or actually dt/extra size in this case length += ((headerVersion + pageSize - 1) / pageSize) * pageSize; } length = ((length + pageSize - 1) / pageSize) * pageSize; From 2dad751889ba285deb1b7ca25093f8a61f4ea92c Mon Sep 17 00:00:00 2001 From: Oliver Cervera Date: Fri, 1 Nov 2019 23:38:18 +0100 Subject: [PATCH 21/74] Update Italian translation - updated existing strings based on english updates - added new strings --- app/src/main/res/values-it/strings.xml | 46 ++++++++++++++------------ 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 7d20c8d46..390f51311 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -18,7 +18,7 @@ Controllo stato SafetyNet… Controllo SafetyNet Superato Errore API SafetyNet - La risposta non è valida. + La risposta non è valida Magisk è aggiornato Magisk Manager è aggiornato Impostazioni avanzate @@ -28,7 +28,7 @@ Ultima: %1$s Disinstalla Disinstalla Magisk - Tutti i moduli verranno disabilitati/rimossi. Il root verrà rimosso e i tuoi dati potrebbero essere criptati, nel caso non lo siano già. + Tutti i moduli verranno disabilitati/rimossi!\nIl root verrà rimosso e i tuoi dati potrebbero essere criptati, nel caso non lo siano già. Aggiorna (Modalità solo Core abilitata) @@ -74,7 +74,7 @@ È disponibile un aggiornamento di Magisk Manager! - Apri questa notifica per scaricare e installare. + Apri questa notifica per scaricare e installare Scarica solo il file zip Installazione diretta (raccomandata) Installa nello slot inattivo (dopo un OTA) @@ -90,7 +90,7 @@ Vuoi installare %1$s? Download Riavvia - Riavvia per applicare i cambiamenti. + Riavvia per applicare i cambiamenti Note di rilascio La cache delle repository è stata svuotata @@ -101,7 +101,7 @@ Fallito Nascondendo Magisk Manager… Non è stato possibile nascondere Magisk Manager. - Nessuna app disponibile per aprire il link. + Nessuna app disponibile per aprire il link Attenzione Disinstallazione completa Ripristina Immagini @@ -110,7 +110,7 @@ Non esiste un\'immagine originale di boot! Scarica codice proprietario Magisk Manager è FOSS e non contiene codice proprietario delle API Google SafetyNet.\n\nVuoi scaricare un\'estensione (contenente GoogleApiClient) per controllare lo stato di SafetyNet? - Configurazione fallita. + Configurazione fallita Configurazione aggiuntiva richiesta Il tuo dispositivo necessita di una configurazione aggiuntiva per far funzionare Magisk correttamente. Verrà scaricato il file zip di Magisk, vuoi procedere ora? Configurazione dell\'ambiente in corso… @@ -118,20 +118,20 @@ Generale Tema scuro - Abilita il tema scuro. + Abilita il tema scuro Percorso di download I file verranno salvati in %1$s Svuota cache repository - Svuota la cache delle repository. Questa opzione forza l\'aggiornamento online dell\'app. + Svuota la cache delle repository. Questa opzione forza l\'aggiornamento online dell\'app Nascondi Magisk Manager - Reinstalla Magisk Manager con un nome pacchetto casuale. + Reinstalla Magisk Manager con un nome pacchetto e app casuali Ripristina Magisk Manager - Ripristina Magisk Manager con il nome pacchetto originale + Ripristina Magisk Manager con il nome pacchetto e app originali Lingua (Sistema) Impostazioni aggiornamento Controlla aggiornamenti - Controlla automaticamente gli aggiornamenti in background. + Controlla automaticamente gli aggiornamenti in background Canale di aggiornamento Stabile Beta @@ -139,12 +139,16 @@ Inserisci un URL personalizzato Modalità Magisk Core Abilita solo le funzioni principali. Nessun modulo verrà caricato. MagiskSU e MagiskHide rimarranno abilitati - Nasconde Magisk da numerose rilevazioni. + Nasconde Magisk da numerose rilevazioni Host systemless - Supporto a host systemless per le app che bloccano le pubblicità. + Supporto a host systemless per le app che bloccano le pubblicità Aggiunto modulo per host systemless - App e ADB + Inserisci il nome desiderato per l\'app + Nuovo nome + L\'app sarà ricreata con questo nome + Formato non valido + App e ADB Solo app Solo ADB Disabilitato @@ -169,18 +173,18 @@ Solo proprietario del dispositivo Gestito dal proprietario utente Idipendente dall\'utente - Solo il proprietario ha i permessi di root. - Solo il proprietario può gestire accesso root e ricevere richieste. - Ogni utente ha le sue regole di root indpendenti. + Solo il proprietario ha i permessi di root + Solo il proprietario può gestire accesso root e ricevere richieste + Ogni utente ha le sue regole di root indpendenti Modalità mount namespace Namespace globale Namespace ereditato Namespace isolato - Tutte le sessioni di root erediteranno il namespace globale. - Le sessioni di root erediteranno il namespace del loro richiedente. - Ogni sessione di root avrà il suo namespace isolato. - Non è presente alcuna impronta o il dispositivo non è supportato. + Tutte le sessioni di root erediteranno il namespace globale + Le sessioni di root erediteranno il namespace del loro richiedente + Ogni sessione di root avrà il suo namespace isolato + Non è presente alcuna impronta o il dispositivo non è supportato Errore durante la creazione della cartella. Deve essere accessibile dalla radice della memoria di archiviazione e non essere un file. From b31d986c8debb45e2a8b527252a1559b89c0a7ee Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 2 Nov 2019 00:41:51 -0400 Subject: [PATCH 22/74] Update scripts --- scripts/util_functions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/util_functions.sh b/scripts/util_functions.sh index 6482e114a..8977f28cb 100644 --- a/scripts/util_functions.sh +++ b/scripts/util_functions.sh @@ -406,7 +406,7 @@ find_manager_apk() { DBAPK=`magisk --sqlite "SELECT value FROM strings WHERE key='requester'" 2>/dev/null | cut -d= -f2` [ -z $DBAPK ] && DBAPK=`strings /data/adb/magisk.db | grep 5requester | cut -c11-` [ -z $DBAPK ] || APK=/data/user_de/*/$DBAPK/dyn/*.apk - [ -f $APK ] || APK=/data/app/$DBAPK*/*.apk + [ -f $APK ] || [ -z $DBAPK ] || APK=/data/app/$DBAPK*/*.apk fi [ -f $APK ] || ui_print "! Unable to detect Magisk Manager APK for BootSigner" } From a9f11b28c8d35888dd568b98bbe12daca6d2c3c0 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 2 Nov 2019 01:16:54 -0400 Subject: [PATCH 23/74] Fix busybox scripts again --- native/jni/external/busybox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/jni/external/busybox b/native/jni/external/busybox index e8f5c72a7..23132f3d4 160000 --- a/native/jni/external/busybox +++ b/native/jni/external/busybox @@ -1 +1 @@ -Subproject commit e8f5c72a76b627796ff47e0796ba04ad10c2efc3 +Subproject commit 23132f3d45089f9c0e495ec5365993f4b2ef348c From 7da97489cc0f6bb0ddcde8d5a91fa1719bf01791 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 2 Nov 2019 01:24:56 -0400 Subject: [PATCH 24/74] Add v7.4.0 release notes --- app/src/main/res/raw/changelog.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/raw/changelog.md b/app/src/main/res/raw/changelog.md index dd0d950b3..cfb6136f2 100644 --- a/app/src/main/res/raw/changelog.md +++ b/app/src/main/res/raw/changelog.md @@ -1,4 +1,5 @@ -# v7.3.5 -- Sort installed modules by name -- Better pre-5.0 support -- Fix potential issues when patching tar files +# v7.4.0 +- Hide Magisk Manager with stub APKs on Android 9.0+. Not all devices will be supported, please refer to Magisk v20.1 release notes. +- Allow customizing app name when hiding Magisk Manager +- Generate random keys to sign the hidden Magisk Manager to prevent signature detections +- Fix fingerprint UI infinite loop From 14ba002cbc285bac67bee3f2a1142b461c3cfd23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Kubala?= <37414585+kubalav@users.noreply.github.com> Date: Sat, 2 Nov 2019 16:59:20 +0100 Subject: [PATCH 25/74] Update Slovak translation --- app/src/main/res/values-sk/strings.xml | 74 ++++++++++++++++--------- stub/src/main/res/values-sk/strings.xml | 2 + 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 9d62740bf..3905a01ad 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -10,7 +10,6 @@ Nepodporovaná verzia Magisku Táto verzia Magisk Managera podporuje Magisk od verzie v18.0.\n\nBuď upgradujte Magisk manuálne alebo nainštalujte staršiu verziu aplikácie. - Magisk nie je nainštalovaný Kontrola aktualizácií… @@ -25,26 +24,28 @@ Pokročilé nastavenia Ponechať vynútené šifrovanie Ponechať AVB 2.0/dm-verity + Režim Recovery Nainštalovaná verzia: %1$s Najnovšia verzia: %1$s Odinštalovať - Všetky moduly budú zakázané/odstránené. Root bude odstránený a teoreticky môže dôjsť k zašifrovaniu vašich dát, ak ich momentálne nemáte zašifrované Odinštalovať Magisk + Všetky moduly budú zakázané/odstránené!\nRoot bude odstránený!\nMôže dôjsť k zašifrovaniu vašich dát, ak už nie sú zašifrované! Aktualizovať - (Režim Core povolený) + (Povolený iba režim Core) (Nie sú k dispozícii žiadne informácie) Neboli nájdené žiadne moduly - Modul bude aktualizovaný pri ďalšom reštarte - Modul bude odstránený pri ďalšom reštarte - Modul nebude odstránený pri ďalšom reštarte - Modul bude zakázaný pri ďalšom reštarte - Modul bude povolený pri ďalšom reštarte + Modul bude aktualizovaný pri ďalšom reštarte! + Modul bude odstránený pri ďalšom reštarte! + Modul nebude odstránený pri ďalšom reštarte! + Modul bude zakázaný pri ďalšom reštarte! + Modul bude povolený pri ďalšom reštarte! Vytvoril %1$s Reštartovať do Recovery Reštartovať do Bootloader Reštartovať do Download + Reštartovať do EDL Dostupná aktualizácia @@ -59,18 +60,33 @@ Uložiť záznam Obnoviť Odstrániť záznamy - Záznamy boli odstránené + Záznamy boli odstránené. Zoznam zmien + Aktualizácie Magisk - Notifkikácie priebehu + Upozornenia o priebehu Sťahovanie ukončené Chyba sťahovania súboru + Zobraziť v nadradenom priečinku + Zobraziť súbor Je dostupná aktualizácia Magisk! Je dostupná aktualizácia Magisk Manager! + + Stlačte pre stiahnutie a inštaláciu + Stiahnuť iba zip + Priama inštalácia (Odporúča sa) + Inštalovať na aktívny slot (Po OTA) + Vaše zariadenie bude po reštarte PRINÚTENÉ nabootovať do aktuálne neaktívneho slotu!\nTúto voľbu použite iba po skončení OTA.\nPokračovať? + Vyberte metódu + Ďalšie nastavenie + Vybrať a zaplátať súbor + Vyberte raw súbor (*.img) alebo tar súbor ODIN (*.tar) + Reštart o 5 sekúnd… + Nainštalovať %1$s Chcete teraz nainštalovať %1$s? @@ -79,17 +95,16 @@ Pre aplikovanie nastavení je potrebný reštart Poznámky k vydaniu Cache repo odstránená - Stlačte pre stiahnutie a inštaláciu + DTBO bol zaplátaný! - Magisk Manager zaplátal dtbo.img, prosím, reštartujte + Magisk Manager zaplátal dtbo.img, prosím, reštartujte. Flashovanie + Hotovo! + Zlyhalo + Skrytie Magisk Managera… + Skrytie Magisk Manager zlyhalo. Nepodarilo sa nájsť vhodnú aplikáciu na otvorenie odkazu… - Stiahnuť iba zip - Priama inštalácia (Odporúča sa) - Inštalovať na aktívny slot (Po OTA) Upozornenie - Vaše zariadenie bude po reštarte PRINÚTENÉ nabootovať do aktuálne neaktívneho slotu!\nTúto voľbu použite iba po skončení OTA.\nPokračovať? - Vyberte metódu Úplne odinštalovať Obnoviť obrazy Obnovovanie… @@ -100,14 +115,15 @@ Nastavenie zlyhalo Vyžaduje sa ďalšie nastavenie Vaše zariadenie si vyžaduje ďalšie nastavenie pre správne fungovanie Magisk. Stiahne sa inštalačný zip súbor Magisk, chcete pokračovať teraz? - Ďalšie nastavenie - Spustené prostredie nastavenia… + Nastavenie je spustené… Všeobecné Tmavý motív Povoliť tmavý motív - Vyčistiť cache repo + Cesta na sťahovanie + Súbory budú uložené do %1$s + Vyčistiť cache repo Vyčistí uložené informácie pre online repo, aplikácia si nové informácie stiahne online Skryť Magisk Manager Prebalí Magisk Manager s náhodným názvom balíčka @@ -124,12 +140,16 @@ Vlastný Zadajte vlastnú URL Režim Magisk Core - Povolí iba hlavné funkcie. Stále budú povolené MagiskSU, MagiskHide a systemless hosts, ale žiadne moduly nebudú zavedené. + Povolí iba hlavné funkcie. Stále budú povolené MagiskSU, MagiskHide a systemless hosts, ale žiadne moduly nebudú zavedené Ukryje Magisk pre rôznymi detekciami Systemless hosts Podpora pre aplikácie systemless hosts Adblock Pridaný modul systemless hosts + Zadajte požadovaný názov aplikácie + Nový názov + Zadajte požadovaný názov aplikácie + Neplatný formát Aplikácie a ADB Iba aplikácie Iba ADB @@ -160,13 +180,14 @@ Každý používateľ má vlastné pravidlá pre root Režim Mount Namespace - Globálne Namespace - Zdedené Namespace - Izolované Namespace - Všetky relácie root použijú globálne mount namespace - Relácie root zdedia namespace od požadovateľa + Globálny Namespace + Zdedený Namespace + Izolovaný Namespace + Všetky relácie root použijú globálny mount namespace + Relácie root zdedia namespace od žiadateľa Každá relácia root bude mať vlastný izolovaný namespace Neboli odoslané žiadne odtlačky prsta alebo ich zariadenie nepodporuje + Chyba pri vytváraní priečinka. Musí byť prístupný z koreňového adresára a nemôže to byť súbor. Žiadosť superuser @@ -199,6 +220,7 @@ PID:%1$d Cieľový UID: %1$d Príkaz: %1$s + Zobraziť systémové aplikácie diff --git a/stub/src/main/res/values-sk/strings.xml b/stub/src/main/res/values-sk/strings.xml index d123d58b7..19d3005a3 100644 --- a/stub/src/main/res/values-sk/strings.xml +++ b/stub/src/main/res/values-sk/strings.xml @@ -1,4 +1,6 @@ + Pre dokončenie inštalácie sa vyžaduje upgrade Magisk Managera. Stiahnuť a nainštalovať? Pripojte sa na internet! Upgrade Magisk Managera je potrebný. + Sťahuje sa From 0c9feedb37795dc78ceab9b9cc12592c5316e2a9 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 3 Nov 2019 02:55:12 -0500 Subject: [PATCH 26/74] Support restarting app when obfuscated --- .../main/java/com/topjohnwu/magisk/Hacks.kt | 15 +++------ .../magisk/model/download/DownloadService.kt | 2 +- .../magisk/model/download/ManagerUpgrade.kt | 2 +- .../magisk/model/receiver/GeneralReceiver.kt | 2 +- .../com/topjohnwu/magisk/ui/MainActivity.kt | 2 +- .../com/topjohnwu/magisk/ui/SplashActivity.kt | 7 ++--- .../magisk/ui/flash/FlashActivity.kt | 2 +- .../magisk/ui/module/ModulesFragment.kt | 2 +- .../topjohnwu/magisk/view/Notifications.kt | 6 ++-- .../com/topjohnwu/magisk/view/Shortcuts.kt | 5 +-- .../java/com/topjohnwu/magisk/DynAPK.java | 2 +- .../com/topjohnwu/magisk/ProcessPhoenix.java | 31 ++++--------------- .../topjohnwu/magisk/DownloadActivity.java | 9 +++++- .../topjohnwu/magisk/obfuscate/Mapping.java | 16 +++++++--- 14 files changed, 46 insertions(+), 57 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt index 272d299ae..4f7abfe80 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt @@ -53,18 +53,12 @@ fun Context.wrapJob(): Context = object : GlobalResContext(this) { } } -fun Class<*>.cmp(pkg: String = BuildConfig.APPLICATION_ID): ComponentName { +fun Class<*>.cmp(pkg: String): ComponentName { val name = ClassMap[this].name return ComponentName(pkg, Info.stub?.componentMap?.get(name) ?: name) } -fun Context.intent(c: Class<*>): Intent { - val cls = ClassMap[c] - return Info.stub?.let { - val className = it.componentMap.getOrElse(cls.name) { cls.name } - Intent().setComponent(ComponentName(this, className)) - } ?: Intent(this, cls) -} +inline fun Context.intent() = Intent().setComponent(T::class.java.cmp(packageName)) private open class GlobalResContext(base: Context) : ContextWrapper(base) { open val mRes: Resources get() = ResourceMgr.resource @@ -192,8 +186,9 @@ object ClassMap { UpdateCheckService::class.java to a.g::class.java, GeneralReceiver::class.java to a.h::class.java, DownloadService::class.java to a.j::class.java, - SuRequestActivity::class.java to a.m::class.java + SuRequestActivity::class.java to a.m::class.java, + ProcessPhoenix::class.java to a.r::class.java ) - operator fun get(c: Class<*>) = map.getOrElse(c) { throw IllegalArgumentException() } + operator fun get(c: Class<*>) = map.getOrElse(c) { c } } diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt index dbf3e07d4..4ab17d563 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/DownloadService.kt @@ -140,7 +140,7 @@ open class DownloadService : RemoteFileService() { inline operator fun invoke(context: Context, argBuilder: Builder.() -> Unit) { val app = context.applicationContext val builder = Builder().apply(argBuilder) - val intent = app.intent(DownloadService::class.java).putExtra(ARG_URL, builder.subject) + val intent = app.intent().putExtra(ARG_URL, builder.subject) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { app.startForegroundService(intent) diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt index 9e7079bb7..697a87533 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt @@ -38,7 +38,7 @@ private fun RemoteFileService.upgrade(apk: File, id: Int) { patch(apk, id) } else { // Simply relaunch the app - ProcessPhoenix.triggerRebirth(this) + ProcessPhoenix.triggerRebirth(this, intent()) } } else { patch(apk, id) diff --git a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt index 86ade4d75..f31fa40f1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt @@ -63,7 +63,7 @@ open class GeneralReceiver : BaseReceiver() { } when (action) { REQUEST -> { - val i = context.intent(SuRequestActivity::class.java) + val i = context.intent() .setAction(action) .putExtra("socket", intent.getStringExtra("socket")) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt index 05fcaa90d..d629ad77c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt @@ -60,7 +60,7 @@ open class MainActivity : BaseActivity(), Na override fun onCreate(savedInstanceState: Bundle?) { if (!SplashActivity.DONE) { - startActivity(intent(SplashActivity::class.java)) + startActivity(intent()) finish() } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt index 879d0ae65..c2c710374 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt @@ -40,7 +40,7 @@ open class SplashActivity : Activity() { Config.suManager = "" Shell.su("pm uninstall $pkg").submit() } - if (TextUtils.equals(pkg, packageName)) { + if (pkg == packageName) { runCatching { // We are the manager, remove com.topjohnwu.magisk as it could be malware packageManager.getApplicationInfo(BuildConfig.APPLICATION_ID, 0) @@ -60,10 +60,9 @@ open class SplashActivity : Activity() { // Setup shortcuts Shortcuts.setup(this) - val intent = intent(MainActivity::class.java) - intent.putExtra(Const.Key.OPEN_SECTION, getIntent().getStringExtra(Const.Key.OPEN_SECTION)) DONE = true - startActivity(intent) + + startActivity(intent().apply { intent?.also { putExtras(it) } }) finish() } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt index cade5a5f1..9964a56d2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt @@ -60,7 +60,7 @@ open class FlashActivity : BaseActivity() companion object { - private fun intent(context: Context) = context.intent(FlashActivity::class.java) + private fun intent(context: Context) = context.intent() .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) private fun intent(context: Context, file: File) = intent(context).setData(file.toUri()) diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt index 61892804b..ba2520707 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt @@ -28,7 +28,7 @@ class ModulesFragment : BaseFragment() override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) { // Get the URI of the selected file - val intent = activity.intent(FlashActivity::class.java) + val intent = activity.intent() intent.setData(data.data).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP) startActivity(intent) } diff --git a/app/src/main/java/com/topjohnwu/magisk/view/Notifications.kt b/app/src/main/java/com/topjohnwu/magisk/view/Notifications.kt index 5efa6c789..d056a2e83 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/Notifications.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/Notifications.kt @@ -47,7 +47,7 @@ object Notifications { } fun magiskUpdate(context: Context) { - val intent = context.intent(SplashActivity::class.java) + val intent = context.intent() .putExtra(Const.Key.OPEN_SECTION, "magisk") val stackBuilder = TaskStackBuilder.create(context) stackBuilder.addParentStack(SplashActivity::class.java.cmp(context.packageName)) @@ -65,7 +65,7 @@ object Notifications { } fun managerUpdate(context: Context) { - val intent = context.intent(GeneralReceiver::class.java) + val intent = context.intent() .setAction(Const.Key.BROADCAST_MANAGER_UPDATE) .putExtra(Const.Key.INTENT_SET_APP, Info.remote.app) @@ -82,7 +82,7 @@ object Notifications { } fun dtboPatched(context: Context) { - val intent = context.intent(GeneralReceiver::class.java) + val intent = context.intent() .setAction(Const.Key.BROADCAST_REBOOT) val pendingIntent = PendingIntent.getBroadcast(context, Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) diff --git a/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt b/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt index 548b4fa42..5dfd051c3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt @@ -7,6 +7,7 @@ import android.content.pm.ShortcutManager import android.graphics.drawable.Icon import android.os.Build import androidx.annotation.RequiresApi +import androidx.core.content.getSystemService import androidx.core.graphics.drawable.toAdaptiveIcon import androidx.core.graphics.drawable.toIcon import com.topjohnwu.magisk.* @@ -19,7 +20,7 @@ object Shortcuts { fun setup(context: Context) { if (Build.VERSION.SDK_INT >= 25) { - val manager = context.getSystemService(ShortcutManager::class.java) + val manager = context.getSystemService() manager?.dynamicShortcuts = getShortCuts(context) } } @@ -28,7 +29,7 @@ object Shortcuts { private fun getShortCuts(context: Context): List { val shortCuts = mutableListOf() val root = Shell.rootAccess() - val intent = context.intent(SplashActivity::class.java) + val intent = context.intent() fun getIcon(id: Int): Icon { return if (Build.VERSION.SDK_INT >= 26) diff --git a/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java b/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java index 648e054e1..03b44e9e7 100644 --- a/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java +++ b/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java @@ -11,7 +11,7 @@ import static android.os.Build.VERSION.SDK_INT; public class DynAPK { - private static final int STUB_VERSION = 3; + private static final int STUB_VERSION = 4; // Indices of the object array private static final int STUB_VERSION_ENTRY = 0; diff --git a/shared/src/main/java/com/topjohnwu/magisk/ProcessPhoenix.java b/shared/src/main/java/com/topjohnwu/magisk/ProcessPhoenix.java index 4bc113f0a..78196b573 100644 --- a/shared/src/main/java/com/topjohnwu/magisk/ProcessPhoenix.java +++ b/shared/src/main/java/com/topjohnwu/magisk/ProcessPhoenix.java @@ -22,7 +22,6 @@ import android.content.Intent; import android.os.Bundle; import java.util.ArrayList; -import java.util.Arrays; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -37,27 +36,11 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; * Trigger process recreation by calling {@link #triggerRebirth} with a {@link Context} instance. */ public class ProcessPhoenix extends Activity { - private static final String KEY_RESTART_INTENTS = "phoenix_restart_intents"; + private static final String KEY_RESTART_INTENT = "phoenix_restart_intent"; - /** - * Call to restart the application process using the {@linkplain Intent#CATEGORY_DEFAULT default} - * activity as an intent. - *

- * Behavior of the current process after invoking this method is undefined. - */ - public static void triggerRebirth(Context context) { - triggerRebirth(context, getRestartIntent(context)); - } - - /** - * Call to restart the application process using the specified intents. - *

- * Behavior of the current process after invoking this method is undefined. - */ - public static void triggerRebirth(Context context, Intent... nextIntents) { - Intent intent = new Intent(context, a.r.class); + public static void triggerRebirth(Context context, Intent intent) { intent.addFlags(FLAG_ACTIVITY_NEW_TASK); // In case we are called with non-Activity context. - intent.putParcelableArrayListExtra(KEY_RESTART_INTENTS, new ArrayList<>(Arrays.asList(nextIntents))); + intent.putExtra(KEY_RESTART_INTENT, getRestartIntent(context)); context.startActivity(intent); if (context instanceof Activity) { ((Activity) context).finish(); @@ -73,17 +56,15 @@ public class ProcessPhoenix extends Activity { return defaultIntent; } - throw new IllegalStateException("Unable to determine default activity for " - + packageName - + ". Does an activity specify the DEFAULT category in its intent filter?"); + throw new IllegalStateException(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ArrayList intents = getIntent().getParcelableArrayListExtra(KEY_RESTART_INTENTS); - startActivities(intents.toArray(new Intent[0])); + Intent intent = getIntent().getParcelableExtra(KEY_RESTART_INTENT); + startActivity(intent); finish(); Runtime.getRuntime().exit(0); } diff --git a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java index 231a24d54..4aa789f8d 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java +++ b/stub/src/main/java/com/topjohnwu/magisk/DownloadActivity.java @@ -4,6 +4,8 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.Application; import android.app.ProgressDialog; +import android.content.ComponentName; +import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.util.Log; @@ -11,6 +13,7 @@ import android.util.Log; import com.topjohnwu.magisk.net.ErrorHandler; import com.topjohnwu.magisk.net.Networking; import com.topjohnwu.magisk.net.ResponseListener; +import com.topjohnwu.magisk.obfuscate.Mapping; import com.topjohnwu.magisk.obfuscate.RawData; import com.topjohnwu.magisk.utils.APKInstall; @@ -48,7 +51,11 @@ public class DownloadActivity extends Activity { // Download and relaunch the app Networking.get(apkLink) .setErrorHandler(err) - .getAsFile(MANAGER_APK, f -> ProcessPhoenix.triggerRebirth(this)); + .getAsFile(MANAGER_APK, apk -> { + Intent intent = new Intent() + .setComponent(new ComponentName(this, Mapping.inverse("a.r"))); + ProcessPhoenix.triggerRebirth(this, intent); + }); } else { // Download and upgrade the app Application app = getApplication(); diff --git a/stub/src/main/java/com/topjohnwu/magisk/obfuscate/Mapping.java b/stub/src/main/java/com/topjohnwu/magisk/obfuscate/Mapping.java index 99186a0bc..9e8a91933 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/obfuscate/Mapping.java +++ b/stub/src/main/java/com/topjohnwu/magisk/obfuscate/Mapping.java @@ -7,9 +7,14 @@ import static com.topjohnwu.magisk.DynAPK.Data; public class Mapping { private static Map map = new HashMap<>(); + private static Map inverseMap; static { map.put("a.x", "androidx.work.impl.background.systemjob.SystemJobService"); + inverseMap = new HashMap<>(map.size()); + for (Map.Entry e : map.entrySet()) { + inverseMap.put(e.getValue(), e.getKey()); + } } public static String get(String name) { @@ -17,13 +22,14 @@ public class Mapping { return n != null ? n : name; } + public static String inverse(String name) { + String n = inverseMap.get(name); + return n != null ? n : name; + } + public static Data data() { - Map componentMap = new HashMap<>(map.size()); - for (Map.Entry e : map.entrySet()) { - componentMap.put(e.getValue(), e.getKey()); - } Data data = new Data(); - data.componentMap = componentMap; + data.componentMap = inverseMap; return data; } From 6fd357962f5e26177842b5a1bb60f42d678def3b Mon Sep 17 00:00:00 2001 From: osm0sis Date: Sat, 2 Nov 2019 22:50:33 -0300 Subject: [PATCH 27/74] scripts: fix signing in recovery with addon.d-v1 - change to $TMPDIR in addon.d.sh since recovery addon.d-v1 backup + restore leaves you in /tmp/addon.d which the restore then deletes, which would break $BOOTSIGNER execution with the following: libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 1078 (main), pid 1078 (main) Segmentation fault - also move $BOOTSIGNER execution to after `cd $MAGISKBIN` to ensure it's in a working directory in all cases - addon.d.sh data mount wasn't doing anything since /data has to already be mounted for the script to be running, so move it into /system/addon.d/99-magisk.sh stub script where it might be useful on recoveries that don't mount /data initially Fixes #2013 --- scripts/addon.d.sh | 6 ++++-- scripts/flash_script.sh | 2 ++ scripts/util_functions.sh | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/scripts/addon.d.sh b/scripts/addon.d.sh index 53aa676c4..8aae989b6 100644 --- a/scripts/addon.d.sh +++ b/scripts/addon.d.sh @@ -20,9 +20,8 @@ else fi initialize() { - mount /data 2>/dev/null - MAGISKBIN=/data/adb/magisk + if [ ! -d $MAGISKBIN ]; then echo "! Cannot find Magisk binaries!" exit 1 @@ -46,6 +45,9 @@ main() { sleep 5 fi + # Ensure we aren't in /tmp/addon.d anymore (since it's been deleted by addon.d) + cd $TMPDIR + $BOOTMODE || recovery_actions ui_print "************************" diff --git a/scripts/flash_script.sh b/scripts/flash_script.sh index df92b5e48..6b8f6bbcc 100644 --- a/scripts/flash_script.sh +++ b/scripts/flash_script.sh @@ -84,6 +84,8 @@ if [ -d /system/addon.d ]; then #!/sbin/sh # ADDOND_VERSION=2 +mount /data 2>/dev/null + if [ -f /data/adb/magisk/addon.d.sh ]; then exec sh /data/adb/magisk/addon.d.sh "\$@" else diff --git a/scripts/util_functions.sh b/scripts/util_functions.sh index 8977f28cb..25e5fdfe3 100644 --- a/scripts/util_functions.sh +++ b/scripts/util_functions.sh @@ -295,12 +295,12 @@ patch_dtbo_image() { patch_boot_image() { # Common installation script for flash_script.sh (updater-script) and addon.d.sh - eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true - $BOOTSIGNED && ui_print "- Boot image is signed with AVB 1.0" - SOURCEDMODE=true cd $MAGISKBIN + eval $BOOTSIGNER -verify < $BOOTIMAGE && BOOTSIGNED=true + $BOOTSIGNED && ui_print "- Boot image is signed with AVB 1.0" + $IS64BIT && mv -f magiskinit64 magiskinit 2>/dev/null || rm -f magiskinit64 # Source the boot patcher From e7d668502c2f3cb1f29aef6dd7beb356b3a84c1c Mon Sep 17 00:00:00 2001 From: osm0sis Date: Sun, 3 Nov 2019 04:22:21 -0500 Subject: [PATCH 28/74] SignBoot: improve error catching/reporting - `!= remain` shouldn't indicate "not signed", it should indicate a read error as with `!= hdr.length` - attempt to catch unsigned images at signature read, before they make it to `BootSignature bootsig = new BootSignature(signature);` and result in the following: java.io.IOException: unexpected end-of-contents marker at org.bouncycastle.asn1.ASN1InputStream.readObject(Unknown Source:14) at com.topjohnwu.signing.SignBoot$BootSignature.(SignBoot.java:230) at com.topjohnwu.signing.SignBoot.verifySignature(SignBoot.java:139) at com.topjohnwu.signing.BootSigner.main(BootSigner.java:15) at a.a.main(a.java:20) --- .../main/java/com/topjohnwu/signing/SignBoot.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/signing/src/main/java/com/topjohnwu/signing/SignBoot.java b/signing/src/main/java/com/topjohnwu/signing/SignBoot.java index c7347b2b7..b717df9b2 100644 --- a/signing/src/main/java/com/topjohnwu/signing/SignBoot.java +++ b/signing/src/main/java/com/topjohnwu/signing/SignBoot.java @@ -33,10 +33,10 @@ public class SignBoot { private static final int BOOT_IMAGE_HEADER_V1_RECOVERY_DTBO_SIZE_OFFSET = 1632; private static final int BOOT_IMAGE_HEADER_V2_DTB_SIZE_OFFSET = 1648; - /* Arbitrary maximum header version value; when greater assume the field is dt/extra size */ + // Arbitrary maximum header version value; when greater assume the field is dt/extra size private static final int BOOT_IMAGE_HEADER_VERSION_MAXIMUM = 8; - /* Maximum header size byte value to read (bootimg minimum page size) */ + // Maximum header size byte value to read (currently the bootimg minimum page size) private static final int BOOT_IMAGE_HEADER_SIZE_MAXIMUM = 2048; private static class PushBackRWStream extends FilterInputStream { @@ -120,21 +120,26 @@ public class SignBoot { try { // Read the header for size byte[] hdr = new byte[BOOT_IMAGE_HEADER_SIZE_MAXIMUM]; - if (imgIn.read(hdr) != hdr.length) + if (imgIn.read(hdr) != hdr.length) { + System.err.println("Unable to read image header"); return false; + } int signableSize = getSignableImageSize(hdr); // Read the rest of the image byte[] rawImg = Arrays.copyOf(hdr, signableSize); int remain = signableSize - hdr.length; if (imgIn.read(rawImg, hdr.length, remain) != remain) { - System.err.println("Invalid image: not signed"); + System.err.println("Unable to read image"); return false; } // Read footer, which contains the signature byte[] signature = new byte[4096]; - imgIn.read(signature); + if (imgIn.read(signature) == -1) { + System.err.println("Invalid image: not signed"); + return false; + } BootSignature bootsig = new BootSignature(signature); if (certIn != null) { From 6f7c13b8145c9765a21f2ea3a4965c8e0719e240 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 3 Nov 2019 04:45:35 -0500 Subject: [PATCH 29/74] Refactor JarMap --- .../com/topjohnwu/magisk/utils/PatchAPK.kt | 2 +- .../java/com/topjohnwu/signing/JarMap.java | 178 +++++++++++------- .../java/com/topjohnwu/signing/SignAPK.java | 2 +- .../java/com/topjohnwu/signing/ZipSigner.java | 2 +- 4 files changed, 108 insertions(+), 76 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt index fa1cf6aa2..ebc4a8c94 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt @@ -118,7 +118,7 @@ object PatchAPK { @JvmOverloads fun patch(apk: String, out: String, pkg: String, label: String = "Manager"): Boolean { try { - val jar = JarMap(apk) + val jar = JarMap.open(apk) val je = jar.getJarEntry(Const.ANDROID_MANIFEST) val xml = jar.getRawData(je) diff --git a/signing/src/main/java/com/topjohnwu/signing/JarMap.java b/signing/src/main/java/com/topjohnwu/signing/JarMap.java index e9677e7e0..2524c5a0b 100644 --- a/signing/src/main/java/com/topjohnwu/signing/JarMap.java +++ b/signing/src/main/java/com/topjohnwu/signing/JarMap.java @@ -15,117 +15,149 @@ import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -/* -* A universal random access interface for both JarFile and JarInputStream -* -* In the case when JarInputStream is provided to constructor, the whole stream -* will be loaded into memory for random access purposes. -* On the other hand, when a JarFile is provided, it simply works as a wrapper. -* */ +public abstract class JarMap implements Closeable { -public class JarMap implements Closeable { + LinkedHashMap entryMap; - private JarFile jarFile; - private JarInputStream jis; - private LinkedHashMap bufMap; - private Manifest manifest; - - public JarMap(File file) throws IOException { - this(file, true); + public static JarMap open(String file) throws IOException { + return new FileMap(new File(file), true, ZipFile.OPEN_READ); } - public JarMap(File file, boolean verify) throws IOException { - this(file, verify, ZipFile.OPEN_READ); + public static JarMap open(File file, boolean verify) throws IOException { + return new FileMap(file, verify, ZipFile.OPEN_READ); } - public JarMap(File file, boolean verify, int mode) throws IOException { - jarFile = new JarFile(file, verify, mode); - manifest = jarFile.getManifest(); + public static JarMap open(String file, boolean verify) throws IOException { + return new FileMap(new File(file), verify, ZipFile.OPEN_READ); } - public JarMap(String name) throws IOException { - this(new File(name)); - } - - public JarMap(String name, boolean verify) throws IOException { - this(new File(name), verify); - } - - public JarMap(InputStream is) throws IOException { - this(is, true); - } - - public JarMap(InputStream is, boolean verify) throws IOException { - jis = new JarInputStream(is, verify); - bufMap = new LinkedHashMap<>(); - JarEntry entry; - while ((entry = jis.getNextJarEntry()) != null) { - bufMap.put(entry.getName(), new JarMapEntry(entry, jis)); - } - manifest = jis.getManifest(); + public static JarMap open(InputStream is, boolean verify) throws IOException { + return new StreamMap(is, verify); } public File getFile() { - return jarFile == null ? null : new File(jarFile.getName()); + return null; } - public Manifest getManifest() { - return manifest; - } + public abstract Manifest getManifest() throws IOException; public InputStream getInputStream(ZipEntry ze) throws IOException { - if (bufMap != null) { - JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName()); - if (e != null) - return e.data.getInputStream(); - } - return jarFile.getInputStream(ze); + JarMapEntry e = getMapEntry(ze.getName()); + return e != null ? e.data.getInputStream() : null; } public OutputStream getOutputStream(ZipEntry ze) { - manifest = null; /* Invalidate the manifest */ - if (bufMap == null) - bufMap = new LinkedHashMap<>(); + if (entryMap == null) + entryMap = new LinkedHashMap<>(); JarMapEntry e = new JarMapEntry(ze.getName()); - bufMap.put(ze.getName(), e); + entryMap.put(ze.getName(), e); return e.data; } public byte[] getRawData(ZipEntry ze) throws IOException { - if (bufMap != null) { - JarMapEntry e = (JarMapEntry) bufMap.get(ze.getName()); - if (e != null) - return e.data.toByteArray(); - } - ByteArrayStream bytes = new ByteArrayStream(); - bytes.readFrom(jarFile.getInputStream(ze)); - return bytes.toByteArray(); + JarMapEntry e = getMapEntry(ze.getName()); + return e != null ? e.data.toByteArray() : null; } - public Enumeration entries() { - return jarFile == null ? Collections.enumeration(bufMap.values()) : jarFile.entries(); - } + public abstract Enumeration entries(); - public ZipEntry getEntry(String name) { + public final ZipEntry getEntry(String name) { return getJarEntry(name); } public JarEntry getJarEntry(String name) { - JarEntry e = jarFile == null ? bufMap.get(name) : jarFile.getJarEntry(name); - if (e == null && bufMap != null) - return bufMap.get(name); + return getMapEntry(name); + } + + JarMapEntry getMapEntry(String name) { + JarMapEntry e = null; + if (entryMap != null) + e = (JarMapEntry) entryMap.get(name); return e; } - @Override - public void close() throws IOException { - if (jarFile != null) + private static class FileMap extends JarMap { + + private JarFile jarFile; + + FileMap(File file, boolean verify, int mode) throws IOException { + jarFile = new JarFile(file, verify, mode); + } + + @Override + public File getFile() { + return new File(jarFile.getName()); + } + + @Override + public Manifest getManifest() throws IOException { + return jarFile.getManifest(); + } + + @Override + public InputStream getInputStream(ZipEntry ze) throws IOException { + InputStream is = super.getInputStream(ze); + return is != null ? is : jarFile.getInputStream(ze); + } + + @Override + public byte[] getRawData(ZipEntry ze) throws IOException { + byte[] b = super.getRawData(ze); + if (b != null) + return b; + ByteArrayStream bytes = new ByteArrayStream(); + bytes.readFrom(jarFile.getInputStream(ze)); + return bytes.toByteArray(); + } + + @Override + public Enumeration entries() { + return jarFile.entries(); + } + + @Override + public JarEntry getJarEntry(String name) { + JarEntry e = getMapEntry(name); + return e != null ? e : jarFile.getJarEntry(name); + } + + @Override + public void close() throws IOException { jarFile.close(); - else + } + } + + private static class StreamMap extends JarMap { + + private JarInputStream jis; + + StreamMap(InputStream is, boolean verify) throws IOException { + jis = new JarInputStream(is, verify); + entryMap = new LinkedHashMap<>(); + JarEntry entry; + while ((entry = jis.getNextJarEntry()) != null) { + entryMap.put(entry.getName(), new JarMapEntry(entry, jis)); + } + } + + @Override + public Manifest getManifest() { + return jis.getManifest(); + } + + @Override + public Enumeration entries() { + return Collections.enumeration(entryMap.values()); + } + + @Override + public void close() throws IOException { jis.close(); + } } private static class JarMapEntry extends JarEntry { + ByteArrayStream data; JarMapEntry(JarEntry je, InputStream is) { diff --git a/signing/src/main/java/com/topjohnwu/signing/SignAPK.java b/signing/src/main/java/com/topjohnwu/signing/SignAPK.java index 1c84f33de..f84779666 100644 --- a/signing/src/main/java/com/topjohnwu/signing/SignAPK.java +++ b/signing/src/main/java/com/topjohnwu/signing/SignAPK.java @@ -72,7 +72,7 @@ public class SignAPK { ZipAdjust.adjust(temp1, temp2); - try (JarMap map = new JarMap(temp2, false)) { + try (JarMap map = JarMap.open(temp2, false)) { sign(cert, key, map, output, true); } } finally { diff --git a/signing/src/main/java/com/topjohnwu/signing/ZipSigner.java b/signing/src/main/java/com/topjohnwu/signing/ZipSigner.java index 9e3fce6ed..53b6b9f1f 100644 --- a/signing/src/main/java/com/topjohnwu/signing/ZipSigner.java +++ b/signing/src/main/java/com/topjohnwu/signing/ZipSigner.java @@ -65,7 +65,7 @@ public class ZipSigner { Security.insertProviderAt(new BouncyCastleProvider(), 1); - try (JarMap in = new JarMap(args[args.length - 2], false); + try (JarMap in = JarMap.open(args[args.length - 2], false); OutputStream out = new FileOutputStream(args[args.length - 1])) { if (args.length == 2) { sign(in, out); From 26618f8d735ef5003a216392ba9e19d0ea034085 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 3 Nov 2019 17:01:09 -0500 Subject: [PATCH 30/74] Don't do broadcast tests from app Running broadcast tests from the app does not accurately verifies whether the broadcasts can be delivered when the app is not running in the foreground, which is why we are running the test. The only sane way to verify broadcasts is to trigger the broadcast test directly from the daemon on boot complete. If it is not deliverable, then activity mode shall be chosen. In the meantime, cleanup AndroidManifest.xml --- app/src/main/AndroidManifest.xml | 31 ++++--------------- .../main/java/com/topjohnwu/magisk/Const.kt | 2 +- .../main/java/com/topjohnwu/magisk/Info.kt | 7 +---- .../magisk/model/receiver/GeneralReceiver.kt | 23 +++++--------- .../com/topjohnwu/magisk/ui/SplashActivity.kt | 5 +++ scripts/emulator.sh | 1 + stub/src/main/AndroidManifest.xml | 7 ----- 7 files changed, 21 insertions(+), 55 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9ba7c0f60..86d1c906b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,29 +1,11 @@ - - - @@ -36,7 +18,6 @@ tools:replace="android:appComponentFactory"> - - - - - - @@ -89,7 +65,6 @@ - @@ -105,6 +80,12 @@ android:authorities="${applicationId}.workmanager-init" tools:node="remove" /> + + + diff --git a/app/src/main/java/com/topjohnwu/magisk/Const.kt b/app/src/main/java/com/topjohnwu/magisk/Const.kt index 79738f20f..beb603d8a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Const.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Const.kt @@ -24,7 +24,7 @@ object Const { object Version { const val MIN_SUPPORT = 18000 - const val CONNECT_MODE = 20002 + const val CONNECT_MODE = 20100 } object ID { diff --git a/app/src/main/java/com/topjohnwu/magisk/Info.kt b/app/src/main/java/com/topjohnwu/magisk/Info.kt index 2eec3295a..7b4820c25 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Info.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Info.kt @@ -37,13 +37,8 @@ object Info { val code = ShellUtils.fastCmd("magisk -V").toInt() val hide = Shell.su("magiskhide --status").exec().isSuccess var mode = -1 - if (code >= Const.Version.CONNECT_MODE) { + if (code >= Const.Version.CONNECT_MODE) mode = Shell.su("magisk --connect-mode").exec().code - if (mode == 0) { - // Manually trigger broadcast test - Shell.su("magisk --broadcast-test").exec() - } - } Env(code, str, hide, mode) }.getOrElse { Env() } diff --git a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt index f31fa40f1..4c72c992c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt @@ -51,23 +51,14 @@ open class GeneralReceiver : BaseReceiver() { } when (intent.action ?: return) { - Intent.ACTION_REBOOT, Intent.ACTION_BOOT_COMPLETED -> { - val action = intent.getStringExtra("action") - if (action == null) { - // Actual boot completed event - Shell.su("mm_patch_dtbo").submit { - if (it.isSuccess) - Notifications.dtboPatched(context) - } - return - } - when (action) { + Intent.ACTION_REBOOT -> { + when (val action = intent.getStringExtra("action") ?: return) { REQUEST -> { val i = context.intent() - .setAction(action) - .putExtra("socket", intent.getStringExtra("socket")) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) + .setAction(action) + .putExtra("socket", intent.getStringExtra("socket")) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) if (SDK_INT >= 29) { // Android Q does not allow starting activity from background i.startActivityWithRoot() @@ -92,7 +83,7 @@ open class GeneralReceiver : BaseReceiver() { Intent.ACTION_PACKAGE_FULLY_REMOVED -> { val pkg = getPkg(intent) policyDB.delete(pkg).blockingGet() - "magiskhide --rm $pkg".su().blockingGet() + Shell.su("magiskhide --rm $pkg").submit() } Intent.ACTION_LOCALE_CHANGED -> Shortcuts.setup(context) Const.Key.BROADCAST_MANAGER_UPDATE -> { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt index c2c710374..845cb2efb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt @@ -60,6 +60,11 @@ open class SplashActivity : Activity() { // Setup shortcuts Shortcuts.setup(this) + Shell.su("mm_patch_dtbo").submit { + if (it.isSuccess) + Notifications.dtboPatched(this) + } + DONE = true startActivity(intent().apply { intent?.also { putExtras(it) } }) diff --git a/scripts/emulator.sh b/scripts/emulator.sh index ceb3e8388..a01e0f040 100755 --- a/scripts/emulator.sh +++ b/scripts/emulator.sh @@ -118,3 +118,4 @@ mkdir -p /data/adb/modules 2>/dev/null mkdir /data/adb/post-fs-data.d 2>/dev/null mkdir /data/adb/services.d 2>/dev/null /sbin/magisk --daemon +/sbin/magisk --broadcast-test diff --git a/stub/src/main/AndroidManifest.xml b/stub/src/main/AndroidManifest.xml index 6d3dcb4bd..109e8d1b7 100644 --- a/stub/src/main/AndroidManifest.xml +++ b/stub/src/main/AndroidManifest.xml @@ -1,19 +1,13 @@ - - - - - From 73525d19e9409fd58d350048a68a0eaeb4137c67 Mon Sep 17 00:00:00 2001 From: linar10 Date: Sun, 3 Nov 2019 23:15:17 +0100 Subject: [PATCH 31/74] Update strings.xml --- app/src/main/res/values-pl/strings.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index a96a201e1..4804748e1 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -24,6 +24,7 @@ Zaawansowane Ustawienia Zachowaj force encryption (wymuszenie szyfrowania) Zachowaj AVB 2.0/dm-verity (weryfikację procesu bootowania) + Tryb Recovery Zainstalowana: %1$s Ostatnia: %1$s Odinstaluj @@ -145,6 +146,10 @@ Wsparcie systemless dla aplikacji Adblock Dodano moduł systemless hosts + Wpisz żądaną nazwę aplikacji + Nowa Nazwa + Aplikacja zostanie ponownie spakowana do tej nazwy + Zły format Aplikacje i ADB Tylko Aplikacje Tylko ADB From 472cde29b87fe2b4ab986b2caa2eeee90aad2806 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 4 Nov 2019 03:24:16 -0500 Subject: [PATCH 32/74] Allow non supported Magisk to use Magisk Manager Close #1576 --- app/src/main/java/com/topjohnwu/magisk/Const.kt | 3 ++- app/src/main/java/com/topjohnwu/magisk/Info.kt | 7 ++++++- .../java/com/topjohnwu/magisk/ui/MainActivity.kt | 11 +++++++++++ .../com/topjohnwu/magisk/ui/MainViewModel.kt | 2 ++ .../com/topjohnwu/magisk/ui/SplashActivity.kt | 16 +--------------- app/src/main/res/values-ar/strings.xml | 1 - app/src/main/res/values-az/strings.xml | 1 - app/src/main/res/values-ca/strings.xml | 1 - app/src/main/res/values-cs/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 1 - app/src/main/res/values-et/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-hi/strings.xml | 1 - app/src/main/res/values-in/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 1 - app/src/main/res/values-ja/strings.xml | 1 - app/src/main/res/values-ko/strings.xml | 1 - app/src/main/res/values-mk/strings.xml | 1 - app/src/main/res/values-pl/strings.xml | 1 - app/src/main/res/values-ro/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-sk/strings.xml | 1 - app/src/main/res/values-tr/strings.xml | 1 - app/src/main/res/values-uk/strings.xml | 1 - app/src/main/res/values-zh-rCN/strings.xml | 1 - app/src/main/res/values-zh-rTW/strings.xml | 1 - app/src/main/res/values/strings.xml | 3 +-- 28 files changed, 23 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/Const.kt b/app/src/main/java/com/topjohnwu/magisk/Const.kt index beb603d8a..d4f1d7081 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Const.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Const.kt @@ -23,7 +23,8 @@ object Const { val USER_ID = Process.myUid() / 100000 object Version { - const val MIN_SUPPORT = 18000 + const val MIN_VERSION = "v18.0" + const val MIN_VERCODE = 18000 const val CONNECT_MODE = 20100 } diff --git a/app/src/main/java/com/topjohnwu/magisk/Info.kt b/app/src/main/java/com/topjohnwu/magisk/Info.kt index 7b4820c25..66c601a91 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Info.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Info.kt @@ -43,12 +43,17 @@ object Info { }.getOrElse { Env() } class Env( - val magiskVersionCode: Int = -1, + code: Int = -1, val magiskVersionString: String = "", hide: Boolean = false, var connectionMode: Int = -1 ) { val magiskHide get() = Config.magiskHide + val magiskVersionCode = when (code) { + in Int.MIN_VALUE..Const.Version.MIN_VERCODE -> -1 + else -> code + } + val unsupported = code > 0 && code < Const.Version.MIN_VERCODE init { Config.magiskHide = hide diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt index d629ad77c..60f26422e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt @@ -2,11 +2,13 @@ package com.topjohnwu.magisk.ui import android.content.Intent import android.os.Bundle +import androidx.appcompat.app.AlertDialog import androidx.core.view.GravityCompat import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentTransaction import com.ncapdevi.fragnav.FragNavController import com.ncapdevi.fragnav.FragNavTransactionOptions +import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.Const.Key.OPEN_SECTION import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R @@ -66,6 +68,15 @@ open class MainActivity : BaseActivity(), Na super.onCreate(savedInstanceState) + if (Info.env.unsupported && !viewModel.shownUnsupportedDialog) { + viewModel.shownUnsupportedDialog = true + AlertDialog.Builder(this) + .setTitle(R.string.unsupport_magisk_title) + .setMessage(getString(R.string.unsupport_magisk_msg, Const.Version.MIN_VERSION)) + .setPositiveButton(android.R.string.ok, null) + .show() + } + navigationController.apply { rootFragmentListener = this@MainActivity transactionListener = this@MainActivity diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt index 35688183f..8bb5bf5a1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt @@ -8,6 +8,8 @@ import com.topjohnwu.magisk.model.navigation.Navigation class MainViewModel : BaseViewModel() { + var shownUnsupportedDialog = false + fun navPressed() = Navigation.Main.OPEN_NAV.publish() fun navigationItemPressed(item: MenuItem): Boolean { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt index 845cb2efb..1a0ddf02b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt @@ -3,8 +3,6 @@ package com.topjohnwu.magisk.ui import android.app.Activity import android.content.Context import android.os.Bundle -import android.text.TextUtils -import androidx.appcompat.app.AlertDialog import com.topjohnwu.magisk.* import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.view.Notifications @@ -19,19 +17,7 @@ open class SplashActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - Shell.getShell { - if (Info.env.magiskVersionCode > 0 && Info.env.magiskVersionCode < Const.Version.MIN_SUPPORT) { - AlertDialog.Builder(this) - .setTitle(R.string.unsupport_magisk_title) - .setMessage(R.string.unsupport_magisk_message) - .setNegativeButton(android.R.string.ok, null) - .setOnDismissListener { finish() } - .show() - } else { - initAndStart() - } - } + Shell.getShell { initAndStart() } } private fun initAndStart() { diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index a7be4c183..e53d65cd8 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -9,7 +9,6 @@ الإعدادات التثبيت إصدار Magisk غير مدعوم - لا يدعم هذا الإصدار من Magisk Manager إصدارا لـ Magisk vأقل من 18\n\n بإمكانك تحديث Magisk يدويا أو تثبيت إصدار أدنى. Magisk غير مثبت diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index d8ae0ac98..2dbf1e601 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -8,7 +8,6 @@ Tənzimləmələr Quraşdır Dəstəklənməyən Magisk Versiyası - Magisk Manager\'in bu versiyası Magisk\'in v18.0 versiyasndan aşağısını dəstəkləmir.\n\nMagisk\'i əllə yüksəldə, yaxud tətbiqi əvvəlki versiyalarına qaytara bilərsiniz. Magisk yüklənməyib. diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 76edbd704..1de2eb50f 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -8,7 +8,6 @@ Configuració Instal·lar Versió de Magisk incompatible - Aquesta versió de Magisk Manager no suporta una versió inferior a la 18.0.\n\nPots actualitzar Magisk manualment o pots instalar una versió anterior de la app. Magisk no està instal·lat diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 7f9d1abfc..1c6ff351a 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -8,7 +8,6 @@ Nastavení Instalovat Nepodporovaná verze Magisk - Tato verze Magisk Managera nepodporuje verzi Magisk nižší než v18.0.\n\nMůžete buď ručně aktualizovat Magisk, nebo aplikaci downgradovat na starší verzi. Magisk není nainstalován. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 4a954d4c2..017a84bdd 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -8,7 +8,6 @@ Einstellungen Installieren Nicht unterstützte Magisk Version - Diese Version von Magisk Manager unterstützt keine Magisk-Version kleiner als 18.0. \n\nSie können entweder Magisk manuell aktualisieren oder die App auf eine ältere Version herabstufen. Magisk ist nicht installiert diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 547e88569..2d3ac095b 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -8,7 +8,6 @@ Ajustes Instalar Versión de Magisk no soportada - Esta versión de Magisk Manager no admite una versión de Magisk inferior a la v18.0.\n\nPuede actualizar Magisk de forma manual o instalar una versión anterior de la aplicación. Magisk no está instalado diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 4effbee81..6c4f3bcde 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -8,7 +8,6 @@ Seaded Installi Mittetoetatud Magisk\'i versioon - See Magisk Manager\'i versioon ei toeta Magisk\'ist vanemat versiooni kui v18.0.\n\nSa võid kas Magisk\'i käsitsi täiendada või alandad rakenduse vanemale versioonile. Magisk pole installitud diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 13708f5b5..505c1d223 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -8,7 +8,6 @@ Paramètres Installer Version de Magisk non prise en charge - Cette version de Magisk Manager ne prend pas en charge les versions de Magisk inférieures à v18.0.\n\nSi vous continuez, toutes les fonctionnalités de Magisk ne seront pas disponibles et vous ne pourrez que mettre à jour Magisk. Magisk n’est pas installé. diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 759d5dd33..2b7c04211 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -8,7 +8,6 @@ सेटिंग्स स्थापित करें असमर्थित Magisk संस्करण - Magisk Manager का यह संस्करण Magisk के v18.0 संस्करण से कम का समर्थन नहीं करता है.\n\nआप या तो खुद से Magisk को अपग्रेड करें, या फिर एप्लीकेशन को पुराने संस्करण पे डाउनग्रेड करें . Magisk स्थापित नहीं है diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 361e0e09f..c53445891 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -7,7 +7,6 @@ Setelan Pasang Versi Magisk Tidak Didukung - Magisk Manager versi ini tidak mendukung versi Magisk di bawah v18.0.\n\nAnda dapat memperbarui Magisk secara manual, atau menurunkan aplikasi ke versi sebelumnya. Magisk tidak terpasang. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 390f51311..473f2f714 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -8,7 +8,6 @@ Impostazioni Installa Versione di Magisk non supportata - Questa versione di Magisk Manager non supporta versioni di Magisk inferiori alla v18.0.\n\nPuoi aggiornare manualmente Magisk o tornare a una versione meno recente dell\'app. Magisk non è installato. diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 61faf6ea1..76966996f 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -8,7 +8,6 @@ 設定 インストール 対応していないMagiskバージョン - このバージョンのMagisk ManagerはMagisk v18.0以下には対応していません。\n\n手動でMagiskを更新するか、または古いバージョンのMagisk Managerをインストールしてください。 Magiskがインストールされていません diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 395760601..f66ad2e3c 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -8,7 +8,6 @@ 설정 설치 지원되지 않는 Magisk 버전 - 이 버전의 Magisk Manager는 v18.0보다 낮은 버전의 Magisk를 지원하지 않습니다.\n\n직접 Magisk를 업데이트 하거나 이전 버전의 앱으로 다운그레이드하십시오.. Magisk가 설치되지 않음 diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index c891083a7..a846ac366 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -8,7 +8,6 @@ Поставки Инсталирај Неподдржана верзија на Magisk - Оваа верзија на Magisk Manager не ја поддржува верзијата на Magisk пониска од v18.0.\n\nМожно е да рачно да го надградите Magisk или да ја вратите апликацијата на постара верзија. Magisk не е инсталиран. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 4804748e1..a5eaa480a 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -8,7 +8,6 @@ Ustawienia Instaluj Nieobsługiwana Wersja Magisk - Ta wersja Magisk Managera nie obsługuje wersji Magisk niższej niż v18.0.\n\nMożesz albo ręcznie zaktualizować Magisk lub obniżyć w aplikacji do starszej wersji. Magisk nie jest zainstalowany. diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 6e6a8e423..2fba59981 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -8,7 +8,6 @@ Setări Instalează Versiune Magisk nesuportată - Această versiune de Magisk Manager nu suportă versiunea Magisk mai mică de v18.0.\n\nPoți fie să actualizezi manual Magisk, fie să treci la o versiune mai veche. Magisk nu este instalat. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index be293523f..32b800414 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -8,7 +8,6 @@ Настройки Установка Неподдерживаемая версия Magisk - Эта версия Magisk Manager не поддерживает версию Magisk ниже v18.0.\n\nВы можете вручную обновить Magisk или понизить версию приложения до более старой. Magisk не установлен diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 3905a01ad..c39bf3cad 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -8,7 +8,6 @@ Nastavenia Inštalovať Nepodporovaná verzia Magisku - Táto verzia Magisk Managera podporuje Magisk od verzie v18.0.\n\nBuď upgradujte Magisk manuálne alebo nainštalujte staršiu verziu aplikácie. Magisk nie je nainštalovaný diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index bc574a0b8..1fed35e24 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -8,7 +8,6 @@ Ayarlar Yükle Desteklenmeyen Magisk Sürümü - Magisk Manager\'ın bu sürümü v18.0\'dan daha düşük Magisk versiyonlarını desteklememektedir.\n\nMagisk\'i manuel olarak yükseltebilir veya uygulamayı daha eski bir sürüme düşürebilirsiniz. Magisk yüklü değil diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 4a3f86992..ae9573824 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -8,7 +8,6 @@ Налаштування Встановлення Версія Magisk не підтримується - Ця версія Magisk Manager не підтримує версію Magisk нижчу, ніж v18.0.\n\nВи можете або вручну оновити Magisk, або понизити програму до старішої версії. Magisk не встановлено. diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index e52a41ad6..beec9c798 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -8,7 +8,6 @@ 设置 安装 不支持的 Magisk 版本 - 此版本的 Magisk Manager 不支持低于 v18.0 的 Magisk。\n\n请手动升级 Magisk,或将应用降级到旧版本。 未安装 Magisk diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 7e092939d..d90e20a38 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -8,7 +8,6 @@ 設定 安裝 不支援此 Magisk 版本 - 此 Magisk Manager 版本最低支援 Magisk 18.0。\n\n請手動更新 Magisk,或安裝舊版 Magisk Manager。 未安裝 Magisk diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ca53b8223..1e5551fdc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,8 +8,7 @@ Settings Install Unsupported Magisk Version - This version of Magisk Manager does not support Magisk version lower than v18.0.\n\nYou can either manually upgrade Magisk, or downgrade the app to an older version. - + This version of Magisk Manager does not support Magisk version lower than %1$s.\n\nThe app will behave as if no Magisk is installed, please upgrade Magisk as soon as possible. Magisk is not installed Checking for updates… From 25c557248cab19f9761bbeb5a8ba110318d79d85 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 4 Nov 2019 14:32:28 -0500 Subject: [PATCH 33/74] Use ContentProvider call method for communication Previously, we use either BroadcastReceivers or Activities to receive messages from our native daemon, but both have their own downsides. Some OEMs blocks broadcasts if the app is not running in the background, regardless of who the caller is. Activities on the other hand, despite working 100% of the time, will steal the focus of the current foreground app, even though we are just doing some logging and showing a toast. In addition, since stubs for hiding Magisk Manager is introduced, our only communication method is left with the broadcast option, as only broadcasting allows targeting a specific package name, not a component name (which will be obfuscated in the case of stubs). To make sure root requests will work on all devices, Magisk had to do some experiments every boot to test whether broadcast is deliverable or not. This makes the whole thing even more complicated then ever. So lets take a look at another kind of component in Android apps: ContentProviders. It is a vital part of Android's ecosystem, and as far as I know no OEMs will block requests to ContentProviders (or else tons of functionality will break catastrophically). Starting at API 11, the system supports calling a specific method in ContentProviders, optionally sending extra data along with the method call. This is perfect for the native daemon to start a communication with Magisk Manager. Another cool thing is that we no longer need to know the component name of the reciever, as ContentProviders identify themselves with an "authority" name, which in Magisk Manager's case is tied to the package name. We already have a mechanism to keep track of our current manager package name, so this works out of the box. So yay! No more flaky broadcast tests, no more stupid OEMs blocking broadcasts for some bizzare reasons. This method should in theory work on almost all devices and situations. --- app/src/main/java/com/topjohnwu/magisk/App.kt | 2 + .../main/java/com/topjohnwu/magisk/Const.kt | 1 + .../main/java/com/topjohnwu/magisk/Hacks.kt | 5 - .../main/java/com/topjohnwu/magisk/Info.kt | 8 +- .../magisk/model/entity/MagiskPolicy.kt | 7 +- .../magisk/model/receiver/GeneralReceiver.kt | 57 +----- .../com/topjohnwu/magisk/ui/SplashActivity.kt | 5 +- .../magisk/ui/surequest/SuRequestActivity.kt | 21 +- .../com/topjohnwu/magisk/utils/PatchAPK.kt | 5 +- .../com/topjohnwu/magisk/utils/SuHandler.kt | 137 +++++++++++++ .../com/topjohnwu/magisk/utils/SuLogger.kt | 92 --------- native/jni/core/bootstages.cpp | 3 - native/jni/core/daemon.cpp | 8 - native/jni/core/magisk.cpp | 15 -- native/jni/include/daemon.h | 4 - native/jni/su/connect.cpp | 191 +++++------------- scripts/emulator.sh | 1 - .../com/topjohnwu/magisk/FileProvider.java | 10 + .../topjohnwu/magisk/ProviderCallHandler.java | 8 + 19 files changed, 237 insertions(+), 343 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/SuHandler.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.kt create mode 100644 shared/src/main/java/com/topjohnwu/magisk/ProviderCallHandler.java diff --git a/app/src/main/java/com/topjohnwu/magisk/App.kt b/app/src/main/java/com/topjohnwu/magisk/App.kt index 391c2563f..c5d4ed0dc 100644 --- a/app/src/main/java/com/topjohnwu/magisk/App.kt +++ b/app/src/main/java/com/topjohnwu/magisk/App.kt @@ -16,6 +16,7 @@ import com.topjohnwu.magisk.di.koinModules import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.unwrap import com.topjohnwu.magisk.utils.RootInit +import com.topjohnwu.magisk.utils.SuHandler import com.topjohnwu.magisk.utils.updateConfig import com.topjohnwu.superuser.Shell import org.koin.android.ext.koin.androidContext @@ -34,6 +35,7 @@ open class App() : Application() { Shell.Config.verboseLogging(BuildConfig.DEBUG) Shell.Config.addInitializers(RootInit::class.java) Shell.Config.setTimeout(2) + FileProvider.callHandler = SuHandler Room.setFactory { when (it) { WorkDatabase::class.java -> WorkDatabase_Impl() diff --git a/app/src/main/java/com/topjohnwu/magisk/Const.kt b/app/src/main/java/com/topjohnwu/magisk/Const.kt index d4f1d7081..4ae5299fc 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Const.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Const.kt @@ -26,6 +26,7 @@ object Const { const val MIN_VERSION = "v18.0" const val MIN_VERCODE = 18000 const val CONNECT_MODE = 20100 + const val PROVIDER_CONNECT = 20102 } object ID { diff --git a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt index 4f7abfe80..6703d2e6b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt @@ -14,8 +14,6 @@ import android.content.res.AssetManager import android.content.res.Configuration import android.content.res.Resources import androidx.annotation.RequiresApi -import androidx.annotation.StringRes -import com.topjohnwu.magisk.extensions.langTagToLocale import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.receiver.GeneralReceiver import com.topjohnwu.magisk.model.update.UpdateCheckService @@ -23,11 +21,8 @@ import com.topjohnwu.magisk.ui.MainActivity import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.ui.flash.FlashActivity import com.topjohnwu.magisk.ui.surequest.SuRequestActivity -import com.topjohnwu.magisk.utils.currentLocale -import com.topjohnwu.magisk.utils.defaultLocale import com.topjohnwu.magisk.utils.refreshLocale import com.topjohnwu.magisk.utils.updateConfig -import java.util.* fun AssetManager.addAssetPath(path: String) { DynAPK.addAssetPath(this, path) diff --git a/app/src/main/java/com/topjohnwu/magisk/Info.kt b/app/src/main/java/com/topjohnwu/magisk/Info.kt index 66c601a91..317a6357c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Info.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Info.kt @@ -36,17 +36,13 @@ object Info { val str = ShellUtils.fastCmd("magisk -v").split(":".toRegex())[0] val code = ShellUtils.fastCmd("magisk -V").toInt() val hide = Shell.su("magiskhide --status").exec().isSuccess - var mode = -1 - if (code >= Const.Version.CONNECT_MODE) - mode = Shell.su("magisk --connect-mode").exec().code - Env(code, str, hide, mode) + Env(code, str, hide) }.getOrElse { Env() } class Env( code: Int = -1, val magiskVersionString: String = "", - hide: Boolean = false, - var connectionMode: Int = -1 + hide: Boolean = false ) { val magiskHide get() = Config.magiskHide val magiskVersionCode = when (code) { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskPolicy.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskPolicy.kt index 2a843b9b3..cf803c7b7 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskPolicy.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskPolicy.kt @@ -56,14 +56,15 @@ fun Map.toPolicy(pm: PackageManager): MagiskPolicy { } @Throws(PackageManager.NameNotFoundException::class) -fun Int.toPolicy(pm: PackageManager): MagiskPolicy { +fun Int.toPolicy(pm: PackageManager, policy: Int = INTERACTIVE): MagiskPolicy { val pkg = pm.getPackagesForUid(this)?.firstOrNull() ?: throw PackageManager.NameNotFoundException() val info = pm.getApplicationInfo(pkg, 0) return MagiskPolicy( uid = this, packageName = pkg, + policy = policy, applicationInfo = info, - appName = info.loadLabel(pm).toString() + appName = info.getLabel(pm) ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt index 4c72c992c..90134a8eb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.kt @@ -2,37 +2,25 @@ package com.topjohnwu.magisk.model.receiver import android.content.ContextWrapper import android.content.Intent -import android.os.Build.VERSION.SDK_INT -import com.topjohnwu.magisk.* +import com.topjohnwu.magisk.Config +import com.topjohnwu.magisk.Const +import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.base.BaseReceiver import com.topjohnwu.magisk.data.database.PolicyDao -import com.topjohnwu.magisk.data.database.base.su import com.topjohnwu.magisk.extensions.reboot -import com.topjohnwu.magisk.extensions.startActivity -import com.topjohnwu.magisk.extensions.startActivityWithRoot import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.entity.ManagerJson import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.DownloadSubject -import com.topjohnwu.magisk.ui.surequest.SuRequestActivity -import com.topjohnwu.magisk.utils.SuLogger -import com.topjohnwu.magisk.view.Notifications +import com.topjohnwu.magisk.utils.SuHandler import com.topjohnwu.magisk.view.Shortcuts import com.topjohnwu.superuser.Shell import org.koin.core.inject -import timber.log.Timber open class GeneralReceiver : BaseReceiver() { private val policyDB: PolicyDao by inject() - companion object { - const val REQUEST = "request" - const val LOG = "log" - const val NOTIFY = "notify" - const val TEST = "test" - } - private fun getPkg(intent: Intent): String { return intent.data?.encodedSchemeSpecificPart.orEmpty() } @@ -40,46 +28,15 @@ open class GeneralReceiver : BaseReceiver() { override fun onReceive(context: ContextWrapper, intent: Intent?) { intent ?: return - // Debug messages - if (BuildConfig.DEBUG) { - Timber.d(intent.action) - intent.extras?.let { bundle -> - bundle.keySet().forEach { - Timber.d("[%s]=[%s]", it, bundle[it]) - } - } - } - when (intent.action ?: return) { Intent.ACTION_REBOOT -> { - when (val action = intent.getStringExtra("action") ?: return) { - REQUEST -> { - val i = context.intent() - .setAction(action) - .putExtra("socket", intent.getStringExtra("socket")) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - if (SDK_INT >= 29) { - // Android Q does not allow starting activity from background - i.startActivityWithRoot() - } else { - i.startActivity(context) - } - } - LOG -> SuLogger.handleLogs(context, intent) - NOTIFY -> SuLogger.handleNotify(context, intent) - TEST -> { - val mode = intent.getIntExtra("mode", 1 shl 1) - if (mode > Info.env.connectionMode) - Info.env.connectionMode = mode - Shell.su("magisk --connect-mode $mode").submit() - } - } + SuHandler(context, intent.getStringExtra("action"), intent.extras) } - Intent.ACTION_PACKAGE_REPLACED -> + Intent.ACTION_PACKAGE_REPLACED -> { // This will only work pre-O if (Config.suReAuth) policyDB.delete(getPkg(intent)).blockingGet() + } Intent.ACTION_PACKAGE_FULLY_REMOVED -> { val pkg = getPkg(intent) policyDB.delete(pkg).blockingGet() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt index 1a0ddf02b..eabbd9c00 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt @@ -3,10 +3,13 @@ package com.topjohnwu.magisk.ui import android.app.Activity import android.content.Context import android.os.Bundle -import com.topjohnwu.magisk.* +import com.topjohnwu.magisk.BuildConfig +import com.topjohnwu.magisk.Config +import com.topjohnwu.magisk.intent import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.view.Notifications import com.topjohnwu.magisk.view.Shortcuts +import com.topjohnwu.magisk.wrap import com.topjohnwu.superuser.Shell open class SplashActivity : Activity() { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt index 4ce2a60be..75b395cac 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt @@ -10,8 +10,7 @@ import com.topjohnwu.magisk.databinding.ActivityRequestBinding import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.events.DieEvent import com.topjohnwu.magisk.model.events.ViewEvent -import com.topjohnwu.magisk.model.receiver.GeneralReceiver -import com.topjohnwu.magisk.utils.SuLogger +import com.topjohnwu.magisk.utils.SuHandler import org.koin.androidx.viewmodel.ext.android.viewModel open class SuRequestActivity : BaseActivity() { @@ -29,19 +28,13 @@ open class SuRequestActivity : BaseActivity { - if (!viewModel.handleRequest(intent)) - finish() - return - } - GeneralReceiver.LOG -> SuLogger.handleLogs(this, intent) - GeneralReceiver.NOTIFY -> SuLogger.handleNotify(this, intent) + if (intent?.action == SuHandler.REQUEST) { + if (!viewModel.handleRequest(intent)) + finish() + } else { + SuHandler(this, intent.action, intent.extras) + finish() } - - finish() } override fun onEventDispatched(event: ViewEvent) { diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt index ebc4a8c94..511409a7d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt @@ -77,8 +77,9 @@ object PatchAPK { } private fun patchAndHide(context: Context, label: String): Boolean { - // If not running as stub, and we are compatible with stub, use stub - val src = if (!isRunningAsStub && SDK_INT >= 28 && Info.env.connectionMode == 3) { + val src = if (!isRunningAsStub && SDK_INT >= 28 && + Info.env.magiskVersionCode >= Const.Version.PROVIDER_CONNECT) { + // If not running as stub, and we are compatible with stub, use stub val stub = File(context.cacheDir, "stub.apk") val svc = get() runCatching { diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/SuHandler.kt b/app/src/main/java/com/topjohnwu/magisk/utils/SuHandler.kt new file mode 100644 index 000000000..ba6521c32 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/SuHandler.kt @@ -0,0 +1,137 @@ +package com.topjohnwu.magisk.utils + +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.os.Process +import android.widget.Toast +import com.topjohnwu.magisk.* +import com.topjohnwu.magisk.data.repository.LogRepository +import com.topjohnwu.magisk.extensions.get +import com.topjohnwu.magisk.extensions.startActivity +import com.topjohnwu.magisk.extensions.startActivityWithRoot +import com.topjohnwu.magisk.extensions.subscribeK +import com.topjohnwu.magisk.model.entity.MagiskPolicy +import com.topjohnwu.magisk.model.entity.toLog +import com.topjohnwu.magisk.model.entity.toPolicy +import com.topjohnwu.magisk.ui.surequest.SuRequestActivity +import com.topjohnwu.superuser.Shell +import timber.log.Timber +import java.util.* + +object SuHandler : ProviderCallHandler { + + const val REQUEST = "request" + const val LOG = "log" + const val NOTIFY = "notify" + const val TEST = "test" + + override fun call(context: Context, method: String, arg: String?, extras: Bundle?): Bundle? { + invoke(context.wrap(), method, extras) + return null + } + + operator fun invoke(context: Context, action: String?, data: Bundle?) { + data ?: return + + // Debug messages + if (BuildConfig.DEBUG) { + Timber.d(action) + data.let { bundle -> + bundle.keySet().forEach { + Timber.d("[%s]=[%s]", it, bundle[it]) + } + } + } + + when (action) { + REQUEST -> { + val intent = context.intent() + .setAction(action) + .putExtras(data) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) + if (Build.VERSION.SDK_INT >= 29) { + // Android Q does not allow starting activity from background + intent.startActivityWithRoot() + } else { + intent.startActivity(context) + } + } + LOG -> handleLogs(context, data) + NOTIFY -> handleNotify(context, data) + TEST -> { + val mode = data.getInt("mode", 2) + Shell.su( + "magisk --connect-mode $mode", + "magisk --use-broadcast" + ).submit() + } + } + } + + private fun Any?.toInt(): Int? { + return when (this) { + is Int -> this + is Long -> this.toInt() + else -> null + } + } + + private fun handleLogs(context: Context, data: Bundle) { + val fromUid = data["from.uid"].toInt() ?: return + if (fromUid == Process.myUid()) + return + + val pm = context.packageManager + + val notify = data.getBoolean("notify", true) + val allow = data["policy"].toInt() ?: return + + val policy = runCatching { fromUid.toPolicy(pm, allow) }.getOrElse { return } + + if (notify) + notify(context, policy) + + val toUid = data["to.uid"].toInt() ?: return + val pid = data["pid"].toInt() ?: return + + val command = data.getString("command") ?: return + val log = policy.toLog( + toUid = toUid, + fromPid = pid, + command = command, + date = Date() + ) + + val logRepo = get() + logRepo.put(log).subscribeK(onError = { Timber.e(it) }) + } + + private fun handleNotify(context: Context, data: Bundle) { + val fromUid = data["from.uid"].toInt() ?: return + if (fromUid == Process.myUid()) + return + + val pm = context.packageManager + val allow = data["policy"].toInt() ?: return + + runCatching { + val policy = fromUid.toPolicy(pm, allow) + if (policy.policy >= 0) + notify(context, policy) + } + } + + private fun notify(context: Context, policy: MagiskPolicy) { + if (policy.notification && Config.suNotification == Config.Value.NOTIFICATION_TOAST) { + val resId = if (policy.policy == MagiskPolicy.ALLOW) + R.string.su_allow_toast + else + R.string.su_deny_toast + + Utils.toast(context.getString(resId, policy.appName), Toast.LENGTH_SHORT) + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.kt b/app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.kt deleted file mode 100644 index 98b201235..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.kt +++ /dev/null @@ -1,92 +0,0 @@ -package com.topjohnwu.magisk.utils - -import android.content.Context -import android.content.Intent -import android.os.Process -import android.widget.Toast -import com.topjohnwu.magisk.Config -import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.data.database.PolicyDao -import com.topjohnwu.magisk.data.repository.LogRepository -import com.topjohnwu.magisk.extensions.get -import com.topjohnwu.magisk.model.entity.MagiskPolicy -import com.topjohnwu.magisk.model.entity.toLog -import com.topjohnwu.magisk.model.entity.toPolicy -import java.util.* - -object SuLogger { - - fun handleLogs(context: Context, intent: Intent) { - - val fromUid = intent.getIntExtra("from.uid", -1) - if (fromUid < 0) return - if (fromUid == Process.myUid()) return - - val pm = context.packageManager - - val notify: Boolean - val data = intent.extras - val policy: MagiskPolicy = if (data!!.containsKey("notify")) { - notify = data.getBoolean("notify") - runCatching { - fromUid.toPolicy(pm) - }.getOrElse { return } - } else { - // Doesn't report whether notify or not, check database ourselves - val policyDB = get() - val policy = policyDB.fetch(fromUid).blockingGet() ?: return - notify = policy.notification - policy - }.copy(policy = data.getInt("policy", -1)) - - if (policy.policy < 0) - return - - if (notify) - handleNotify(context, policy) - - val toUid = intent.getIntExtra("to.uid", -1) - if (toUid < 0) return - - val pid = intent.getIntExtra("pid", -1) - if (pid < 0) return - - val command = intent.getStringExtra("command") ?: return - val log = policy.toLog( - toUid = toUid, - fromPid = pid, - command = command, - date = Date() - ) - - val logRepo = get() - logRepo.put(log).blockingGet()?.printStackTrace() - } - - private fun handleNotify(context: Context, policy: MagiskPolicy) { - if (policy.notification && Config.suNotification == Config.Value.NOTIFICATION_TOAST) { - Utils.toast( - context.getString( - if (policy.policy == MagiskPolicy.ALLOW) - R.string.su_allow_toast - else - R.string.su_deny_toast, policy.appName - ), - Toast.LENGTH_SHORT - ) - } - } - - fun handleNotify(context: Context, intent: Intent) { - val fromUid = intent.getIntExtra("from.uid", -1) - if (fromUid < 0) return - if (fromUid == Process.myUid()) return - runCatching { - val pm = context.packageManager - val policy = fromUid.toPolicy(pm) - .copy(policy = intent.getIntExtra("policy", -1)) - if (policy.policy >= 0) - handleNotify(context, policy) - } - } -} diff --git a/native/jni/core/bootstages.cpp b/native/jni/core/bootstages.cpp index 9c57c776e..9e6f10c97 100644 --- a/native/jni/core/bootstages.cpp +++ b/native/jni/core/bootstages.cpp @@ -800,7 +800,4 @@ void boot_complete(int client) { install_apk("/data/magisk.apk"); } } - - // Test whether broadcast can be used or not - broadcast_test(); } diff --git a/native/jni/core/daemon.cpp b/native/jni/core/daemon.cpp index 2948b1497..49a22a419 100644 --- a/native/jni/core/daemon.cpp +++ b/native/jni/core/daemon.cpp @@ -46,8 +46,6 @@ static void *request_handler(void *args) { case LATE_START: case BOOT_COMPLETE: case SQLITE_CMD: - case BROADCAST_ACK: - case BROADCAST_TEST: if (credential.uid != 0) { write_int(client, ROOT_REQUIRED); close(client); @@ -84,12 +82,6 @@ static void *request_handler(void *args) { case SQLITE_CMD: exec_sql(client); break; - case BROADCAST_ACK: - broadcast_ack(client); - break; - case BROADCAST_TEST: - broadcast_test(client); - break; case REMOVE_MODULES: if (credential.uid == UID_SHELL || credential.uid == UID_ROOT) { remove_modules(); diff --git a/native/jni/core/magisk.cpp b/native/jni/core/magisk.cpp index 821e53be1..79b5a65c5 100644 --- a/native/jni/core/magisk.cpp +++ b/native/jni/core/magisk.cpp @@ -35,8 +35,6 @@ Advanced Options (Internal APIs): --clone-attr SRC DEST clone permission, owner, and selinux context --clone SRC DEST clone SRC to DEST --sqlite SQL exec SQL commands to Magisk database - --connect-mode [MODE] get/set connect mode for su request and notify - --broadcast-test manually trigger broadcast tests Supported init triggers: post-fs-data, service, boot-complete @@ -113,23 +111,10 @@ int magisk_main(int argc, char *argv[]) { printf("%s\n", res); free(res); } - } else if (argv[1] == "--connect-mode"sv) { - int fd = connect_daemon(); - write_int(fd, BROADCAST_ACK); - if (argc >= 3) { - write_int(fd, parse_int(argv[2])); - } else { - write_int(fd, -1); - } - return read_int(fd); } else if (argv[1] == "--remove-modules"sv) { int fd = connect_daemon(); write_int(fd, REMOVE_MODULES); return read_int(fd); - } else if (argv[1] == "--broadcast-test"sv) { - int fd = connect_daemon(); - write_int(fd, BROADCAST_TEST); - return read_int(fd); } #if 0 /* Entry point for testing stuffs */ diff --git a/native/jni/include/daemon.h b/native/jni/include/daemon.h index 6af462f43..71ac92b9b 100644 --- a/native/jni/include/daemon.h +++ b/native/jni/include/daemon.h @@ -17,9 +17,7 @@ enum { BOOT_COMPLETE, MAGISKHIDE, SQLITE_CMD, - BROADCAST_ACK, REMOVE_MODULES, - BROADCAST_TEST, }; // Return codes for daemon @@ -84,8 +82,6 @@ void magiskhide_handler(int client); *************/ void su_daemon_handler(int client, struct ucred *credential); -void broadcast_test(int client = -1); -void broadcast_ack(int client); /********************* * Daemon Global Vars diff --git a/native/jni/su/connect.cpp b/native/jni/su/connect.cpp index f83cf2c9c..a5789f45d 100644 --- a/native/jni/su/connect.cpp +++ b/native/jni/su/connect.cpp @@ -13,46 +13,28 @@ using namespace std; -enum connect_mode { - UNINITIALIZED = 0, - MODE_ACTIVITY, - MODE_BROADCAST_COMPONENT, - MODE_BROADCAST_PACKAGE -}; +#define CALL_PROVIDER \ +"/system/bin/app_process", "/system/bin", "com.android.commands.content.Content", \ +"call", "--uri", nullptr, "--user", nullptr, "--method" -static connect_mode current_mode = UNINITIALIZED; - -#define START_ACTIVITY \ -"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \ -"start", "-n", nullptr, "--user", nullptr, "-f", "0x18000020", "-a" - -// 0x18000020 = FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_MULTIPLE_TASK|FLAG_INCLUDE_STOPPED_PACKAGES - -#define START_BROADCAST \ -"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \ -"broadcast", "-n", nullptr, "--user", nullptr, "-f", "0x00000020", \ -"-a", "android.intent.action.REBOOT", "--es", "action" - -#define START_BROADCAST_PKG \ -"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \ -"broadcast", "-p", nullptr, "--user", nullptr, "-f", "0x00000020", \ -"-a", "android.intent.action.REBOOT", "--es", "action" - -// 0x00000020 = FLAG_INCLUDE_STOPPED_PACKAGES - -#define am_app_info(info, ...) \ -if (current_mode == MODE_BROADCAST_PACKAGE) { \ - const char *cmd[] = { START_BROADCAST_PKG, __VA_ARGS__, nullptr }; \ - exec_am_cmd(cmd, info); \ -} else if (current_mode == MODE_BROADCAST_COMPONENT) { \ - const char *cmd[] = { START_BROADCAST, __VA_ARGS__, nullptr }; \ - exec_am_cmd(cmd, info); \ -} else { \ - const char *cmd[] = { START_ACTIVITY, __VA_ARGS__, nullptr }; \ - exec_am_cmd(cmd, info); \ +#define content_exec_info(info, ...) {\ + const char *cmd[] = { CALL_PROVIDER, __VA_ARGS__, nullptr }; \ + exec_content_cmd(cmd, info); \ } -#define am_app(...) am_app_info(ctx.info.get(), __VA_ARGS__) +#define content_exec(...) content_exec_info(ctx.info.get(), __VA_ARGS__) + +#define ex(s) "--extra", s + +#define get_user(info) \ +(info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_USER \ +? info->uid / 100000 \ +: 0) + +#define get_uid(info) \ +(info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_OWNER_MANAGED \ +? info->uid % 100000 \ +: info->uid) static const char *get_command(const su_request *to) { if (to->command[0]) @@ -62,48 +44,22 @@ static const char *get_command(const su_request *to) { return DEFAULT_SHELL; } -static void get_user(char *user, const su_info *info) { - sprintf(user, "%d", - info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_USER - ? info->uid / 100000 - : 0); -} - -static void get_uid(char *uid, const su_info *info) { - sprintf(uid, "%d", - info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_OWNER_MANAGED - ? info->uid % 100000 - : info->uid); -} - -static void exec_am_cmd(const char **args, const su_info *info) { +static void exec_content_cmd(const char **args, const su_info *info) { char target[128]; - if (args[3][0] == 'b') { - // Broadcast - if (args[4][1] == 'p') { - // Broadcast to package (receiver can be obfuscated) - strcpy(target, info->str[SU_MANAGER].data()); - } else { - // a.h is the broadcast receiver - sprintf(target, "%s/a.h", info->str[SU_MANAGER].data()); - } - } else { - // a.m is the activity - sprintf(target, "%s/a.m", info->str[SU_MANAGER].data()); - } - char user[8]; - get_user(user, info); + sprintf(target, "content://%s.provider", info->str[SU_MANAGER].data()); + char user[4]; + sprintf(user, "%d", get_user(info)); // Fill in non static arguments args[5] = target; args[7] = user; exec_t exec { - .pre_exec = []() -> void { + .pre_exec = [] { int null = xopen("/dev/null", O_WRONLY | O_CLOEXEC); dup2(null, STDOUT_FILENO); dup2(null, STDERR_FILENO); - setenv("CLASSPATH", "/system/framework/am.jar", 1); + setenv("CLASSPATH", "/system/framework/content.jar", 1); }, .fork = fork_dont_care, .argv = args @@ -113,94 +69,51 @@ static void exec_am_cmd(const char **args, const su_info *info) { #define LOG_BODY \ "log", \ -"--ei", "from.uid", fromUid, \ -"--ei", "to.uid", toUid, \ -"--ei", "pid", pid, \ -"--ei", "policy", policy, \ -"--es", "command", get_command(&ctx.req), \ -"--ez", "notify", ctx.info->access.notify ? "true" : "false" +ex(fromUid), ex(toUid), ex(pid), ex(policy), \ +ex(command.data()), ex(notify) void app_log(const su_context &ctx) { - char fromUid[8]; - get_uid(fromUid, ctx.info.get()); + char fromUid[16]; + sprintf(fromUid, "from.uid:i:%d", get_uid(ctx.info)); - char toUid[8]; - sprintf(toUid, "%d", ctx.req.uid); + char toUid[16]; + sprintf(toUid, "to.uid:i:%d", ctx.req.uid); - char pid[8]; - sprintf(pid, "%d", ctx.pid); + char pid[16]; + sprintf(pid, "pid:i:%d", ctx.pid); - char policy[2]; - sprintf(policy, "%d", ctx.info->access.policy); + char policy[16]; + sprintf(policy, "policy:i:%d", ctx.info->access.policy); - am_app(LOG_BODY) + string command("command:s:"); + command += get_command(&ctx.req); + + char notify[16]; + sprintf(notify, "notify:b:%s", ctx.info->access.notify ? "true" : "false"); + + content_exec(LOG_BODY) } #define NOTIFY_BODY \ -"notify", \ -"--ei", "from.uid", fromUid, \ -"--ei", "policy", policy +"notify", ex(fromUid), ex(policy) void app_notify(const su_context &ctx) { - char fromUid[8]; - get_uid(fromUid, ctx.info.get()); + char fromUid[16]; + sprintf(fromUid, "from.uid:i:%d", get_uid(ctx.info)); - char policy[2]; - sprintf(policy, "%d", ctx.info->access.policy); + char policy[16]; + sprintf(policy, "policy:i:%d", ctx.info->access.policy); - am_app(NOTIFY_BODY) + content_exec(NOTIFY_BODY) } #define SOCKET_BODY \ -"request", \ -"--es", "socket", socket +"request", ex(sock) void app_socket(const char *socket, const shared_ptr &info) { - am_app_info(info.get(), SOCKET_BODY) -} - -#define TEST_BODY \ -"test", "--ei", "mode", mode, nullptr - -void broadcast_test(int client) { - if (client >= 0) { - // Make it not uninitialized - current_mode = MODE_ACTIVITY; - write_int(client, 0); - close(client); - } - - su_info info; - get_db_settings(info.cfg); - get_db_strings(info.str); - validate_manager(info.str[SU_MANAGER], 0, &info.mgr_st); - - char mode[2]; - { - sprintf(mode, "%d", MODE_BROADCAST_PACKAGE); - const char *cmd[] = { START_BROADCAST_PKG, TEST_BODY }; - exec_am_cmd(cmd, &info); - } - { - sprintf(mode, "%d", MODE_BROADCAST_COMPONENT); - const char *cmd[] = { START_BROADCAST, TEST_BODY }; - exec_am_cmd(cmd, &info); - } -} - -void broadcast_ack(int client) { - int mode = read_int(client); - if (mode < 0) { - // Return connection mode to client - write_int(client, current_mode); - } else { - if (mode > current_mode) { - LOGD("* Use connect mode [%d] for su request and notify\n", mode); - current_mode = static_cast(mode); - } - write_int(client, 0); - } - close(client); + char sock[128]; + sprintf(sock, "socket:s:%s", socket); + content_exec_info(info.get(), SOCKET_BODY) } void socket_send_request(int fd, const shared_ptr &info) { diff --git a/scripts/emulator.sh b/scripts/emulator.sh index a01e0f040..ceb3e8388 100755 --- a/scripts/emulator.sh +++ b/scripts/emulator.sh @@ -118,4 +118,3 @@ mkdir -p /data/adb/modules 2>/dev/null mkdir /data/adb/post-fs-data.d 2>/dev/null mkdir /data/adb/services.d 2>/dev/null /sbin/magisk --daemon -/sbin/magisk --broadcast-test diff --git a/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java b/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java index 9b7ea0a73..8ce8d2de0 100644 --- a/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java +++ b/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java @@ -10,6 +10,7 @@ import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import android.os.Build; +import android.os.Bundle; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.provider.OpenableColumns; @@ -54,6 +55,7 @@ public class FileProvider extends ContentProvider { private PathStrategy mStrategy; + public static ProviderCallHandler callHandler; @Override public boolean onCreate() { @@ -156,6 +158,14 @@ public class FileProvider extends ContentProvider { return file.delete() ? 1 : 0; } + + @Override + public Bundle call(String method, String arg, Bundle extras) { + if (callHandler != null) + return callHandler.call(getContext(), method, arg, extras); + return null; + } + @Override public ParcelFileDescriptor openFile(Uri uri, String mode) diff --git a/shared/src/main/java/com/topjohnwu/magisk/ProviderCallHandler.java b/shared/src/main/java/com/topjohnwu/magisk/ProviderCallHandler.java new file mode 100644 index 000000000..c3ff5c447 --- /dev/null +++ b/shared/src/main/java/com/topjohnwu/magisk/ProviderCallHandler.java @@ -0,0 +1,8 @@ +package com.topjohnwu.magisk; + +import android.content.Context; +import android.os.Bundle; + +public interface ProviderCallHandler { + Bundle call(Context context, String method, String arg, Bundle extras); +} From b1d25e050339856674ff1260f9068b78f87c8aa8 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 4 Nov 2019 15:42:40 -0500 Subject: [PATCH 34/74] Reuse ALPHANUM --- app/src/main/java/com/topjohnwu/magisk/utils/Keygen.kt | 2 +- app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Keygen.kt b/app/src/main/java/com/topjohnwu/magisk/utils/Keygen.kt index 5f937459d..d1ffcf04a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Keygen.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Keygen.kt @@ -5,6 +5,7 @@ import android.util.Base64 import android.util.Base64OutputStream import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.di.koinModules +import com.topjohnwu.magisk.utils.PatchAPK.ALPHANUM import com.topjohnwu.signing.CryptoUtils.readCertificate import com.topjohnwu.signing.CryptoUtils.readPrivateKey import com.topjohnwu.superuser.internal.InternalUtils @@ -38,7 +39,6 @@ class Keygen: CertKeyProvider { private const val ALIAS = "magisk" private val PASSWORD get() = "magisk".toCharArray() private const val TESTKEY_CERT = "61ed377e85d386a8dfee6b864bd85b0bfaa5af81" - private const val ALPHANUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" private const val BASE64_FLAG = Base64.NO_PADDING or Base64.NO_WRAP } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt index 511409a7d..b1d4c00d3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt @@ -27,7 +27,7 @@ object PatchAPK { private val UPPERALPHA = LOWERALPHA.toUpperCase() private val ALPHA = LOWERALPHA + UPPERALPHA private const val DIGITS = "0123456789" - private val ALPHANUM = ALPHA + DIGITS + val ALPHANUM = ALPHA + DIGITS private val ALPHANUMDOTS = "$ALPHANUM............" private fun genPackageName(prefix: String, length: Int): String { From a6e62e07a22691dee1e8839ae40966c3eefb9d1d Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 4 Nov 2019 17:14:18 -0500 Subject: [PATCH 35/74] Sort modules ignore case Close #2024 --- .../java/com/topjohnwu/magisk/model/entity/module/Module.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/module/Module.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/module/Module.kt index f71e4c71d..8017dea4d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/module/Module.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/module/Module.kt @@ -65,7 +65,7 @@ class Module(path: String) : BaseModule() { val module = Module(Const.MAGISK_PATH + "/" + file.name) moduleList.add(module) } - return moduleList.sortedBy { it.name } + return moduleList.sortedBy { it.name.toLowerCase() } } } } From 46447f7cfd966da458e83f33b6205a89196fe598 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 5 Nov 2019 01:46:46 -0500 Subject: [PATCH 36/74] Proper string buffer size --- native/jni/su/connect.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/native/jni/su/connect.cpp b/native/jni/su/connect.cpp index a5789f45d..501f6020c 100644 --- a/native/jni/su/connect.cpp +++ b/native/jni/su/connect.cpp @@ -73,10 +73,10 @@ ex(fromUid), ex(toUid), ex(pid), ex(policy), \ ex(command.data()), ex(notify) void app_log(const su_context &ctx) { - char fromUid[16]; + char fromUid[32]; sprintf(fromUid, "from.uid:i:%d", get_uid(ctx.info)); - char toUid[16]; + char toUid[32]; sprintf(toUid, "to.uid:i:%d", ctx.req.uid); char pid[16]; @@ -98,7 +98,7 @@ void app_log(const su_context &ctx) { "notify", ex(fromUid), ex(policy) void app_notify(const su_context &ctx) { - char fromUid[16]; + char fromUid[32]; sprintf(fromUid, "from.uid:i:%d", get_uid(ctx.info)); char policy[16]; From d952cc2327839855002951d5ee7c6586698fe119 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 7 Nov 2019 17:41:59 -0500 Subject: [PATCH 37/74] Properly solve the connection problem --- app/src/main/AndroidManifest.xml | 9 +- .../magisk/ui/surequest/SuRequestActivity.kt | 23 +- build.gradle | 2 +- native/jni/su/connect.cpp | 219 ++++++++++++------ native/jni/su/su.h | 6 +- native/jni/utils/misc.cpp | 7 +- shared/src/main/AndroidManifest.xml | 4 +- stub/src/main/AndroidManifest.xml | 9 +- 8 files changed, 194 insertions(+), 85 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 86d1c906b..7a536d0f7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -43,10 +43,15 @@ + android:exported="false" + tools:ignore="AppLinkUrlError"> + + + + + () { @@ -28,13 +30,28 @@ open class SuRequestActivity : BaseActivitycfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_USER \ -? info->uid / 100000 \ -: 0) +? info->uid / 100000 : 0) #define get_uid(info) \ (info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_OWNER_MANAGED \ -? info->uid % 100000 \ -: info->uid) +? info->uid % 100000 : info->uid) -static const char *get_command(const su_request *to) { - if (to->command[0]) - return to->command; - if (to->shell[0]) - return to->shell; - return DEFAULT_SHELL; +#define get_cmd(to) \ +(to.command[0] ? to.command : to.shell[0] ? to.shell : DEFAULT_SHELL) + +class Extra { + const char *key; + enum { + INT, + BOOL, + STRING + } type; + union { + int int_val; + bool bool_val; + const char * str_val; + }; + char i_buf[16]; + char b_buf[32]; +public: + Extra(const char *k, int v): key(k), type(INT), int_val(v) {} + Extra(const char *k, bool v): key(k), type(BOOL), bool_val(v) {} + Extra(const char *k, const char *v): key(k), type(STRING), str_val(v) {} + + void add_intent(vector &vec) { + const char *val; + switch (type) { + case INT: + vec.push_back("--ei"); + sprintf(i_buf, "%d", int_val); + val = i_buf; + break; + case BOOL: + vec.push_back("--ez"); + val = bool_val ? "true" : "false"; + break; + case STRING: + vec.push_back("--es"); + val = str_val; + break; + } + vec.push_back(key); + vec.push_back(val); + } + + void add_bind(vector &vec) { + switch (type) { + case INT: + sprintf(b_buf, "%s:i:%d", key, int_val); + break; + case BOOL: + sprintf(b_buf, "%s:b:%s", key, bool_val ? "true" : "false"); + break; + case STRING: + sprintf(b_buf, "%s:s:%s", key, str_val); + break; + } + vec.push_back("--extra"); + vec.push_back(b_buf); + } +}; + +static bool check_error(int fd) { + char buf[1024]; + unique_ptr out(xfdopen(fd, "r"), fclose); + while (fgets(buf, sizeof(buf), out.get())) { + if (strncmp(buf, "Error", 5) == 0) + return false; + } + return true; } -static void exec_content_cmd(const char **args, const su_info *info) { +static void exec_cmd(const char *action, vector &data, + const shared_ptr &info, int mode = CONTENT_PROVIDER) { char target[128]; - sprintf(target, "content://%s.provider", info->str[SU_MANAGER].data()); char user[4]; sprintf(user, "%d", get_user(info)); - // Fill in non static arguments - args[5] = target; - args[7] = user; + // First try content provider call method + if (mode >= CONTENT_PROVIDER) { + sprintf(target, "content://%s.provider", info->str[SU_MANAGER].data()); + vector args{ CALL_PROVIDER }; + for (auto &e : data) { + e.add_bind(args); + } + args.push_back(nullptr); + exec_t exec { + .err = true, + .fd = -1, + .pre_exec = [] { setenv("CLASSPATH", "/system/framework/content.jar", 1); }, + .argv = args.data() + }; + exec_command_sync(exec); + if (check_error(exec.fd)) + return; + } + vector args{ START_ACTIVITY }; + for (auto &e : data) { + e.add_intent(args); + } + args.push_back(nullptr); exec_t exec { - .pre_exec = [] { - int null = xopen("/dev/null", O_WRONLY | O_CLOEXEC); - dup2(null, STDOUT_FILENO); - dup2(null, STDERR_FILENO); - setenv("CLASSPATH", "/system/framework/content.jar", 1); - }, - .fork = fork_dont_care, - .argv = args + .err = true, + .fd = -1, + .pre_exec = [] { setenv("CLASSPATH", "/system/framework/am.jar", 1); }, + .argv = args.data() }; + + if (mode >= PKG_ACTIVITY) { + // Then try start activity without component name + strcpy(target, info->str[SU_MANAGER].data()); + exec_command_sync(exec); + if (check_error(exec.fd)) + return; + } + + // Finally, fallback to start activity with component name + args[4] = "-n"; + sprintf(target, "%s/a.m", info->str[SU_MANAGER].data()); + exec.fd = -2; + exec.fork = fork_dont_care; exec_command(exec); } -#define LOG_BODY \ -"log", \ -ex(fromUid), ex(toUid), ex(pid), ex(policy), \ -ex(command.data()), ex(notify) - void app_log(const su_context &ctx) { - char fromUid[32]; - sprintf(fromUid, "from.uid:i:%d", get_uid(ctx.info)); + if (fork_dont_care() == 0) { + vector extras; + extras.reserve(6); + extras.emplace_back("from.uid", get_uid(ctx.info)); + extras.emplace_back("to.uid", ctx.req.uid); + extras.emplace_back("pid", ctx.pid); + extras.emplace_back("policy", ctx.info->access.policy); + extras.emplace_back("command", get_cmd(ctx.req)); + extras.emplace_back("notify", (bool) ctx.info->access.notify); - char toUid[32]; - sprintf(toUid, "to.uid:i:%d", ctx.req.uid); - - char pid[16]; - sprintf(pid, "pid:i:%d", ctx.pid); - - char policy[16]; - sprintf(policy, "policy:i:%d", ctx.info->access.policy); - - string command("command:s:"); - command += get_command(&ctx.req); - - char notify[16]; - sprintf(notify, "notify:b:%s", ctx.info->access.notify ? "true" : "false"); - - content_exec(LOG_BODY) + exec_cmd("log", extras, ctx.info); + exit(0); + } } -#define NOTIFY_BODY \ -"notify", ex(fromUid), ex(policy) - void app_notify(const su_context &ctx) { - char fromUid[32]; - sprintf(fromUid, "from.uid:i:%d", get_uid(ctx.info)); + if (fork_dont_care() == 0) { + vector extras; + extras.reserve(2); + extras.emplace_back("from.uid", get_uid(ctx.info)); + extras.emplace_back("policy", ctx.info->access.policy); - char policy[16]; - sprintf(policy, "policy:i:%d", ctx.info->access.policy); - - content_exec(NOTIFY_BODY) + exec_cmd("notify", extras, ctx.info); + exit(0); + } } -#define SOCKET_BODY \ -"request", ex(sock) - void app_socket(const char *socket, const shared_ptr &info) { - char sock[128]; - sprintf(sock, "socket:s:%s", socket); - content_exec_info(info.get(), SOCKET_BODY) + vector extras; + extras.reserve(1); + extras.emplace_back("socket", socket); + + exec_cmd("request", extras, info, PKG_ACTIVITY); } void socket_send_request(int fd, const shared_ptr &info) { diff --git a/native/jni/su/su.h b/native/jni/su/su.h index 2c18fc5c8..02fc0aebc 100644 --- a/native/jni/su/su.h +++ b/native/jni/su/su.h @@ -17,7 +17,7 @@ class su_info { public: /* Unique key */ - const unsigned uid; + const int uid; /* These should be guarded with internal lock */ db_settings cfg; @@ -39,7 +39,7 @@ private: }; struct su_req_base { - unsigned uid = UID_ROOT; + int uid = UID_ROOT; bool login = false; bool keepenv = false; bool mount_master = false; @@ -63,7 +63,7 @@ private: struct su_context { std::shared_ptr info; su_request req; - pid_t pid; + int pid; }; void app_log(const su_context &ctx); diff --git a/native/jni/utils/misc.cpp b/native/jni/utils/misc.cpp index 4678fb41e..2db95f556 100644 --- a/native/jni/utils/misc.cpp +++ b/native/jni/utils/misc.cpp @@ -73,7 +73,8 @@ int strend(const char *s1, const char *s2) { } int exec_command(exec_t &exec) { - int pipefd[2] = {-1, -1}, outfd = -1; + int pipefd[] = {-1, -1}; + int outfd = -1; if (exec.fd == -1) { if (xpipe2(pipefd, O_CLOEXEC) == -1) @@ -113,10 +114,10 @@ int exec_command(exec_t &exec) { } int exec_command_sync(exec_t &exec) { - int pid, status; - pid = exec_command(exec); + int pid = exec_command(exec); if (pid < 0) return -1; + int status; waitpid(pid, &status, 0); return WEXITSTATUS(status); } diff --git a/shared/src/main/AndroidManifest.xml b/shared/src/main/AndroidManifest.xml index c42a2dd7f..bcec9546e 100644 --- a/shared/src/main/AndroidManifest.xml +++ b/shared/src/main/AndroidManifest.xml @@ -9,11 +9,11 @@ android:icon="@drawable/ic_launcher" android:installLocation="internalOnly" android:label="Magisk Manager" - android:supportsRtl="true"> + android:supportsRtl="true" + android:theme="@android:style/Theme.Translucent.NoTitleBar"> + android:exported="false" + tools:ignore="AppLinkUrlError"> + + + + + Date: Fri, 8 Nov 2019 02:15:30 -0500 Subject: [PATCH 38/74] Cleanup manifest --- app/src/main/AndroidManifest.xml | 17 ++++------------- .../topjohnwu/magisk/ui/flash/FlashActivity.kt | 6 ++++-- shared/src/main/AndroidManifest.xml | 6 ++++-- stub/src/main/AndroidManifest.xml | 17 ++++------------- 4 files changed, 16 insertions(+), 30 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7a536d0f7..3ed9ea51d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,14 +13,12 @@ android:name="a.e" android:appComponentFactory="a.a" android:allowBackup="true" - android:usesCleartextTraffic="true" tools:ignore="UnusedAttribute,GoogleAppIndexingWarning" tools:replace="android:appComponentFactory"> @@ -29,16 +27,10 @@ - + - + @@ -70,9 +63,7 @@ - + () } override fun onCreate(savedInstanceState: Bundle?) { + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR super.onCreate(savedInstanceState) val id = intent.getIntExtra(Const.Key.DISMISS_ID, -1) if (id != -1) - NotificationManagerCompat.from(this).cancel(id) + Notifications.mgr.cancel(id) } override fun onBackPressed() { diff --git a/shared/src/main/AndroidManifest.xml b/shared/src/main/AndroidManifest.xml index bcec9546e..2b313fbaa 100644 --- a/shared/src/main/AndroidManifest.xml +++ b/shared/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ @@ -7,10 +8,11 @@ + tools:ignore="UnusedAttribute"> @@ -30,21 +28,16 @@ - + - + @@ -67,9 +60,7 @@ - + Date: Fri, 8 Nov 2019 02:59:09 -0500 Subject: [PATCH 39/74] Get XMLs directly --- shared/src/main/AndroidManifest.xml | 4 +- .../com/topjohnwu/magisk/FileProvider.java | 37 +++---------------- 2 files changed, 7 insertions(+), 34 deletions(-) diff --git a/shared/src/main/AndroidManifest.xml b/shared/src/main/AndroidManifest.xml index 2b313fbaa..ed324d3c5 100644 --- a/shared/src/main/AndroidManifest.xml +++ b/shared/src/main/AndroidManifest.xml @@ -16,6 +16,7 @@ - diff --git a/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java b/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java index 8ce8d2de0..45c123b9d 100644 --- a/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java +++ b/shared/src/main/java/com/topjohnwu/magisk/FileProvider.java @@ -3,7 +3,6 @@ package com.topjohnwu.magisk; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; -import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.res.XmlResourceParser; import android.database.Cursor; @@ -17,6 +16,8 @@ import android.provider.OpenableColumns; import android.text.TextUtils; import android.webkit.MimeTypeMap; +import com.topjohnwu.shared.R; + import org.xmlpull.v1.XmlPullParserException; import java.io.File; @@ -35,9 +36,6 @@ public class FileProvider extends ContentProvider { private static final String[] COLUMNS = { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE }; - private static final String - META_DATA_FILE_PROVIDER_PATHS = "android.support.FILE_PROVIDER_PATHS"; - private static final String TAG_ROOT_PATH = "root-path"; private static final String TAG_FILES_PATH = "files-path"; private static final String TAG_CACHE_PATH = "cache-path"; @@ -56,13 +54,12 @@ public class FileProvider extends ContentProvider { private PathStrategy mStrategy; public static ProviderCallHandler callHandler; - + @Override public boolean onCreate() { return true; } - @Override public void attachInfo(Context context, ProviderInfo info) { super.attachInfo(context, info); @@ -85,7 +82,6 @@ public class FileProvider extends ContentProvider { return strategy.getUriForFile(file); } - @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, @@ -118,7 +114,6 @@ public class FileProvider extends ContentProvider { return cursor; } - @Override public String getType(Uri uri) { @@ -136,20 +131,17 @@ public class FileProvider extends ContentProvider { return "application/octet-stream"; } - @Override public Uri insert(Uri uri, ContentValues values) { throw new UnsupportedOperationException("No external inserts"); } - @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { throw new UnsupportedOperationException("No external updates"); } - @Override public int delete(Uri uri, String selection, String[] selectionArgs) { @@ -158,7 +150,6 @@ public class FileProvider extends ContentProvider { return file.delete() ? 1 : 0; } - @Override public Bundle call(String method, String arg, Bundle extras) { if (callHandler != null) @@ -166,7 +157,6 @@ public class FileProvider extends ContentProvider { return null; } - @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { @@ -176,7 +166,6 @@ public class FileProvider extends ContentProvider { return ParcelFileDescriptor.open(file, fileMode); } - private static PathStrategy getPathStrategy(Context context, String authority) { PathStrategy strat; synchronized (sCache) { @@ -185,11 +174,9 @@ public class FileProvider extends ContentProvider { try { strat = parsePathStrategy(context, authority); } catch (IOException e) { - throw new IllegalArgumentException( - "Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e); + throw new IllegalArgumentException("Failed to parse xml", e); } catch (XmlPullParserException e) { - throw new IllegalArgumentException( - "Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e); + throw new IllegalArgumentException("Failed to parse xml", e); } sCache.put(authority, strat); } @@ -197,19 +184,11 @@ public class FileProvider extends ContentProvider { return strat; } - private static PathStrategy parsePathStrategy(Context context, String authority) throws IOException, XmlPullParserException { final SimplePathStrategy strat = new SimplePathStrategy(authority); - final ProviderInfo info = context.getPackageManager() - .resolveContentProvider(authority, PackageManager.GET_META_DATA); - final XmlResourceParser in = info.loadXmlMetaData( - context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS); - if (in == null) { - throw new IllegalArgumentException( - "Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data"); - } + final XmlResourceParser in = context.getResources().getXml(R.xml.file_paths); int type; while ((type = in.next()) != END_DOCUMENT) { @@ -255,16 +234,13 @@ public class FileProvider extends ContentProvider { return strat; } - interface PathStrategy { Uri getUriForFile(File file); - File getFileForUri(Uri uri); } - static class SimplePathStrategy implements PathStrategy { private final String mAuthority; private final HashMap mRoots = new HashMap<>(); @@ -273,7 +249,6 @@ public class FileProvider extends ContentProvider { mAuthority = authority; } - void addRoot(String name, File root) { if (TextUtils.isEmpty(name)) { throw new IllegalArgumentException("Name must not be empty"); From 8b0b4a2c393d4f37c4f7c6a5e625b880feec579c Mon Sep 17 00:00:00 2001 From: osm0sis Date: Tue, 5 Nov 2019 02:39:55 -0400 Subject: [PATCH 40/74] SignBoot: also catch empty streamed signature as indicating not signed - compare against new byte[] array as a quick tell, since when streaming from a partition with an unsigned image "signature" would of course read without issue but then remain filled by zero padding, resulting in the following: java.io.IOException: unexpected end-of-contents marker at org.bouncycastle.asn1.ASN1InputStream.readObject(Unknown Source:14) at com.topjohnwu.signing.SignBoot$BootSignature.(SignBoot.java:235) at com.topjohnwu.signing.SignBoot.verifySignature(SignBoot.java:144) at com.topjohnwu.signing.BootSigner.main(BootSigner.java:15) at a.a.main(a.java:20) --- signing/src/main/java/com/topjohnwu/signing/SignBoot.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/signing/src/main/java/com/topjohnwu/signing/SignBoot.java b/signing/src/main/java/com/topjohnwu/signing/SignBoot.java index b717df9b2..3936f8cf3 100644 --- a/signing/src/main/java/com/topjohnwu/signing/SignBoot.java +++ b/signing/src/main/java/com/topjohnwu/signing/SignBoot.java @@ -136,7 +136,7 @@ public class SignBoot { // Read footer, which contains the signature byte[] signature = new byte[4096]; - if (imgIn.read(signature) == -1) { + if (imgIn.read(signature) == -1 || Arrays.equals(signature, new byte [signature.length])) { System.err.println("Invalid image: not signed"); return false; } From 65eca316359b477f2350e72d7afdaa67af23dd19 Mon Sep 17 00:00:00 2001 From: Ilya Kushnir Date: Sat, 9 Nov 2019 11:40:10 +0200 Subject: [PATCH 41/74] Updating RU translation --- app/src/main/res/values-ru/strings.xml | 17 +++++++++-------- app/src/main/res/values/strings.xml | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 32b800414..160bda820 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -8,6 +8,7 @@ Настройки Установка Неподдерживаемая версия Magisk + Эта версия Magisk Manager не поддерживает версию Magisk ниже %1$s.\n\nПриложение будет работать так, как будто Magisk не установлен, пожалуйста, обновите Magisk как можно быстрее. Magisk не установлен @@ -62,7 +63,7 @@ Лог успешно очищен - Список изменений + Изменения Обновления Magisk @@ -76,13 +77,13 @@ Нажмите, чтобы загрузить и установить - Только загрузка ZIP + Загрузка установочного ZIP Прямая установка (Рекомендуется) Установка во второй слот (OTA) Ваше устройство будет принудительно перезагружено в неактивный (противоположный) слот!\nИспользуйте эту опцию только при интеграции после OTA.\nПродолжить? - Выбор способа + Способ установки Дополнительная установка - Вручную пропатчить образ + Пропатчить образ вручную Выберите файл образа (*.img) или архив ODIN (*.tar) Перезагрузка через 5 секунд… @@ -120,7 +121,7 @@ Основные Тёмная тема Включить тёмное оформление - Папка загрузки + Папка для загрузки Файлы будут загружаться в %1$s Очистка кэша репозитория Очистить кэш репозитория. Будет загружен заново @@ -141,9 +142,9 @@ Magisk Core Активировать только основные возможности. Модули не будут загружены. MagiskSU и Magisk Hide останутся активными Скрывать Magisk от различных обнаружений - Внесистемные хосты - Поддержка внесистемных хостов для приложений, блокирующих рекламу - Добавлен модуль внесистемных хостов + Внесистемный hosts файл + Поддержка внесистемного hosts файла для приложений, блокирующих рекламу + Добавлен модуль внесистемного hosts файла Укажите имя приложения Новое имя diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1e5551fdc..5df6c7e97 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -9,6 +9,7 @@ Install Unsupported Magisk Version This version of Magisk Manager does not support Magisk version lower than %1$s.\n\nThe app will behave as if no Magisk is installed, please upgrade Magisk as soon as possible. + Magisk is not installed Checking for updates… From a2ddf362d8609e3556790125a14232a3e72ed542 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 9 Nov 2019 15:57:12 -0500 Subject: [PATCH 42/74] Make a.a not extend AppComponentFactory Fix #2053 --- app/src/main/AndroidManifest.xml | 4 +--- app/src/main/java/a/a.java | 4 +--- app/src/main/java/com/topjohnwu/magisk/Hacks.kt | 4 ++-- shared/src/main/java/com/topjohnwu/magisk/DynAPK.java | 8 ++++---- .../java/com/topjohnwu/magisk/DelegateApplication.java | 8 ++++---- .../main/java/com/topjohnwu/magisk/obfuscate/Mapping.java | 3 +-- 6 files changed, 13 insertions(+), 18 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3ed9ea51d..32de4b85e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,10 +11,8 @@ + tools:ignore="UnusedAttribute,GoogleAppIndexingWarning"> .cmp(pkg: String): ComponentName { val name = ClassMap[this].name - return ComponentName(pkg, Info.stub?.componentMap?.get(name) ?: name) + return ComponentName(pkg, Info.stub?.classToComponent?.get(name) ?: name) } inline fun Context.intent() = Intent().setComponent(T::class.java.cmp(packageName)) @@ -131,7 +131,7 @@ private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler val name = service.className val component = ComponentName( service.packageName, - Info.stub!!.componentMap[name] ?: name) + Info.stub!!.classToComponent[name] ?: name) // Clone the JobInfo except component val builder = JobInfo.Builder(id, component) diff --git a/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java b/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java index 03b44e9e7..3a397290f 100644 --- a/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java +++ b/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java @@ -15,7 +15,7 @@ public class DynAPK { // Indices of the object array private static final int STUB_VERSION_ENTRY = 0; - private static final int COMPONENT_MAP = 1; + private static final int CLASS_COMPONENT_MAP = 1; private static File dynDir; private static Method addAssetPath; @@ -44,14 +44,14 @@ public class DynAPK { Object[] arr = (Object[]) o; Data data = new Data(); data.version = (int) arr[STUB_VERSION_ENTRY]; - data.componentMap = (Map) arr[COMPONENT_MAP]; + data.classToComponent = (Map) arr[CLASS_COMPONENT_MAP]; return data; } public static Object pack(Data data) { Object[] arr = new Object[2]; arr[STUB_VERSION_ENTRY] = data.version; - arr[COMPONENT_MAP] = data.componentMap; + arr[CLASS_COMPONENT_MAP] = data.classToComponent; return arr; } @@ -65,6 +65,6 @@ public class DynAPK { public static class Data { public int version = STUB_VERSION; - public Map componentMap; + public Map classToComponent; } } diff --git a/stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java b/stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java index 30d7b89ca..c6b808802 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java +++ b/stub/src/main/java/com/topjohnwu/magisk/DelegateApplication.java @@ -19,12 +19,12 @@ public class DelegateApplication extends Application { static File MANAGER_APK; - private Object factory; + private DelegateComponentFactory factory; private Application delegate; public DelegateApplication() {} - public DelegateApplication(Object o) { + public DelegateApplication(DelegateComponentFactory o) { factory = o; } @@ -46,7 +46,6 @@ public class DelegateApplication extends Application { @SuppressLint("NewApi") private void setUpDynAPK() { - DelegateComponentFactory factory = (DelegateComponentFactory) this.factory; MANAGER_APK = DynAPK.current(this); File update = DynAPK.update(this); if (update.exists()) @@ -55,7 +54,8 @@ public class DelegateApplication extends Application { ClassLoader cl = new DynamicClassLoader(MANAGER_APK, factory.loader); try { // Create the delegate AppComponentFactory - AppComponentFactory df = (AppComponentFactory) cl.loadClass("a.a").newInstance(); + AppComponentFactory df = (AppComponentFactory) + cl.loadClass("androidx.core.app.CoreComponentFactory").newInstance(); // Create the delegate Application delegate = (Application) cl.loadClass("a.e").getConstructor(Object.class) diff --git a/stub/src/main/java/com/topjohnwu/magisk/obfuscate/Mapping.java b/stub/src/main/java/com/topjohnwu/magisk/obfuscate/Mapping.java index 9e8a91933..5b781d80e 100644 --- a/stub/src/main/java/com/topjohnwu/magisk/obfuscate/Mapping.java +++ b/stub/src/main/java/com/topjohnwu/magisk/obfuscate/Mapping.java @@ -29,8 +29,7 @@ public class Mapping { public static Data data() { Data data = new Data(); - data.componentMap = inverseMap; + data.classToComponent = inverseMap; return data; } - } From ad40e533491f1c9f4089a9415996df48c6be8b63 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 9 Nov 2019 18:17:16 -0500 Subject: [PATCH 43/74] Update hacks --- .../main/java/com/topjohnwu/magisk/Hacks.kt | 46 +++---------------- .../topjohnwu/magisk/extensions/XAndroid.kt | 3 ++ .../com/topjohnwu/magisk/extensions/XJava.kt | 37 +++++++++++++-- 3 files changed, 41 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt index 954d4ff24..c1e0c735f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Hacks.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Hacks.kt @@ -14,6 +14,7 @@ import android.content.res.AssetManager import android.content.res.Configuration import android.content.res.Resources import androidx.annotation.RequiresApi +import com.topjohnwu.magisk.extensions.forceGetDeclaredField import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.receiver.GeneralReceiver import com.topjohnwu.magisk.model.update.UpdateCheckService @@ -57,14 +58,13 @@ inline fun Context.intent() = Intent().setComponent(T::class.java.cm private open class GlobalResContext(base: Context) : ContextWrapper(base) { open val mRes: Resources get() = ResourceMgr.resource - private val loader by lazy { javaClass.classLoader!! } override fun getResources(): Resources { return mRes } override fun getClassLoader(): ClassLoader { - return loader + return javaClass.classLoader!! } override fun createConfigurationContext(config: Configuration): Context { @@ -98,7 +98,7 @@ object ResourceMgr { } } -@RequiresApi(api = 28) +@RequiresApi(28) private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler() { override fun schedule(job: JobInfo): Int { @@ -126,48 +126,14 @@ private class JobSchedulerWrapper(private val base: JobScheduler) : JobScheduler } private fun JobInfo.patch(): JobInfo { - // We need to patch the component of JobInfo to access WorkManager SystemJobService - + // We need to swap out the service of JobInfo val name = service.className val component = ComponentName( service.packageName, Info.stub!!.classToComponent[name] ?: name) - // Clone the JobInfo except component - val builder = JobInfo.Builder(id, component) - .setExtras(extras) - .setTransientExtras(transientExtras) - .setClipData(clipData, clipGrantFlags) - .setRequiredNetwork(requiredNetwork) - .setEstimatedNetworkBytes(estimatedNetworkDownloadBytes, estimatedNetworkUploadBytes) - .setRequiresCharging(isRequireCharging) - .setRequiresDeviceIdle(isRequireDeviceIdle) - .setRequiresBatteryNotLow(isRequireBatteryNotLow) - .setRequiresStorageNotLow(isRequireStorageNotLow) - .also { - triggerContentUris?.let { uris -> - for (uri in uris) - it.addTriggerContentUri(uri) - } - } - .setTriggerContentUpdateDelay(triggerContentUpdateDelay) - .setTriggerContentMaxDelay(triggerContentMaxDelay) - .setImportantWhileForeground(isImportantWhileForeground) - .setPrefetch(isPrefetch) - .setPersisted(isPersisted) - - if (isPeriodic) { - builder.setPeriodic(intervalMillis, flexMillis) - } else { - if (minLatencyMillis > 0) - builder.setMinimumLatency(minLatencyMillis) - if (maxExecutionDelayMillis > 0) - builder.setOverrideDeadline(maxExecutionDelayMillis) - } - if (!isRequireDeviceIdle) - builder.setBackoffCriteria(initialBackoffMillis, backoffPolicy) - - return builder.build() + javaClass.forceGetDeclaredField("service")?.set(this, component) + return this } } diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt index 5266230fb..7d695bc59 100644 --- a/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt @@ -26,6 +26,7 @@ import androidx.annotation.ColorRes import androidx.annotation.DrawableRes import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.ContextCompat +import androidx.core.net.toFile import androidx.core.net.toUri import com.topjohnwu.magisk.Const import com.topjohnwu.magisk.FileProvider @@ -306,3 +307,5 @@ fun Context.unwrap() : Context { } return context } + +fun Uri.writeTo(file: File) = toFile().copyTo(file) diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/XJava.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/XJava.kt index 1f8c7c007..ebdcd6cf3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/extensions/XJava.kt +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/XJava.kt @@ -1,11 +1,11 @@ package com.topjohnwu.magisk.extensions -import android.net.Uri import android.os.Build -import androidx.core.net.toFile import java.io.File import java.io.InputStream import java.io.OutputStream +import java.lang.reflect.Field +import java.lang.reflect.Method import java.util.* import java.util.zip.ZipEntry import java.util.zip.ZipInputStream @@ -19,8 +19,6 @@ fun ZipInputStream.forEach(callback: (ZipEntry) -> Unit) { } } -fun Uri.writeTo(file: File) = toFile().copyTo(file) - fun InputStream.writeTo(file: File) = withStreams(this, file.outputStream()) { reader, writer -> reader.copyTo(writer) } @@ -100,4 +98,33 @@ fun Locale.toLangTag(): String { tag.append('-').append(variant) return tag.toString() } -} \ No newline at end of file +} + +// Reflection hacks + +private val loadClass = ClassLoader::class.java.getMethod("loadClass", String::class.java) +private val getDeclaredMethod = Class::class.java.getMethod("getDeclaredMethod", + String::class.java, arrayOf>()::class.java) +private val getDeclaredField = Class::class.java.getMethod("getDeclaredField", String::class.java) + +fun ClassLoader.forceLoadClass(name: String) = + runCatching { loadClass.invoke(this, name) }.getOrNull() as Class<*>? + +fun Class<*>.forceGetDeclaredMethod(name: String, vararg types: Class<*>) = + (runCatching { getDeclaredMethod.invoke(this, name, *types) }.getOrNull() as Method?)?.also { + it.isAccessible = true + } + +fun Class<*>.forceGetDeclaredField(name: String) = + (runCatching { getDeclaredField.invoke(this, name) }.getOrNull() as Field?)?.also { + it.isAccessible = true + } + +inline fun T.forceGetClass(name: String) = + T::class.java.classLoader?.forceLoadClass(name) + +fun Class<*>.forceGetField(name: String): Field? = + forceGetDeclaredField(name) ?: superclass?.forceGetField(name) + +fun Class<*>.forceGetMethod(name: String, vararg types: Class<*>): Method? = + forceGetDeclaredMethod(name, *types) ?: superclass?.forceGetMethod(name, *types) From 1a38f25bd9912b668e87929bbaae7c88ccaa0980 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 10 Nov 2019 14:59:19 -0500 Subject: [PATCH 44/74] Properly invoke method --- app/src/main/java/com/topjohnwu/magisk/extensions/XJava.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/XJava.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/XJava.kt index ebdcd6cf3..a3aa1cf06 100644 --- a/app/src/main/java/com/topjohnwu/magisk/extensions/XJava.kt +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/XJava.kt @@ -111,7 +111,7 @@ fun ClassLoader.forceLoadClass(name: String) = runCatching { loadClass.invoke(this, name) }.getOrNull() as Class<*>? fun Class<*>.forceGetDeclaredMethod(name: String, vararg types: Class<*>) = - (runCatching { getDeclaredMethod.invoke(this, name, *types) }.getOrNull() as Method?)?.also { + (runCatching { getDeclaredMethod.invoke(this, name, types) }.getOrNull() as Method?)?.also { it.isAccessible = true } From 817cdf711380bb80981524ae99605bdb23ebec3b Mon Sep 17 00:00:00 2001 From: vvb2060 Date: Tue, 29 Jan 2019 04:41:51 +0800 Subject: [PATCH 45/74] fix multiuser owner_managed mode --- .../com/topjohnwu/magisk/model/entity/MagiskPolicy.kt | 6 +++--- native/jni/su/connect.cpp | 8 ++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskPolicy.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskPolicy.kt index cf803c7b7..01bdfcb11 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskPolicy.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskPolicy.kt @@ -38,7 +38,7 @@ fun MagiskPolicy.toMap() = mapOf( fun Map.toPolicy(pm: PackageManager): MagiskPolicy { val uid = get("uid")?.toIntOrNull() ?: -1 val packageName = get("package_name").orEmpty() - val info = pm.getApplicationInfo(packageName, 0) + val info = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES) if (info.uid != uid) throw PackageManager.NameNotFoundException() @@ -59,9 +59,9 @@ fun Map.toPolicy(pm: PackageManager): MagiskPolicy { fun Int.toPolicy(pm: PackageManager, policy: Int = INTERACTIVE): MagiskPolicy { val pkg = pm.getPackagesForUid(this)?.firstOrNull() ?: throw PackageManager.NameNotFoundException() - val info = pm.getApplicationInfo(pkg, 0) + val info = pm.getApplicationInfo(pkg, PackageManager.GET_UNINSTALLED_PACKAGES) return MagiskPolicy( - uid = this, + uid = info.uid, packageName = pkg, policy = policy, applicationInfo = info, diff --git a/native/jni/su/connect.cpp b/native/jni/su/connect.cpp index f2f91b564..6b0ca720e 100644 --- a/native/jni/su/connect.cpp +++ b/native/jni/su/connect.cpp @@ -34,10 +34,6 @@ enum { (info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_USER \ ? info->uid / 100000 : 0) -#define get_uid(info) \ -(info->cfg[SU_MULTIUSER_MODE] == MULTIUSER_MODE_OWNER_MANAGED \ -? info->uid % 100000 : info->uid) - #define get_cmd(to) \ (to.command[0] ? to.command : to.shell[0] ? to.shell : DEFAULT_SHELL) @@ -165,7 +161,7 @@ void app_log(const su_context &ctx) { if (fork_dont_care() == 0) { vector extras; extras.reserve(6); - extras.emplace_back("from.uid", get_uid(ctx.info)); + extras.emplace_back("from.uid", ctx.info->uid); extras.emplace_back("to.uid", ctx.req.uid); extras.emplace_back("pid", ctx.pid); extras.emplace_back("policy", ctx.info->access.policy); @@ -181,7 +177,7 @@ void app_notify(const su_context &ctx) { if (fork_dont_care() == 0) { vector extras; extras.reserve(2); - extras.emplace_back("from.uid", get_uid(ctx.info)); + extras.emplace_back("from.uid", ctx.info->uid); extras.emplace_back("policy", ctx.info->access.policy); exec_cmd("notify", extras, ctx.info); From 2aee0b0be0eb1190b594b3e8f124559803bcbb66 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 11 Nov 2019 15:46:02 -0500 Subject: [PATCH 46/74] Refactor code for handling MagiskDB --- .../topjohnwu/magisk/data/database/LogDao.kt | 8 ++-- .../magisk/data/database/PolicyDao.kt | 9 ++-- .../magisk/data/database/SettingsDao.kt | 9 ++-- .../magisk/data/database/StringDao.kt | 9 ++-- .../magisk/data/database/base/BaseDao.kt | 15 ------ .../data/database/base/DatabaseDefinition.kt | 30 ------------ .../magisk/data/database/base/MagiskQuery.kt | 5 -- .../magisk/data/database/magiskdb/BaseDao.kt | 44 ++++++++++++++++++ .../Query.kt} | 46 ++++++------------- .../magisk/data/repository/LogRepository.kt | 9 ++-- .../data/repository/MagiskRepository.kt | 3 +- .../topjohnwu/magisk/ui/hide/HideViewModel.kt | 8 ++-- 12 files changed, 89 insertions(+), 106 deletions(-) delete mode 100644 app/src/main/java/com/topjohnwu/magisk/data/database/base/BaseDao.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/data/database/base/DatabaseDefinition.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/data/database/base/MagiskQuery.kt create mode 100644 app/src/main/java/com/topjohnwu/magisk/data/database/magiskdb/BaseDao.kt rename app/src/main/java/com/topjohnwu/magisk/data/database/{base/MagiskQueryBuilder.kt => magiskdb/Query.kt} (76%) diff --git a/app/src/main/java/com/topjohnwu/magisk/data/database/LogDao.kt b/app/src/main/java/com/topjohnwu/magisk/data/database/LogDao.kt index 5e6bfb782..ae7e5d6b9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/data/database/LogDao.kt +++ b/app/src/main/java/com/topjohnwu/magisk/data/database/LogDao.kt @@ -1,6 +1,6 @@ package com.topjohnwu.magisk.data.database -import com.topjohnwu.magisk.data.database.base.* +import com.topjohnwu.magisk.data.database.magiskdb.* import com.topjohnwu.magisk.model.entity.MagiskLog import com.topjohnwu.magisk.model.entity.toLog import com.topjohnwu.magisk.model.entity.toMap @@ -8,7 +8,7 @@ import java.util.concurrent.TimeUnit class LogDao : BaseDao() { - override val table = DatabaseDefinition.Table.LOG + override val table = Table.LOG fun deleteOutdated( suTimeout: Long = TimeUnit.DAYS.toMillis(14) @@ -18,7 +18,7 @@ class LogDao : BaseDao() { } }.ignoreElement() - fun deleteAll() = query {}.ignoreElement() + fun deleteAll() = query().ignoreElement() fun fetchAll() = query { - orderBy("time", Order.DESC) - }.flattenAsFlowable { it } - .map { it.toLog() } - .toList() - - fun put(log: MagiskLog) = query { - values(log.toMap()) - }.ignoreElement() - -} diff --git a/app/src/main/java/com/topjohnwu/magisk/data/database/SuLogDao.kt b/app/src/main/java/com/topjohnwu/magisk/data/database/SuLogDao.kt new file mode 100644 index 000000000..e422fc778 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/data/database/SuLogDao.kt @@ -0,0 +1,36 @@ +package com.topjohnwu.magisk.data.database + +import androidx.room.* +import com.topjohnwu.magisk.model.entity.MagiskLog +import io.reactivex.Completable +import io.reactivex.Single +import java.util.* + +@Database(version = 1, entities = [MagiskLog::class]) +abstract class SuLogDatabase : RoomDatabase() { + + abstract fun suLogDao(): SuLogDao +} + +@Dao +abstract class SuLogDao(private val db: SuLogDatabase) { + + private val twoWeeksAgo = + Calendar.getInstance().apply { add(Calendar.WEEK_OF_YEAR, -2) }.timeInMillis + + fun deleteAll() = Completable.fromAction { db.clearAllTables() } + + fun fetchAll() = deleteOutdated().andThen(fetch()) + + @Query("SELECT * FROM logs ORDER BY time DESC") + protected abstract fun fetch(): Single> + + @Insert + abstract fun insert(log: MagiskLog): Completable + + @Query("DELETE FROM logs WHERE time < :timeout") + protected abstract fun deleteOutdated( + timeout: Long = twoWeeksAgo + ): Completable + +} diff --git a/app/src/main/java/com/topjohnwu/magisk/data/repository/LogRepository.kt b/app/src/main/java/com/topjohnwu/magisk/data/repository/LogRepository.kt index b39f52dee..2f0a3eaba 100644 --- a/app/src/main/java/com/topjohnwu/magisk/data/repository/LogRepository.kt +++ b/app/src/main/java/com/topjohnwu/magisk/data/repository/LogRepository.kt @@ -1,39 +1,36 @@ package com.topjohnwu.magisk.data.repository import com.topjohnwu.magisk.Const -import com.topjohnwu.magisk.data.database.LogDao -import com.topjohnwu.magisk.extensions.toSingle +import com.topjohnwu.magisk.data.database.SuLogDao import com.topjohnwu.magisk.model.entity.MagiskLog import com.topjohnwu.magisk.model.entity.WrappedMagiskLog import com.topjohnwu.superuser.Shell +import io.reactivex.Completable import io.reactivex.Single import java.util.concurrent.TimeUnit class LogRepository( - private val logDao: LogDao + private val logDao: SuLogDao ) { - fun fetchLogs() = logDao.fetchAll() - .map { it.sortByDescending { it.date.time }; it } - .map { it.wrap() } + fun fetchLogs() = logDao.fetchAll().map { it.wrap() } fun fetchMagiskLogs() = Single.fromCallable { Shell.su("tail -n 5000 ${Const.MAGISK_LOG}").exec().out - }.filter { it.isNotEmpty() } + }.flattenAsFlowable { it }.filter { it.isNotEmpty() } fun clearLogs() = logDao.deleteAll() - fun clearOutdated() = logDao.deleteOutdated() - fun clearMagiskLogs() = Shell.su("echo -n > " + Const.MAGISK_LOG) - .toSingle() - .map { it.exec() } + fun clearMagiskLogs() = Completable.fromAction { + Shell.su("echo -n > ${Const.MAGISK_LOG}").exec() + } - fun put(log: MagiskLog) = logDao.put(log) + fun insert(log: MagiskLog) = logDao.insert(log) private fun List.wrap(): List { val day = TimeUnit.DAYS.toMillis(1) - return groupBy { it.date.time / day } + return groupBy { it.time / day } .map { WrappedMagiskLog(it.key * day, it.value) } } diff --git a/app/src/main/java/com/topjohnwu/magisk/di/DatabaseModule.kt b/app/src/main/java/com/topjohnwu/magisk/di/DatabaseModule.kt index bf3b566c9..6f1168687 100644 --- a/app/src/main/java/com/topjohnwu/magisk/di/DatabaseModule.kt +++ b/app/src/main/java/com/topjohnwu/magisk/di/DatabaseModule.kt @@ -8,15 +8,20 @@ import org.koin.dsl.module val databaseModule = module { - single { LogDao() } single { PolicyDao(get()) } single { SettingsDao() } single { StringDao() } single { createRepoDatabase(get()).repoDao() } + single { createSuLogDatabase(get(Protected)).suLogDao() } single { RepoUpdater(get(), get()) } } fun createRepoDatabase(context: Context) = - Room.databaseBuilder(context, RepoDatabase::class.java, "repo.db") - .fallbackToDestructiveMigration() - .build() + Room.databaseBuilder(context, RepoDatabase::class.java, "repo.db") + .fallbackToDestructiveMigration() + .build() + +fun createSuLogDatabase(context: Context) = + Room.databaseBuilder(context, SuLogDatabase::class.java, "sulogs.db") + .fallbackToDestructiveMigration() + .build() diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskLog.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskLog.kt index 85f815b08..fd72d8869 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskLog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskLog.kt @@ -1,10 +1,14 @@ package com.topjohnwu.magisk.model.entity +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.PrimaryKey +import com.topjohnwu.magisk.extensions.now import com.topjohnwu.magisk.extensions.timeFormatTime import com.topjohnwu.magisk.extensions.toTime import com.topjohnwu.magisk.model.entity.MagiskPolicy.Companion.ALLOW -import java.util.* +@Entity(tableName = "logs") data class MagiskLog( val fromUid: Int, val toUid: Int, @@ -13,9 +17,10 @@ data class MagiskLog( val appName: String, val command: String, val action: Boolean, - val date: Date + val time: Long = -1 ) { - val timeString = date.time.toTime(timeFormatTime) + @PrimaryKey(autoGenerate = true) var id: Int = 0 + @Ignore val timeString = time.toTime(timeFormatTime) } data class WrappedMagiskLog( @@ -23,35 +28,8 @@ data class WrappedMagiskLog( val items: List ) -fun Map.toLog(): MagiskLog { - return MagiskLog( - fromUid = get("from_uid")?.toIntOrNull() ?: -1, - toUid = get("to_uid")?.toIntOrNull() ?: -1, - fromPid = get("from_pid")?.toIntOrNull() ?: -1, - packageName = get("package_name").orEmpty(), - appName = get("app_name").orEmpty(), - command = get("command").orEmpty(), - action = get("action")?.toIntOrNull() != 0, - date = get("time")?.toLongOrNull()?.toDate() ?: Date() - ) -} - -fun Long.toDate() = Date(this) - -fun MagiskLog.toMap() = mapOf( - "from_uid" to fromUid, - "to_uid" to toUid, - "from_pid" to fromPid, - "package_name" to packageName, - "app_name" to appName, - "command" to command, - "action" to action, - "time" to date.time -) - fun MagiskPolicy.toLog( toUid: Int, fromPid: Int, - command: String, - date: Date -) = MagiskLog(uid, toUid, fromPid, packageName, appName, command, policy == ALLOW, date) + command: String +) = MagiskLog(uid, toUid, fromPid, packageName, appName, command, policy == ALLOW, now) diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt index ad278ef80..57b398dae 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt @@ -50,13 +50,7 @@ class LogItemEntryRvItem(val item: MagiskLog) : ComparableRvItem() { override fun contentSameAs(other: MagiskLogRvItem): Boolean = false override fun itemSameAs(other: MagiskLogRvItem): Boolean = false -} \ No newline at end of file +} diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt index 21beec611..d3d50e3fd 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt @@ -104,7 +104,6 @@ class LogViewModel( .add() private fun clearMagiskLogs(callback: () -> Unit) = logRepo.clearMagiskLogs() - .ignoreElement() .doOnComplete(callback) .subscribeK { SnackbarEvent(R.string.logs_cleared).publish() } .add() @@ -115,8 +114,7 @@ class LogViewModel( .toList() private fun fetchMagiskLog() = logRepo.fetchMagiskLogs() - .flattenAsFlowable { it } .map { ConsoleRvItem(it) } .toList() -} \ No newline at end of file +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/SuHandler.kt b/app/src/main/java/com/topjohnwu/magisk/utils/SuHandler.kt index ba6521c32..58c22bb42 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/SuHandler.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/SuHandler.kt @@ -18,7 +18,6 @@ import com.topjohnwu.magisk.model.entity.toPolicy import com.topjohnwu.magisk.ui.surequest.SuRequestActivity import com.topjohnwu.superuser.Shell import timber.log.Timber -import java.util.* object SuHandler : ProviderCallHandler { @@ -101,12 +100,11 @@ object SuHandler : ProviderCallHandler { val log = policy.toLog( toUid = toUid, fromPid = pid, - command = command, - date = Date() + command = command ) val logRepo = get() - logRepo.put(log).subscribeK(onError = { Timber.e(it) }) + logRepo.insert(log).subscribeK(onError = { Timber.e(it) }) } private fun handleNotify(context: Context, data: Bundle) { diff --git a/native/jni/core/db.cpp b/native/jni/core/db.cpp index bd451b64d..ce3479f7b 100644 --- a/native/jni/core/db.cpp +++ b/native/jni/core/db.cpp @@ -10,7 +10,7 @@ #include #include -#define DB_VERSION 9 +#define DB_VERSION 10 using namespace std; @@ -72,13 +72,6 @@ static char *open_and_init_db(sqlite3 *&db) { "logging INT, notification INT, PRIMARY KEY(uid))", nullptr, nullptr, &err); err_ret(err); - // Logs - sqlite3_exec(db, - "CREATE TABLE IF NOT EXISTS logs " - "(from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, " - "to_uid INT, action INT, time INT, command TEXT)", - nullptr, nullptr, &err); - err_ret(err); // Settings sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS settings " @@ -144,6 +137,12 @@ static char *open_and_init_db(sqlite3 *&db) { ver = 9; upgrade = true; } + if (ver < 10) { + sqlite3_exec(db, "DROP TABLE logs", nullptr, nullptr, &err); + err_ret(err); + ver = 10; + upgrade = true; + } if (upgrade) { // Set version From b29f0ca4d1504cf2d9358ed098bc0e456ec56195 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 14 Nov 2019 05:42:39 -0500 Subject: [PATCH 57/74] Support using BiometricPrompt --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 7 +- .../main/java/com/topjohnwu/magisk/Config.kt | 27 ++-- .../magisk/base/viewmodel/BaseViewModel.kt | 4 +- .../magisk/data/repository/DBConfig.kt | 32 ++--- .../magisk/model/events/ViewEvents.kt | 4 +- .../magisk/ui/settings/SettingsFragment.kt | 21 ++- .../magisk/ui/superuser/SuperuserViewModel.kt | 13 +- .../magisk/ui/surequest/SuRequestActivity.kt | 2 + .../magisk/ui/surequest/SuRequestViewModel.kt | 43 ++----- .../topjohnwu/magisk/utils/BiometricHelper.kt | 60 +++++++++ .../magisk/utils/FingerprintHelper.kt | 121 ------------------ .../view/dialogs/FingerprintAuthDialog.kt | 88 ------------- app/src/main/res/layout/activity_request.xml | 12 +- app/src/main/res/values-ar/strings.xml | 5 - app/src/main/res/values-az/strings.xml | 5 - app/src/main/res/values-bg/strings.xml | 5 - app/src/main/res/values-ca/strings.xml | 5 - app/src/main/res/values-cs/strings.xml | 5 - app/src/main/res/values-de/strings.xml | 5 - app/src/main/res/values-es/strings.xml | 5 - app/src/main/res/values-et/strings.xml | 5 - app/src/main/res/values-fr/strings.xml | 5 - app/src/main/res/values-hi/strings.xml | 5 - app/src/main/res/values-in/strings.xml | 5 - app/src/main/res/values-it/strings.xml | 5 - app/src/main/res/values-ja/strings.xml | 5 - app/src/main/res/values-ko/strings.xml | 5 - app/src/main/res/values-lt/strings.xml | 4 - app/src/main/res/values-mk/strings.xml | 5 - app/src/main/res/values-nb/strings.xml | 5 - app/src/main/res/values-nl/strings.xml | 4 - app/src/main/res/values-pl/strings.xml | 5 - app/src/main/res/values-pt-rBR/strings.xml | 4 - app/src/main/res/values-ro/strings.xml | 5 - app/src/main/res/values-ru/strings.xml | 5 - app/src/main/res/values-sk/strings.xml | 5 - app/src/main/res/values-th/strings.xml | 5 - app/src/main/res/values-tr/strings.xml | 5 - app/src/main/res/values-uk/strings.xml | 5 - app/src/main/res/values-vi/strings.xml | 9 +- app/src/main/res/values-zh-rCN/strings.xml | 5 - app/src/main/res/values-zh-rTW/strings.xml | 5 - app/src/main/res/values/strings.xml | 10 +- app/src/main/res/xml/app_settings.xml | 8 +- .../java/com/topjohnwu/magisk/DynAPK.java | 2 +- stub/src/main/AndroidManifest.xml | 1 + 47 files changed, 146 insertions(+), 456 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/BiometricHelper.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/FingerprintHelper.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/view/dialogs/FingerprintAuthDialog.kt diff --git a/app/build.gradle b/app/build.gradle index 6cd88a0d6..8270643fe 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,6 +120,7 @@ dependencies { implementation "androidx.navigation:navigation-fragment-ktx:${vNav}" implementation "androidx.navigation:navigation-ui-ktx:${vNav}" + implementation 'androidx.biometric:biometric:1.0.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03' implementation 'androidx.preference:preference:1.1.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 32de4b85e..46ed3d6da 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,7 +7,6 @@ - + + + diff --git a/app/src/main/java/com/topjohnwu/magisk/Config.kt b/app/src/main/java/com/topjohnwu/magisk/Config.kt index d065a86be..845521236 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Config.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Config.kt @@ -11,9 +11,8 @@ import com.topjohnwu.magisk.data.repository.DBConfig import com.topjohnwu.magisk.di.Protected import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.inject -import com.topjohnwu.magisk.extensions.packageName import com.topjohnwu.magisk.model.preference.PreferenceModel -import com.topjohnwu.magisk.utils.FingerprintHelper +import com.topjohnwu.magisk.utils.BiometricHelper import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.io.SuFile @@ -32,7 +31,7 @@ object Config : PreferenceModel, DBConfig { const val ROOT_ACCESS = "root_access" const val SU_MULTIUSER_MODE = "multiuser_mode" const val SU_MNT_NS = "mnt_ns" - const val SU_FINGERPRINT = "su_fingerprint" + const val SU_BIOMETRIC = "su_biometric" const val SU_MANAGER = "requester" const val KEYSTORE = "keystore" @@ -127,7 +126,7 @@ object Config : PreferenceModel, DBConfig { var rootMode by dbSettings(Key.ROOT_ACCESS, Value.ROOT_ACCESS_APPS_AND_ADB) var suMntNamespaceMode by dbSettings(Key.SU_MNT_NS, Value.NAMESPACE_MODE_REQUESTER) var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY) - var suFingerprint by dbSettings(Key.SU_FINGERPRINT, false) + var suBiometric by dbSettings(Key.SU_BIOMETRIC, false) var suManager by dbStrings(Key.SU_MANAGER, "", true) var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true) @@ -135,9 +134,18 @@ object Config : PreferenceModel, DBConfig { val downloadDirectory get() = Utils.ensureDownloadPath(downloadPath) ?: get().getExternalFilesDir(null)!! - fun initialize() = prefs.edit { + private const val SU_FINGERPRINT = "su_fingerprint" + + fun initialize() = prefs.also { + if (it.getBoolean(SU_FINGERPRINT, false)) { + suBiometric = true + } + }.edit { parsePrefs(this) + // Legacy stuff + remove(SU_FINGERPRINT) + // Get actual state putBoolean(Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists()) @@ -145,7 +153,7 @@ object Config : PreferenceModel, DBConfig { putString(Key.ROOT_ACCESS, rootMode.toString()) putString(Key.SU_MNT_NS, suMntNamespaceMode.toString()) putString(Key.SU_MULTIUSER_MODE, suMultiuserMode.toString()) - putBoolean(Key.SU_FINGERPRINT, FingerprintHelper.useFingerprint()) + putBoolean(Key.SU_BIOMETRIC, BiometricHelper.isEnabled) }.also { if (!prefs.contains(Key.UPDATE_CHANNEL)) prefs.edit().putString(Key.UPDATE_CHANNEL, defaultChannel.toString()).apply() @@ -154,7 +162,7 @@ object Config : PreferenceModel, DBConfig { private fun parsePrefs(editor: SharedPreferences.Editor) = editor.apply { val config = SuFile.open("/data/adb", Const.MANAGER_CONFIGS) if (config.exists()) runCatching { - val input = SuFileInputStream(config).buffered() + val input = SuFileInputStream(config) val parser = Xml.newPullParser() parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) parser.setInput(input, "UTF-8") @@ -205,9 +213,10 @@ object Config : PreferenceModel, DBConfig { fun export() { // Flush prefs to disk prefs.edit().commit() + val context = get(Protected) val xml = File( - "${get(Protected).filesDir.parent}/shared_prefs", - "${packageName}_preferences.xml" + "${context.filesDir.parent}/shared_prefs", + "${context.packageName}_preferences.xml" ) Shell.su("cat $xml > /data/adb/${Const.MANAGER_CONFIGS}").exec() } diff --git a/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/BaseViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/BaseViewModel.kt index 987f1bf6d..56338b1ce 100644 --- a/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/BaseViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/base/viewmodel/BaseViewModel.kt @@ -1,6 +1,6 @@ package com.topjohnwu.magisk.base.viewmodel -import android.app.Activity +import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.extensions.doOnSubscribeUi import com.topjohnwu.magisk.model.events.BackPressEvent import com.topjohnwu.magisk.model.events.PermissionEvent @@ -21,7 +21,7 @@ abstract class BaseViewModel( } } - fun withView(action: Activity.() -> Unit) { + fun withView(action: BaseActivity<*, *>.() -> Unit) { ViewActionEvent(action).publish() } diff --git a/app/src/main/java/com/topjohnwu/magisk/data/repository/DBConfig.kt b/app/src/main/java/com/topjohnwu/magisk/data/repository/DBConfig.kt index 1129a18cd..0634ee1e6 100644 --- a/app/src/main/java/com/topjohnwu/magisk/data/repository/DBConfig.kt +++ b/app/src/main/java/com/topjohnwu/magisk/data/repository/DBConfig.kt @@ -29,8 +29,8 @@ interface DBConfig { } class DBSettingsValue( - private val name: String, - private val default: Int + private val name: String, + private val default: Int ) : ReadWriteProperty { private var value: Int? = null @@ -47,29 +47,29 @@ class DBSettingsValue( this.value = value } thisRef.settingsDao.put(name, value) - .subscribeOn(Schedulers.io()) - .subscribe() + .subscribeOn(Schedulers.io()) + .subscribe() } } class DBBoolSettings( - name: String, - default: Boolean + name: String, + default: Boolean ) : ReadWriteProperty { val base = DBSettingsValue(name, if (default) 1 else 0) - override fun getValue(thisRef: DBConfig, property: KProperty<*>): Boolean - = base.getValue(thisRef, property) != 0 + override fun getValue(thisRef: DBConfig, property: KProperty<*>): Boolean = + base.getValue(thisRef, property) != 0 override fun setValue(thisRef: DBConfig, property: KProperty<*>, value: Boolean) = - base.setValue(thisRef, property, if (value) 1 else 0) + base.setValue(thisRef, property, if (value) 1 else 0) } class DBStringsValue( - private val name: String, - private val default: String, - private val sync: Boolean + private val name: String, + private val default: String, + private val sync: Boolean ) : ReadWriteProperty { private var value: String? = null @@ -90,16 +90,16 @@ class DBStringsValue( thisRef.stringDao.delete(name).blockingAwait() } else { thisRef.stringDao.delete(name) - .subscribeOn(Schedulers.io()) - .subscribe() + .subscribeOn(Schedulers.io()) + .subscribe() } } else { if (sync) { thisRef.stringDao.put(name, value).blockingAwait() } else { thisRef.stringDao.put(name, value) - .subscribeOn(Schedulers.io()) - .subscribe() + .subscribeOn(Schedulers.io()) + .subscribe() } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt index f8e273bd7..ef10c39c5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt @@ -1,6 +1,6 @@ package com.topjohnwu.magisk.model.events -import android.app.Activity +import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.model.entity.module.Repo import io.reactivex.subjects.PublishSubject @@ -28,7 +28,7 @@ class EnvFixEvent : ViewEvent() class UpdateSafetyNetEvent : ViewEvent() -class ViewActionEvent(val action: Activity.() -> Unit) : ViewEvent() +class ViewActionEvent(val action: BaseActivity<*, *>.() -> Unit) : ViewEvent() class OpenFilePickerEvent : ViewEvent() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt index fdcac23a5..88239ff4c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt @@ -24,7 +24,6 @@ import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.observer.Observer import com.topjohnwu.magisk.utils.* -import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog import com.topjohnwu.superuser.Shell import io.reactivex.Completable import org.koin.android.ext.android.inject @@ -61,7 +60,7 @@ class SettingsFragment : BasePreferenceFragment() { multiuserConfig = findPreference(Config.Key.SU_MULTIUSER_MODE)!! nsConfig = findPreference(Config.Key.SU_MNT_NS)!! val reauth = findPreference(Config.Key.SU_REAUTH)!! - val fingerprint = findPreference(Config.Key.SU_FINGERPRINT)!! + val biometric = findPreference(Config.Key.SU_BIOMETRIC)!! val generalCatagory = findPreference("general")!! val magiskCategory = findPreference("magisk")!! val suCategory = findPreference("superuser")!! @@ -88,11 +87,11 @@ class SettingsFragment : BasePreferenceFragment() { suCategory.removePreference(reauth) } - // Disable fingerprint option if not possible - if (!FingerprintHelper.canUseFingerprint()) { - fingerprint.isEnabled = false - fingerprint.isChecked = false - fingerprint.setSummary(R.string.disable_fingerprint) + // Disable biometric option if not possible + if (!BiometricHelper.isSupported) { + biometric.isEnabled = false + biometric.isChecked = false + biometric.setSummary(R.string.no_biometric) } if (Const.USER_ID == 0 && Info.isConnected.value && Shell.rootAccess()) { @@ -208,13 +207,13 @@ class SettingsFragment : BasePreferenceFragment() { override fun onPreferenceTreeClick(preference: Preference): Boolean { when (preference.key) { - Config.Key.SU_FINGERPRINT -> { + Config.Key.SU_BIOMETRIC -> { val checked = (preference as SwitchPreferenceCompat).isChecked preference.isChecked = !checked - FingerprintAuthDialog(requireActivity()) { + BiometricHelper.authenticate(requireActivity()) { preference.isChecked = checked - Config.suFingerprint = checked - }.show() + Config.suBiometric = checked + } } } return true diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt index a8fa02f18..57f24bcc5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt @@ -15,11 +15,10 @@ import com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem import com.topjohnwu.magisk.model.events.PolicyEnableEvent import com.topjohnwu.magisk.model.events.PolicyUpdateEvent import com.topjohnwu.magisk.model.events.SnackbarEvent +import com.topjohnwu.magisk.utils.BiometricHelper import com.topjohnwu.magisk.utils.DiffObservableList -import com.topjohnwu.magisk.utils.FingerprintHelper import com.topjohnwu.magisk.utils.RxBus import com.topjohnwu.magisk.view.dialogs.CustomAlertDialog -import com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog import io.reactivex.Single import io.reactivex.disposables.Disposable import me.tatarka.bindingcollectionadapter2.ItemBinding @@ -83,8 +82,8 @@ class SuperuserViewModel( .add() withView { - if (FingerprintHelper.useFingerprint()) { - FingerprintAuthDialog(this) { updateState() }.show() + if (BiometricHelper.isEnabled) { + BiometricHelper.authenticate(this) { updateState() } } else { CustomAlertDialog(this) .setTitle(R.string.su_revoke_title) @@ -131,12 +130,12 @@ class SuperuserViewModel( .add() } - if (FingerprintHelper.useFingerprint()) { + if (BiometricHelper.isEnabled) { withView { - FingerprintAuthDialog(this, { updateState() }, { + BiometricHelper.authenticate(this, onError = { ignoreNext = item item.isEnabled.toggle() - }).show() + }) { updateState() } } } else { updateState() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt index 57d99e5de..2fdac91ab 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt @@ -9,6 +9,7 @@ import com.topjohnwu.magisk.R import com.topjohnwu.magisk.base.BaseActivity import com.topjohnwu.magisk.databinding.ActivityRequestBinding import com.topjohnwu.magisk.model.events.DieEvent +import com.topjohnwu.magisk.model.events.ViewActionEvent import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.utils.SuHandler import com.topjohnwu.magisk.utils.SuHandler.REQUEST @@ -56,6 +57,7 @@ open class SuRequestActivity : BaseActivity event.action(this) is DieEvent -> finish() } } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt index 20bd9ceea..c9971d738 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt @@ -5,7 +5,6 @@ import android.content.SharedPreferences import android.content.pm.PackageManager import android.content.res.Resources import android.graphics.drawable.Drawable -import android.hardware.fingerprint.FingerprintManager import android.os.CountDownTimer import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Config @@ -19,8 +18,8 @@ import com.topjohnwu.magisk.model.entity.MagiskPolicy import com.topjohnwu.magisk.model.entity.recycler.SpinnerRvItem import com.topjohnwu.magisk.model.entity.toPolicy import com.topjohnwu.magisk.model.events.DieEvent +import com.topjohnwu.magisk.utils.BiometricHelper import com.topjohnwu.magisk.utils.DiffObservableList -import com.topjohnwu.magisk.utils.FingerprintHelper import com.topjohnwu.magisk.utils.KObservableField import com.topjohnwu.magisk.utils.SuConnector import me.tatarka.bindingcollectionadapter2.BindingListViewAdapter @@ -43,7 +42,6 @@ class SuRequestViewModel( val denyText = KObservableField(resources.getString(R.string.deny)) val warningText = KObservableField(resources.getString(R.string.su_warning)) - val canUseFingerprint = KObservableField(FingerprintHelper.useFingerprint()) val selectedItemPosition = KObservableField(0) private val items = DiffObservableList(ComparableRvItem.callback) @@ -68,8 +66,16 @@ class SuRequestViewModel( } fun grantPressed() { - handleAction(MagiskPolicy.ALLOW) - timer.cancel() + cancelTimer() + if (BiometricHelper.isEnabled) { + withView { + BiometricHelper.authenticate(this) { + handleAction(MagiskPolicy.ALLOW) + } + } + } else { + handleAction(MagiskPolicy.ALLOW) + } } fun denyPressed() { @@ -137,13 +143,6 @@ class SuRequestViewModel( } timer.start() cancelTasks.add { cancelTimer() } - - if (canUseFingerprint.value) - runCatching { - val helper = SuFingerprint() - helper.authenticate() - cancelTasks.add { helper.cancel() } - } } private fun handleAction() { @@ -182,24 +181,4 @@ class SuRequestViewModel( } } - private inner class SuFingerprint @Throws(Exception::class) - internal constructor() : FingerprintHelper() { - - override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { - warningText.value = errString - } - - override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) { - warningText.value = helpString - } - - override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) { - handleAction(MagiskPolicy.ALLOW) - } - - override fun onAuthenticationFailed() { - warningText.value = resources.getString(R.string.auth_fail) - } - } - } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/BiometricHelper.kt b/app/src/main/java/com/topjohnwu/magisk/utils/BiometricHelper.kt new file mode 100644 index 000000000..ac6c0cfea --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/BiometricHelper.kt @@ -0,0 +1,60 @@ +package com.topjohnwu.magisk.utils + +import androidx.biometric.BiometricManager +import androidx.biometric.BiometricPrompt +import androidx.fragment.app.FragmentActivity +import com.topjohnwu.magisk.Config +import com.topjohnwu.magisk.R +import com.topjohnwu.superuser.internal.UiThreadHandler +import org.koin.core.KoinComponent +import org.koin.core.get + +object BiometricHelper: KoinComponent { + + private val mgr by lazy { BiometricManager.from(get()) } + + val isSupported get() = when (mgr.canAuthenticate()) { + BiometricManager.BIOMETRIC_SUCCESS -> true + else -> false + } + + val isEnabled: Boolean get() { + val enabled = Config.suBiometric + if (enabled && !isSupported) { + Config.suBiometric = false + return false + } + return enabled + } + + fun authenticate( + activity: FragmentActivity, + onError: () -> Unit = {}, + onSuccess: () -> Unit): BiometricPrompt { + val prompt = BiometricPrompt(activity, + { cmd: Runnable -> UiThreadHandler.run(cmd) }, + object : BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + onError() + } + + override fun onAuthenticationFailed() { + onError() + } + + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + onSuccess() + } + } + ) + val info = BiometricPrompt.PromptInfo.Builder() + .setConfirmationRequired(true) + .setDeviceCredentialAllowed(false) + .setTitle(activity.getString(R.string.authenticate)) + .setNegativeButtonText(activity.getString(android.R.string.cancel)) + .build() + prompt.authenticate(info) + return prompt + } + +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/FingerprintHelper.kt b/app/src/main/java/com/topjohnwu/magisk/utils/FingerprintHelper.kt deleted file mode 100644 index e174f26f9..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/FingerprintHelper.kt +++ /dev/null @@ -1,121 +0,0 @@ -package com.topjohnwu.magisk.utils - -import android.annotation.TargetApi -import android.app.KeyguardManager -import android.content.Context -import android.hardware.fingerprint.FingerprintManager -import android.os.Build -import android.os.CancellationSignal -import android.security.keystore.KeyGenParameterSpec -import android.security.keystore.KeyProperties -import com.topjohnwu.magisk.Config -import com.topjohnwu.magisk.extensions.get -import com.topjohnwu.magisk.extensions.inject -import java.security.KeyStore -import javax.crypto.Cipher -import javax.crypto.KeyGenerator -import javax.crypto.SecretKey - -@TargetApi(Build.VERSION_CODES.M) -abstract class FingerprintHelper @Throws(Exception::class) -protected constructor() { - - private val manager: FingerprintManager? - private val cipher: Cipher - private var cancel: CancellationSignal? = null - private val context: Context by inject() - - init { - val keyStore = KeyStore.getInstance("AndroidKeyStore") - manager = context.getSystemService(FingerprintManager::class.java) - cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" - + KeyProperties.BLOCK_MODE_CBC + "/" - + KeyProperties.ENCRYPTION_PADDING_PKCS7) - keyStore.load(null) - var key = keyStore.getKey(SU_KEYSTORE_KEY, null) as SecretKey? ?: generateKey() - runCatching { - cipher.init(Cipher.ENCRYPT_MODE, key) - }.onFailure { - // Only happens on Marshmallow - key = generateKey() - cipher.init(Cipher.ENCRYPT_MODE, key) - } - } - - abstract fun onAuthenticationError(errorCode: Int, errString: CharSequence) - - abstract fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) - - abstract fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) - - abstract fun onAuthenticationFailed() - - fun authenticate() { - cancel = CancellationSignal() - val cryptoObject = FingerprintManager.CryptoObject(cipher) - manager!!.authenticate(cryptoObject, cancel, 0, Callback(), null) - } - - fun cancel() { - if (cancel != null) - cancel!!.cancel() - } - - @Throws(Exception::class) - private fun generateKey(): SecretKey { - val keygen = KeyGenerator - .getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore") - val builder = KeyGenParameterSpec.Builder( - SU_KEYSTORE_KEY, - KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_CBC) - .setUserAuthenticationRequired(true) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - builder.setInvalidatedByBiometricEnrollment(false) - } - keygen.init(builder.build()) - return keygen.generateKey() - } - - private inner class Callback : FingerprintManager.AuthenticationCallback() { - override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { - this@FingerprintHelper.onAuthenticationError(errorCode, errString) - } - - override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) { - this@FingerprintHelper.onAuthenticationHelp(helpCode, helpString) - } - - override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) { - this@FingerprintHelper.onAuthenticationSucceeded(result) - } - - override fun onAuthenticationFailed() { - this@FingerprintHelper.onAuthenticationFailed() - } - } - - companion object { - private const val SU_KEYSTORE_KEY = "su_key" - - fun useFingerprint(): Boolean { - var fp = Config.suFingerprint - if (fp && !canUseFingerprint()) { - Config.suFingerprint = false - fp = false - } - return fp - } - - fun canUseFingerprint(context: Context = get()): Boolean { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) - return false - val km = context.getSystemService(KeyguardManager::class.java) - val fm = context.getSystemService(FingerprintManager::class.java) - return km?.isKeyguardSecure ?: false && - fm != null && fm.isHardwareDetected && fm.hasEnrolledFingerprints() - } - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/FingerprintAuthDialog.kt b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/FingerprintAuthDialog.kt deleted file mode 100644 index a27d599de..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/FingerprintAuthDialog.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.topjohnwu.magisk.view.dialogs - -import android.annotation.TargetApi -import android.app.Activity -import android.graphics.Color -import android.hardware.fingerprint.FingerprintManager -import android.os.Build -import android.view.Gravity -import android.widget.Toast -import androidx.appcompat.app.AlertDialog -import androidx.core.content.ContextCompat -import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.utils.FingerprintHelper -import com.topjohnwu.magisk.utils.Utils - -@TargetApi(Build.VERSION_CODES.M) -class FingerprintAuthDialog(activity: Activity, private val callback: () -> Unit) - : CustomAlertDialog(activity) { - - private var failureCallback: (() -> Unit)? = null - private var helper: DialogFingerprintHelper? = null - - init { - val fingerprint = ContextCompat.getDrawable(activity, R.drawable.ic_fingerprint) - fingerprint?.setBounds(0, 0, Utils.dpInPx(50), Utils.dpInPx(50)) - val theme = activity.theme - val ta = theme.obtainStyledAttributes(intArrayOf(R.attr.imageColorTint)) - fingerprint?.setTint(ta.getColor(0, Color.GRAY)) - ta.recycle() - binding.message.setCompoundDrawables(null, null, null, fingerprint) - binding.message.compoundDrawablePadding = Utils.dpInPx(20) - binding.message.gravity = Gravity.CENTER - setMessage(R.string.auth_fingerprint) - setNegativeButton(android.R.string.cancel) { _, _ -> - helper?.cancel() - failureCallback?.invoke() - } - setOnCancelListener { - helper?.cancel() - failureCallback?.invoke() - } - runCatching { - helper = DialogFingerprintHelper() - } - - } - - constructor(activity: Activity, onSuccess: () -> Unit, onFailure: () -> Unit) - : this(activity, onSuccess) { - failureCallback = onFailure - } - - override fun show(): AlertDialog { - return create().apply { - if (helper == null) { - dismiss() - Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT) - } else { - helper?.authenticate() - show() - } - } - } - - internal inner class DialogFingerprintHelper @Throws(Exception::class) - constructor() : FingerprintHelper() { - - override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { - binding.message.setTextColor(Color.RED) - binding.message.text = errString - } - - override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) { - binding.message.setTextColor(Color.RED) - binding.message.text = helpString - } - - override fun onAuthenticationFailed() { - binding.message.setTextColor(Color.RED) - binding.message.setText(R.string.auth_fail) - } - - override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) { - dismiss() - callback() - } - } -} diff --git a/app/src/main/res/layout/activity_request.xml b/app/src/main/res/layout/activity_request.xml index 86257262d..70af73e7d 100644 --- a/app/src/main/res/layout/activity_request.xml +++ b/app/src/main/res/layout/activity_request.xml @@ -129,24 +129,14 @@ - - \ No newline at end of file + diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index e53d65cd8..fbe808ace 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -163,9 +163,6 @@ %1$d ثانية إعادة المصادقة بعد الترقية أعد المصادقة على صلاحيات المستخدم المتميز بعد إجراء ترقيات للتطبيق - استخدام قارئ بصمات الأصابع للسماح بطلبات المستخدم المتميز - تمكين مصادقة البصمة - مصادقة البصمة نمط تعدد المستخدمين مالك الجهاز فقط @@ -182,7 +179,6 @@ تستخدم كافة جلسات العمل للروت مساحة الاسم ذات التركيب العامة سترث جلسات العمل للروت مساحة الأسماء لطالبيها سيكون لكل جلسة عمل للروت مساحة اسم معزولة خاصة بها - لم تُعين بصمات الأصابع أو لا يوجد قارئ بصمات خطأ عند إنشاء مجلد. عليه أن يكون سهلا الوصول إليه من خلال مجلد التخزين للروت و ألا يكون ملفا. @@ -210,7 +206,6 @@ تأكيد لسحب صلاحيات %1$s ? ملاحظة منبثقة بدون - فشل المصادقة PID: %1$d diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 2dbf1e601..3ca98dcb7 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -151,9 +151,6 @@ %1$d saniyə Yüksəltmədən sonra Yenidən İdentifikasiya et Tətbiq yeniləmələridən sonra superuser icazələrini yenidən identifikasiya et - Barmaq İzi İdentifikasiyasını Aç - Barmaq izi oxuyucunu superuser icazələri üçün işlət - Barmaq izini İdentifikasiya et Çox-istifadəçi modu Yalnız cihaz sahibi Cihaz sahibinin idarəçiliyində @@ -168,7 +165,6 @@ Bütün root sessyaları qlobal qoşma namespace\'dən istifadə edir. Root sessyaları soruşulan namespace\'ləri birindən digərinə keçirəcək. Hər bir root sessyasının ayrılmış namespace\'i olacaq. - Barmaq izi təyin edilməyib ya da dəstəklənmir. Superuser Tələbi @@ -195,7 +191,6 @@ %1$s üçün haqları ləğv etməyi təsdiq edirsiniz? Tost Heçnə - İdentifikasiya xətası PID: %1$d diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 10c8b3378..6c920bd84 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -142,9 +142,6 @@ %1$d секунди Повторно запитване след актуализация Повторно запитване за Superuser достъп след актуализация на приложенията. - Superuser права само с пръстов отпечатък - Използване на сензора за пръстови отпечатъци за разрешаване на Superuser достъп. - Удостоверете с пръстов отпечатък. Потребителски достъп Само собственик @@ -161,7 +158,6 @@ Всички сесии с руут достъп използват глобалното именно пространство. Всички сесии с руут достъп наследяват именното пространство на запитващото приложение. Всички сесии с руут достъп имат собствени именни пространства. - Не са добавени пръстови отпечатъци или устройството не поддържа тази функция. Запитване за Superuser достъп @@ -188,7 +184,6 @@ Потвърждавате ли анулирането на настройките за достъп на %1$s? Toast Без - Неуспешна заверка. Целеви UID: %1$d diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 1de2eb50f..ac9eeaf6e 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -166,9 +166,6 @@ %1$d segons Demanar després d\'una actualització Demanar permisos de superusuari novament si una aplicació és actualitzada o reinstal·lada - Autenticació per Empremta Dactilar - Utilitza el sensor d\'Empremta Dactilar per permetre les sol·licituds de superusuari - Autenticar Emprempta Digital Mode Multiusuari Només Administrador del Dispositiu @@ -185,7 +182,6 @@ Totes les sessions d\'arrel utilitzen el suport Namespace Global Les sessions d\'arrel heretaran les peticiones Namespace Totes les sessions d\'arrel tindran la seva pròpia Namespace - No s\'han establert empremtes dactilars o no existeix el suport del dispositiu Error al crear la carpeta. El directori ha de ser accesible desde el directori arrel i no pot ser un arxiu. @@ -213,7 +209,6 @@ Confirma per revocar drets de %1$s Avís Cap - Autenticació fallida PID: %1$d diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 1c6ff351a..be0a93176 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -154,9 +154,6 @@ %1$d sekund Opětovné ověření po aktualizaci Opětovné ověření oprávnění Superuser po aktualizaci aplikace - Povolit ověřování otisky prstů - Chcete-li povolit požadavky Superuser, použijte snímač otisků prstů - Ověřování otisky prstů Režim více uživatelů Pouze vlastník zařízení @@ -173,7 +170,6 @@ Všechny relace root používají globální připojení jmenného prostoru. Kořenové relace dědí jmenný prostor žadatele. Každá relace root bude mít svůj vlastní izolovaný jmenný prostor. - Nebyly nastaveny žádné otisky prstů ani žádná podpora zařízení. Požadavek Superuser @@ -200,7 +196,6 @@ Smazat záznam ohledně oprávnění pro %1$s? Informační text Žádný - Ověření se nezdařilo PID: %1$d diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 017a84bdd..57aa1e972 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -156,9 +156,6 @@ %1$d Sekunden Nach Aktualisierung erneut authentifizieren Superuser-Zugriff nach App-Aktualisierung erneut abfragen - Aktiviere Authentifizierung durch Fingerabdruck - Fingerabdrucksensor benutzen um Superuser-Anfragen zu erlauben - Authentifiziere Fingerabdruck Mehrbenutzermodus Nur der Gerätebesitzer @@ -175,7 +172,6 @@ Alle Root-Sitzungen benutzen den global angelegten Namespace Root-Sitzungen erben den Namespace des Abfragenden Jede Root-Sitzung hat ihren isolierten Namespace - Keine Fingerabdrücke gespeichert oder keine Geräteunterstützung Superuser-Anfrage @@ -202,7 +198,6 @@ Möchtest du die Rechte für %1$s entziehen? Popup Keine - Authentifizierung fehlgeschlagen PID: %1$d diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 2d3ac095b..5f998c5c3 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -159,9 +159,6 @@ %1$d segundos Re-autenticación Pedir permisos de superusuario nuevamente si una aplicación es actualizada o reinstalada - Autenticación por Huella Dactilar - Utilizar el sensor de Huella Dactilar para permitir las solicitudes de superusuario - Autenticar Huella Dactilar Modo MultiUsuario Sólo Administrador del Dispositivo @@ -178,7 +175,6 @@ Todas las sesiones de root utilizan el soporte Global Namespace Las sesiones de root heredarán las peticiones Namespace Cada sesión root tendrá su propia Namespace - No se establecieron huellas dactilares o no existe soporte del dispositivo Error al crear la carpeta. Debe ser accesible desde el directorio raíz de almacenamiento y no debe ser un archivo. @@ -206,7 +202,6 @@ ¿Confirmar para revocar derechos de %1$s? Aviso Nada - Autenticación fallida PID: %1$d diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 6c4f3bcde..df63f0b5a 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -162,9 +162,6 @@ %1$d sekundit Taas-autendi peale täiendust Taas-autendi superkasutaja õigused peale rakenduse täiendamist - Luba sõrmejäljega autentimine - Kasuta sõrmejäljelugejat superkasutaja taotluste lubamiseks - Autendi sõrmejälg Mitmikkasutaja režiim Ainult seadme omanik @@ -181,7 +178,6 @@ Kõik juurkasutaja sessioonid kasutavad globaalset monteerimise nimeruumi. Juurkasutaja sessioonid võtavad üle selle taotleja nimeruumi. Iga juurkasutaja sessioon saab oma isoleeritud nimeruumi. - Sõrmejälgi pole määratud või seade pole toetatud. Faili loomisel esines viga. See peab olema ligipääsetav mäluruumi juurkaustast ning ei tohi olla fail. @@ -209,7 +205,6 @@ Kinnitad rakenduse %1$s õiguste eemaldamise? Hüpik Puudub - Autentimine ebaõnnestus PID: %1$d diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 505c1d223..68e27a37d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -165,9 +165,6 @@ %1$d secondes Authentifier à nouveau après la mise à niveau Authentifier à nouveau les autorisations super‐utilisateur après une mise à jour de l’application - Activer l’authentification par empreinte digitale - Utiliser le lecteur d’empreintes digitales pour autoriser les demandes super‐utilisateur - Authentifier l’empreinte digitale Mode multi‐utilisateur Propriétaire de l’appareil uniquement @@ -184,7 +181,6 @@ Toutes les sessions super‐utilisateur utilisent l’espace de noms global du montage. Les sessions super‐utilisateur hériteront de l’espace de noms de leur demandeur. Chaque session super‐utilisateur aura son propre espace de noms isolé. - Aucune empreinte digitale n’a été définie ou le lecteur d’empreinte n’est pas pris en charge. Erreur lors de la création du dossier. Il doit être accessible depuis le répertoire racine du stockage et ne doit pas être un fichier. @@ -212,7 +208,6 @@ Confirmez‐vous l’annulation des droits pour %1$s ? Toast Aucun - Échec de l’authentification PID : %1$d diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 2b7c04211..b4736fdec 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -162,9 +162,6 @@ %1$d सेकंड्‌स अपग्रेड के बाद फिर से प्रमाणित करें एप्लीकेशन अपग्रेड होने के बाद उत्तम उपयोगकर्ता की अनुमतियों को फिर से प्रमाणित करें - फिंगरप्रिंट प्रमाणीकरण सक्षम करें - उत्तम उपयोगकर्ता के अनुरोधों की अनुमति के लिए फिंगरप्रिंट स्कैनर का उपयोग करें - फिंगरप्रिंट को प्रमाणित करें बहु उपयोगकर्ता मोड केवल डिवाइस का मालिक @@ -181,7 +178,6 @@ सभी रूट सत्र वैश्विक माउंट नेमस्पेस का उपयोग करते हैं. रूट सत्रों को उनके अनुरोधकर्ताओं के नेमस्पेस विरासत में मिलेंगे. प्रत्येक रूट सत्र का अपना अलग नेमस्पेस होगा. - कोई फ़िंगरप्रिंट नहीं सेट किया गया या डिवाइस का समर्थन नहीं है. फोल्डर बनाने में त्रुटि. यह स्टोरेज रूट डायरेक्टरी से एक्सेस होना चाहिए और फाइल नहीं होना चाहिए. @@ -209,7 +205,6 @@ %1$s के अधिकारों को वापस लेने की पुष्टि करें? पॉप-अप नोट कोई नहीं - प्रमाणीकरण विफल हुआ PID: %1$d diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index c53445891..cf36665dc 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -160,9 +160,6 @@ %1$d detik Otentikasi ulang setelah pembaruan Otentikasi ulang izin superuser setelah pembaruan sebuah aplikasi - Aktifkan Otentikasi Sidik Jari - Gunakan pemindai sidik jari untuk mengizinkan permintaan superuser - Otentikasi Sidik Jari Mode Multipengguna Pemilik Perangkat Saja @@ -179,7 +176,6 @@ Semua sesi root menggunakan mount ruang nama global. Sesi root akan mewarisi ruang nama peminta mereka. Setiap sesi root akan memiliki ruang nama tersendiri. - Tidak ada sidik jari diatur atau tidak ada dukungan perangkat. Kesalahan membuat folder. Folder harus dapat diakses dari direktori penyimpanan root dan bukan merupakan file. @@ -207,7 +203,6 @@ Konfirmasi untuk mencabut akses %1$s? Toast Tidak ada - Otentikasi Gagal PID: %1$d diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 473f2f714..b37b534b5 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -164,9 +164,6 @@ %1$d secondi Riautentica dopo aggiornamento Riautentica i permessi Superuser dopo un aggiornamento dell\'app - Abilita autenticazione impronta - Utilizza il sensore di impronte per accettare le richieste Superuser - Conferma impronta Modalità multiutente Solo proprietario del dispositivo @@ -183,7 +180,6 @@ Tutte le sessioni di root erediteranno il namespace globale Le sessioni di root erediteranno il namespace del loro richiedente Ogni sessione di root avrà il suo namespace isolato - Non è presente alcuna impronta o il dispositivo non è supportato Errore durante la creazione della cartella. Deve essere accessibile dalla radice della memoria di archiviazione e non essere un file. @@ -211,7 +207,6 @@ Confermi la revoca dei diritti di %1$s? Toast Nessuno - Autenticatione fallita PID: %1$d diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 76966996f..fd02196b2 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -148,9 +148,6 @@ %1$d秒 アップグレード後の再認証 アプリのアップグレード後にスーパーユーザー権限を再認証します - 指紋認証の有効化 - スーパーユーザー権限のリクエストの許可に指紋認証を使います - 指紋認証 マルチユーザーモード 端末の管理者のみ @@ -167,7 +164,6 @@ すべてのrootセッションがグローバル名前空間を使用します rootセッションはリクエスト者の名前空間を継承します rootセッション毎に分離された名前空間を使用します - 指紋が登録されていないか、お使いの端末でサポートされていません。 スーパーユーザーリクエスト @@ -194,7 +190,6 @@ %1$s の権限を取り消しますか? トースト通知 なし - 認証に失敗しました PID: %1$d diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index f66ad2e3c..5d9f62ef8 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -155,9 +155,6 @@ %1$d초 업데이트 후 재승인 앱 업데이트 후에 슈퍼유저 권한 재승인 - 지문 인식 사용 - 지문 인식으로 슈퍼유저 권한을 허가합니다. - 지문 인식 다중 사용자 모드 기기 소유자만 @@ -174,7 +171,6 @@ 모든 루트 세션이 전역 마운트 이름공간을 사용합니다. 루트 세션은 요청자의 이름공간을 상속합니다. 각각의 루트 세션은 자신만의 독립된 이름공간을 사용합니다. - 지문이 등록되지 않았거나 기기가 지문 인식을 지원하지 않습니다. 슈퍼유저 요청 @@ -201,7 +197,6 @@ 정말 %1$s의 권한을 취소하시겠습니까? 토스트 없음 - 인증 실패 PID: %1$d diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 4484d78a6..b03ba36c1 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -137,8 +137,6 @@ %1$d sekundžių Pakartotinai patvirtinti po atnaujinimo Pakartotinai patvirtinti supervartotojo leidimus po programėlės atnaujinimo - Įgalinti patvirtinimą piršto antspaudu - Naudoti piršto antspaudą supervartotojo leidimo prašymų atsakymui Daugialypio vartotojo režimas Tik įrenginio savininkas @@ -155,7 +153,6 @@ Visos root sesijos naudoja globalią vardų sritį Root sesijos paveldi jos išprašytojo/s vardų sritį Kiekviena root sesija turi savo izoliuotą vardų sritį - Jūsų įrenginyje nebuvo surasta pirštų antspaudų arba jūsų įrenginys neturi pirštų antspaudų skaitytuvo Supervartotojo prašymas @@ -182,7 +179,6 @@ Neleisti %1$s naudotis supervartotojo teisėmis? Išmesti Nėra - Patvirtinimas žlugo Target UID: %1$d diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index a846ac366..ba525b832 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -154,9 +154,6 @@ %1$d секунди Повторна автентикација по надградба Повторна автентикација за супер-корисник дозвола по надградбата на апликацијата - Овозможи автентикација на отпечатоци - Користете скенер за отпечатоци за да дозволите супер-корисник барања - Автентикација на отпечатоци Режим на повеќе корисници Само сопственикот на уредот @@ -173,7 +170,6 @@ Сите рут сесии го користат глобалниот именски простор. Рут сесиите ќе го наследат именскиот простор на нивниот барател. Секоја рут сесија ќе има свој изолиран именски простор. - Нема регистрирано отпечатоци од прсти или уредот не ја поддржува оваа функција. Супер-корисник барање @@ -200,7 +196,6 @@ Дали потврдувате анулирање на поставките за пристап на %1$s? Тост Ниеден - Неуспешна автентикација PID: %1$d diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index c8d884ccf..275141605 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -146,9 +146,6 @@ %1$d sekunder Autentiser på nytt etter oppdateringer after upgrade Autentiser superbruker-tillatelser på nytt etter at en app har blitt oppdatert - Skru på fingeravtrykksautentisering - Bruk fingeravtrykksskanneren for å godkjenne superbruker-forespørsler - Autentiser fingeravtrykk Flerbrukermoduse Kun enhetens eier @@ -165,7 +162,6 @@ Alle root-økter benytter det altdekkende monteringsnavnefeltet. Root-økter vil arve forespørrerens navnefelt. Hver root-økt vil ha sitt eget isolerte navnefelt. - Ingen fingeravtrykk ble gitt, eller så støttes det ikke av enheten. Superbruker-forespørsel @@ -192,7 +188,6 @@ Vil du bekrefte for å oppheve %1$s sine rettigheter? Varselfelt Ingen - Autentisering mislyktes Mål-UID: %1$d diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 2d00fb929..5ee7c6acc 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -132,8 +132,6 @@ %1$d seconden Opnieuw verzoeken na bijwerken Superuser rechten opnieuw opvragen na bijwerken applicatie - Vingerafdruk authenticatie inschakelen - Vingerafdruk gebruiken om superuser verzoeken toe te staan Multi-gebruiker modus Alleen apparaateigenaar @@ -150,7 +148,6 @@ Alle rootsessies gebruiken de globale naamruimte Rootsessies verkrijgen de verzoeker\'s naamruimte Iedere rootsessie heeft een eigen geïsoleerde naamruimte - Geen vingerafdrukken ingesteld, of geen apparaatondersteuning Superuser verzoek @@ -177,7 +174,6 @@ De rechten van %1$s intrekken? Toast Geen - Authenticatie mislukt Doel UID: %1$d diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index a5eaa480a..0bd45c811 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -166,9 +166,6 @@ %1$d sekund Ponowienie uwierzytelnienia po aktualizacji Ponowne uwierzytelnianie uprawnienia superużytkownika po aktualizacji aplikacji - Włącz Uwierzytelnienie Odciskiem Palca - Użyj skanera linii papilarnych, aby zezwolić na żądania supersu - Uwierzytelnianie Odciskiem Palca Tryb Multiusera Tylko Właściciel Urządzenia @@ -185,7 +182,6 @@ Wszystkie sesje root za pomocą globalnej przestrzeni montowań nazw Sesje Root będzie dziedziczyć prośby i nazwy W każdej sesji root będzie miał własną odosobnioną nazwę - Nie ustawiono żadnych odcisków palców lub brak obsługi urządzenia Błąd podczas tworzenia folderu. Musi być dostępny z głównego katalogu pamięci i nie może być plikiem. @@ -213,7 +209,6 @@ Potwierdzasz odwołanie uprawnień %1$s? Powiadomienie Brak - Uwierzytelnienie Nieudane PID: %1$d diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index b69f2f4fe..2ed94706b 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -134,8 +134,6 @@ %1$d segundos Reautenticar após atualizar Reautenticar permissões de superusuário após um app atualizar - Ativar Autenticação de Impressão Digital - Usar escaneador de impressão digital para permitir solicitações de superusuário Modo de Multiusuário Proprietário do Dispositivo Apenas @@ -152,7 +150,6 @@ Todas as sessões root usam montagem de espaço de nome global As sessões root herdarão espaço de nome de seu solicitante Cada sessão root terá seu próprio espaço de nome isolado - Nenhuma impressão digital foi definida ou o dispostivo não tem suporte Solicitação de Superusuário @@ -179,7 +176,6 @@ Revogar os direitos de %1$s? Notificação toast Nenhuma - Falha de Autenticação PID: %1$d diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 2fba59981..545bd4240 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -161,9 +161,6 @@ %1$d secunde Reautentificare după actualizare Reautentifică permisiunile pentru superutilizator după o actualizare a aplicației - Activează autentificarea cu amprenta - Folosește scannerul de amprente pentru a permite solicitările de superutilizator - Autentifică amprenta Mod de multiutilizator Numai proprietarul dispozitivului @@ -180,7 +177,6 @@ Toate sesiunile de root folosesc spațiul de nume global. Sesiunile de root vor moșteni spațiul de nume al solicitantului. Fiecare sesiune de root va avea propriul spațiu de nume izolat. - Nu au fost setate amprente sau scannerul de amprentă lipsește. Eroare la crearea dosarului. Acesta trebuie să fie accesibil din directorul rădăcină al stocării și nu trebuie să fie un fișier. @@ -208,7 +204,6 @@ Confirmi revocarea drepturilor pentru %1$s? Mesaj Nimic - Autentificare eșuată PID: %1$d diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 160bda820..3c723c763 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -167,9 +167,6 @@ %1$d секунд Повторная аутентификация Повторный запрос прав суперпользователя после обновления приложений - Биометрическая аутентификация - Использовать сканер отпечатков пальцев для запросов прав суперпользователя - Подтвердите отпечаток пальца Многопользовательский режим Только владелец @@ -186,7 +183,6 @@ Сессии суперпользователя используют общее пространство имён Сессии суперпользователя наследуют пространство имён запрашивающего Сессии суперпользователя используют изолированные пространства имён - Не поддерживается устройством или не заданы отпечатки Ошибка создания папки. Она должна быть доступна из корневой директории хранилища и не должна быть файлом. @@ -214,7 +210,6 @@ Сбросить настройки для %1$s? Всплывающие уведомления Нет - Ошибка аутентификации PID: %1$d diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index c39bf3cad..c50628129 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -166,9 +166,6 @@ %1$d sekúnd Overenie autentifikácie po upgrade Overí autentifikáciu oprávnení superuser po upgrade aplikácie - Povoliť autentifikáciu odtlačkom prsta - Použite snímač odtlačkov prstov pre povolenie žiadostí superuser - Autentifikovať odtlačok prsta Režim viacerých používateľov Iba majiteľ zariadenia @@ -185,7 +182,6 @@ Všetky relácie root použijú globálny mount namespace Relácie root zdedia namespace od žiadateľa Každá relácia root bude mať vlastný izolovaný namespace - Neboli odoslané žiadne odtlačky prsta alebo ich zariadenie nepodporuje Chyba pri vytváraní priečinka. Musí byť prístupný z koreňového adresára a nemôže to byť súbor. @@ -213,7 +209,6 @@ Potvrdzujete zrušenie práv %1$s? Toast Nič - Autentifikácia zlyhala PID:%1$d diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 066df9c69..c966803f2 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -146,9 +146,6 @@ %1$d วินาที ขอสิทธิ์ใหม่หลังจากอัพเกรด ขอสิทธิ์ superuser ใหม่หลังจากแอปถูกอัพเกรด - ใช้การยืนยันลายนิ้วมือ - ใช้ตัวแสกนลายนิ้วมือเพื่ออนุญาตารขอเข้าถึง superuser - ยืนยันลายนิ้วมือ โหมดผู้ใช้หลายคน เจ้าของอุปกรณ์เท่านั้น @@ -165,7 +162,6 @@ All root sessions use the global mount namespace. Root sessions will inherit their requester\'s namespace. Each root session will have its own isolated namespace. - ไม่มีลายนิ้วมือหรืออุปกรณ์แสกน การขอเข้าถึง Superuser @@ -192,7 +188,6 @@ ยืนยันการลบสิทธิ์ของ %1$s? การเตือน ไม่มี - การยืนยันล้มเหลว UID เป้าหมาย: %1$d diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 1fed35e24..3ec7b2b94 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -166,9 +166,6 @@ %1$d saniye Yükseltmeden sonra yeniden kimlik doğrula Uygulama yükseltmeleri sonrasında yetkili kullanıcı izinlerini yeniden doğrula - Parmak İzi Kimlik Doğrulamayı Etkinleştir - Yetkili kullanıcı isteklerine izin vermek için parmak izi tarayıcısını kullan - Parmak izini doğrula Çok Kullanıcılı Mod Yalnızca Cihaz Sahibi @@ -185,7 +182,6 @@ Tüm kök oturumları genel bağlama ad alanını kullanır Kök oturumları, istekte bulunanın ad alanını devralır Her bir kök oturumunun kendi izole ad alanı olacaktır - Parmak izi ayarlanmadı veya cihaz desteği yok Klasör oluşturma hatası. Depolama kök dizininden erişilebilir olmalı ve bir dosya olmamalıdır. @@ -213,7 +209,6 @@ %1$s hakları geri alınsın mı? Pencere Hiçbiri - Kimlik Doğrulama Başarısız PID: %1$d diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index ae9573824..d445db68c 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -166,9 +166,6 @@ %1$d сек. Повторна автентифікація Перевидача прав суперкористувача після оновлення програм - Автентифікація за відбитком - Використовувати сканер відбитків пальців, щоб надавати дозвіл суперкористувача - Автентифікація за відбитком Багатокористувацький режим Тільки власник @@ -185,7 +182,6 @@ Всі сеанси Суперкористувача використовують глобальний простір імен. Сеанси Суперкористувача наслідують простір імен запитувача. Кожнен сеанс Суперкористувача має власний ізольований простір імен. - Немає відбитків пальця або пристрій не підтримується. Помилка створення папки. Вона повинна бути доступна з кореневої директорії сховища і не повинна бути файлом. @@ -213,7 +209,6 @@ Підтвердити відкликання прав для %1$s? Спливаюче сповіщення Ні - Помилка автентифікації PID: %1$d diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 1b9cef3be..f68e6d5c8 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -141,9 +141,6 @@ %1$d giây Xác thực lại sau khi nâng cấp Xác thực lại quyền superuser sau khi nâng cấp ứng dụng - Bật xác thực vân tay - Sử dụng quét vân tay để cho phép quyền superuser - Xác thực vân tay Chế độ đa người dùng Chỉ chủ sở hữu thiết bị Chủ sở hữu thiết bị được quản lý @@ -158,8 +155,7 @@ Tất cả các phiên root sử dụng không gian tên gắn kết chung. Các phiên root sẽ kế thừa không gian tên của người yêu cầu. Mỗi phiên root sẽ có không gian tên riêng biệt. - Không có dấu vân tay nào được thiết lập hoặc thiết bị không hỗ trợ. - + Yêu cầu Superuser Từ chối @@ -185,8 +181,7 @@ Xác nhận thu hồi quyền của %1$s? Thông báo ngắn Không có - Xác thực thất bại - + Mục UID: %1$d Điều khiển: %1$s diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index e97518fb3..2be863b8b 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -167,9 +167,6 @@ %1$d 秒 更新后重新认证 应用更新后重新认证超级用户权限 - 启用指纹验证 - 使用指纹识别来允许超级用户请求 - 验证指纹 多用户模式 仅设备所有者 @@ -186,7 +183,6 @@ 所有的 ROOT 会话使用全局挂载命名空间 ROOT 会话继承原程序的命名空间 每一个 ROOT 会话使用自己独立的命名空间 - 没有设置指纹或设备不支持 创建文件夹出错。路径需要位于内部存储空间,并且不能有同名文件。 @@ -214,7 +210,6 @@ 确认撤销 %1$s 的权限? 消息提示 - 验证失败 PID: %1$d diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 7225bff86..46361b5ed 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -167,9 +167,6 @@ %1$d 秒 更新後重新驗證 應用程式更新後,重新驗證超級使用者的請求 - 指紋驗證 - 使用指紋辨識器允許超級使用者的請求 - 指紋驗證 多重使用者模式 僅限裝置擁有者 @@ -186,7 +183,6 @@ 所有 Root 工作階段皆使用全域命名空間 所有 Root 工作階段皆繼承原程式的命名空間 所有 Root 工作階段都擁有獨立的命名空間 - 未設定指紋或無指紋感測器 建立資料夾發生錯誤。檔案存放的路徑需要位於內部儲存空間且不能有同樣名稱的檔案。 @@ -214,7 +210,6 @@ 確定撤銷 %1$s 的權限? 快顯通知 - 驗證失敗 PID:%1$d diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5df6c7e97..d9a127161 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -95,7 +95,6 @@ Reboot to apply settings Release notes Repo cache cleared - DTBO was patched! Magisk Manager has patched dtbo.img. Please reboot. Flashing… @@ -116,6 +115,7 @@ Requires Additional Setup Your device needs additional setup for Magisk to work properly. It will download the Magisk setup zip, do you want to proceed now? Running environment setup… + Authenticate General @@ -167,9 +167,9 @@ %1$d seconds Reauthenticate after upgrade Reauthenticate superuser permissions after an application upgrades - Enable Fingerprint Authentication - Use fingerprint scanner to allow superuser requests - Authenticate Fingerprint + Enable Biometric Authentication + Use biometric authentication to allow superuser requests + Unsupported device or no biometric settings are enabled Multiuser Mode Device Owner Only @@ -186,7 +186,6 @@ All root sessions use the global mount namespace Root sessions will inherit their requester\'s namespace Each root session will have its own isolated namespace - No fingerprints were set or no device support Error creating folder. It must be accessible from storage root directory and must not be a file. @@ -214,7 +213,6 @@ Confirm to revoke %1$s rights? Toast None - Authentication Failed PID: %1$d diff --git a/app/src/main/res/xml/app_settings.xml b/app/src/main/res/xml/app_settings.xml index 27345d2d8..44a1ffeb7 100644 --- a/app/src/main/res/xml/app_settings.xml +++ b/app/src/main/res/xml/app_settings.xml @@ -119,9 +119,9 @@ + android:key="su_biometric" + android:title="@string/settings_su_biometric_title" + android:summary="@string/settings_su_biometric_summary" /> - \ No newline at end of file + diff --git a/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java b/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java index 651fd5314..41f9fb906 100644 --- a/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java +++ b/shared/src/main/java/com/topjohnwu/magisk/DynAPK.java @@ -11,7 +11,7 @@ import static android.os.Build.VERSION.SDK_INT; public class DynAPK { - private static final int STUB_VERSION = 5; + private static final int STUB_VERSION = 6; // Indices of the object array private static final int STUB_VERSION_ENTRY = 0; diff --git a/stub/src/main/AndroidManifest.xml b/stub/src/main/AndroidManifest.xml index b04b4dc95..8222e9ca7 100644 --- a/stub/src/main/AndroidManifest.xml +++ b/stub/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + Date: Thu, 14 Nov 2019 17:25:25 +0200 Subject: [PATCH 58/74] Update RU strings --- app/src/main/res/values-ru/strings.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 3c723c763..1e8b51bd3 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -95,7 +95,6 @@ Для применения настроек перезагрузите устройство О версии Кэш репозитория очищен - DTBO пропатчен! Magisk Manager пропатчил dtbo.img. Перезагрузите устройство. Прошивка… @@ -116,6 +115,7 @@ Требуется дополнительная установка Вашему устройству требуется дополнительная установка Magisk для корректной работы. Будет загружен установочный ZIP Magisk, продолжить? Настройка рабочей среды… + Аутентификация Основные @@ -167,6 +167,9 @@ %1$d секунд Повторная аутентификация Повторный запрос прав суперпользователя после обновления приложений + Биометрическая аутентификация + Использовать биометрическую аутентификацию для запросов прав суперпользователя + Не поддерживается устройством или не заданы ностройки защитной блокировки Многопользовательский режим Только владелец From 1dc531930d92da7312830bc2840f9595f47e8c2e Mon Sep 17 00:00:00 2001 From: vvb2060 Date: Fri, 15 Nov 2019 14:46:20 +0800 Subject: [PATCH 59/74] Update zh-rCN translation --- app/src/main/res/values-zh-rCN/strings.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 2be863b8b..075c5a4fd 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -95,7 +95,6 @@ 重启设备以应用设置 发布说明 仓库缓存已清除 - 已修补 DTBO! Magisk Manager 为 dtbo 分区进行了修补,请立即重新启动 正在刷入 @@ -116,6 +115,7 @@ 需要修复运行环境 缺失 Magisk 正常工作所需的文件。 将下载 Magisk 安装包进行修复安装,完成后自动重启,是否继续? 正在修复运行环境… + 验证您的身份 常规 @@ -167,6 +167,9 @@ %1$d 秒 更新后重新认证 应用更新后重新认证超级用户权限 + 启用生物识别认证 + 使用生物识别验证超级用户请求 + 不支持的设备或未配置生物识别功能 多用户模式 仅设备所有者 From 3d285b91c6e94f1f45ee2d205d8c0548478a69c9 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 15 Nov 2019 11:01:39 -0500 Subject: [PATCH 60/74] Use ContextCompat --- .../main/java/com/topjohnwu/magisk/utils/BiometricHelper.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/BiometricHelper.kt b/app/src/main/java/com/topjohnwu/magisk/utils/BiometricHelper.kt index ac6c0cfea..0b2bae237 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/BiometricHelper.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/BiometricHelper.kt @@ -2,10 +2,10 @@ package com.topjohnwu.magisk.utils import androidx.biometric.BiometricManager import androidx.biometric.BiometricPrompt +import androidx.core.content.ContextCompat import androidx.fragment.app.FragmentActivity import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R -import com.topjohnwu.superuser.internal.UiThreadHandler import org.koin.core.KoinComponent import org.koin.core.get @@ -32,7 +32,7 @@ object BiometricHelper: KoinComponent { onError: () -> Unit = {}, onSuccess: () -> Unit): BiometricPrompt { val prompt = BiometricPrompt(activity, - { cmd: Runnable -> UiThreadHandler.run(cmd) }, + ContextCompat.getMainExecutor(activity), object : BiometricPrompt.AuthenticationCallback() { override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { onError() From bab856bce2d26d0648f55693ba39514dbf27ec6f Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 16 Nov 2019 03:19:25 -0500 Subject: [PATCH 61/74] Move biometric settings higher in the list --- app/src/main/res/xml/app_settings.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/xml/app_settings.xml b/app/src/main/res/xml/app_settings.xml index 44a1ffeb7..6fe5e52c6 100644 --- a/app/src/main/res/xml/app_settings.xml +++ b/app/src/main/res/xml/app_settings.xml @@ -78,6 +78,12 @@ android:key="superuser" android:title="@string/superuser"> + + - - Date: Fri, 15 Nov 2019 21:28:19 +0100 Subject: [PATCH 62/74] Fixed log items not being refreshed Close #2079 --- .../com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt index 57b398dae..bb68251e7 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt @@ -37,8 +37,10 @@ class LogItemRvItem( fun toggle() = isExpanded.toggle() - override fun contentSameAs(other: LogItemRvItem): Boolean = items - .any { !other.items.contains(it) } + override fun contentSameAs(other: LogItemRvItem): Boolean { + if (items.size != other.items.size) return false + return items.all { it in other.items } + } override fun itemSameAs(other: LogItemRvItem): Boolean = date == other.date } From 9964e1bb8e820711f2be4991b3ac8a27661c3b86 Mon Sep 17 00:00:00 2001 From: dark-basic Date: Sat, 16 Nov 2019 05:23:20 -0300 Subject: [PATCH 63/74] Update strings.xml --- app/src/main/res/values-es/strings.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 5f998c5c3..95ec435cd 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -113,6 +113,7 @@ Se Requiere una Instalación Adicional Su dispositivo requiere una instalación adicional para que Magisk funcione correctamente. Se descargará el zip de instalación de Magisk, desea continuar ahora? Ejecutando Configuración de Entorno + Autenticar General @@ -159,7 +160,10 @@ %1$d segundos Re-autenticación Pedir permisos de superusuario nuevamente si una aplicación es actualizada o reinstalada - + Habilitar autenticación biométrica + Usar autenticación biométrica para permitir solicitudes de superusuario + Dispositivo no compatible o las configuraciones biométricas no están habilitadas + Modo MultiUsuario Sólo Administrador del Dispositivo Administrador del Dispositivo From 44ed0a3279c8b3a32f4587d21d44b5bc761567f2 Mon Sep 17 00:00:00 2001 From: Nick <28346376+groozchique@users.noreply.github.com> Date: Sat, 16 Nov 2019 00:42:17 +0300 Subject: [PATCH 64/74] Update RU strings Minor improvements and fixes --- app/src/main/res/values-ru/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 1e8b51bd3..8132e3f74 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -124,7 +124,7 @@ Папка для загрузки Файлы будут загружаться в %1$s Очистка кэша репозитория - Очистить кэш репозитория. Будет загружен заново + Очистить кэш репозитория. Кэш будет загружен заново Скрытие Magisk Manager Пересобрать Magisk Manager со случайным названием и именем пакета Восстановление Magisk Manager @@ -169,7 +169,7 @@ Повторный запрос прав суперпользователя после обновления приложений Биометрическая аутентификация Использовать биометрическую аутентификацию для запросов прав суперпользователя - Не поддерживается устройством или не заданы ностройки защитной блокировки + Эта функция не поддерживается устройством или не заданы настройки блокировки экрана Многопользовательский режим Только владелец From 7f6a6016d6830383ab941b6c641dc0b4a2e296d2 Mon Sep 17 00:00:00 2001 From: osm0sis Date: Fri, 15 Nov 2019 11:52:58 -0400 Subject: [PATCH 65/74] magiskboot: add simple workaround for Samsung offset header variant - some Samsung devices (e.g. Galaxy S5 SMG-900H) use a slightly different AOSP bootimg.h variant with `#define BOOT_NAME_SIZE 20` instead of 16 - since all known examples of these device images do not have anything in the NAME or CMDLINE fields, and the bootloader also accepts standard AOSP images, simply offset the SHA1/SHA256 detection by 4 bytes to avoid false positives from these images, remain an equally effective detection shortcut, and ensure a proper SHA1 checksum on repack aosp-dtbhdt2-4offhash-seandroid-256sig-samsung_gs5-smg900h-boot.img UNPACK CHECKSUM [00000000b11580f7d20f70297cdc31e02626def0356c82b90000000000000000] REPACK CHECKSUM [73b18751202e56c433f89dfd1902c290eaf4eef3e167fcf03b814b59a5e984b6] AIK CHECKSUM [b11580f7d20f70297cdc31e02626def0356c82b9000000000000000000000000] This patch should result in a `magiskboot unpack -n boot.img; magiskboot repack boot.img` new-boot.img matching the AIK CHECKSUM above. --- native/jni/magiskboot/bootimg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/jni/magiskboot/bootimg.cpp b/native/jni/magiskboot/bootimg.cpp index 78c0347c9..d31f7d807 100644 --- a/native/jni/magiskboot/bootimg.cpp +++ b/native/jni/magiskboot/bootimg.cpp @@ -221,7 +221,7 @@ void boot_img::parse_image(uint8_t *addr) { hdr = new dyn_img_v0(addr); } - for (int i = SHA_DIGEST_SIZE; i < SHA256_DIGEST_SIZE; ++i) { + for (int i = SHA_DIGEST_SIZE + 4; i < SHA256_DIGEST_SIZE; ++i) { if (hdr->id()[i]) { flags |= SHA256_FLAG; break; From da159e46550eecc38ef909a172bd020bc5a02777 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 16 Nov 2019 17:38:10 -0500 Subject: [PATCH 66/74] Better environment status detection --- .../main/java/com/topjohnwu/magisk/Info.kt | 9 ++-- .../com/topjohnwu/magisk/ui/MainActivity.kt | 18 +++---- .../topjohnwu/magisk/ui/home/HomeViewModel.kt | 4 +- .../magisk/ui/settings/SettingsFragment.kt | 48 +++++++++++-------- .../java/com/topjohnwu/magisk/utils/Utils.kt | 3 +- .../com/topjohnwu/magisk/view/Shortcuts.kt | 6 +-- app/src/main/res/layout/fragment_magisk.xml | 6 +-- 7 files changed, 47 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/com/topjohnwu/magisk/Info.kt b/app/src/main/java/com/topjohnwu/magisk/Info.kt index 317a6357c..fd1120ee3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Info.kt +++ b/app/src/main/java/com/topjohnwu/magisk/Info.kt @@ -36,20 +36,21 @@ object Info { val str = ShellUtils.fastCmd("magisk -v").split(":".toRegex())[0] val code = ShellUtils.fastCmd("magisk -V").toInt() val hide = Shell.su("magiskhide --status").exec().isSuccess - Env(code, str, hide) + Env(str, code, hide) }.getOrElse { Env() } class Env( - code: Int = -1, val magiskVersionString: String = "", + code: Int = -1, hide: Boolean = false ) { val magiskHide get() = Config.magiskHide val magiskVersionCode = when (code) { in Int.MIN_VALUE..Const.Version.MIN_VERCODE -> -1 - else -> code + else -> if(Shell.rootAccess()) code else -1 } - val unsupported = code > 0 && code < Const.Version.MIN_VERCODE + val isUnsupported = code > 0 && code < Const.Version.MIN_VERCODE + val isActive = magiskVersionCode >= 0 init { Config.magiskHide = hide diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt index 60f26422e..e1d83a2a5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt @@ -31,7 +31,6 @@ import com.topjohnwu.magisk.ui.module.ReposFragment import com.topjohnwu.magisk.ui.settings.SettingsFragment import com.topjohnwu.magisk.ui.superuser.SuperuserFragment import com.topjohnwu.magisk.utils.Utils -import com.topjohnwu.superuser.Shell import org.koin.androidx.viewmodel.ext.android.viewModel import timber.log.Timber import kotlin.reflect.KClass @@ -68,7 +67,7 @@ open class MainActivity : BaseActivity(), Na super.onCreate(savedInstanceState) - if (Info.env.unsupported && !viewModel.shownUnsupportedDialog) { + if (Info.env.isUnsupported && !viewModel.shownUnsupportedDialog) { viewModel.shownUnsupportedDialog = true AlertDialog.Builder(this) .setTitle(R.string.unsupport_magisk_title) @@ -164,16 +163,11 @@ open class MainActivity : BaseActivity(), Na private fun checkHideSection() { val menu = binding.navView.menu - menu.findItem(R.id.magiskHideFragment).isVisible = - Shell.rootAccess() && Info.env.magiskHide - menu.findItem(R.id.modulesFragment).isVisible = - Shell.rootAccess() && Info.env.magiskVersionCode >= 0 - menu.findItem(R.id.reposFragment).isVisible = - (viewModel.isConnected.value && Shell.rootAccess() && Info.env.magiskVersionCode >= 0) - menu.findItem(R.id.logFragment).isVisible = - Shell.rootAccess() - menu.findItem(R.id.superuserFragment).isVisible = - Utils.showSuperUser() + menu.findItem(R.id.magiskHideFragment).isVisible = Info.env.isActive && Info.env.magiskHide + menu.findItem(R.id.modulesFragment).isVisible = Info.env.isActive + menu.findItem(R.id.reposFragment).isVisible = Info.isConnected.value && Info.env.isActive + menu.findItem(R.id.logFragment).isVisible = Info.env.isActive + menu.findItem(R.id.superuserFragment).isVisible = Utils.showSuperUser() } private fun FragNavTransactionOptions.Builder.customAnimations(options: MagiskAnimBuilder) = diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt index a84b5c2fe..2b8fc887f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt @@ -92,7 +92,7 @@ class HomeViewModel( } } - val hasRoot = KObservableField(false) + val isActive = KObservableField(false) private var shownDialog = false @@ -175,7 +175,7 @@ class HomeViewModel( if (invalidate) Info.envRef.invalidate() - hasRoot.value = Shell.rootAccess() + isActive.value = Info.env.isActive val fetchUpdate = if (isConnected.value) magiskRepo.fetchUpdate().ignoreElement() diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt index 88239ff4c..04a3485a1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt @@ -61,7 +61,7 @@ class SettingsFragment : BasePreferenceFragment() { nsConfig = findPreference(Config.Key.SU_MNT_NS)!! val reauth = findPreference(Config.Key.SU_REAUTH)!! val biometric = findPreference(Config.Key.SU_BIOMETRIC)!! - val generalCatagory = findPreference("general")!! + val generalCategory = findPreference("general")!! val magiskCategory = findPreference("magisk")!! val suCategory = findPreference("superuser")!! val hideManager = findPreference("hide")!! @@ -94,9 +94,9 @@ class SettingsFragment : BasePreferenceFragment() { biometric.setSummary(R.string.no_biometric) } - if (Const.USER_ID == 0 && Info.isConnected.value && Shell.rootAccess()) { + if (Const.USER_ID == 0 && Info.isConnected.value && Info.env.isActive) { if (activity.packageName == BuildConfig.APPLICATION_ID) { - generalCatagory.removePreference(restoreManager) + generalCategory.removePreference(restoreManager) hideManager.setOnPreferenceClickListener { showManagerNameDialog { PatchAPK.hideManager(requireContext(), it) @@ -104,7 +104,7 @@ class SettingsFragment : BasePreferenceFragment() { true } } else { - generalCatagory.removePreference(hideManager) + generalCategory.removePreference(hideManager) restoreManager.setOnPreferenceClickListener { DownloadService(requireContext()) { subject = DownloadSubject.Manager(Configuration.APK.Restore) @@ -114,25 +114,32 @@ class SettingsFragment : BasePreferenceFragment() { } } else { // Remove if not primary user, no connection, or no root - generalCatagory.removePreference(restoreManager) - generalCatagory.removePreference(hideManager) + generalCategory.removePreference(restoreManager) + generalCategory.removePreference(hideManager) } if (!Utils.showSuperUser()) { preferenceScreen.removePreference(suCategory) } - if (!Shell.rootAccess()) { + if (!Info.env.isActive) { preferenceScreen.removePreference(magiskCategory) - generalCatagory.removePreference(hideManager) + generalCategory.removePreference(hideManager) } - findPreference("clear")?.setOnPreferenceClickListener { - Completable.fromAction { repoDB.clear() }.subscribeK { - Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT) + findPreference("clear")?.also { + if (Info.env.isActive) { + it.setOnPreferenceClickListener { + Completable.fromAction { repoDB.clear() }.subscribeK { + Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT) + } + true + } + } else { + generalCategory.removePreference(it) } - true } + findPreference("hosts")?.setOnPreferenceClickListener { Shell.su("add_hosts_module").submit { Utils.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT) @@ -142,20 +149,21 @@ class SettingsFragment : BasePreferenceFragment() { findPreference(Config.Key.DOWNLOAD_PATH)?.apply { summary = Config.downloadPath - }?.setOnPreferenceClickListener { preference -> - activity.withExternalRW { - onSuccess { - showDownloadDialog { - Config.downloadPath = it - preference.summary = it + setOnPreferenceClickListener { pref -> + activity.withExternalRW { + onSuccess { + showDownloadDialog { + Config.downloadPath = it + pref.summary = it + } } } + true } - true } updateChannel.setOnPreferenceChangeListener { _, value -> - val channel = Integer.parseInt(value as String) + val channel = value.toString().toInt() val previous = Config.updateChannel if (channel == Config.Value.CUSTOM_CHANNEL) { diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt index 252b7b6d8..810b9eba6 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt @@ -11,7 +11,6 @@ import com.topjohnwu.magisk.* import com.topjohnwu.magisk.R import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.model.update.UpdateCheckService -import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.internal.UiThreadHandler import java.io.File import java.util.concurrent.TimeUnit @@ -34,7 +33,7 @@ object Utils { } fun showSuperUser(): Boolean { - return Shell.rootAccess() && (Const.USER_ID == 0 + return Info.env.isActive && (Const.USER_ID == 0 || Config.suMultiuserMode != Config.Value.MULTIUSER_MODE_OWNER_MANAGED) } diff --git a/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt b/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt index 5dfd051c3..829cb5e8c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.kt @@ -14,7 +14,6 @@ import com.topjohnwu.magisk.* import com.topjohnwu.magisk.extensions.getBitmap import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.utils.Utils -import com.topjohnwu.superuser.Shell object Shortcuts { @@ -28,7 +27,6 @@ object Shortcuts { @RequiresApi(api = 25) private fun getShortCuts(context: Context): List { val shortCuts = mutableListOf() - val root = Shell.rootAccess() val intent = context.intent() fun getIcon(id: Int): Icon { @@ -53,7 +51,7 @@ object Shortcuts { .build() ) } - if (root && Info.env.magiskHide) { + if (Info.env.magiskHide) { shortCuts.add( ShortcutInfo.Builder(context, "magiskhide") .setShortLabel(context.getString(R.string.magiskhide)) @@ -68,7 +66,7 @@ object Shortcuts { .build() ) } - if (!Config.coreOnly && root && Info.env.magiskVersionCode >= 0) { + if (!Config.coreOnly && Info.env.isActive) { shortCuts.add( ShortcutInfo.Builder(context, "modules") .setShortLabel(context.getString(R.string.modules)) diff --git a/app/src/main/res/layout/fragment_magisk.xml b/app/src/main/res/layout/fragment_magisk.xml index 366867ead..70b580dca 100644 --- a/app/src/main/res/layout/fragment_magisk.xml +++ b/app/src/main/res/layout/fragment_magisk.xml @@ -201,7 +201,7 @@ @@ -482,13 +482,13 @@ Date: Mon, 18 Nov 2019 17:18:56 -0500 Subject: [PATCH 67/74] Fix kmsg logging in magiskinit --- native/jni/init/init.cpp | 53 ++++++++++++++++++++++--------------- native/jni/init/init.h | 1 + native/jni/init/rootdir.cpp | 18 ------------- 3 files changed, 33 insertions(+), 39 deletions(-) diff --git a/native/jni/init/init.cpp b/native/jni/init/init.cpp index c1b56dd9e..e6f632a2b 100644 --- a/native/jni/init/init.cpp +++ b/native/jni/init/init.cpp @@ -31,31 +31,44 @@ constexpr int (*init_applet_main[])(int, char *[]) = #ifdef MAGISK_DEBUG static FILE *kmsg; +static char kmsg_buf[4096]; static int vprintk(const char *fmt, va_list ap) { - fprintf(kmsg, "magiskinit: "); - return vfprintf(kmsg, fmt, ap); + vsnprintf(kmsg_buf + 12, sizeof(kmsg_buf) - 12, fmt, ap); + return fprintf(kmsg, "%s", kmsg_buf); } -static void setup_klog() { - mknod("/kmsg", S_IFCHR | 0666, makedev(1, 11)); - int fd = xopen("/kmsg", O_WRONLY | O_CLOEXEC); - kmsg = fdopen(fd, "w"); - setbuf(kmsg, nullptr); - unlink("/kmsg"); - log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk; - log_cb.ex = nop_ex; - - // Prevent file descriptor confusion - mknod("/null", S_IFCHR | 0666, makedev(1, 3)); - int null = xopen("/null", O_RDWR | O_CLOEXEC); - unlink("/null"); +void setup_klog() { + // Shut down first 3 fds + int null; + if (access("/dev/null", W_OK) == 0) { + null = xopen("/dev/null", O_RDWR | O_CLOEXEC); + } else { + mknod("/null", S_IFCHR | 0666, makedev(1, 3)); + null = xopen("/null", O_RDWR | O_CLOEXEC); + unlink("/null"); + } xdup3(null, STDIN_FILENO, O_CLOEXEC); xdup3(null, STDOUT_FILENO, O_CLOEXEC); xdup3(null, STDERR_FILENO, O_CLOEXEC); if (null > STDERR_FILENO) close(null); + + int fd; + if (access("/proc/kmsg", W_OK) == 0) { + fd = xopen("/proc/kmsg", O_WRONLY | O_CLOEXEC); + } else { + mknod("/kmsg", S_IFCHR | 0666, makedev(1, 11)); + fd = xopen("/kmsg", O_WRONLY | O_CLOEXEC); + unlink("/kmsg"); + } + + kmsg = fdopen(fd, "w"); + setbuf(kmsg, nullptr); + log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk; + log_cb.ex = nop_ex; + strcpy(kmsg_buf, "magiskinit: "); } #else -#define setup_klog(...) +void setup_klog() {} #endif static bool unxz(int fd, const uint8_t *buf, size_t size) { @@ -131,13 +144,11 @@ public: } }; -class TestInit : public SARInit { +class TestInit : public BaseInit { public: - TestInit(char *argv[], cmdline *cmd) : SARInit(argv, cmd) {}; + TestInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {}; void start() override { - early_mount(); - patch_rootdir(); - cleanup(); + // Write init tests here } }; diff --git a/native/jni/init/init.h b/native/jni/init/init.h index c02545872..fc03cb541 100644 --- a/native/jni/init/init.h +++ b/native/jni/init/init.h @@ -168,3 +168,4 @@ public: void load_kernel_info(cmdline *cmd); int dump_magisk(const char *path, mode_t mode); int magisk_proxy_main(int argc, char *argv[]); +void setup_klog(); diff --git a/native/jni/init/rootdir.cpp b/native/jni/init/rootdir.cpp index e5a362e8f..9c3afd1ba 100644 --- a/native/jni/init/rootdir.cpp +++ b/native/jni/init/rootdir.cpp @@ -8,7 +8,6 @@ #include #include "init.h" -#include "flags.h" #include "magiskrc.h" #ifdef USE_64BIT @@ -466,23 +465,6 @@ void AFirstStageInit::prepare() { rename("/.backup/init", "/init"); } -#ifdef MAGISK_DEBUG -static FILE *kmsg; -static int vprintk(const char *fmt, va_list ap) { - fprintf(kmsg, "magiskinit: "); - return vfprintf(kmsg, fmt, ap); -} -static void setup_klog() { - int fd = xopen("/proc/kmsg", O_WRONLY | O_CLOEXEC); - kmsg = fdopen(fd, "w"); - setbuf(kmsg, nullptr); - log_cb.d = log_cb.i = log_cb.w = log_cb.e = vprintk; - log_cb.ex = nop_ex; -} -#else -#define setup_klog(...) -#endif - int magisk_proxy_main(int argc, char *argv[]) { setup_klog(); From 7681fde4d06e838db30c170764725f81c739821b Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 19 Nov 2019 00:16:20 -0500 Subject: [PATCH 68/74] Record mounts to be cleaned up in a vector --- native/jni/init/early_mount.cpp | 23 +++++++++-------------- native/jni/init/init.h | 16 +++++----------- native/jni/init/rootdir.cpp | 9 +++++---- native/jni/utils/misc.h | 21 ++++++++++++++++++++- 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/native/jni/init/early_mount.cpp b/native/jni/init/early_mount.cpp index eef42c888..672584ba2 100644 --- a/native/jni/init/early_mount.cpp +++ b/native/jni/init/early_mount.cpp @@ -84,6 +84,14 @@ static bool is_lnk(const char *name) { return S_ISLNK(st.st_mode); } +void BaseInit::cleanup() { + // Unmount in reverse order + for (auto &p : reversed(mount_list)) { + LOGD("Unmount [%s]\n", p.data()); + umount(p.data()); + } +} + bool MagiskInit::read_dt_fstab(const char *name, char *partname, char *fstype) { char path[128]; int fd; @@ -118,7 +126,7 @@ if (!is_lnk("/" #name) && read_dt_fstab(#name, partname, fstype)) { \ setup_block(partname, block_dev); \ xmkdir("/" #name, 0755); \ xmount(block_dev, "/" #name, fstype, MS_RDONLY, nullptr); \ - mnt_##name = true; \ + mount_list.emplace_back("/" #name); \ } void RootFSInit::early_mount() { @@ -237,16 +245,3 @@ void SecondStageInit::early_mount() { switch_root("/system_root"); } - -#define umount_root(name) \ -if (mnt_##name) \ - umount("/" #name); - -void MagiskInit::cleanup() { - umount(SELINUX_MNT); - BaseInit::cleanup(); - umount_root(system); - umount_root(vendor); - umount_root(product); - umount_root(odm); -} diff --git a/native/jni/init/init.h b/native/jni/init/init.h index fc03cb541..cb28a6ffa 100644 --- a/native/jni/init/init.h +++ b/native/jni/init/init.h @@ -1,6 +1,7 @@ #include #include #include +#include struct cmdline { bool system_as_root; @@ -34,19 +35,17 @@ class BaseInit { protected: cmdline *cmd; char **argv; + std::vector mount_list; void exec_init(const char *init = "/init") { cleanup(); execv(init, argv); exit(1); } - virtual void cleanup() { - umount("/sys"); - umount("/proc"); - umount("/dev"); - } + virtual void cleanup(); public: - BaseInit(char *argv[], cmdline *cmd) : cmd(cmd), argv(argv) {} + BaseInit(char *argv[], cmdline *cmd) : + cmd(cmd), argv(argv), mount_list{"/sys", "/proc", "/dev"} {} virtual ~BaseInit() = default; virtual void start() = 0; }; @@ -54,15 +53,10 @@ public: class MagiskInit : public BaseInit { protected: raw_data self; - bool mnt_system = false; - bool mnt_vendor = false; - bool mnt_product = false; - bool mnt_odm = false; virtual void early_mount() = 0; bool read_dt_fstab(const char *name, char *partname, char *fstype); bool patch_sepolicy(const char *file = "/sepolicy"); - void cleanup() override; public: MagiskInit(char *argv[], cmdline *cmd) : BaseInit(argv, cmd) {}; }; diff --git a/native/jni/init/rootdir.cpp b/native/jni/init/rootdir.cpp index 9c3afd1ba..9b59b34d7 100644 --- a/native/jni/init/rootdir.cpp +++ b/native/jni/init/rootdir.cpp @@ -167,6 +167,7 @@ bool MagiskInit::patch_sepolicy(const char *file) { // Mount selinuxfs to communicate with kernel xmount("selinuxfs", SELINUX_MNT, "selinuxfs", 0, nullptr); + mount_list.emplace_back(SELINUX_MNT); if (patch_init) load_split_cil(); @@ -230,7 +231,7 @@ static void sbin_overlay(const raw_data &self, const raw_data &config) { #define PATCHPOLICY "/sbin/.se" #define LIBSELINUX "/system/" LIBNAME "/libselinux.so" -static string mount_list; +static string magic_mount_list; static void magic_mount(int dirfd, const string &path) { DIR *dir = xfdopendir(dirfd); @@ -248,8 +249,8 @@ static void magic_mount(int dirfd, const string &path) { string src = ROOTOVL + dest; LOGD("Mount [%s] -> [%s]\n", src.data(), dest.data()); xmount(src.data(), dest.data(), nullptr, MS_BIND, nullptr); - mount_list += dest; - mount_list += '\n'; + magic_mount_list += dest; + magic_mount_list += '\n'; } } } @@ -379,7 +380,7 @@ void SARBase::patch_rootdir() { magic_mount(src, ""); close(src); dest = xopen(ROOTMNT, O_WRONLY | O_CREAT | O_CLOEXEC); - write(dest, mount_list.data(), mount_list.length()); + write(dest, magic_mount_list.data(), magic_mount_list.length()); close(dest); } diff --git a/native/jni/utils/misc.h b/native/jni/utils/misc.h index c5addb0fa..8733c0dfe 100644 --- a/native/jni/utils/misc.h +++ b/native/jni/utils/misc.h @@ -56,9 +56,28 @@ public: ~run_finally() { if (fn) fn(); } private: - std::function fn; + std::function fn; }; +template +class reversed_container { +public: + reversed_container(T &base) : base(base) {} + decltype(std::declval().rbegin()) begin() { return base.rbegin(); } + decltype(std::declval().crbegin()) begin() const { return base.crbegin(); } + decltype(std::declval().crbegin()) cbegin() const { return base.crbegin(); } + decltype(std::declval().rend()) end() { return base.rend(); } + decltype(std::declval().crend()) end() const { return base.crend(); } + decltype(std::declval().crend()) cend() const { return base.crend(); } +private: + T &base; +}; + +template +reversed_container reversed(T &base) { + return reversed_container(base); +} + static inline int parse_int(std::string s) { return parse_int(s.data()); } static inline int parse_int(std::string_view s) { return parse_int(s.data()); } From 9aff1a57d31d69815182f142820d0aaac1134fcc Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 19 Nov 2019 02:04:47 -0500 Subject: [PATCH 69/74] Cleanup headers --- native/jni/magiskpolicy/api.cpp | 5 +++ native/jni/magiskpolicy/magiskpolicy.h | 4 --- native/jni/magiskpolicy/sepolicy.c | 23 ++++++------- native/jni/magiskpolicy/sepolicy.h | 10 +++--- native/jni/systemproperties/include/hacks.h | 2 +- native/jni/utils/files.h | 15 ++------- native/jni/utils/include/logging.h | 11 ++----- native/jni/utils/include/selinux.h | 4 --- native/jni/utils/misc.h | 36 +++++++-------------- native/jni/utils/missing.h | 4 --- native/jni/utils/xwrap.h | 20 +++--------- 11 files changed, 45 insertions(+), 89 deletions(-) diff --git a/native/jni/magiskpolicy/api.cpp b/native/jni/magiskpolicy/api.cpp index ac4895bdf..2c29c4e68 100644 --- a/native/jni/magiskpolicy/api.cpp +++ b/native/jni/magiskpolicy/api.cpp @@ -51,6 +51,11 @@ int sepol_typemember(const char *s, const char *t, const char *c, const char *d) return add_type_rule(s, t, c, d, AVTAB_MEMBER); } +int sepol_nametrans(const char *s, const char *t, const char *c, const char *d, const char *o) { + // printf("name_trans %s %s %s %s %s\n", s, t, c, d, o); + return add_filename_trans(s, t, c, d, o); +} + int sepol_permissive(const char *s) { // printf("permissive %s\n", s); return set_domain_state(s, 1); diff --git a/native/jni/magiskpolicy/magiskpolicy.h b/native/jni/magiskpolicy/magiskpolicy.h index b7bd1a725..0dea4734f 100644 --- a/native/jni/magiskpolicy/magiskpolicy.h +++ b/native/jni/magiskpolicy/magiskpolicy.h @@ -8,8 +8,6 @@ #define ALL NULL -__BEGIN_DECLS - // policydb functions int load_policydb(const char *file); int load_split_cil(); @@ -37,5 +35,3 @@ int sepol_exists(const char *source); // Built in rules void sepol_magisk_rules(); - -__END_DECLS diff --git a/native/jni/magiskpolicy/sepolicy.c b/native/jni/magiskpolicy/sepolicy.c index ebf58ed73..f876fcd81 100644 --- a/native/jni/magiskpolicy/sepolicy.c +++ b/native/jni/magiskpolicy/sepolicy.c @@ -1,12 +1,13 @@ #include #include -#include #include -#include "magiskpolicy.h" #include "sepolicy.h" +extern void *xmalloc(size_t size); +extern void *xcalloc(size_t nmemb, size_t size); +extern void *xrealloc(void *ptr, size_t size); extern int policydb_index_decls(sepol_handle_t * handle, policydb_t * p); static int get_attr(const char *type, int value) { @@ -39,9 +40,9 @@ static int set_attr(const char *type, int value) { if (attr->flavor != TYPE_ATTRIB) return 1; - if(ebitmap_set_bit(&policydb->type_attr_map[value-1], attr->s.value-1, 1)) + if(ebitmap_set_bit(&policydb->type_attr_map[value - 1], attr->s.value - 1, 1)) return 1; - if(ebitmap_set_bit(&policydb->attr_type_map[attr->s.value-1], value-1, 1)) + if(ebitmap_set_bit(&policydb->attr_type_map[attr->s.value - 1], value - 1, 1)) return 1; return 0; @@ -223,7 +224,7 @@ int create_domain(const char *d) { return 0; } - type_datum_t *typedatum = (type_datum_t *) malloc(sizeof(type_datum_t)); + type_datum_t *typedatum = (type_datum_t *) xmalloc(sizeof(type_datum_t)); type_datum_init(typedatum); typedatum->primary = 1; typedatum->flavor = TYPE_TYPE; @@ -236,8 +237,8 @@ int create_domain(const char *d) { return 1; } - policydb->type_attr_map = realloc(policydb->type_attr_map, sizeof(ebitmap_t) * policydb->p_types.nprim); - policydb->attr_type_map = realloc(policydb->attr_type_map, sizeof(ebitmap_t) * policydb->p_types.nprim); + policydb->type_attr_map = xrealloc(policydb->type_attr_map, sizeof(ebitmap_t) * policydb->p_types.nprim); + policydb->attr_type_map = xrealloc(policydb->attr_type_map, sizeof(ebitmap_t) * policydb->p_types.nprim); ebitmap_init(&policydb->type_attr_map[value-1]); ebitmap_init(&policydb->attr_type_map[value-1]); ebitmap_set_bit(&policydb->type_attr_map[value-1], value-1, 1); @@ -292,7 +293,7 @@ int set_domain_state(const char *s, int state) { return 0; } -int sepol_nametrans(const char *s, const char *t, const char *c, const char *d, const char *o) { +int add_filename_trans(const char *s, const char *t, const char *c, const char *d, const char *o) { type_datum_t *src, *tgt, *def; class_datum_t *cls; @@ -350,12 +351,12 @@ int add_typeattribute(const char *domainS, const char *attr) { int typeId = get_attr_id(attr); //Now let's update all constraints! //(kernel doesn't support (yet?) type_names rules) - for(int i=0; ip_classes.nprim; ++i) { + for(int i = 0; i < policydb->p_classes.nprim; ++i) { class_datum_t *cl = policydb->class_val_to_struct[i]; for(constraint_node_t *n = cl->constraints; n ; n=n->next) { - for(constraint_expr_t *e = n->expr; e; e=e->next) { + for(constraint_expr_t *e = n->expr; e; e = e->next) { if(e->expr_type == CEXPR_NAMES) { - if(ebitmap_get_bit(&e->type_names->types, typeId-1)) { + if(ebitmap_get_bit(&e->type_names->types, typeId - 1)) { ebitmap_set_bit(&e->names, domain->s.value-1, 1); } } diff --git a/native/jni/magiskpolicy/sepolicy.h b/native/jni/magiskpolicy/sepolicy.h index 1ff36ee4c..b582cb9e1 100644 --- a/native/jni/magiskpolicy/sepolicy.h +++ b/native/jni/magiskpolicy/sepolicy.h @@ -1,6 +1,3 @@ -/* sepolicy.h - Header for magiskpolicy non-public APIs - */ - #pragma once #include @@ -21,10 +18,12 @@ extern policydb_t *policydb; } \ // hashtab traversal -#define hashtab_for_each(hashtab, cur, block) hash_for_each(htable, size, hashtab, cur, block) +#define hashtab_for_each(hashtab, cur, block) \ +hash_for_each(htable, size, hashtab, cur, block) // avtab traversal -#define avtab_for_each(avtab, cur, block) hash_for_each(htable, nslot, avtab, cur, block) +#define avtab_for_each(avtab, cur, block) \ +hash_for_each(htable, nslot, avtab, cur, block) int create_domain(const char *d); int set_domain_state(const char *s, int state); @@ -32,5 +31,6 @@ int add_typeattribute(const char *domainS, const char *attr); int add_rule(const char *s, const char *t, const char *c, const char *p, int effect, int n); int add_xperm_rule(const char *s, const char *t, const char *c, const char *range, int effect, int n); int add_type_rule(const char *s, const char *t, const char *c, const char *d, int effect); +int add_filename_trans(const char *s, const char *t, const char *c, const char *d, const char *o); __END_DECLS diff --git a/native/jni/systemproperties/include/hacks.h b/native/jni/systemproperties/include/hacks.h index a5abdb155..ddcf13b85 100644 --- a/native/jni/systemproperties/include/hacks.h +++ b/native/jni/systemproperties/include/hacks.h @@ -11,4 +11,4 @@ #endif #define getline __getline #define fsetxattr(...) syscall(__NR_fsetxattr, __VA_ARGS__) -extern "C" ssize_t __getline(char **, size_t *, FILE *); +ssize_t __getline(char **, size_t *, FILE *); diff --git a/native/jni/utils/files.h b/native/jni/utils/files.h index e38a5e8af..e23fba6b1 100644 --- a/native/jni/utils/files.h +++ b/native/jni/utils/files.h @@ -1,5 +1,8 @@ #pragma once +#include +#include + #define do_align(p, a) (((p) + (a) - 1) / (a) * (a)) #define align_off(p, a) (do_align(p, a) - (p)) @@ -8,10 +11,6 @@ struct file_attr { char con[128]; }; -#ifdef __cplusplus -extern "C" { -#endif - ssize_t fd_path(int fd, char *path, size_t size); int fd_pathat(int dirfd, const char *name, char *path, size_t size); int mkdirs(const char *pathname, mode_t mode); @@ -32,12 +31,6 @@ void fd_full_read(int fd, void **buf, size_t *size); void full_read(const char *filename, void **buf, size_t *size); void write_zero(int fd, size_t size); -#ifdef __cplusplus -} - -#include -#include - void file_readline(const char *file, const std::function &fn, bool trim = false); void parse_prop_file(const char *file, const std::function &fn); @@ -81,5 +74,3 @@ void mmap_rw(const char *filename, B &buf, L &sz) { buf = (B) __mmap(filename, &__sz, true); sz = __sz; } - -#endif \ No newline at end of file diff --git a/native/jni/utils/include/logging.h b/native/jni/utils/include/logging.h index 7dc1cd9ef..d4676f044 100644 --- a/native/jni/utils/include/logging.h +++ b/native/jni/utils/include/logging.h @@ -1,15 +1,10 @@ -/* logging.h - Error handling and logging - */ - #pragma once #include #include #include -#ifdef __cplusplus -extern "C" { -#endif +__BEGIN_DECLS typedef enum { L_DEBUG, @@ -43,6 +38,4 @@ void cmdline_logging(); int log_handler(log_type t, const char *fmt, ...); -#ifdef __cplusplus -} -#endif +__END_DECLS diff --git a/native/jni/utils/include/selinux.h b/native/jni/utils/include/selinux.h index 322afaaff..806d68458 100644 --- a/native/jni/utils/include/selinux.h +++ b/native/jni/utils/include/selinux.h @@ -18,8 +18,6 @@ #define SEPOL_PROC_DOMAIN "magisk" #define SEPOL_FILE_DOMAIN "magisk_file" -__BEGIN_DECLS - extern void (*freecon)(char *con); extern int (*setcon)(const char *con); extern int (*getfilecon)(const char *path, char **con); @@ -35,5 +33,3 @@ void selinux_builtin_impl(); void dload_selinux(); void restorecon(); void restore_rootcon(); - -__END_DECLS diff --git a/native/jni/utils/misc.h b/native/jni/utils/misc.h index 8733c0dfe..e5cd3721b 100644 --- a/native/jni/utils/misc.h +++ b/native/jni/utils/misc.h @@ -1,30 +1,11 @@ #pragma once -#define UID_ROOT 0 -#define UID_SHELL 2000 - -#ifdef __cplusplus -extern "C" { -#endif - -int fork_dont_care(); -int fork_no_zombie(); -int strend(const char *s1, const char *s2); -char *rtrim(char *str); -void init_argv0(int argc, char **argv); -void set_nice_name(const char *name); -int parse_int(const char *s); -uint32_t binary_gcd(uint32_t u, uint32_t v); -int switch_mnt_ns(int pid); - -#ifdef __cplusplus -} - #include #include #include -void gen_rand_str(char *buf, int len, bool varlen = true); +#define UID_ROOT 0 +#define UID_SHELL 2000 #define str_contains(s, ss) ((ss) != nullptr && (s).find(ss) != std::string::npos) #define str_starts(s, ss) ((ss) != nullptr && (s).compare(0, strlen(ss), ss) == 0) @@ -78,8 +59,8 @@ reversed_container reversed(T &base) { return reversed_container(base); } +int parse_int(const char *s); static inline int parse_int(std::string s) { return parse_int(s.data()); } - static inline int parse_int(std::string_view s) { return parse_int(s.data()); } int new_daemon_thread(void *(*start_routine) (void *), void *arg = nullptr, @@ -115,5 +96,12 @@ int exec_command_sync(Args &&...args) { } bool ends_with(const std::string_view &s1, const std::string_view &s2); - -#endif +int fork_dont_care(); +int fork_no_zombie(); +int strend(const char *s1, const char *s2); +char *rtrim(char *str); +void init_argv0(int argc, char **argv); +void set_nice_name(const char *name); +uint32_t binary_gcd(uint32_t u, uint32_t v); +int switch_mnt_ns(int pid); +void gen_rand_str(char *buf, int len, bool varlen = true); diff --git a/native/jni/utils/missing.h b/native/jni/utils/missing.h index d437f784a..7909e56c3 100644 --- a/native/jni/utils/missing.h +++ b/native/jni/utils/missing.h @@ -19,8 +19,6 @@ #define endmntent __endmntent #define hasmntopt __hasmntopt -__BEGIN_DECLS - ssize_t __getline(char **lineptr, size_t *n, FILE *stream); ssize_t __getdelim(char **lineptr, size_t *n, int delim, FILE *stream); struct mntent *__getmntent_r(FILE* fp, struct mntent* e, char* buf, int buf_len); @@ -60,5 +58,3 @@ static inline int __linkat(int olddirfd, const char *oldpath, static inline int __inotify_init1(int flags) { return syscall(__NR_inotify_init1, flags); } - -__END_DECLS diff --git a/native/jni/utils/xwrap.h b/native/jni/utils/xwrap.h index 4f4d003d6..106e15b87 100644 --- a/native/jni/utils/xwrap.h +++ b/native/jni/utils/xwrap.h @@ -1,11 +1,9 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - FILE *xfopen(const char *pathname, const char *mode); FILE *xfdopen(int fd, const char *mode); +int xopen(const char *pathname, int flags); +int xopen(const char *pathname, int flags, mode_t mode); int xopenat(int dirfd, const char *pathname, int flags); ssize_t xwrite(int fd, const void *buf, size_t count); ssize_t xread(int fd, void *buf, size_t count); @@ -22,9 +20,9 @@ int xbind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int xconnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); int xlisten(int sockfd, int backlog); int xaccept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags); -void *xmalloc(size_t size); -void *xcalloc(size_t nmemb, size_t size); -void *xrealloc(void *ptr, size_t size); +extern "C" void *xmalloc(size_t size); +extern "C" void *xcalloc(size_t nmemb, size_t size); +extern "C" void *xrealloc(void *ptr, size_t size); ssize_t xsendmsg(int sockfd, const struct msghdr *msg, int flags); ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags); int xpthread_create(pthread_t *thread, const pthread_attr_t *attr, @@ -54,11 +52,3 @@ pid_t xfork(); int xpoll(struct pollfd *fds, nfds_t nfds, int timeout); int xinotify_init1(int flags); -#ifdef __cplusplus -} - -int xopen(const char *pathname, int flags); -int xopen(const char *pathname, int flags, mode_t mode); - -#endif - From d6fb9868bf6f25e58e77c1c35629c0905f8f60da Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Tue, 19 Nov 2019 05:20:18 -0500 Subject: [PATCH 70/74] Small sepolicy refactor and fixes --- native/jni/magiskpolicy/api.cpp | 37 ++-- native/jni/magiskpolicy/magiskpolicy.cpp | 2 +- native/jni/magiskpolicy/policydb.cpp | 22 +-- native/jni/magiskpolicy/rules.cpp | 10 +- native/jni/magiskpolicy/sepolicy.c | 225 +++++++++++------------ native/jni/magiskpolicy/sepolicy.h | 23 +-- 6 files changed, 149 insertions(+), 170 deletions(-) diff --git a/native/jni/magiskpolicy/api.cpp b/native/jni/magiskpolicy/api.cpp index 2c29c4e68..8fa498ae2 100644 --- a/native/jni/magiskpolicy/api.cpp +++ b/native/jni/magiskpolicy/api.cpp @@ -1,81 +1,84 @@ #include "magiskpolicy.h" #include "sepolicy.h" +//#define vprint(fmt, ...) printf(fmt, __VA_ARGS__) +#define vprint(...) + int sepol_allow(const char *s, const char *t, const char *c, const char *p) { -// printf("allow %s %s %s %s\n", s, t, c, p); + vprint("allow %s %s %s %s\n", s, t, c, p); return add_rule(s, t, c, p, AVTAB_ALLOWED, 0); } int sepol_deny(const char *s, const char *t, const char *c, const char *p) { - // printf("deny %s %s %s %s\n", s, t, c, p); + vprint("deny %s %s %s %s\n", s, t, c, p); return add_rule(s, t, c, p, AVTAB_ALLOWED, 1); } int sepol_auditallow(const char *s, const char *t, const char *c, const char *p) { - // printf("auditallow %s %s %s %s\n", s, t, c, p); + vprint("auditallow %s %s %s %s\n", s, t, c, p); return add_rule(s, t, c, p, AVTAB_AUDITALLOW, 0); } int sepol_dontaudit(const char *s, const char *t, const char *c, const char *p) { - // printf("dontaudit %s %s %s %s\n", s, t, c, p); - return add_rule(s, t, c, p, AVTAB_AUDITDENY, 0); + vprint("dontaudit %s %s %s %s\n", s, t, c, p); + return add_rule(s, t, c, p, AVTAB_AUDITDENY, 1); } int sepol_allowxperm(const char *s, const char *t, const char *c, const char *range) { - // printf("allowxperm %s %s %s %s\n", s, t, c, range); + vprint("allowxperm %s %s %s %s\n", s, t, c, range); return add_xperm_rule(s, t, c, range, AVTAB_XPERMS_ALLOWED, 0); } int sepol_auditallowxperm(const char *s, const char *t, const char *c, const char *range) { - // printf("auditallowxperm %s %s %s %s\n", s, t, c, range); + vprint("auditallowxperm %s %s %s %s\n", s, t, c, range); return add_xperm_rule(s, t, c, range, AVTAB_XPERMS_AUDITALLOW, 0); } int sepol_dontauditxperm(const char *s, const char *t, const char *c, const char *range) { - // printf("dontauditxperm %s %s %s %s\n", s, t, c, range); + vprint("dontauditxperm %s %s %s %s\n", s, t, c, range); return add_xperm_rule(s, t, c, range, AVTAB_XPERMS_DONTAUDIT, 0); } int sepol_typetrans(const char *s, const char *t, const char *c, const char *d) { - // printf("type_transition %s %s %s %s\n", s, t, c, d); + vprint("type_transition %s %s %s %s\n", s, t, c, d); return add_type_rule(s, t, c, d, AVTAB_TRANSITION); } int sepol_typechange(const char *s, const char *t, const char *c, const char *d) { - // printf("type_change %s %s %s %s\n", s, t, c, d); + vprint("type_change %s %s %s %s\n", s, t, c, d); return add_type_rule(s, t, c, d, AVTAB_CHANGE); } int sepol_typemember(const char *s, const char *t, const char *c, const char *d) { - // printf("type_member %s %s %s %s\n", s, t, c, d); + vprint("type_member %s %s %s %s\n", s, t, c, d); return add_type_rule(s, t, c, d, AVTAB_MEMBER); } int sepol_nametrans(const char *s, const char *t, const char *c, const char *d, const char *o) { - // printf("name_trans %s %s %s %s %s\n", s, t, c, d, o); + vprint("name_trans %s %s %s %s %s\n", s, t, c, d, o); return add_filename_trans(s, t, c, d, o); } int sepol_permissive(const char *s) { - // printf("permissive %s\n", s); + vprint("permissive %s\n", s); return set_domain_state(s, 1); } int sepol_enforce(const char *s) { - // printf("enforce %s\n", s); + vprint("enforce %s\n", s); return set_domain_state(s, 0); } int sepol_create(const char *s) { - // printf("create %s\n", s); + vprint("create %s\n", s); return create_domain(s); } int sepol_attradd(const char *s, const char *a) { - // printf("attradd %s %s\n", s, a); + vprint("attradd %s %s\n", s, a); return add_typeattribute(s, a); } int sepol_exists(const char *source) { - return hashtab_search(policydb->p_types.table, source) != nullptr; + return hashtab_search(magisk_policydb->p_types.table, source) != nullptr; } diff --git a/native/jni/magiskpolicy/magiskpolicy.cpp b/native/jni/magiskpolicy/magiskpolicy.cpp index 9344bb886..d612ee3fb 100644 --- a/native/jni/magiskpolicy/magiskpolicy.cpp +++ b/native/jni/magiskpolicy/magiskpolicy.cpp @@ -487,7 +487,7 @@ int magiskpolicy_main(int argc, char *argv[]) { } // Use current policy if nothing is loaded - if (policydb == nullptr && load_policydb(SELINUX_POLICY)) { + if (magisk_policydb == nullptr && load_policydb(SELINUX_POLICY)) { fprintf(stderr, "Cannot load policy from " SELINUX_POLICY "\n"); return 1; } diff --git a/native/jni/magiskpolicy/policydb.cpp b/native/jni/magiskpolicy/policydb.cpp index 9c04938c6..ec115efb7 100644 --- a/native/jni/magiskpolicy/policydb.cpp +++ b/native/jni/magiskpolicy/policydb.cpp @@ -11,10 +11,8 @@ #include "magiskpolicy.h" #include "sepolicy.h" -policydb_t *policydb = nullptr; - int load_policydb(const char *file) { - if (policydb) + if (magisk_policydb) destroy_policydb(); struct policy_file pf; @@ -22,8 +20,8 @@ int load_policydb(const char *file) { pf.fp = xfopen(file, "re"); pf.type = PF_USE_STDIO; - policydb = new policydb_t(); - if (policydb_init(policydb) || policydb_read(policydb, &pf, 0)) + magisk_policydb = static_cast(xmalloc(sizeof(policydb_t))); + if (policydb_init(magisk_policydb) || policydb_read(magisk_policydb, &pf, 0)) return 1; fclose(pf.fp); @@ -169,7 +167,7 @@ int compile_split_cil() { return 1; cil_db_destroy(&db); - policydb = &pdb->p; + magisk_policydb = &pdb->p; return 0; } @@ -177,7 +175,7 @@ int dump_policydb(const char *file) { int fd, ret; void *data = nullptr; size_t len; - policydb_to_image(nullptr, policydb, &data, &len); + policydb_to_image(nullptr, magisk_policydb, &data, &len); if (data == nullptr) { LOGE("Fail to dump policy image!\n"); return 1; @@ -194,9 +192,9 @@ int dump_policydb(const char *file) { } void destroy_policydb() { - if (policydb) { - policydb_destroy(policydb); - delete policydb; - policydb = nullptr; + if (magisk_policydb) { + policydb_destroy(magisk_policydb); + free(magisk_policydb); + magisk_policydb = nullptr; } -} \ No newline at end of file +} diff --git a/native/jni/magiskpolicy/rules.cpp b/native/jni/magiskpolicy/rules.cpp index eb6f6df47..6ea22e408 100644 --- a/native/jni/magiskpolicy/rules.cpp +++ b/native/jni/magiskpolicy/rules.cpp @@ -21,7 +21,7 @@ static void allowSuClient(const char *target) { sepol_allow(target, "untrusted_app_devpts", "chr_file", "ioctl"); sepol_allow(target, "untrusted_app_25_devpts", "chr_file", "ioctl"); sepol_allow(target, "untrusted_app_all_devpts", "chr_file", "ioctl"); - if (policydb->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) { + if (magisk_policydb->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) { sepol_allowxperm(target, "devpts", "chr_file", "0x5400-0x54FF"); sepol_allowxperm(target, "untrusted_app_devpts", "chr_file", "0x5400-0x54FF"); sepol_allowxperm(target, "untrusted_app_25_devpts", "chr_file", "0x5400-0x54FF"); @@ -166,7 +166,7 @@ void sepol_magisk_rules() { sepol_allow(SEPOL_PROC_DOMAIN, ALL, "fifo_file", ALL); // Allow us to do any ioctl on all block devices - if (policydb->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) + if (magisk_policydb->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) sepol_allowxperm(SEPOL_PROC_DOMAIN, ALL, "blk_file", "0x0000-0xFFFF"); // Allow all binder transactions @@ -198,11 +198,7 @@ void sepol_magisk_rules() { #ifdef MAGISK_DEBUG // Remove all dontaudit in debug mode - avtab_ptr_t av; - avtab_for_each(&policydb->te_avtab, av, { - if (av->key.specified == AVTAB_AUDITDENY || av->key.specified == AVTAB_XPERMS_DONTAUDIT) - avtab_remove_node(&policydb->te_avtab, av); - }) + strip_dontaudit(); #endif log_cb.w = bak; diff --git a/native/jni/magiskpolicy/sepolicy.c b/native/jni/magiskpolicy/sepolicy.c index f876fcd81..6b9cd6fad 100644 --- a/native/jni/magiskpolicy/sepolicy.c +++ b/native/jni/magiskpolicy/sepolicy.c @@ -5,47 +5,44 @@ #include "sepolicy.h" +policydb_t *magisk_policydb = NULL; +#define mpdb magisk_policydb + extern void *xmalloc(size_t size); extern void *xcalloc(size_t nmemb, size_t size); extern void *xrealloc(void *ptr, size_t size); extern int policydb_index_decls(sepol_handle_t * handle, policydb_t * p); -static int get_attr(const char *type, int value) { - type_datum_t *attr = hashtab_search(policydb->p_types.table, type); - if (!attr) - return 1; +// Generic hash table traversal +#define hash_for_each(node_ptr, n_slots, table, block) \ + for (int __i = 0; __i < (table)->n_slots; ++__i) { \ + __typeof__(*(table)->node_ptr) node; \ + __typeof__(node) __next; \ + for (node = (table)->node_ptr[__i]; node; node = __next) { \ + __next = node->next; \ + block \ + } \ + } \ - if (attr->flavor != TYPE_ATTRIB) - return 1; +// hashtab traversal +#define hashtab_for_each(hashtab, block) \ +hash_for_each(htable, size, hashtab, block) - return ebitmap_get_bit(&policydb->attr_type_map[attr->s.value - 1], value - 1) != 0; -} - -static int get_attr_id(const char *type) { - type_datum_t *attr = hashtab_search(policydb->p_types.table, type); - if (!attr) - return 1; - - if (attr->flavor != TYPE_ATTRIB) - return 1; - - return attr->s.value; -} +// avtab traversal +#define avtab_for_each(avtab, block) \ +hash_for_each(htable, nslot, avtab, block) static int set_attr(const char *type, int value) { - type_datum_t *attr = hashtab_search(policydb->p_types.table, type); - if (!attr) - return 1; + type_datum_t *attr = hashtab_search(mpdb->p_types.table, type); + if (!attr || attr->flavor != TYPE_ATTRIB) + return -1; - if (attr->flavor != TYPE_ATTRIB) - return 1; + if (ebitmap_set_bit(&mpdb->type_attr_map[value - 1], attr->s.value - 1, 1)) + return -1; + if (ebitmap_set_bit(&mpdb->attr_type_map[attr->s.value - 1], value - 1, 1)) + return -1; - if(ebitmap_set_bit(&policydb->type_attr_map[value - 1], attr->s.value - 1, 1)) - return 1; - if(ebitmap_set_bit(&policydb->attr_type_map[attr->s.value - 1], value - 1, 1)) - return 1; - - return 0; + return attr->s.value; } static void check_avtab_node(avtab_ptr_t node) { @@ -57,7 +54,7 @@ static void check_avtab_node(avtab_ptr_t node) { else redundant = node->datum.data == 0U; if (redundant) - avtab_remove_node(&policydb->te_avtab, node); + avtab_remove_node(&mpdb->te_avtab, node); } static avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xperms) { @@ -67,7 +64,7 @@ static avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xper /* AVTAB_XPERMS entries are not necessarily unique */ if (key->specified & AVTAB_XPERMS) { - node = avtab_search_node(&policydb->te_avtab, key); + node = avtab_search_node(&mpdb->te_avtab, key); while (node) { if ((node->datum.xperms->specified == xperms->specified) && (node->datum.xperms->driver == xperms->driver)) { @@ -79,7 +76,7 @@ static avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xper if (!match) node = NULL; } else { - node = avtab_search_node(&policydb->te_avtab, key); + node = avtab_search_node(&mpdb->te_avtab, key); } if (!node) { @@ -90,26 +87,27 @@ static avtab_ptr_t get_avtab_node(avtab_key_t *key, avtab_extended_perms_t *xper */ avdatum.data = key->specified == AVTAB_AUDITDENY ? ~0U : 0U; /* this is used to get the node - insertion is actually unique */ - node = avtab_insert_nonunique(&policydb->te_avtab, key, &avdatum); + node = avtab_insert_nonunique(&mpdb->te_avtab, key, &avdatum); } return node; } -static int add_avrule(avtab_key_t *key, int p, int not) { +static int add_avrule(avtab_key_t *key, int val, int not) { avtab_ptr_t node = get_avtab_node(key, NULL); - // Support DONTAUDIT (AUDITDENY is inverted) - if (AVTAB_AUDITDENY == node->key.specified == !not) { - if (p < 0) + + if (not) { + if (val < 0) node->datum.data = 0U; else - node->datum.data &= ~(1U << (p - 1)); + node->datum.data &= ~(1U << (val - 1)); } else { - if (p < 0) + if (val < 0) node->datum.data = ~0U; else - node->datum.data |= 1U << (p - 1); + node->datum.data |= 1U << (val - 1); } + check_avtab_node(node); return 0; } @@ -117,22 +115,21 @@ static int add_avrule(avtab_key_t *key, int p, int not) { static int add_rule_auto(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, perm_datum_t *perm, int effect, int not) { avtab_key_t key; - hashtab_ptr_t cur; int ret = 0; if (src == NULL) { - hashtab_for_each(policydb->p_types.table, cur, { - src = cur->datum; + hashtab_for_each(mpdb->p_types.table, { + src = node->datum; ret |= add_rule_auto(src, tgt, cls, perm, effect, not); }) } else if (tgt == NULL) { - hashtab_for_each(policydb->p_types.table, cur, { - tgt = cur->datum; + hashtab_for_each(mpdb->p_types.table, { + tgt = node->datum; ret |= add_rule_auto(src, tgt, cls, perm, effect, not); }) } else if (cls == NULL) { - hashtab_for_each(policydb->p_classes.table, cur, { - cls = cur->datum; + hashtab_for_each(mpdb->p_classes.table, { + cls = node->datum; ret |= add_rule_auto(src, tgt, cls, perm, effect, not); }) } else { @@ -189,22 +186,21 @@ static int add_avxrule(avtab_key_t *key, uint16_t low, uint16_t high, int not) { static int add_xperm_rule_auto(type_datum_t *src, type_datum_t *tgt, class_datum_t *cls, uint16_t low, uint16_t high, int effect, int not) { avtab_key_t key; - hashtab_ptr_t cur; int ret = 0; if (src == NULL) { - hashtab_for_each(policydb->p_types.table, cur, { - src = cur->datum; + hashtab_for_each(mpdb->p_types.table, { + src = node->datum; ret |= add_xperm_rule_auto(src, tgt, cls, low, high, effect, not); }) } else if (tgt == NULL) { - hashtab_for_each(policydb->p_types.table, cur, { - tgt = cur->datum; + hashtab_for_each(mpdb->p_types.table, { + tgt = node->datum; ret |= add_xperm_rule_auto(src, tgt, cls, low, high, effect, not); }) } else if (cls == NULL) { - hashtab_for_each(policydb->p_classes.table, cur, { - cls = cur->datum; + hashtab_for_each(mpdb->p_classes.table, { + cls = node->datum; ret |= add_xperm_rule_auto(src, tgt, cls, low, high, effect, not); }) } else { @@ -218,50 +214,50 @@ static int add_xperm_rule_auto(type_datum_t *src, type_datum_t *tgt, class_datum } int create_domain(const char *d) { - symtab_datum_t *src = hashtab_search(policydb->p_types.table, d); - if(src) { + symtab_datum_t *src = hashtab_search(mpdb->p_types.table, d); + if (src) { LOGW("Domain %s already exists\n", d); return 0; } - type_datum_t *typedatum = (type_datum_t *) xmalloc(sizeof(type_datum_t)); + type_datum_t *typedatum = xmalloc(sizeof(type_datum_t)); type_datum_init(typedatum); typedatum->primary = 1; typedatum->flavor = TYPE_TYPE; uint32_t value = 0; - symtab_insert(policydb, SYM_TYPES, strdup(d), typedatum, SCOPE_DECL, 1, &value); + symtab_insert(mpdb, SYM_TYPES, strdup(d), typedatum, SCOPE_DECL, 1, &value); typedatum->s.value = value; - if (ebitmap_set_bit(&policydb->global->branch_list->declared.scope[SYM_TYPES], value - 1, 1)) { + if (ebitmap_set_bit(&mpdb->global->branch_list->declared.scope[SYM_TYPES], value - 1, 1)) { return 1; } - policydb->type_attr_map = xrealloc(policydb->type_attr_map, sizeof(ebitmap_t) * policydb->p_types.nprim); - policydb->attr_type_map = xrealloc(policydb->attr_type_map, sizeof(ebitmap_t) * policydb->p_types.nprim); - ebitmap_init(&policydb->type_attr_map[value-1]); - ebitmap_init(&policydb->attr_type_map[value-1]); - ebitmap_set_bit(&policydb->type_attr_map[value-1], value-1, 1); + mpdb->type_attr_map = xrealloc(mpdb->type_attr_map, sizeof(ebitmap_t) * mpdb->p_types.nprim); + mpdb->attr_type_map = xrealloc(mpdb->attr_type_map, sizeof(ebitmap_t) * mpdb->p_types.nprim); + ebitmap_init(&mpdb->type_attr_map[value-1]); + ebitmap_init(&mpdb->attr_type_map[value-1]); + ebitmap_set_bit(&mpdb->type_attr_map[value-1], value-1, 1); - src = hashtab_search(policydb->p_types.table, d); + src = hashtab_search(mpdb->p_types.table, d); if(!src) return 1; - if(policydb_index_decls(NULL, policydb)) + if(policydb_index_decls(NULL, mpdb)) return 1; - if(policydb_index_classes(policydb)) + if(policydb_index_classes(mpdb)) return 1; - if(policydb_index_others(NULL, policydb, 0)) + if(policydb_index_others(NULL, mpdb, 0)) return 1; //Add the domain to all roles - for(unsigned i=0; ip_roles.nprim; ++i) { + for(unsigned i = 0; i < mpdb->p_roles.nprim; ++i) { //Not sure all those three calls are needed - ebitmap_set_bit(&policydb->role_val_to_struct[i]->types.negset, value-1, 0); - ebitmap_set_bit(&policydb->role_val_to_struct[i]->types.types, value-1, 1); - type_set_expand(&policydb->role_val_to_struct[i]->types, &policydb->role_val_to_struct[i]->cache, policydb, 0); + ebitmap_set_bit(&mpdb->role_val_to_struct[i]->types.negset, value - 1, 0); + ebitmap_set_bit(&mpdb->role_val_to_struct[i]->types.types, value - 1, 1); + type_set_expand(&mpdb->role_val_to_struct[i]->types, &mpdb->role_val_to_struct[i]->cache, mpdb, 0); } return set_attr("domain", value); @@ -269,22 +265,21 @@ int create_domain(const char *d) { int set_domain_state(const char *s, int state) { type_datum_t *type; - hashtab_ptr_t cur; if (s == NULL) { - hashtab_for_each(policydb->p_types.table, cur, { - type = cur->datum; - if (ebitmap_set_bit(&policydb->permissive_map, type->s.value, state)) { + hashtab_for_each(mpdb->p_types.table, { + type = node->datum; + if (ebitmap_set_bit(&mpdb->permissive_map, type->s.value, state)) { LOGW("Could not set bit in permissive map\n"); return 1; } }) } else { - type = hashtab_search(policydb->p_types.table, s); + type = hashtab_search(mpdb->p_types.table, s); if (type == NULL) { LOGW("type %s does not exist\n", s); return 1; } - if (ebitmap_set_bit(&policydb->permissive_map, type->s.value, state)) { + if (ebitmap_set_bit(&mpdb->permissive_map, type->s.value, state)) { LOGW("Could not set bit in permissive map\n"); return 1; } @@ -297,22 +292,22 @@ int add_filename_trans(const char *s, const char *t, const char *c, const char * type_datum_t *src, *tgt, *def; class_datum_t *cls; - src = hashtab_search(policydb->p_types.table, s); + src = hashtab_search(mpdb->p_types.table, s); if (src == NULL) { LOGW("source type %s does not exist\n", s); return 1; } - tgt = hashtab_search(policydb->p_types.table, t); + tgt = hashtab_search(mpdb->p_types.table, t); if (tgt == NULL) { LOGW("target type %s does not exist\n", t); return 1; } - cls = hashtab_search(policydb->p_classes.table, c); + cls = hashtab_search(mpdb->p_classes.table, c); if (cls == NULL) { LOGW("class %s does not exist\n", c); return 1; } - def = hashtab_search(policydb->p_types.table, d); + def = hashtab_search(mpdb->p_types.table, d); if (def == NULL) { LOGW("default type %s does not exist\n", d); return 1; @@ -325,11 +320,11 @@ int add_filename_trans(const char *s, const char *t, const char *c, const char * trans_key.name = (char *) o; filename_trans_datum_t *trans_datum; - trans_datum = hashtab_search(policydb->p_types.table, (hashtab_key_t) &trans_key); + trans_datum = hashtab_search(mpdb->filename_trans, (hashtab_key_t) &trans_key); if (trans_datum == NULL) { trans_datum = xcalloc(sizeof(*trans_datum), 1); - hashtab_insert(policydb->filename_trans, (hashtab_key_t) &trans_key, trans_datum); + hashtab_insert(mpdb->filename_trans, (hashtab_key_t) &trans_key, trans_datum); } // Overwrite existing @@ -337,32 +332,29 @@ int add_filename_trans(const char *s, const char *t, const char *c, const char * return 0; } -int add_typeattribute(const char *domainS, const char *attr) { - type_datum_t *domain; - - domain = hashtab_search(policydb->p_types.table, domainS); +int add_typeattribute(const char *type, const char *attr) { + type_datum_t *domain = hashtab_search(mpdb->p_types.table, type); if (domain == NULL) { - LOGW("source type %s does not exist\n", domainS); + LOGW("type %s does not exist\n", type); return 1; } - set_attr(attr, domain->s.value); + int attr_id = set_attr(attr, domain->s.value); + if (attr_id < 0) + return 1; - int typeId = get_attr_id(attr); - //Now let's update all constraints! - //(kernel doesn't support (yet?) type_names rules) - for(int i = 0; i < policydb->p_classes.nprim; ++i) { - class_datum_t *cl = policydb->class_val_to_struct[i]; - for(constraint_node_t *n = cl->constraints; n ; n=n->next) { - for(constraint_expr_t *e = n->expr; e; e = e->next) { - if(e->expr_type == CEXPR_NAMES) { - if(ebitmap_get_bit(&e->type_names->types, typeId - 1)) { - ebitmap_set_bit(&e->names, domain->s.value-1, 1); - } + hashtab_for_each(mpdb->p_classes.table, { + class_datum_t *cls = node->datum; + for (constraint_node_t *n = cls->constraints; n ; n = n->next) { + for (constraint_expr_t *e = n->expr; e; e = e->next) { + if (e->expr_type == CEXPR_NAMES && + ebitmap_get_bit(&e->type_names->types, attr_id - 1)) { + ebitmap_set_bit(&e->names, domain->s.value - 1, 1); } } } - } + }) + return 0; } @@ -372,7 +364,7 @@ int add_rule(const char *s, const char *t, const char *c, const char *p, int eff perm_datum_t *perm = NULL; if (s) { - src = hashtab_search(policydb->p_types.table, s); + src = hashtab_search(mpdb->p_types.table, s); if (src == NULL) { LOGW("source type %s does not exist\n", s); return 1; @@ -380,7 +372,7 @@ int add_rule(const char *s, const char *t, const char *c, const char *p, int eff } if (t) { - tgt = hashtab_search(policydb->p_types.table, t); + tgt = hashtab_search(mpdb->p_types.table, t); if (tgt == NULL) { LOGW("target type %s does not exist\n", t); return 1; @@ -388,7 +380,7 @@ int add_rule(const char *s, const char *t, const char *c, const char *p, int eff } if (c) { - cls = hashtab_search(policydb->p_classes.table, c); + cls = hashtab_search(mpdb->p_classes.table, c); if (cls == NULL) { LOGW("class %s does not exist\n", c); return 1; @@ -419,7 +411,7 @@ int add_xperm_rule(const char *s, const char *t, const char *c, const char *rang class_datum_t *cls = NULL; if (s) { - src = hashtab_search(policydb->p_types.table, s); + src = hashtab_search(mpdb->p_types.table, s); if (src == NULL) { LOGW("source type %s does not exist\n", s); return 1; @@ -427,7 +419,7 @@ int add_xperm_rule(const char *s, const char *t, const char *c, const char *rang } if (t) { - tgt = hashtab_search(policydb->p_types.table, t); + tgt = hashtab_search(mpdb->p_types.table, t); if (tgt == NULL) { LOGW("target type %s does not exist\n", t); return 1; @@ -435,7 +427,7 @@ int add_xperm_rule(const char *s, const char *t, const char *c, const char *rang } if (c) { - cls = hashtab_search(policydb->p_classes.table, c); + cls = hashtab_search(mpdb->p_classes.table, c); if (cls == NULL) { LOGW("class %s does not exist\n", c); return 1; @@ -463,22 +455,22 @@ int add_type_rule(const char *s, const char *t, const char *c, const char *d, in type_datum_t *src, *tgt, *def; class_datum_t *cls; - src = hashtab_search(policydb->p_types.table, s); + src = hashtab_search(mpdb->p_types.table, s); if (src == NULL) { LOGW("source type %s does not exist\n", s); return 1; } - tgt = hashtab_search(policydb->p_types.table, t); + tgt = hashtab_search(mpdb->p_types.table, t); if (tgt == NULL) { LOGW("target type %s does not exist\n", t); return 1; } - cls = hashtab_search(policydb->p_classes.table, c); + cls = hashtab_search(mpdb->p_classes.table, c); if (cls == NULL) { LOGW("class %s does not exist\n", c); return 1; } - def = hashtab_search(policydb->p_types.table, d); + def = hashtab_search(mpdb->p_types.table, d); if (def == NULL) { LOGW("default type %s does not exist\n", d); return 1; @@ -495,3 +487,10 @@ int add_type_rule(const char *s, const char *t, const char *c, const char *d, in return 0; } + +void strip_dontaudit() { + avtab_for_each(&mpdb->te_avtab, { + if (node->key.specified == AVTAB_AUDITDENY || node->key.specified == AVTAB_XPERMS_DONTAUDIT) + avtab_remove_node(&magisk_policydb->te_avtab, node); + }) +} diff --git a/native/jni/magiskpolicy/sepolicy.h b/native/jni/magiskpolicy/sepolicy.h index b582cb9e1..ecff77ef0 100644 --- a/native/jni/magiskpolicy/sepolicy.h +++ b/native/jni/magiskpolicy/sepolicy.h @@ -5,32 +5,15 @@ __BEGIN_DECLS // Global policydb -extern policydb_t *policydb; - -// General hash table traversal -#define hash_for_each(table, slots, tab, cur, block) \ - for (int __i = 0; __i < (tab)->slots; ++__i) { \ - __typeof__(cur) __next; \ - for (cur = (tab)->table[__i]; cur; cur = __next) { \ - __next = cur->next; \ - block \ - } \ - } \ - -// hashtab traversal -#define hashtab_for_each(hashtab, cur, block) \ -hash_for_each(htable, size, hashtab, cur, block) - -// avtab traversal -#define avtab_for_each(avtab, cur, block) \ -hash_for_each(htable, nslot, avtab, cur, block) +extern policydb_t *magisk_policydb; int create_domain(const char *d); int set_domain_state(const char *s, int state); -int add_typeattribute(const char *domainS, const char *attr); +int add_typeattribute(const char *type, const char *attr); int add_rule(const char *s, const char *t, const char *c, const char *p, int effect, int n); int add_xperm_rule(const char *s, const char *t, const char *c, const char *range, int effect, int n); int add_type_rule(const char *s, const char *t, const char *c, const char *d, int effect); int add_filename_trans(const char *s, const char *t, const char *c, const char *d, const char *o); +void strip_dontaudit(); __END_DECLS From bb9ce0e8972183dd8fb6ecbfe6f7d4e54fed4999 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 20 Nov 2019 03:47:15 -0500 Subject: [PATCH 71/74] Make sepolicy dump more efficient --- native/jni/magiskpolicy/policydb.cpp | 28 ++++++---- native/jni/utils/file.cpp | 84 ++++++++++++++++++++++++++++ native/jni/utils/files.h | 1 + 3 files changed, 102 insertions(+), 11 deletions(-) diff --git a/native/jni/magiskpolicy/policydb.cpp b/native/jni/magiskpolicy/policydb.cpp index ec115efb7..120c209ff 100644 --- a/native/jni/magiskpolicy/policydb.cpp +++ b/native/jni/magiskpolicy/policydb.cpp @@ -21,11 +21,12 @@ int load_policydb(const char *file) { pf.type = PF_USE_STDIO; magisk_policydb = static_cast(xmalloc(sizeof(policydb_t))); - if (policydb_init(magisk_policydb) || policydb_read(magisk_policydb, &pf, 0)) + if (policydb_init(magisk_policydb) || policydb_read(magisk_policydb, &pf, 0)) { + LOGE("Fail to load policy from %s\n", file); return 1; + } fclose(pf.fp); - return 0; } @@ -172,22 +173,27 @@ int compile_split_cil() { } int dump_policydb(const char *file) { - int fd, ret; - void *data = nullptr; + struct policy_file pf; + policy_file_init(&pf); + + uint8_t *data; size_t len; - policydb_to_image(nullptr, magisk_policydb, &data, &len); - if (data == nullptr) { - LOGE("Fail to dump policy image!\n"); + + pf.type = PF_USE_STDIO; + pf.fp = open_memfile(data, len); + if (policydb_write(magisk_policydb, &pf)) { + LOGE("Fail to create policy image\n"); return 1; } + fclose(pf.fp); - fd = xopen(file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); + int fd = xopen(file, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); if (fd < 0) return 1; - ret = xwrite(fd, data, len); + xwrite(fd, data, len); + close(fd); - if (ret < 0) - return 1; + free(data); return 0; } diff --git a/native/jni/utils/file.cpp b/native/jni/utils/file.cpp index 42f8463b5..c85523359 100644 --- a/native/jni/utils/file.cpp +++ b/native/jni/utils/file.cpp @@ -407,3 +407,87 @@ void parse_mnt(const char *file, const function &fn) { } } } + +struct io_buf { + uint8_t *&buf; + size_t &len; + size_t cap = 0; + size_t pos = 0; + + io_buf(uint8_t *&buf, size_t &len) : buf(buf), len(len) { + buf = nullptr; + len = 0; + } + uint8_t *cur() { + return buf + pos; + } + int max_read() { + return len - pos; + } + void resize(int new_pos, bool zero = false) { + bool resize = false; + size_t old_cap = cap; + while (new_pos > cap) { + cap = cap ? (cap << 1) - (cap >> 1) : 1 << 12; + resize = true; + } + if (resize) { + buf = (uint8_t *) xrealloc(buf, cap); + if (zero) + memset(buf + old_cap, 0, cap - old_cap); + } + } +}; + +static int mem_read(void *v, char *buf, int len) { + auto io = reinterpret_cast(v); + len = std::min(len, io->max_read()); + memcpy(buf, io->cur(), len); + return len; +} + +static int mem_write(void *v, const char *buf, int len) { + auto io = reinterpret_cast(v); + io->resize(io->pos + len); + memcpy(io->cur(), buf, len); + io->pos += len; + io->len = std::max(io->len, io->pos); + return len; +} + +static fpos_t mem_seek(void *v, fpos_t off, int whence) { + auto io = reinterpret_cast(v); + off_t new_pos; + switch (whence) { + case SEEK_CUR: + new_pos = io->pos + off; + break; + case SEEK_END: + new_pos = io->len + off; + break; + case SEEK_SET: + new_pos = off; + break; + default: + return -1; + } + if (new_pos < 0) + return -1; + + io->resize(new_pos, true); + io->pos = new_pos; + return new_pos; +} + +static int mem_close(void *v) { + auto io = reinterpret_cast(v); + delete io; + return 0; +} + +FILE *open_memfile(uint8_t *&buf, size_t &len) { + auto io = new io_buf(buf, len); + FILE *fp = funopen(io, mem_read, mem_write, mem_seek, mem_close); + setbuf(fp, nullptr); + return fp; +} diff --git a/native/jni/utils/files.h b/native/jni/utils/files.h index e23fba6b1..a0c3a12fc 100644 --- a/native/jni/utils/files.h +++ b/native/jni/utils/files.h @@ -38,6 +38,7 @@ void *__mmap(const char *filename, size_t *size, bool rw); void frm_rf(int dirfd, std::initializer_list excl = std::initializer_list()); void clone_dir(int src, int dest, bool overwrite = true); void parse_mnt(const char *file, const std::function &fn); +FILE *open_memfile(uint8_t *&buf, size_t &len); template void full_read(const char *filename, T &buf, size_t &size) { From 4f9a25ee89f4dfcbbd931f80873a361c0f3e92f9 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Wed, 20 Nov 2019 21:48:49 -0500 Subject: [PATCH 72/74] Create generic streams on top of stdio WIP --- native/jni/magiskpolicy/policydb.cpp | 3 +- native/jni/utils/Android.mk | 3 +- native/jni/utils/file.cpp | 84 ------------------ native/jni/utils/files.h | 1 - native/jni/utils/include/stream.h | 98 ++++++++++++++++++++- native/jni/utils/stream.cpp | 122 +++++++++++++++++++++++++++ 6 files changed, 223 insertions(+), 88 deletions(-) create mode 100644 native/jni/utils/stream.cpp diff --git a/native/jni/magiskpolicy/policydb.cpp b/native/jni/magiskpolicy/policydb.cpp index 120c209ff..717ddf5c3 100644 --- a/native/jni/magiskpolicy/policydb.cpp +++ b/native/jni/magiskpolicy/policydb.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "magiskpolicy.h" #include "sepolicy.h" @@ -180,7 +181,7 @@ int dump_policydb(const char *file) { size_t len; pf.type = PF_USE_STDIO; - pf.fp = open_memfile(data, len); + pf.fp = open_stream(data, len); if (policydb_write(magisk_policydb, &pf)) { LOGE("Fail to create policy image\n"); return 1; diff --git a/native/jni/utils/Android.mk b/native/jni/utils/Android.mk index 67aebd666..a746c427b 100644 --- a/native/jni/utils/Android.mk +++ b/native/jni/utils/Android.mk @@ -11,6 +11,7 @@ LOCAL_SRC_FILES := \ selinux.cpp \ logging.cpp \ cpio.cpp \ - xwrap.cpp + xwrap.cpp \ + stream.cpp include $(BUILD_STATIC_LIBRARY) diff --git a/native/jni/utils/file.cpp b/native/jni/utils/file.cpp index c85523359..42f8463b5 100644 --- a/native/jni/utils/file.cpp +++ b/native/jni/utils/file.cpp @@ -407,87 +407,3 @@ void parse_mnt(const char *file, const function &fn) { } } } - -struct io_buf { - uint8_t *&buf; - size_t &len; - size_t cap = 0; - size_t pos = 0; - - io_buf(uint8_t *&buf, size_t &len) : buf(buf), len(len) { - buf = nullptr; - len = 0; - } - uint8_t *cur() { - return buf + pos; - } - int max_read() { - return len - pos; - } - void resize(int new_pos, bool zero = false) { - bool resize = false; - size_t old_cap = cap; - while (new_pos > cap) { - cap = cap ? (cap << 1) - (cap >> 1) : 1 << 12; - resize = true; - } - if (resize) { - buf = (uint8_t *) xrealloc(buf, cap); - if (zero) - memset(buf + old_cap, 0, cap - old_cap); - } - } -}; - -static int mem_read(void *v, char *buf, int len) { - auto io = reinterpret_cast(v); - len = std::min(len, io->max_read()); - memcpy(buf, io->cur(), len); - return len; -} - -static int mem_write(void *v, const char *buf, int len) { - auto io = reinterpret_cast(v); - io->resize(io->pos + len); - memcpy(io->cur(), buf, len); - io->pos += len; - io->len = std::max(io->len, io->pos); - return len; -} - -static fpos_t mem_seek(void *v, fpos_t off, int whence) { - auto io = reinterpret_cast(v); - off_t new_pos; - switch (whence) { - case SEEK_CUR: - new_pos = io->pos + off; - break; - case SEEK_END: - new_pos = io->len + off; - break; - case SEEK_SET: - new_pos = off; - break; - default: - return -1; - } - if (new_pos < 0) - return -1; - - io->resize(new_pos, true); - io->pos = new_pos; - return new_pos; -} - -static int mem_close(void *v) { - auto io = reinterpret_cast(v); - delete io; - return 0; -} - -FILE *open_memfile(uint8_t *&buf, size_t &len) { - auto io = new io_buf(buf, len); - FILE *fp = funopen(io, mem_read, mem_write, mem_seek, mem_close); - setbuf(fp, nullptr); - return fp; -} diff --git a/native/jni/utils/files.h b/native/jni/utils/files.h index a0c3a12fc..e23fba6b1 100644 --- a/native/jni/utils/files.h +++ b/native/jni/utils/files.h @@ -38,7 +38,6 @@ void *__mmap(const char *filename, size_t *size, bool rw); void frm_rf(int dirfd, std::initializer_list excl = std::initializer_list()); void clone_dir(int src, int dest, bool overwrite = true); void parse_mnt(const char *file, const std::function &fn); -FILE *open_memfile(uint8_t *&buf, size_t &len); template void full_read(const char *filename, T &buf, size_t &size) { diff --git a/native/jni/utils/include/stream.h b/native/jni/utils/include/stream.h index 5216e75e2..43d0562d0 100644 --- a/native/jni/utils/include/stream.h +++ b/native/jni/utils/include/stream.h @@ -1,9 +1,105 @@ #pragma once #include +#include #include -#include "utils.h" +#include + +class stream; + +FILE *open_stream(stream *strm); + +template +FILE *open_stream(Args &&... args) { + return open_stream(new T(args...)); +} + +/* Base classes */ + +class stream { +public: + virtual int read(void *buf, size_t len); + virtual int write(const void *buf, size_t len); + virtual off_t seek(off_t off, int whence); + virtual int close(); + virtual ~stream() = default; +}; + +class filter_stream : public stream { +public: + filter_stream(FILE *fp) : fp(fp) {} + int close() override { return fclose(fp); } + virtual ~filter_stream() { close(); } + + void set_base(FILE *f) { + if (fp) fclose(fp); + fp = f; + } + + template + void set_base(Args&&... args) { + set_base(open_stream(args...)); + } + +protected: + FILE *fp; +}; + +class filter_in_stream : public filter_stream { +public: + filter_in_stream(FILE *fp = nullptr) : filter_stream(fp) {} + int read(void *buf, size_t len) override { return fread(buf, len, 1, fp); } +}; + +class filter_out_stream : public filter_stream { +public: + filter_out_stream(FILE *fp = nullptr) : filter_stream(fp) {} + int write(const void *buf, size_t len) override { return fwrite(buf, len, 1, fp); } +}; + +class seekable_stream : public stream { +protected: + size_t _pos = 0; + + off_t new_pos(off_t off, int whence); + virtual size_t end_pos() = 0; +}; + +/* Concrete classes */ + +class byte_stream : public seekable_stream { +public: + byte_stream(uint8_t *&buf, size_t &len); + template + byte_stream(byte *&buf, size_t &len) : byte_stream(reinterpret_cast(buf), len) {} + int read(void *buf, size_t len) override; + int write(const void *buf, size_t len) override; + off_t seek(off_t off, int whence) override; + virtual ~byte_stream() = default; + +private: + uint8_t *&_buf; + size_t &_len; + size_t _cap = 0; + + void resize(size_t new_pos, bool zero = false); + size_t end_pos() override { return _len; } +}; + +class fd_stream : stream { +public: + fd_stream(int fd) : fd(fd) {} + int read(void *buf, size_t len) override; + int write(const void *buf, size_t len) override; + off_t seek(off_t off, int whence) override; + virtual ~fd_stream() = default; + +private: + int fd; +}; + +/* TODO: Replace classes below to new implementation */ class OutStream { public: diff --git a/native/jni/utils/stream.cpp b/native/jni/utils/stream.cpp new file mode 100644 index 000000000..442373cf6 --- /dev/null +++ b/native/jni/utils/stream.cpp @@ -0,0 +1,122 @@ +#include +#include + +static int strm_read(void *v, char *buf, int len) { + auto strm = reinterpret_cast(v); + return strm->read(buf, len); +} + +static int strm_write(void *v, const char *buf, int len) { + auto strm = reinterpret_cast(v); + return strm->write(buf, len); +} + +static fpos_t strm_seek(void *v, fpos_t off, int whence) { + auto strm = reinterpret_cast(v); + return strm->seek(off, whence); +} + +static int strm_close(void *v) { + auto strm = reinterpret_cast(v); + int ret = strm->close(); + delete strm; + return ret; +} + +FILE *open_stream(stream *strm) { + FILE *fp = funopen(strm, strm_read, strm_write, strm_seek, strm_close); + // Disable buffering + setbuf(fp, nullptr); + return fp; +} + +int stream::read(void *buf, size_t len) { + LOGE("This stream does not support read"); + return -1; +} + +int stream::write(const void *buf, size_t len) { + LOGE("This stream does not support write"); + return -1; +} + +off_t stream::seek(off_t off, int whence) { + LOGE("This stream does not support seek"); + return -1; +} + +int stream::close() { + return 0; +} + +off_t seekable_stream::new_pos(off_t off, int whence) { + off_t new_pos; + switch (whence) { + case SEEK_CUR: + new_pos = _pos + off; + break; + case SEEK_END: + new_pos = end_pos() + off; + break; + case SEEK_SET: + new_pos = off; + break; + default: + return -1; + } + return new_pos; +} + +byte_stream::byte_stream(uint8_t *&buf, size_t &len) : _buf(buf), _len(len) { + buf = nullptr; + len = 0; +} + +int byte_stream::read(void *buf, size_t len) { + len = std::min(len, _len - _pos); + memcpy(buf, _buf + _pos, len); + return len; +} + +int byte_stream::write(const void *buf, size_t len) { + resize(_pos + len); + memcpy(_buf + _pos, buf, len); + _pos += len; + _len = std::max(_len, _pos); + return len; +} + +off_t byte_stream::seek(off_t off, int whence) { + off_t np = new_pos(off, whence); + if (np < 0) + return -1; + resize(np, true); + _pos = np; + return np; +} + +void byte_stream::resize(size_t new_pos, bool zero) { + bool resize = false; + size_t old_cap = _cap; + while (new_pos > _cap) { + _cap = _cap ? (_cap << 1) - (_cap >> 1) : 1 << 12; + resize = true; + } + if (resize) { + _buf = (uint8_t *) xrealloc(_buf, _cap); + if (zero) + memset(_buf + old_cap, 0, _cap - old_cap); + } +} + +int fd_stream::read(void *buf, size_t len) { + return ::read(fd, buf, len); +} + +int fd_stream::write(const void *buf, size_t len) { + return ::write(fd, buf, len); +} + +off_t fd_stream::seek(off_t off, int whence) { + return lseek(fd, off, whence); +} From d26d804cc271879670c63b58f39aea0d464c6b53 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 21 Nov 2019 06:08:02 -0500 Subject: [PATCH 73/74] Migrate to generic stream implementation --- native/jni/magiskboot/bootimg.cpp | 19 +- native/jni/magiskboot/compress.cpp | 1036 ++++++++++++++++------------ native/jni/magiskboot/compress.h | 168 +---- native/jni/magiskboot/ramdisk.cpp | 32 +- native/jni/utils/cpio.cpp | 16 +- native/jni/utils/include/cpio.h | 6 +- native/jni/utils/include/stream.h | 122 +--- native/jni/utils/stream.cpp | 19 + 8 files changed, 648 insertions(+), 770 deletions(-) diff --git a/native/jni/magiskboot/bootimg.cpp b/native/jni/magiskboot/bootimg.cpp index d31f7d807..1e8d3bffe 100644 --- a/native/jni/magiskboot/bootimg.cpp +++ b/native/jni/magiskboot/bootimg.cpp @@ -21,19 +21,18 @@ using namespace std; uint32_t dyn_img_hdr::j32 = 0; uint64_t dyn_img_hdr::j64 = 0; -static int64_t one_step(unique_ptr &&ptr, int fd, const void *in, size_t size) { - ptr->setOut(make_unique(fd)); - if (!ptr->write(in, size)) - return -1; - return ptr->finalize(); -} - -static int64_t decompress(format_t type, int fd, const void *in, size_t size) { - return one_step(unique_ptr(get_decoder(type)), fd, in, size); +static void decompress(format_t type, int fd, const void *in, size_t size) { + unique_ptr ptr(get_decoder(type, open_stream(fd))); + ptr->write(in, size); } static int64_t compress(format_t type, int fd, const void *in, size_t size) { - return one_step(unique_ptr(get_encoder(type)), fd, in, size); + auto prev = lseek(fd, 0, SEEK_CUR); + unique_ptr ptr(get_encoder(type, open_stream(fd))); + ptr->write(in, size); + ptr->close(); + auto now = lseek(fd, 0, SEEK_CUR); + return now - prev; } static void dump(void *buf, size_t size, const char *filename) { diff --git a/native/jni/magiskboot/compress.cpp b/native/jni/magiskboot/compress.cpp index 4f37ac591..0694766ba 100644 --- a/native/jni/magiskboot/compress.cpp +++ b/native/jni/magiskboot/compress.cpp @@ -6,6 +6,13 @@ #include #include +#include +#include +#include +#include +#include +#include + #include #include @@ -14,32 +21,565 @@ using namespace std; -static bool read_file(FILE *fp, const function &fn) { - char buf[4096]; - size_t len; - while ((len = fread(buf, 1, sizeof(buf), fp))) - fn(buf, len); - return true; +#define bwrite filter_stream::write +#define bclose filter_stream::close + +constexpr size_t CHUNK = 0x40000; +constexpr size_t LZ4_UNCOMPRESSED = 0x800000; +constexpr size_t LZ4_COMPRESSED = LZ4_COMPRESSBOUND(LZ4_UNCOMPRESSED); + +class cpr_stream : public filter_stream { +public: + explicit cpr_stream(FILE *fp) : filter_stream(fp) {} + + int read(void *buf, size_t len) final { + return stream::read(buf, len); + } + + int close() final { + finish(); + return bclose(); + } + +protected: + // If finish is overridden, destroy should be called in the destructor + virtual void finish() {} + void destroy() { if (fp) finish(); } +}; + +class gz_strm : public cpr_stream { +public: + ~gz_strm() override { destroy(); } + + int write(const void *buf, size_t len) override { + return len ? write(buf, len, Z_NO_FLUSH) : 0; + } + +protected: + enum mode_t { + DECODE, + ENCODE + } mode; + + gz_strm(mode_t mode, FILE *fp) : cpr_stream(fp), mode(mode) { + switch(mode) { + case DECODE: + inflateInit2(&strm, 15 | 16); + break; + case ENCODE: + deflateInit2(&strm, 9, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY); + break; + } + } + + void finish() override { + write(nullptr, 0, Z_FINISH); + switch(mode) { + case DECODE: + inflateEnd(&strm); + break; + case ENCODE: + deflateEnd(&strm); + break; + } + } + +private: + z_stream strm; + uint8_t outbuf[CHUNK]; + + int write(const void *buf, size_t len, int flush) { + strm.next_in = (Bytef *) buf; + strm.avail_in = len; + do { + int code; + strm.next_out = outbuf; + strm.avail_out = sizeof(outbuf); + switch(mode) { + case DECODE: + code = inflate(&strm, flush); + break; + case ENCODE: + code = deflate(&strm, flush); + break; + } + if (code == Z_STREAM_ERROR) { + LOGW("gzip %s failed (%d)\n", mode ? "encode" : "decode", code); + return -1; + } + bwrite(outbuf, sizeof(outbuf) - strm.avail_out); + } while (strm.avail_out == 0); + return len; + } +}; + +class gz_decoder : public gz_strm { +public: + explicit gz_decoder(FILE *fp) : gz_strm(DECODE, fp) {}; +}; + +class gz_encoder : public gz_strm { +public: + explicit gz_encoder(FILE *fp) : gz_strm(ENCODE, fp) {}; +}; + +class bz_strm : public cpr_stream { +public: + ~bz_strm() override { destroy(); } + + int write(const void *buf, size_t len) override { + return len ? write(buf, len, BZ_RUN) : 0; + } + +protected: + enum mode_t { + DECODE, + ENCODE + } mode; + + bz_strm(mode_t mode, FILE *fp) : cpr_stream(fp), mode(mode) { + switch(mode) { + case DECODE: + BZ2_bzDecompressInit(&strm, 0, 0); + break; + case ENCODE: + BZ2_bzCompressInit(&strm, 9, 0, 0); + break; + } + } + + void finish() override { + switch(mode) { + case DECODE: + BZ2_bzDecompressEnd(&strm); + break; + case ENCODE: + write(nullptr, 0, BZ_FINISH); + BZ2_bzCompressEnd(&strm); + break; + } + } + +private: + bz_stream strm; + char outbuf[CHUNK]; + + int write(const void *buf, size_t len, int flush) { + strm.next_in = (char *) buf; + strm.avail_in = len; + do { + int code; + strm.avail_out = sizeof(outbuf); + strm.next_out = outbuf; + switch(mode) { + case DECODE: + code = BZ2_bzDecompress(&strm); + break; + case ENCODE: + code = BZ2_bzCompress(&strm, flush); + break; + } + if (code < 0) { + LOGW("bzip2 %s failed (%d)\n", mode ? "encode" : "decode", code); + return -1; + } + bwrite(outbuf, sizeof(outbuf) - strm.avail_out); + } while (strm.avail_out == 0); + return len; + } +}; + +class bz_decoder : public bz_strm { +public: + explicit bz_decoder(FILE *fp) : bz_strm(DECODE, fp) {}; +}; + +class bz_encoder : public bz_strm { +public: + explicit bz_encoder(FILE *fp) : bz_strm(ENCODE, fp) {}; +}; + +class lzma_strm : public cpr_stream { +public: + ~lzma_strm() override { destroy(); } + + int write(const void *buf, size_t len) override { + return len ? write(buf, len, LZMA_RUN) : 0; + } + +protected: + enum mode_t { + DECODE, + ENCODE_XZ, + ENCODE_LZMA + } mode; + + lzma_strm(mode_t mode, FILE *fp) : cpr_stream(fp), mode(mode), strm(LZMA_STREAM_INIT) { + lzma_options_lzma opt; + + // Initialize preset + lzma_lzma_preset(&opt, 9); + lzma_filter filters[] = { + { .id = LZMA_FILTER_LZMA2, .options = &opt }, + { .id = LZMA_VLI_UNKNOWN, .options = nullptr }, + }; + + lzma_ret ret; + switch(mode) { + case DECODE: + ret = lzma_auto_decoder(&strm, UINT64_MAX, 0); + break; + case ENCODE_XZ: + ret = lzma_stream_encoder(&strm, filters, LZMA_CHECK_CRC32); + break; + case ENCODE_LZMA: + ret = lzma_alone_encoder(&strm, &opt); + break; + } + } + + void finish() override { + write(nullptr, 0, LZMA_FINISH); + lzma_end(&strm); + } + +private: + lzma_stream strm; + uint8_t outbuf[CHUNK]; + + int write(const void *buf, size_t len, lzma_action flush) { + strm.next_in = (uint8_t *) buf; + strm.avail_in = len; + do { + strm.avail_out = sizeof(outbuf); + strm.next_out = outbuf; + int code = lzma_code(&strm, flush); + if (code != LZMA_OK && code != LZMA_STREAM_END) { + LOGW("LZMA %s failed (%d)\n", mode ? "encode" : "decode", code); + return -1; + } + bwrite(outbuf, sizeof(outbuf) - strm.avail_out); + } while (strm.avail_out == 0); + return len; + } +}; + +class lzma_decoder : public lzma_strm { +public: + lzma_decoder(FILE *fp) : lzma_strm(DECODE, fp) {} +}; + +class xz_encoder : public lzma_strm { +public: + xz_encoder(FILE *fp) : lzma_strm(ENCODE_XZ, fp) {} +}; + +class lzma_encoder : public lzma_strm { +public: + lzma_encoder(FILE *fp) : lzma_strm(ENCODE_LZMA, fp) {} +}; + +class LZ4F_decoder : public cpr_stream { +public: + explicit LZ4F_decoder(FILE *fp) : cpr_stream(fp), outbuf(nullptr) { + LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); + } + + ~LZ4F_decoder() override { + LZ4F_freeDecompressionContext(ctx); + delete[] outbuf; + } + + int write(const void *buf, size_t len) override { + auto ret = len; + auto inbuf = reinterpret_cast(buf); + if (!outbuf) + read_header(inbuf, len); + size_t read, write; + LZ4F_errorCode_t code; + do { + read = len; + write = outCapacity; + code = LZ4F_decompress(ctx, outbuf, &write, inbuf, &read, nullptr); + if (LZ4F_isError(code)) { + LOGW("LZ4F decode error: %s\n", LZ4F_getErrorName(code)); + return -1; + } + len -= read; + inbuf += read; + bwrite(outbuf, write); + } while (len != 0 || write != 0); + return ret; + } + +private: + LZ4F_decompressionContext_t ctx; + uint8_t *outbuf; + size_t outCapacity; + + void read_header(const uint8_t *&in, size_t &size) { + size_t read = size; + LZ4F_frameInfo_t info; + LZ4F_getFrameInfo(ctx, &info, in, &read); + switch (info.blockSizeID) { + case LZ4F_default: + case LZ4F_max64KB: outCapacity = 1 << 16; break; + case LZ4F_max256KB: outCapacity = 1 << 18; break; + case LZ4F_max1MB: outCapacity = 1 << 20; break; + case LZ4F_max4MB: outCapacity = 1 << 22; break; + } + outbuf = new uint8_t[outCapacity]; + in += read; + size -= read; + } +}; + +class LZ4F_encoder : public cpr_stream { +public: + explicit LZ4F_encoder(FILE *fp) : cpr_stream(fp), outbuf(nullptr), outCapacity(0) { + LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); + } + + ~LZ4F_encoder() override { + destroy(); + LZ4F_freeCompressionContext(ctx); + delete[] outbuf; + } + + int write(const void *buf, size_t len) override { + auto ret = len; + if (!outbuf) + write_header(); + if (len == 0) + return 0; + auto inbuf = reinterpret_cast(buf); + size_t read, write; + do { + read = len > BLOCK_SZ ? BLOCK_SZ : len; + write = LZ4F_compressUpdate(ctx, outbuf, outCapacity, inbuf, read, nullptr); + if (LZ4F_isError(write)) { + LOGW("LZ4F encode error: %s\n", LZ4F_getErrorName(write)); + return -1; + } + len -= read; + inbuf += read; + bwrite(outbuf, write); + } while (len != 0); + return ret; + } + +protected: + void finish() override { + size_t len = LZ4F_compressEnd(ctx, outbuf, outCapacity, nullptr); + bwrite(outbuf, len); + } + +private: + LZ4F_compressionContext_t ctx; + uint8_t *outbuf; + size_t outCapacity; + + static constexpr size_t BLOCK_SZ = 1 << 22; + + void write_header() { + LZ4F_preferences_t prefs { + .autoFlush = 1, + .compressionLevel = 9, + .frameInfo = { + .blockMode = LZ4F_blockIndependent, + .blockSizeID = LZ4F_max4MB, + .blockChecksumFlag = LZ4F_noBlockChecksum, + .contentChecksumFlag = LZ4F_contentChecksumEnabled + } + }; + outCapacity = LZ4F_compressBound(BLOCK_SZ, &prefs); + outbuf = new uint8_t[outCapacity]; + size_t write = LZ4F_compressBegin(ctx, outbuf, outCapacity, &prefs); + bwrite(outbuf, write); + } +}; + +class LZ4_decoder : public cpr_stream { +public: + explicit LZ4_decoder(FILE *fp) + : cpr_stream(fp), out_buf(new char[LZ4_UNCOMPRESSED]), buffer(new char[LZ4_COMPRESSED]), + init(false), block_sz(0), buf_off(0) {} + + ~LZ4_decoder() override { + delete[] out_buf; + delete[] buffer; + } + + int write(const void *in, size_t size) override { + auto ret = size; + auto inbuf = static_cast(in); + if (!init) { + // Skip magic + inbuf += 4; + size -= 4; + init = true; + } + int write; + size_t consumed; + do { + if (block_sz == 0) { + block_sz = *((unsigned *) inbuf); + inbuf += sizeof(unsigned); + size -= sizeof(unsigned); + } else if (buf_off + size >= block_sz) { + consumed = block_sz - buf_off; + memcpy(buffer + buf_off, inbuf, consumed); + inbuf += consumed; + size -= consumed; + + write = LZ4_decompress_safe(buffer, out_buf, block_sz, LZ4_UNCOMPRESSED); + if (write < 0) { + LOGW("LZ4HC decompression failure (%d)\n", write); + return -1; + } + bwrite(out_buf, write); + + // Reset + buf_off = 0; + block_sz = 0; + } else { + // Copy to internal buffer + memcpy(buffer + buf_off, inbuf, size); + buf_off += size; + size = 0; + } + } while (size != 0); + return ret; + } + +private: + char *out_buf; + char *buffer; + bool init; + unsigned block_sz; + int buf_off; +}; + +class LZ4_encoder : public cpr_stream { +public: + explicit LZ4_encoder(FILE *fp) + : cpr_stream(fp), outbuf(new char[LZ4_COMPRESSED]), buf(new char[LZ4_UNCOMPRESSED]), + init(false), buf_off(0), in_total(0) {} + + ~LZ4_encoder() override { + destroy(); + delete[] outbuf; + delete[] buf; + } + + int write(const void *in, size_t size) override { + if (!init) { + bwrite("\x02\x21\x4c\x18", 4); + init = true; + } + if (size == 0) + return 0; + in_total += size; + const char *inbuf = (const char *) in; + size_t consumed; + int write; + do { + if (buf_off + size >= LZ4_UNCOMPRESSED) { + consumed = LZ4_UNCOMPRESSED - buf_off; + memcpy(buf + buf_off, inbuf, consumed); + inbuf += consumed; + size -= consumed; + + write = LZ4_compress_HC(buf, outbuf, LZ4_UNCOMPRESSED, LZ4_COMPRESSED, 9); + if (write == 0) { + LOGW("LZ4HC compression failure\n"); + return false; + } + bwrite(&write, sizeof(write)); + bwrite(outbuf, write); + + // Reset buffer + buf_off = 0; + } else { + // Copy to internal buffer + memcpy(buf + buf_off, inbuf, size); + buf_off += size; + size = 0; + } + } while (size != 0); + return true; + } + +protected: + void finish() override { + if (buf_off) { + int write = LZ4_compress_HC(buf, outbuf, buf_off, LZ4_COMPRESSED, 9); + bwrite(&write, sizeof(write)); + bwrite(outbuf, write); + } + bwrite(&in_total, sizeof(in_total)); + } + +private: + char *outbuf; + char *buf; + bool init; + int buf_off; + unsigned in_total; +}; + +filter_stream *get_encoder(format_t type, FILE *fp) { + switch (type) { + case XZ: + return new xz_encoder(fp); + case LZMA: + return new lzma_encoder(fp); + case BZIP2: + return new bz_encoder(fp); + case LZ4: + return new LZ4F_encoder(fp); + case LZ4_LEGACY: + return new LZ4_encoder(fp); + case GZIP: + default: + return new gz_encoder(fp); + } +} + +filter_stream *get_decoder(format_t type, FILE *fp) { + switch (type) { + case XZ: + case LZMA: + return new lzma_decoder(fp); + case BZIP2: + return new bz_decoder(fp); + case LZ4: + return new LZ4F_decoder(fp); + case LZ4_LEGACY: + return new LZ4_decoder(fp); + case GZIP: + default: + return new gz_decoder(fp); + } } void decompress(char *infile, const char *outfile) { - bool in_std = strcmp(infile, "-") == 0; + bool in_std = infile == "-"sv; bool rm_in = false; - FILE *in_file = in_std ? stdin : xfopen(infile, "re"); - int out_fd = -1; - unique_ptr cmp; + FILE *in_fp = in_std ? stdin : xfopen(infile, "re"); + unique_ptr strm; - read_file(in_file, [&](void *buf, size_t len) -> void { - if (out_fd < 0) { + char buf[4096]; + size_t len; + while ((len = fread(buf, 1, sizeof(buf), in_fp))) { + if (!strm) { format_t type = check_fmt(buf, len); - fprintf(stderr, "Detected format: [%s]\n", fmt2name[type]); - if (!COMPRESSED(type)) LOGE("Input file is not a supported compressed type!\n"); - cmp.reset(get_decoder(type)); + fprintf(stderr, "Detected format: [%s]\n", fmt2name[type]); /* If user does not provide outfile, infile has to be either * .[ext], or '-'. Outfile will be either or '-'. @@ -60,18 +600,16 @@ void decompress(char *infile, const char *outfile) { } } - out_fd = strcmp(outfile, "-") == 0 ? - STDOUT_FILENO : xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); - cmp->setOut(make_unique(out_fd)); + FILE *out_fp = outfile == "-"sv ? stdout : xfopen(outfile, "we"); + strm.reset(get_decoder(type, out_fp)); if (ext) *ext = '.'; } - if (!cmp->write(buf, len)) + if (strm->write(buf, len) < 0) LOGE("Decompression error!\n"); - }); + } - cmp->finalize(); - fclose(in_file); - close(out_fd); + strm->close(); + fclose(in_fp); if (rm_in) unlink(infile); @@ -80,458 +618,42 @@ void decompress(char *infile, const char *outfile) { void compress(const char *method, const char *infile, const char *outfile) { auto it = name2fmt.find(method); if (it == name2fmt.end()) - LOGE("Unsupported compression method: [%s]\n", method); + LOGE("Unknown compression method: [%s]\n", method); - unique_ptr cmp(get_encoder(it->second)); - - bool in_std = strcmp(infile, "-") == 0; + bool in_std = infile == "-"sv; bool rm_in = false; - FILE *in_file = in_std ? stdin : xfopen(infile, "re"); - int out_fd; + FILE *in_fp = in_std ? stdin : xfopen(infile, "re"); + FILE *out_fp; if (outfile == nullptr) { if (in_std) { - out_fd = STDOUT_FILENO; + out_fp = stdout; } else { /* If user does not provide outfile and infile is not * STDIN, output to .[ext] */ - char *tmp = new char[strlen(infile) + 5]; - sprintf(tmp, "%s%s", infile, fmt2ext[it->second]); - out_fd = xopen(tmp, O_WRONLY | O_CREAT | O_TRUNC, 0644); - fprintf(stderr, "Compressing to [%s]\n", tmp); - delete[] tmp; + string tmp(infile); + tmp += fmt2ext[it->second]; + out_fp = xfopen(tmp.data(), "we"); + fprintf(stderr, "Compressing to [%s]\n", tmp.data()); rm_in = true; } } else { - out_fd = strcmp(outfile, "-") == 0 ? - STDOUT_FILENO : xopen(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + out_fp = outfile == "-"sv ? stdout : xfopen(outfile, "we"); } - cmp->setOut(make_unique(out_fd)); + unique_ptr strm(get_encoder(it->second, out_fp)); - read_file(in_file, [&](void *buf, size_t len) -> void { - if (!cmp->write(buf, len)) + char buf[4096]; + size_t len; + while ((len = fread(buf, 1, sizeof(buf), in_fp))) { + if (strm->write(buf, len) < 0) LOGE("Compression error!\n"); - }); + }; - cmp->finalize(); - fclose(in_file); - close(out_fd); + strm->close(); + fclose(in_fp); if (rm_in) unlink(infile); } - -Compression *get_encoder(format_t type) { - switch (type) { - case XZ: - return new XZEncoder(); - case LZMA: - return new LZMAEncoder(); - case BZIP2: - return new BZEncoder(); - case LZ4: - return new LZ4FEncoder(); - case LZ4_LEGACY: - return new LZ4Encoder(); - case GZIP: - default: - return new GZEncoder(); - } -} - -Compression *get_decoder(format_t type) { - switch (type) { - case XZ: - case LZMA: - return new LZMADecoder(); - case BZIP2: - return new BZDecoder(); - case LZ4: - return new LZ4FDecoder(); - case LZ4_LEGACY: - return new LZ4Decoder(); - case GZIP: - default: - return new GZDecoder(); - } -} - -GZStream::GZStream(int mode) : mode(mode), strm({}) { - switch(mode) { - case 0: - inflateInit2(&strm, 15 | 16); - break; - case 1: - deflateInit2(&strm, 9, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY); - break; - } -} - -bool GZStream::write(const void *in, size_t size) { - return size ? write(in, size, Z_NO_FLUSH) : true; -} - -uint64_t GZStream::finalize() { - write(nullptr, 0, Z_FINISH); - uint64_t total = strm.total_out; - switch(mode) { - case 0: - inflateEnd(&strm); - break; - case 1: - deflateEnd(&strm); - break; - } - return total; -} - -bool GZStream::write(const void *in, size_t size, int flush) { - int ret; - strm.next_in = (Bytef *) in; - strm.avail_in = size; - do { - strm.next_out = outbuf; - strm.avail_out = sizeof(outbuf); - switch(mode) { - case 0: - ret = inflate(&strm, flush); - break; - case 1: - ret = deflate(&strm, flush); - break; - } - if (ret == Z_STREAM_ERROR) { - LOGW("Gzip %s failed (%d)\n", mode ? "encode" : "decode", ret); - return false; - } - FilterOutStream::write(outbuf, sizeof(outbuf) - strm.avail_out); - } while (strm.avail_out == 0); - return true; -} - -BZStream::BZStream(int mode) : mode(mode), strm({}) { - switch(mode) { - case 0: - BZ2_bzDecompressInit(&strm, 0, 0); - break; - case 1: - BZ2_bzCompressInit(&strm, 9, 0, 0); - break; - } -} - -bool BZStream::write(const void *in, size_t size) { - return size ? write(in, size, BZ_RUN) : true; -} - -uint64_t BZStream::finalize() { - if (mode) - write(nullptr, 0, BZ_FINISH); - uint64_t total = ((uint64_t) strm.total_out_hi32 << 32) + strm.total_out_lo32; - switch(mode) { - case 0: - BZ2_bzDecompressEnd(&strm); - break; - case 1: - BZ2_bzCompressEnd(&strm); - break; - } - return total; -} - -bool BZStream::write(const void *in, size_t size, int flush) { - int ret; - strm.next_in = (char *) in; - strm.avail_in = size; - do { - strm.avail_out = sizeof(outbuf); - strm.next_out = outbuf; - switch(mode) { - case 0: - ret = BZ2_bzDecompress(&strm); - break; - case 1: - ret = BZ2_bzCompress(&strm, flush); - break; - } - if (ret < 0) { - LOGW("Bzip2 %s failed (%d)\n", mode ? "encode" : "decode", ret); - return false; - } - FilterOutStream::write(outbuf, sizeof(outbuf) - strm.avail_out); - } while (strm.avail_out == 0); - return true; -} - -LZMAStream::LZMAStream(int mode) : mode(mode), strm(LZMA_STREAM_INIT) { - lzma_options_lzma opt; - int ret; - - // Initialize preset - lzma_lzma_preset(&opt, 9); - lzma_filter filters[] = { - { .id = LZMA_FILTER_LZMA2, .options = &opt }, - { .id = LZMA_VLI_UNKNOWN, .options = nullptr }, - }; - - switch(mode) { - case 0: - ret = lzma_auto_decoder(&strm, UINT64_MAX, 0); - break; - case 1: - ret = lzma_stream_encoder(&strm, filters, LZMA_CHECK_CRC32); - break; - case 2: - ret = lzma_alone_encoder(&strm, &opt); - break; - } -} - -bool LZMAStream::write(const void *in, size_t size) { - return size ? write(in, size, LZMA_RUN) : true; -} - -uint64_t LZMAStream::finalize() { - write(nullptr, 0, LZMA_FINISH); - uint64_t total = strm.total_out; - lzma_end(&strm); - return total; -} - -bool LZMAStream::write(const void *in, size_t size, lzma_action flush) { - int ret; - strm.next_in = (uint8_t *) in; - strm.avail_in = size; - do { - strm.avail_out = sizeof(outbuf); - strm.next_out = outbuf; - ret = lzma_code(&strm, flush); - if (ret != LZMA_OK && ret != LZMA_STREAM_END) { - LOGW("LZMA %s failed (%d)\n", mode ? "encode" : "decode", ret); - return false; - } - FilterOutStream::write(outbuf, sizeof(outbuf) - strm.avail_out); - } while (strm.avail_out == 0); - return true; -} - -LZ4FDecoder::LZ4FDecoder() : outbuf(nullptr), total(0) { - LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); -} - -LZ4FDecoder::~LZ4FDecoder() { - LZ4F_freeDecompressionContext(ctx); - delete[] outbuf; -} - -bool LZ4FDecoder::write(const void *in, size_t size) { - auto inbuf = (const uint8_t *) in; - if (!outbuf) - read_header(inbuf, size); - size_t read, write; - LZ4F_errorCode_t ret; - do { - read = size; - write = outCapacity; - ret = LZ4F_decompress(ctx, outbuf, &write, inbuf, &read, nullptr); - if (LZ4F_isError(ret)) { - LOGW("LZ4 decode error: %s\n", LZ4F_getErrorName(ret)); - return false; - } - size -= read; - inbuf += read; - total += write; - FilterOutStream::write(outbuf, write); - } while (size != 0 || write != 0); - return true; -} - -uint64_t LZ4FDecoder::finalize() { - return total; -} - -void LZ4FDecoder::read_header(const uint8_t *&in, size_t &size) { - size_t read = size; - LZ4F_frameInfo_t info; - LZ4F_getFrameInfo(ctx, &info, in, &read); - switch (info.blockSizeID) { - case LZ4F_default: - case LZ4F_max64KB: outCapacity = 1 << 16; break; - case LZ4F_max256KB: outCapacity = 1 << 18; break; - case LZ4F_max1MB: outCapacity = 1 << 20; break; - case LZ4F_max4MB: outCapacity = 1 << 22; break; - } - outbuf = new uint8_t[outCapacity]; - in += read; - size -= read; -} - -LZ4FEncoder::LZ4FEncoder() : outbuf(nullptr), outCapacity(0), total(0) { - LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); -} - -LZ4FEncoder::~LZ4FEncoder() { - LZ4F_freeCompressionContext(ctx); - delete[] outbuf; -} - -bool LZ4FEncoder::write(const void *in, size_t size) { - if (!outbuf) - write_header(); - if (size == 0) - return true; - auto inbuf = (const uint8_t *) in; - size_t read, write; - do { - read = size > BLOCK_SZ ? BLOCK_SZ : size; - write = LZ4F_compressUpdate(ctx, outbuf, outCapacity, inbuf, read, nullptr); - if (LZ4F_isError(write)) { - LOGW("LZ4 encode error: %s\n", LZ4F_getErrorName(write)); - return false; - } - size -= read; - inbuf += read; - total += write; - FilterOutStream::write(outbuf, write); - } while (size != 0); - return true; -} - -uint64_t LZ4FEncoder::finalize() { - size_t write = LZ4F_compressEnd(ctx, outbuf, outCapacity, nullptr); - total += write; - FilterOutStream::write(outbuf, write); - return total; -} - -void LZ4FEncoder::write_header() { - LZ4F_preferences_t prefs { - .autoFlush = 1, - .compressionLevel = 9, - .frameInfo = { - .blockMode = LZ4F_blockIndependent, - .blockSizeID = LZ4F_max4MB, - .blockChecksumFlag = LZ4F_noBlockChecksum, - .contentChecksumFlag = LZ4F_contentChecksumEnabled - } - }; - outCapacity = LZ4F_compressBound(BLOCK_SZ, &prefs); - outbuf = new uint8_t[outCapacity]; - size_t write = LZ4F_compressBegin(ctx, outbuf, outCapacity, &prefs); - total += write; - FilterOutStream::write(outbuf, write); -} - -LZ4Decoder::LZ4Decoder() : outbuf(new char[LZ4_UNCOMPRESSED]), buf(new char[LZ4_COMPRESSED]), -init(false), block_sz(0), buf_off(0), total(0) {} - -LZ4Decoder::~LZ4Decoder() { - delete[] outbuf; - delete[] buf; -} - -bool LZ4Decoder::write(const void *in, size_t size) { - const char *inbuf = (const char *) in; - if (!init) { - // Skip magic - inbuf += 4; - size -= 4; - init = true; - } - int write; - size_t consumed; - do { - if (block_sz == 0) { - block_sz = *((unsigned *) inbuf); - inbuf += sizeof(unsigned); - size -= sizeof(unsigned); - } else if (buf_off + size >= block_sz) { - consumed = block_sz - buf_off; - memcpy(buf + buf_off, inbuf, consumed); - inbuf += consumed; - size -= consumed; - - write = LZ4_decompress_safe(buf, outbuf, block_sz, LZ4_UNCOMPRESSED); - if (write < 0) { - LOGW("LZ4HC decompression failure (%d)\n", write); - return false; - } - FilterOutStream::write(outbuf, write); - total += write; - - // Reset - buf_off = 0; - block_sz = 0; - } else { - // Copy to internal buffer - memcpy(buf + buf_off, inbuf, size); - buf_off += size; - size = 0; - } - } while (size != 0); - return true; -} - -uint64_t LZ4Decoder::finalize() { - return total; -} - -LZ4Encoder::LZ4Encoder() : outbuf(new char[LZ4_COMPRESSED]), buf(new char[LZ4_UNCOMPRESSED]), -init(false), buf_off(0), out_total(0), in_total(0) {} - -LZ4Encoder::~LZ4Encoder() { - delete[] outbuf; - delete[] buf; -} - -bool LZ4Encoder::write(const void *in, size_t size) { - if (!init) { - FilterOutStream::write("\x02\x21\x4c\x18", 4); - init = true; - } - if (size == 0) - return true; - in_total += size; - const char *inbuf = (const char *) in; - size_t consumed; - int write; - do { - if (buf_off + size >= LZ4_UNCOMPRESSED) { - consumed = LZ4_UNCOMPRESSED - buf_off; - memcpy(buf + buf_off, inbuf, consumed); - inbuf += consumed; - size -= consumed; - - write = LZ4_compress_HC(buf, outbuf, LZ4_UNCOMPRESSED, LZ4_COMPRESSED, 9); - if (write == 0) { - LOGW("LZ4HC compression failure\n"); - return false; - } - FilterOutStream::write(&write, sizeof(write)); - FilterOutStream::write(outbuf, write); - out_total += write + sizeof(write); - - // Reset buffer - buf_off = 0; - } else { - // Copy to internal buffer - memcpy(buf + buf_off, inbuf, size); - buf_off += size; - size = 0; - } - } while (size != 0); - return true; -} - -uint64_t LZ4Encoder::finalize() { - if (buf_off) { - int write = LZ4_compress_HC(buf, outbuf, buf_off, LZ4_COMPRESSED, 9); - FilterOutStream::write(&write, sizeof(write)); - FilterOutStream::write(outbuf, write); - out_total += write + sizeof(write); - } - FilterOutStream::write(&in_total, sizeof(in_total)); - return out_total + sizeof(in_total); -} diff --git a/native/jni/magiskboot/compress.h b/native/jni/magiskboot/compress.h index 81b9bead8..d3c92e999 100644 --- a/native/jni/magiskboot/compress.h +++ b/native/jni/magiskboot/compress.h @@ -1,174 +1,10 @@ #pragma once -#include -#include -#include -#include -#include -#include #include #include "format.h" -#define CHUNK 0x40000 - -class Compression : public FilterOutStream { -public: - virtual uint64_t finalize() = 0; -}; - -class GZStream : public Compression { -public: - bool write(const void *in, size_t size) override; - uint64_t finalize() override; - -protected: - explicit GZStream(int mode); - -private: - int mode; - z_stream strm; - uint8_t outbuf[CHUNK]; - - bool write(const void *in, size_t size, int flush); -}; - -class GZDecoder : public GZStream { -public: - GZDecoder() : GZStream(0) {}; -}; - -class GZEncoder : public GZStream { -public: - GZEncoder() : GZStream(1) {}; -}; - -class BZStream : public Compression { -public: - bool write(const void *in, size_t size) override; - uint64_t finalize() override; - -protected: - explicit BZStream(int mode); - -private: - int mode; - bz_stream strm; - char outbuf[CHUNK]; - - bool write(const void *in, size_t size, int flush); -}; - -class BZDecoder : public BZStream { -public: - BZDecoder() : BZStream(0) {}; -}; - -class BZEncoder : public BZStream { -public: - BZEncoder() : BZStream(1) {}; -}; - -class LZMAStream : public Compression { -public: - bool write(const void *in, size_t size) override; - uint64_t finalize() override; - -protected: - explicit LZMAStream(int mode); - -private: - int mode; - lzma_stream strm; - uint8_t outbuf[CHUNK]; - - bool write(const void *in, size_t size, lzma_action flush); -}; - -class LZMADecoder : public LZMAStream { -public: - LZMADecoder() : LZMAStream(0) {} -}; - -class XZEncoder : public LZMAStream { -public: - XZEncoder() : LZMAStream(1) {} -}; - -class LZMAEncoder : public LZMAStream { -public: - LZMAEncoder() : LZMAStream(2) {} -}; - -class LZ4FDecoder : public Compression { -public: - LZ4FDecoder(); - ~LZ4FDecoder() override; - bool write(const void *in, size_t size) override; - uint64_t finalize() override; - -private: - LZ4F_decompressionContext_t ctx; - uint8_t *outbuf; - size_t outCapacity; - uint64_t total; - - void read_header(const uint8_t *&in, size_t &size); -}; - -class LZ4FEncoder : public Compression { -public: - LZ4FEncoder(); - ~LZ4FEncoder() override; - bool write(const void *in, size_t size) override; - uint64_t finalize() override; - -private: - static constexpr size_t BLOCK_SZ = 1 << 22; - LZ4F_compressionContext_t ctx; - uint8_t *outbuf; - size_t outCapacity; - uint64_t total; - - void write_header(); -}; - -#define LZ4_UNCOMPRESSED 0x800000 -#define LZ4_COMPRESSED LZ4_COMPRESSBOUND(LZ4_UNCOMPRESSED) - -class LZ4Decoder : public Compression { -public: - LZ4Decoder(); - ~LZ4Decoder() override; - bool write(const void *in, size_t size) override; - uint64_t finalize() override; - -private: - char *outbuf; - char *buf; - bool init; - unsigned block_sz; - int buf_off; - uint64_t total; -}; - -class LZ4Encoder : public Compression { -public: - LZ4Encoder(); - ~LZ4Encoder() override; - bool write(const void *in, size_t size) override; - uint64_t finalize() override; - -private: - char *outbuf; - char *buf; - bool init; - int buf_off; - uint64_t out_total; - unsigned in_total; -}; - -Compression *get_encoder(format_t type); -Compression *get_decoder(format_t type); +filter_stream *get_encoder(format_t type, FILE *fp = nullptr); +filter_stream *get_decoder(format_t type, FILE *fp = nullptr); void compress(const char *method, const char *infile, const char *outfile); void decompress(char *infile, const char *outfile); diff --git a/native/jni/magiskboot/ramdisk.cpp b/native/jni/magiskboot/ramdisk.cpp index 157ae5ac4..faabde059 100644 --- a/native/jni/magiskboot/ramdisk.cpp +++ b/native/jni/magiskboot/ramdisk.cpp @@ -241,14 +241,17 @@ void magisk_cpio::compress() { return; fprintf(stderr, "Compressing cpio -> [%s]\n", RAMDISK_XZ); auto init = entries.extract("init"); - XZEncoder encoder; - encoder.setOut(make_unique()); - output(encoder); - encoder.finalize(); + + uint8_t *data; + size_t len; + FILE *fp = open_stream(get_encoder(XZ, open_stream(data, len))); + dump(fp); + entries.clear(); entries.insert(std::move(init)); auto xz = new cpio_entry(RAMDISK_XZ, S_IFREG); - static_cast(encoder.getOut())->release(xz->data, xz->filesize); + xz->data = data; + xz->filesize = len; insert(xz); } @@ -257,15 +260,16 @@ void magisk_cpio::decompress() { if (it == entries.end()) return; fprintf(stderr, "Decompressing cpio [%s]\n", RAMDISK_XZ); - LZMADecoder decoder; - decoder.setOut(make_unique()); - decoder.write(it->second->data, it->second->filesize); - decoder.finalize(); + + char *data; + size_t len; + auto strm = get_decoder(XZ, open_stream(data, len)); + strm->write(it->second->data, it->second->filesize); + delete strm; + entries.erase(it); - char *buf; - size_t sz; - static_cast(decoder.getOut())->getbuf(buf, sz); - load_cpio(buf, sz); + load_cpio(data, len); + free(data); } int cpio_commands(int argc, char *argv[]) { @@ -338,4 +342,4 @@ int cpio_commands(int argc, char *argv[]) { cpio.dump(incpio); return 0; -} \ No newline at end of file +} diff --git a/native/jni/utils/cpio.cpp b/native/jni/utils/cpio.cpp index 89840c98d..76e4ccb5e 100644 --- a/native/jni/utils/cpio.cpp +++ b/native/jni/utils/cpio.cpp @@ -49,8 +49,7 @@ cpio_entry_base::cpio_entry_base(const cpio_newc_header *h) void cpio::dump(const char *file) { fprintf(stderr, "Dump cpio: [%s]\n", file); - FDOutStream fd_out(xopen(file, O_WRONLY | O_CREAT | O_TRUNC, 0644), true); - output(fd_out); + dump(xfopen(file, "we")); } void cpio::rm(entry_map::iterator &it) { @@ -110,9 +109,9 @@ bool cpio::exists(const char *name) { return entries.count(name) != 0; } -#define do_out(b, l) out.write(b, l); pos += (l) -#define out_align() out.write(zeros, align_off(pos, 4)); pos = do_align(pos, 4) -void cpio::output(OutStream &out) { +#define do_out(buf, len) pos += fwrite(buf, len, 1, out); +#define out_align() do_out(zeros, align_off(pos, 4)) +void cpio::dump(FILE *out) { size_t pos = 0; unsigned inode = 300000; char header[111]; @@ -147,6 +146,7 @@ void cpio::output(OutStream &out) { do_out(header, 110); do_out("TRAILER!!!\0", 11); out_align(); + fclose(out); } cpio_rw::cpio_rw(const char *file) { @@ -221,12 +221,12 @@ bool cpio_rw::mv(const char *from, const char *to) { #define pos_align(p) p = do_align(p, 4) -void cpio_rw::load_cpio(char *buf, size_t sz) { +void cpio_rw::load_cpio(const char *buf, size_t sz) { size_t pos = 0; - cpio_newc_header *header; + const cpio_newc_header *header; unique_ptr entry; while (pos < sz) { - header = (cpio_newc_header *)(buf + pos); + header = reinterpret_cast(buf + pos); entry = make_unique(header); pos += sizeof(*header); string_view name_view(buf + pos); diff --git a/native/jni/utils/include/cpio.h b/native/jni/utils/include/cpio.h index 4f6f09917..51a2514d3 100644 --- a/native/jni/utils/include/cpio.h +++ b/native/jni/utils/include/cpio.h @@ -30,7 +30,7 @@ struct cpio_entry : public cpio_entry_base { explicit cpio_entry(const char *name, uint32_t mode) : filename(name) { this->mode = mode; } - explicit cpio_entry(cpio_newc_header *h) : cpio_entry_base(h) {} + explicit cpio_entry(const cpio_newc_header *h) : cpio_entry_base(h) {} ~cpio_entry() override { free(data); }; }; @@ -48,7 +48,7 @@ public: protected: entry_map entries; void rm(entry_map::iterator &it); - void output(OutStream &out); + void dump(FILE *out); }; class cpio_rw : public cpio { @@ -64,7 +64,7 @@ public: protected: void insert(cpio_entry *e); void mv(entry_map::iterator &it, const char *to); - void load_cpio(char *buf, size_t sz); + void load_cpio(const char *buf, size_t sz); }; class cpio_mmap : public cpio { diff --git a/native/jni/utils/include/stream.h b/native/jni/utils/include/stream.h index 43d0562d0..b296d72ab 100644 --- a/native/jni/utils/include/stream.h +++ b/native/jni/utils/include/stream.h @@ -15,8 +15,6 @@ FILE *open_stream(Args &&... args) { return open_stream(new T(args...)); } -/* Base classes */ - class stream { public: virtual int read(void *buf, size_t len); @@ -26,17 +24,17 @@ public: virtual ~stream() = default; }; +// Delegates all operations to the base FILE pointer class filter_stream : public stream { public: filter_stream(FILE *fp) : fp(fp) {} - int close() override { return fclose(fp); } - virtual ~filter_stream() { close(); } + ~filter_stream() override { if (fp) close(); } - void set_base(FILE *f) { - if (fp) fclose(fp); - fp = f; - } + int read(void *buf, size_t len) override; + int write(const void *buf, size_t len) override; + int close() override; + void set_base(FILE *f); template void set_base(Args&&... args) { set_base(open_stream(args...)); @@ -46,18 +44,7 @@ protected: FILE *fp; }; -class filter_in_stream : public filter_stream { -public: - filter_in_stream(FILE *fp = nullptr) : filter_stream(fp) {} - int read(void *buf, size_t len) override { return fread(buf, len, 1, fp); } -}; - -class filter_out_stream : public filter_stream { -public: - filter_out_stream(FILE *fp = nullptr) : filter_stream(fp) {} - int write(const void *buf, size_t len) override { return fwrite(buf, len, 1, fp); } -}; - +// Handy interface for classes that need custom seek logic class seekable_stream : public stream { protected: size_t _pos = 0; @@ -66,8 +53,7 @@ protected: virtual size_t end_pos() = 0; }; -/* Concrete classes */ - +// Byte stream that dynamically allocates memory class byte_stream : public seekable_stream { public: byte_stream(uint8_t *&buf, size_t &len); @@ -76,7 +62,6 @@ public: int read(void *buf, size_t len) override; int write(const void *buf, size_t len) override; off_t seek(off_t off, int whence) override; - virtual ~byte_stream() = default; private: uint8_t *&_buf; @@ -87,101 +72,14 @@ private: size_t end_pos() override { return _len; } }; -class fd_stream : stream { +// File stream but does not close the file descriptor at any time +class fd_stream : public stream { public: fd_stream(int fd) : fd(fd) {} int read(void *buf, size_t len) override; int write(const void *buf, size_t len) override; off_t seek(off_t off, int whence) override; - virtual ~fd_stream() = default; private: int fd; }; - -/* TODO: Replace classes below to new implementation */ - -class OutStream { -public: - virtual bool write(const void *buf, size_t len) = 0; - virtual ~OutStream() = default; -}; - -typedef std::unique_ptr strm_ptr; - -class FilterOutStream : public OutStream { -public: - FilterOutStream() = default; - - FilterOutStream(strm_ptr &&ptr) : out(std::move(ptr)) {} - - void setOut(strm_ptr &&ptr) { out = std::move(ptr); } - - OutStream *getOut() { return out.get(); } - - bool write(const void *buf, size_t len) override { - return out ? out->write(buf, len) : false; - } - -protected: - strm_ptr out; -}; - -class FDOutStream : public OutStream { -public: - FDOutStream(int fd, bool close = false) : fd(fd), close(close) {} - - bool write(const void *buf, size_t len) override { - return ::write(fd, buf, len) == len; - } - - ~FDOutStream() override { - if (close) - ::close(fd); - } - -protected: - int fd; - bool close; -}; - -class BufOutStream : public OutStream { -public: - BufOutStream() : buf(nullptr), off(0), cap(0) {}; - - bool write(const void *b, size_t len) override { - bool resize = false; - while (off + len > cap) { - cap = cap ? cap << 1 : 1 << 19; - resize = true; - } - if (resize) - buf = (char *) xrealloc(buf, cap); - memcpy(buf + off, b, len); - off += len; - return true; - } - - template - void release(bytes *&b, length &len) { - b = buf; - len = off; - buf = nullptr; - off = cap = 0; - } - - template - void getbuf(bytes *&b, length &len) const { - b = buf; - len = off; - } - - ~BufOutStream() override { - free(buf); - } - -protected: - char *buf; - size_t off; - size_t cap; -}; diff --git a/native/jni/utils/stream.cpp b/native/jni/utils/stream.cpp index 442373cf6..1aa7d87fd 100644 --- a/native/jni/utils/stream.cpp +++ b/native/jni/utils/stream.cpp @@ -49,6 +49,25 @@ int stream::close() { return 0; } +int filter_stream::read(void *buf, size_t len) { + return fread(buf, len, 1, fp); +} + +int filter_stream::write(const void *buf, size_t len) { + return fwrite(buf, len, 1, fp); +} + +int filter_stream::close() { + int ret = fclose(fp); + fp = nullptr; + return ret; +} + +void filter_stream::set_base(FILE *f) { + if (fp) fclose(fp); + fp = f; +} + off_t seekable_stream::new_pos(off_t off, int whence) { off_t new_pos; switch (whence) { From d3b5cf82d823ce43c917c5b25481ddca123e976e Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 21 Nov 2019 06:17:28 -0500 Subject: [PATCH 74/74] Small adjustments --- native/jni/utils/include/stream.h | 4 ++-- native/jni/utils/stream.cpp | 16 ++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/native/jni/utils/include/stream.h b/native/jni/utils/include/stream.h index b296d72ab..eaed8a144 100644 --- a/native/jni/utils/include/stream.h +++ b/native/jni/utils/include/stream.h @@ -4,7 +4,7 @@ #include #include -#include +// #include class stream; @@ -49,7 +49,7 @@ class seekable_stream : public stream { protected: size_t _pos = 0; - off_t new_pos(off_t off, int whence); + off_t seek_pos(off_t off, int whence); virtual size_t end_pos() = 0; }; diff --git a/native/jni/utils/stream.cpp b/native/jni/utils/stream.cpp index 1aa7d87fd..607b4d026 100644 --- a/native/jni/utils/stream.cpp +++ b/native/jni/utils/stream.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -68,22 +69,17 @@ void filter_stream::set_base(FILE *f) { fp = f; } -off_t seekable_stream::new_pos(off_t off, int whence) { - off_t new_pos; +off_t seekable_stream::seek_pos(off_t off, int whence) { switch (whence) { case SEEK_CUR: - new_pos = _pos + off; - break; + return _pos + off; case SEEK_END: - new_pos = end_pos() + off; - break; + return end_pos() + off; case SEEK_SET: - new_pos = off; - break; + return off; default: return -1; } - return new_pos; } byte_stream::byte_stream(uint8_t *&buf, size_t &len) : _buf(buf), _len(len) { @@ -106,7 +102,7 @@ int byte_stream::write(const void *buf, size_t len) { } off_t byte_stream::seek(off_t off, int whence) { - off_t np = new_pos(off, whence); + off_t np = seek_pos(off, whence); if (np < 0) return -1; resize(np, true);