Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package zed.rainxch.core.data.services.dhizuku;

interface IDhizukuInstallerService {
int installPackage(in ParcelFileDescriptor pfd, long fileSize, String expectedPackageName, long expectedVersionCode);
int installPackage(in ParcelFileDescriptor pfd, long fileSize, String expectedPackageName, long expectedVersionCode, String installerPackageName);
int uninstallPackage(String packageName);
void destroy();
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package zed.rainxch.core.data.services.shizuku;

interface IShizukuInstallerService {
int installPackage(in ParcelFileDescriptor pfd, long fileSize);
int installPackage(in ParcelFileDescriptor pfd, long fileSize, String installerPackageName);
int uninstallPackage(String packageName);
void destroy();
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ class DhizukuInstallerServiceImpl() : IDhizukuInstallerService.Stub() {
fileSize: Long,
expectedPackageName: String?,
expectedVersionCode: Long,
installerPackageName: String?,
): Int {
log("installPackage() called — fileSize=$fileSize, expected=$expectedPackageName@$expectedVersionCode")
log("installPackage() called — fileSize=$fileSize, expected=$expectedPackageName@$expectedVersionCode, installer=$installerPackageName")
log("Process UID: ${android.os.Process.myUid()}, PID: ${android.os.Process.myPid()}")

val ctx: Context = currentApplicationOrNull() ?: run {
Expand All @@ -51,6 +52,13 @@ class DhizukuInstallerServiceImpl() : IDhizukuInstallerService.Stub() {
params.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED)
}
if (fileSize > 0) params.setSize(fileSize)
installerPackageName?.takeIf { it.isNotBlank() }?.let { name ->
try {
params.setInstallerPackageName(name)
} catch (e: Exception) {
logW("setInstallerPackageName($name) failed: ${e.message}")
}
}

var sessionId = -1
var session: PackageInstaller.Session? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import zed.rainxch.core.data.services.dhizuku.DhizukuServiceManager
import zed.rainxch.core.data.services.dhizuku.model.DhizukuStatus
import zed.rainxch.core.data.services.shizuku.ShizukuServiceManager
import zed.rainxch.core.data.services.shizuku.model.ShizukuStatus
import kotlinx.coroutines.flow.first
import zed.rainxch.core.domain.model.GithubAsset
import zed.rainxch.core.domain.model.InstallerAttribution
import zed.rainxch.core.domain.model.InstallerType
import zed.rainxch.core.domain.model.SystemArchitecture
import zed.rainxch.core.domain.repository.TweaksRepository
Expand All @@ -35,13 +37,22 @@ class SilentInstallerDispatcher(
@Volatile
private var cachedInstallerType: InstallerType = InstallerType.DEFAULT

@Volatile
private var cachedInstallerAttribution: InstallerAttribution = InstallerAttribution.SystemDefault

fun observeInstallerPreference() {
scope.launch {
tweaksRepository.getInstallerType().collect { type ->
cachedInstallerType = type
Logger.d(TAG) { "Installer type changed to: $type" }
}
}
scope.launch {
tweaksRepository.getInstallerAttribution().collect { attribution ->
cachedInstallerAttribution = attribution
Logger.d(TAG) { "Installer attribution changed to: $attribution" }
}
}
}

override suspend fun isSupported(extOrMime: String): Boolean = androidInstaller.isSupported(extOrMime)
Expand Down Expand Up @@ -118,6 +129,7 @@ class SilentInstallerDispatcher(

private suspend fun trySilentInstall(filePath: String, backend: Backend): InstallOutcome? {
Logger.d(TAG) { "Routing install through $backend" }
val installerAttribution = cachedInstallerAttribution.resolvePackageName()
return try {
val result = withContext(Dispatchers.IO) {
val file = File(filePath)
Expand All @@ -126,11 +138,11 @@ class SilentInstallerDispatcher(
when (backend) {
Backend.SHIZUKU -> {
val service = shizukuServiceManager.getService() ?: return@use null
service.installPackage(pfd, file.length())
service.installPackage(pfd, file.length(), installerAttribution)
}
Backend.DHIZUKU -> {
val service = dhizukuServiceManager.getService() ?: return@use null
service.installPackage(pfd, file.length(), expectedPkg, expectedVc)
service.installPackage(pfd, file.length(), expectedPkg, expectedVc, installerAttribution)
}
Backend.DEFAULT -> null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,21 @@ class ShizukuInstallerServiceImpl() : IShizukuInstallerService.Stub() {
private fun logE(msg: String, e: Throwable? = null) = android.util.Log.e(TAG, msg, e)
}

override fun installPackage(pfd: ParcelFileDescriptor, fileSize: Long): Int {
log("installPackage() called — fileSize=$fileSize")
override fun installPackage(
pfd: ParcelFileDescriptor,
fileSize: Long,
installerPackageName: String?,
): Int {
log("installPackage() called — fileSize=$fileSize, installer=$installerPackageName")
log("Process UID: ${android.os.Process.myUid()}, PID: ${android.os.Process.myPid()}")

return try {
// Use "pm install -S <size>" which reads the APK from stdin
val command = arrayOf("pm", "install", "-S", fileSize.toString())
val safeInstaller = installerPackageName?.takeIf { it.isNotBlank() }
val command = if (safeInstaller != null) {
arrayOf("pm", "install", "-i", safeInstaller, "-S", fileSize.toString())
} else {
arrayOf("pm", "install", "-S", fileSize.toString())
}
log("Executing: ${command.joinToString(" ")}")

val process = Runtime.getRuntime().exec(command)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,56 @@ class TweaksRepositoryImpl(
}
}

override fun getInstallerAttribution(): Flow<zed.rainxch.core.domain.model.InstallerAttribution> =
preferences.data.map { prefs ->
val raw = prefs[INSTALLER_ATTRIBUTION_KEY]
decodeInstallerAttribution(raw)
}

override suspend fun setInstallerAttribution(
attribution: zed.rainxch.core.domain.model.InstallerAttribution,
) {
preferences.edit { prefs ->
prefs[INSTALLER_ATTRIBUTION_KEY] = encodeInstallerAttribution(attribution)
}
}

private fun decodeInstallerAttribution(
raw: String?,
): zed.rainxch.core.domain.model.InstallerAttribution {
if (raw.isNullOrBlank()) return zed.rainxch.core.domain.model.InstallerAttribution.SystemDefault
val parts = raw.split(":", limit = 2)
return when (parts[0]) {
"preset" -> {
val key = parts.getOrNull(1)?.let {
zed.rainxch.core.domain.model.PresetKey.fromName(it)
}
if (key != null) {
zed.rainxch.core.domain.model.InstallerAttribution.Preset(key)
} else {
zed.rainxch.core.domain.model.InstallerAttribution.SystemDefault
}
}
"custom" -> {
val name = parts.getOrNull(1).orEmpty()
if (name.isNotBlank()) {
zed.rainxch.core.domain.model.InstallerAttribution.Custom(name)
} else {
zed.rainxch.core.domain.model.InstallerAttribution.SystemDefault
}
}
else -> zed.rainxch.core.domain.model.InstallerAttribution.SystemDefault
}
}

private fun encodeInstallerAttribution(
attribution: zed.rainxch.core.domain.model.InstallerAttribution,
): String = when (attribution) {
zed.rainxch.core.domain.model.InstallerAttribution.SystemDefault -> ""
is zed.rainxch.core.domain.model.InstallerAttribution.Preset -> "preset:${attribution.key.name}"
is zed.rainxch.core.domain.model.InstallerAttribution.Custom -> "custom:${attribution.packageName.trim()}"
}

override fun getAutoUpdateEnabled(): Flow<Boolean> =
preferences.data.map { prefs ->
prefs[AUTO_UPDATE_KEY] ?: false
Expand Down Expand Up @@ -368,6 +418,7 @@ class TweaksRepositoryImpl(
private val DISCOVERY_PLATFORMS_KEY = stringSetPreferencesKey("discovery_platforms")
private val AUTO_DETECT_CLIPBOARD_KEY = booleanPreferencesKey("auto_detect_clipboard_links")
private val INSTALLER_TYPE_KEY = stringPreferencesKey("installer_type")
private val INSTALLER_ATTRIBUTION_KEY = stringPreferencesKey("installer_attribution")
private val AUTO_UPDATE_KEY = booleanPreferencesKey("auto_update_enabled")
private val UPDATE_CHECK_INTERVAL_KEY = longPreferencesKey("update_check_interval_hours")
private val INCLUDE_PRE_RELEASES_KEY = booleanPreferencesKey("include_pre_releases")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package zed.rainxch.core.domain.model

import kotlinx.serialization.Serializable

@Serializable
sealed interface InstallerAttribution {
@Serializable
data object SystemDefault : InstallerAttribution

@Serializable
data class Preset(val key: PresetKey) : InstallerAttribution

@Serializable
data class Custom(val packageName: String) : InstallerAttribution

fun resolvePackageName(): String? = when (this) {
SystemDefault -> null
is Preset -> key.packageName
is Custom -> packageName.trim().takeIf { it.isNotBlank() }
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

@Serializable
enum class PresetKey(val packageName: String) {
PLAY_STORE("com.android.vending"),
FDROID("org.fdroid.fdroid"),
OBTAINIUM("dev.imranr.obtainium.app"),
;

companion object {
fun fromName(name: String?): PresetKey? = entries.find { it.name == name }
}
}

object InstallerAttributionDefaults {
val packageNamePattern = Regex("^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)+\$")

fun isValidPackageName(name: String): Boolean {
val trimmed = name.trim()
if (trimmed.isEmpty()) return false
return packageNamePattern.matches(trimmed)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ interface TweaksRepository {

suspend fun setInstallerType(type: InstallerType)

fun getInstallerAttribution(): Flow<zed.rainxch.core.domain.model.InstallerAttribution>

suspend fun setInstallerAttribution(attribution: zed.rainxch.core.domain.model.InstallerAttribution)

fun getAutoUpdateEnabled(): Flow<Boolean>

suspend fun setAutoUpdateEnabled(enabled: Boolean)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"Announcements feed — privacy notices, surveys, and security advisories in Profile.",
"Dhizuku silent install — bypass OEM install prompts on Xiaomi, OPPO, vivo, Huawei devices via Device Owner.",
"Obtainium import/export — bring your library over from Obtainium with one tap, or export to Obtainium any time.",
"Add from starred — surface APK-shipping repos from your GitHub stars and jump straight into installing."
"Add from starred — surface APK-shipping repos from your GitHub stars and jump straight into installing.",
"Installer attribution — set what installer name silent installs claim, so apps that gate on installer source can be coaxed into running."
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"بثّ الإعلانات: إشعارات الخصوصية والاستبيانات والتنبيهات الأمنية داخل «الملف الشخصي».",
"تثبيت صامت عبر Dhizuku: تجاوز نوافذ التثبيت في أجهزة Xiaomi و OPPO و vivo و Huawei عبر صلاحية «مالك الجهاز».",
"استيراد/تصدير Obtainium: انقل مكتبتك من Obtainium بنقرة واحدة، أو صدّر إلى صيغة Obtainium في أي وقت.",
"إضافة من المُنجَّمة: استعرض المستودعات التي تشحن APK ضمن نجوم GitHub لديك وانتقل مباشرة إلى التثبيت."
"إضافة من المُنجَّمة: استعرض المستودعات التي تشحن APK ضمن نجوم GitHub لديك وانتقل مباشرة إلى التثبيت.",
"تخصيص هوية المثبّت: عيّن اسم المثبّت الذي تدّعيه التثبيتات الصامتة، حتى تعمل التطبيقات التي تتحقّق من مصدر التثبيت."
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"ঘোষণা ফিড: গোপনীয়তা নোটিশ, জরিপ আর নিরাপত্তা সতর্কতা প্রোফাইলে এক জায়গায়।",
"Dhizuku সাইলেন্ট ইনস্টল — Device Owner-এর মাধ্যমে Xiaomi, OPPO, vivo, Huawei ডিভাইসে OEM ইনস্টল প্রম্পট এড়িয়ে যান।",
"Obtainium ইম্পোর্ট/এক্সপোর্ট — এক ট্যাপে Obtainium থেকে লাইব্রেরি আনুন, বা যেকোনো সময় Obtainium ফরম্যাটে এক্সপোর্ট করুন।",
"Add from starred — আপনার GitHub স্টার করা যেসব রিপো APK পাঠায় সেগুলো দেখুন আর সরাসরি ইনস্টলে যান।"
"Add from starred — আপনার GitHub স্টার করা যেসব রিপো APK পাঠায় সেগুলো দেখুন আর সরাসরি ইনস্টলে যান।",
"Installer attribution — সাইলেন্ট ইনস্টল কোন ইনস্টলার নাম দাবি করবে তা সেট করুন, যাতে যেসব অ্যাপ ইনস্টলার সোর্স দেখে সেগুলোও চলতে পারে।"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"Canal de anuncios: avisos de privacidad, encuestas y alertas de seguridad en Perfil.",
"Instalación silenciosa con Dhizuku: omite los diálogos de instalación de OEM en Xiaomi, OPPO, vivo y Huawei mediante el propietario del dispositivo.",
"Importar/Exportar Obtainium: trae tu biblioteca desde Obtainium con un toque, o exporta a formato Obtainium cuando quieras.",
"Añadir desde estrellas: descubre los repos de tus estrellas en GitHub que envían APK y salta directo a instalar."
"Añadir desde estrellas: descubre los repos de tus estrellas en GitHub que envían APK y salta directo a instalar.",
"Atribución del instalador: define qué nombre de instalador declaran las instalaciones silenciosas, para que las apps que filtran por origen del instalador funcionen."
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"Fil d’annonces : avis de confidentialité, sondages et alertes de sécurité dans Profil.",
"Installation silencieuse via Dhizuku : contournez les fenêtres d’installation des OEM (Xiaomi, OPPO, vivo, Huawei) grâce au statut Propriétaire de l’appareil.",
"Import/Export Obtainium : récupérez votre bibliothèque depuis Obtainium en un toucher, ou exportez vers Obtainium quand vous voulez.",
"Ajouter depuis les étoiles : repérez parmi vos repos étoilés sur GitHub ceux qui livrent un APK, puis installez-les directement."
"Ajouter depuis les étoiles : repérez parmi vos repos étoilés sur GitHub ceux qui livrent un APK, puis installez-les directement.",
"Attribution de l’installateur : choisissez le nom d’installateur que les installations silencieuses revendiquent, pour que les apps qui filtrent par source d’installation fonctionnent."
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"घोषणा फ़ीड: गोपनीयता सूचनाएँ, सर्वे और सुरक्षा अलर्ट प्रोफ़ाइल में मिलेंगे।",
"Dhizuku साइलेंट इंस्टॉल — Device Owner के ज़रिए Xiaomi, OPPO, vivo, Huawei डिवाइसों पर OEM इंस्टॉल प्रॉम्प्ट बायपास करें।",
"Obtainium इम्पोर्ट/एक्सपोर्ट — एक टैप से Obtainium से अपनी लाइब्रेरी लाएँ, या जब चाहें Obtainium फ़ॉर्मैट में एक्सपोर्ट करें।",
"Add from starred — अपने GitHub स्टार किए हुए रेपो में से APK वाले को सामने लाएँ और सीधे इंस्टॉल पर जाएँ।"
"Add from starred — अपने GitHub स्टार किए हुए रेपो में से APK वाले को सामने लाएँ और सीधे इंस्टॉल पर जाएँ।",
"Installer attribution — साइलेंट इंस्टॉल किस इंस्टॉलर नाम का दावा करेंगे, इसे सेट करें ताकि इंस्टॉलर सोर्स पर निर्भर ऐप्स भी चल सकें।"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"Feed annunci: avvisi sulla privacy, sondaggi e segnalazioni di sicurezza nel Profilo.",
"Installazione silenziosa con Dhizuku: bypassa i prompt di installazione OEM su Xiaomi, OPPO, vivo e Huawei tramite il proprietario del dispositivo.",
"Import/Export Obtainium: porta la tua libreria da Obtainium con un tocco, o esporta verso il formato Obtainium quando vuoi.",
"Aggiungi dalle stelle: scopri i repo che spediscono APK fra le tue stelle GitHub e vai dritto all'installazione."
"Aggiungi dalle stelle: scopri i repo che spediscono APK fra le tue stelle GitHub e vai dritto all'installazione.",
"Attribuzione installatore: scegli quale nome di installatore dichiarano le installazioni silenziose, così le app che filtrano sull'origine funzionano."
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"お知らせフィード: プライバシー通知・アンケート・セキュリティ告知をプロフィール内で配信します。",
"Dhizuku によるサイレントインストール — Device Owner 経由で Xiaomi、OPPO、vivo、Huawei 端末の OEM インストール確認をスキップ。",
"Obtainium インポート/エクスポート — Obtainium のライブラリをワンタップで取り込み、いつでも Obtainium 形式で書き出し。",
"スター付きから追加 — GitHub のスター付きリポから APK を配布しているものを表示し、そのままインストールに進めます。"
"スター付きから追加 — GitHub のスター付きリポから APK を配布しているものを表示し、そのままインストールに進めます。",
"インストーラー属性 — サイレントインストール時に名乗るインストーラー名を変更でき、インストール元を見るアプリも動かせるようにします。"
]
},
{
Expand Down
Loading