Skip to content
Closed
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
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ android {
release {
isMinifyEnabled = true
isShrinkResources = true
signingConfig = signingConfigs.getByName("debug")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
Expand Down
45 changes: 45 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,14 @@
android:taskAffinity=""
android:theme="@style/Theme.Essentials.Translucent" />

<activity
android:name=".ui.activities.NetworkModeSettingsActivity"
android:excludeFromRecents="true"
android:exported="true"
android:noHistory="true"
android:taskAffinity=""
android:theme="@style/Theme.Essentials.Translucent" />

<activity
android:name=".ui.activities.WatermarkActivity"
android:exported="true"
Expand Down Expand Up @@ -797,6 +805,34 @@
android:value="android.service.quicksettings.CATEGORY_CONNECTIVITY" />
</service>

<service
android:name=".services.tiles.DataSimTileService"
android:exported="true"
android:icon="@drawable/rounded_android_cell_dual_4_bar_24"
android:label="@string/tile_data_sim"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
<meta-data
android:name="android.service.quicksettings.TILE_CATEGORY"
android:value="android.service.quicksettings.CATEGORY_CONNECTIVITY" />
</service>

<service
android:name=".services.tiles.NetworkModeTileService"
android:exported="true"
android:icon="@drawable/rounded_signal_cellular_alt_24"
android:label="@string/tile_network_mode"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
<meta-data
android:name="android.service.quicksettings.TILE_CATEGORY"
android:value="android.service.quicksettings.CATEGORY_CONNECTIVITY" />
</service>

<service
android:name=".services.tiles.StayAwakeTileService"
android:exported="true"
Expand Down Expand Up @@ -908,6 +944,15 @@
</intent-filter>
</receiver>

<receiver
android:name=".services.receivers.TelephonyActionReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.sameerasw.essentials.SET_DATA_SIM" />
<action android:name="com.sameerasw.essentials.SET_NETWORK_MODE" />
</intent-filter>
</receiver>

<service
android:name=".services.tiles.BatteryNotificationTileService"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package com.sameerasw.essentials.services.receivers

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import rikka.shizuku.ShizukuBinderWrapper
import rikka.shizuku.SystemServiceHelper

class TelephonyActionReceiver : BroadcastReceiver() {

private val TAG = "TelephonyActionReceiver"

override fun onReceive(context: Context, intent: Intent) {
val action = intent.action ?: return
Log.d(TAG, "Received action: $action")

try {
when (action) {
"com.sameerasw.essentials.SET_DATA_SIM" -> {
val subId = intent.getIntExtra("subId", -1)
if (subId != -1) {
Log.d(TAG, "Setting default data SIM to subId=$subId")
setDefaultDataSubId(subId)
enableMobileData(context, subId)
} else {
Log.w(TAG, "Invalid subId extra in SET_DATA_SIM broadcast")
}
}
"com.sameerasw.essentials.SET_NETWORK_MODE" -> {
val subId = intent.getIntExtra("subId", -1)
val bitmask = intent.getLongExtra("bitmask", -1L)
if (subId != -1 && bitmask != -1L) {
Log.d(TAG, "Setting network mode for subId=$subId to bitmask=$bitmask")
setAllowedNetworkTypes(context, subId, bitmask)
} else {
Log.w(TAG, "Invalid subId or bitmask extra in SET_NETWORK_MODE broadcast")
}
}
}
} catch (e: Exception) {
Log.e(TAG, "Error handling telephony action broadcast", e)
}
}

private fun setDefaultDataSubId(subId: Int) {
val binder = SystemServiceHelper.getSystemService("isub")
if (binder != null) {
val wrappedBinder = ShizukuBinderWrapper(binder)
val stubClass = Class.forName("com.android.internal.telephony.ISub\$Stub")
val asInterfaceMethod = stubClass.getMethod("asInterface", android.os.IBinder::class.java)
val iSubInstance = asInterfaceMethod.invoke(null, wrappedBinder)

val setDefaultDataSubIdMethod = iSubInstance.javaClass.getMethod(
"setDefaultDataSubId",
Int::class.javaPrimitiveType
)
setDefaultDataSubIdMethod.invoke(iSubInstance, subId)
Log.d(TAG, "Successfully set default data subscription to $subId via standard reflection")
} else {
Log.e(TAG, "Shizuku isub binder not available")
}
}

private fun setAllowedNetworkTypes(context: Context, subId: Int, bitmask: Long) {
val binder = SystemServiceHelper.getSystemService("phone")
if (binder != null) {
val wrappedBinder = ShizukuBinderWrapper(binder)
val stubClass = Class.forName("com.android.internal.telephony.ITelephony\$Stub")
val asInterfaceMethod = stubClass.getMethod("asInterface", android.os.IBinder::class.java)
val iTelephonyInstance = asInterfaceMethod.invoke(null, wrappedBinder)

var setMethod: java.lang.reflect.Method? = null
var args = arrayOf<Any>()

// Try 4-argument signature (Android 14/15/16/17+)
try {
setMethod = iTelephonyInstance.javaClass.getMethod(
"setAllowedNetworkTypesForReason",
Int::class.javaPrimitiveType,
Int::class.javaPrimitiveType,
Long::class.javaPrimitiveType,
String::class.java
)
args = arrayOf(subId, 0, bitmask, context.packageName)
} catch (e: NoSuchMethodException) {
// Fall back to 3-argument signature (Android 11/12/13)
try {
setMethod = iTelephonyInstance.javaClass.getMethod(
"setAllowedNetworkTypesForReason",
Int::class.javaPrimitiveType,
Int::class.javaPrimitiveType,
Long::class.javaPrimitiveType
)
args = arrayOf(subId, 0, bitmask)
} catch (ex: NoSuchMethodException) {
Log.e(TAG, "Both 4-argument and 3-argument setAllowedNetworkTypesForReason methods not found", ex)
}
}

if (setMethod != null) {
setMethod.invoke(iTelephonyInstance, *args)
Log.d(TAG, "Successfully set allowed network types to $bitmask via standard reflection")
}
} else {
Log.e(TAG, "Shizuku phone binder not available")
}
}

private fun enableMobileData(context: Context, subId: Int) {
try {
val binder = SystemServiceHelper.getSystemService("phone")
if (binder != null) {
val wrappedBinder = ShizukuBinderWrapper(binder)
val stubClass = Class.forName("com.android.internal.telephony.ITelephony\$Stub")
val asInterfaceMethod = stubClass.getMethod("asInterface", android.os.IBinder::class.java)
val iTelephonyInstance = asInterfaceMethod.invoke(null, wrappedBinder)

var setDataEnabledMethod: java.lang.reflect.Method? = null
var args = arrayOf<Any>()

// Try setDataEnabledForReason(int subId, int reason, boolean enable, String callingPackage) - Android 14+
try {
setDataEnabledMethod = iTelephonyInstance.javaClass.getMethod(
"setDataEnabledForReason",
Int::class.javaPrimitiveType,
Int::class.javaPrimitiveType,
Boolean::class.javaPrimitiveType,
String::class.java
)
args = arrayOf(subId, 0, true, context.packageName)
} catch (e: NoSuchMethodException) {
// Try setDataEnabled(int subId, boolean enable) - older versions
try {
setDataEnabledMethod = iTelephonyInstance.javaClass.getMethod(
"setDataEnabled",
Int::class.javaPrimitiveType,
Boolean::class.javaPrimitiveType
)
args = arrayOf(subId, true)
} catch (ex: NoSuchMethodException) {
Log.e(TAG, "Could not find setDataEnabled or setDataEnabledForReason method", ex)
}
}

if (setDataEnabledMethod != null) {
setDataEnabledMethod.invoke(iTelephonyInstance, *args)
Log.d(TAG, "Successfully enabled mobile data for subId=$subId")
}
}
} catch (e: Exception) {
Log.e(TAG, "Error enabling mobile data for subId=$subId", e)
}
}
}
Loading