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
13 changes: 13 additions & 0 deletions .github/release-notes/v2.2.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Pixel IMS 5G 2.2.0 - Liquid glass, the modem patch up front, and issue fixes

- **Liquid glass UI:** Translucent glass panels over a lit background, in light and dark, with a System / Light / Dark switch in the top bar and a floating tab bar whose highlight slides between tabs.
- **Modem patch leads Setup:** As soon as root is granted, the 5G regional modem patch is the first card on Setup, with its prerequisites checked and one install button. Every install asks for confirmation first, and results or errors are shown on the card.
- **KernelSU and APatch:** The regional modem patch now installs with KernelSU (with a mounting metamodule such as mountify) and APatch, not only Magisk (#13).
- **Reapply after reboot (Shizuku):** An opt-in switch on SIM Config saves your changes and reapplies them once Shizuku is running after a reboot (#6).
- **Crash fix on Android 17:** Applying a setting no longer crashes the app on Android 17 QPR2 (#7, #14).
- **Fix for "did not accept this change":** The app now waits for Android to finish applying a setting before checking it (#14).
- **Fix for the modem patch on Android 15:** The installer no longer fails with "Unable to find app for caller" (#15).
- **Fix for NSA 5G:** Setup no longer reports 5G as not connected while the phone is on NSA 5G.
- **Other fixes:** Pages no longer get stuck on "no SIM" when SIMs load late, and buttons are no longer left disabled after rotating the screen mid-action.

Existing installations using `com.nirmala.pixel5gims` can update normally without uninstalling.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ jobs:
cache: gradle

- uses: android-actions/setup-android@v3
with:
# The default also installs the legacy 'tools' package, which sdkmanager
# no longer offers, and that failure fails the whole step.
packages: platform-tools

- name: Install Android SDK
run: sdkmanager "platforms;android-36" "build-tools;36.0.0"
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ jobs:
java-version: "17"

- uses: android-actions/setup-android@v3
with:
# The default also installs the legacy 'tools' package, which sdkmanager
# no longer offers, and that failure fails the whole step.
packages: platform-tools

- name: Install Android SDK
run: sdkmanager "platforms;android-36" "build-tools;36.0.0"
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,23 @@ An experimental root- or Shizuku-powered IMS and radio configuration app for Goo

Android application ID: `com.nirmala.pixel5gims`.

Current public release: `2.1.0` (`v2.1.0`). The app now **opens on the answer**: a Setup screen that says
Current public release: `2.2.0` (`v2.2.0`). The app now **opens on the answer**: a Setup screen that says
whether 5G is working on this SIM, which check is blocking it, and offers the fix for that check. The one-time uninstall/reinstall notice applies specifically
to version `0.12.6`, where the application ID changed. Existing `0.12.6` installations
can update normally to later versions.

## Changelog

### 2.2.0 - liquid glass, the modem patch up front, and issue fixes

- **Liquid glass UI** in light and dark, with an in-app System / Light / Dark switch in the top bar.
- **The 5G regional modem patch leads Setup** as soon as root is granted, with its prerequisites checked and one install button. Every install goes through the confirmation dialog.
- **KernelSU and APatch** can install the modem patch, not only Magisk ([#13](https://github.com/barrylk/Pixel-IMS-5G/issues/13)).
- **Reapply after reboot** for Shizuku mode, opt-in on SIM Config ([#6](https://github.com/barrylk/Pixel-IMS-5G/issues/6)).
- **Fixed:** the app crashing after applying a setting on Android 17 QPR2 ([#7](https://github.com/barrylk/Pixel-IMS-5G/issues/7)), and settings wrongly reported as "did not accept" ([#14](https://github.com/barrylk/Pixel-IMS-5G/issues/14)).
- **Fixed:** the modem patch failing with "Unable to find app for caller" ([#15](https://github.com/barrylk/Pixel-IMS-5G/issues/15)); install errors now show on the card.
- **Fixed:** NSA 5G reported as not connected; root mode passing the access check through a Shizuku grant; pages stuck on "no SIM" when SIMs loaded late; buttons left disabled after rotating mid-action.

### [2.1.0](https://github.com/barrylk/Pixel-IMS-5G/releases/tag/v2.1.0) - task-first setup

**The app opens on the question you actually have.** Every version until now opened on a settings list,
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ android {
// Every Tensor Pixel (Pixel 6 and newer) launched on Android 12 / API 31 or later.
minSdk 31
targetSdk 36
versionCode 31
versionName "2.1.0"
versionCode 32
versionName "2.2.0"

vectorDrawables {
useSupportLibrary true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.bluehouse.enablevolte

import android.annotation.SuppressLint
import android.app.Activity
import android.app.IActivityManager
import android.app.Instrumentation
import android.content.Context
Expand Down Expand Up @@ -39,7 +40,7 @@ class BrokerInstrumentation : Instrumentation() {
}
} finally {
Log.i(TAG, "applyConfig done")
am.stopDelegateShellPermissionIdentity()
stopShellIdentity(am)
}
}

Expand All @@ -64,28 +65,78 @@ class BrokerInstrumentation : Instrumentation() {
}
} finally {
Log.i(TAG, "clearConfig done")
stopShellIdentity(am)
}
}

/**
* Drops the delegated shell identity.
*
* Android 17 QPR2 changed this AIDL method, and calling the old signature
* throws NoSuchMethodError — after the override has already been applied —
* which took the whole app down with it (issues #7 and #14). The direct call
* is tried first, then any method by that name, and a failure is only
* logged: the activity manager drops the delegation itself when the
* instrumentation finishes.
*/
private fun stopShellIdentity(am: IActivityManager) {
try {
am.stopDelegateShellPermissionIdentity()
return
} catch (e: LinkageError) {
Log.w(TAG, "stopDelegateShellPermissionIdentity() is gone on this build; trying reflection", e)
} catch (e: Exception) {
Log.w(TAG, "stopDelegateShellPermissionIdentity failed", e)
return
}
val method = am.javaClass.methods.firstOrNull { it.name == "stopDelegateShellPermissionIdentity" }
if (method == null) {
Log.w(TAG, "No stopDelegateShellPermissionIdentity on this build; relying on instrumentation teardown")
return
}
runCatching {
val args = method.parameterTypes.map { type ->
when (type) {
Int::class.javaPrimitiveType -> Os.getuid()
Long::class.javaPrimitiveType -> 0L
Boolean::class.javaPrimitiveType -> false
else -> null
}
}
method.invoke(am, *args.toTypedArray())
}.onFailure { Log.w(TAG, "Reflective stopDelegateShellPermissionIdentity failed", it) }
}

override fun onCreate(arguments: Bundle?) {
super.onCreate(arguments)

if (arguments == null) {
finish(Activity.RESULT_CANCELED, Bundle())
return
}

val clear = arguments.getBoolean("moder_clear")
val subId = arguments.getInt("moder_subId")

try {
// This runs on the app's own main thread: anything thrown here kills the
// app, so failures are reported back to the caller instead.
val result = Bundle()
val code = try {
if (clear) {
this.clearConfig(subId)
} else {
this.applyConfig(subId, arguments)
}
} finally {
finish(0, Bundle())
Activity.RESULT_OK
} catch (t: Throwable) {
Log.e(TAG, "Broker failed to apply the carrier config", t)
result.putString(RESULT_ERROR, t.message ?: t.javaClass.simpleName)
Activity.RESULT_CANCELED
}
finish(code, result)
}

companion object {
const val RESULT_ERROR = "moder_error"
}
}
15 changes: 14 additions & 1 deletion app/src/main/java/dev/bluehouse/enablevolte/ControlState.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.bluehouse.enablevolte

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext

/**
Expand Down Expand Up @@ -63,10 +64,19 @@ suspend fun <T> applyAndConfirm(
requested: T,
write: () -> Unit,
readBack: () -> T?,
settleDelaysMs: List<Long> = DEFAULT_SETTLE_DELAYS_MS,
): WriteOutcome<T> = withContext(Dispatchers.IO) {
try {
write()
val actual = readBack()
// CarrierConfig propagates asynchronously (and the Shizuku broker more so),
// so one immediate read can see the old value. Give it a moment to settle
// before calling the change rejected.
var actual = readBack()
for (wait in settleDelaysMs) {
if (actual == requested) break
delay(wait)
actual = readBack()
}
if (actual == requested) {
WriteOutcome.Confirmed(requested)
} else {
Expand All @@ -77,6 +87,9 @@ suspend fun <T> applyAndConfirm(
}
}

/** Roughly two seconds in all, front-loaded because most writes land within the first. */
val DEFAULT_SETTLE_DELAYS_MS = listOf(150L, 250L, 400L, 600L, 800L)

/**
* Reads a value off the main thread, returning null rather than throwing when
* the privileged service is unavailable.
Expand Down
Loading
Loading