Skip to content

Fix miscPath initialization order in daemon cache bootstrap#678

Merged
JingMatrix merged 1 commit intoJingMatrix:masterfrom
kiber-io:master
Apr 17, 2026
Merged

Fix miscPath initialization order in daemon cache bootstrap#678
JingMatrix merged 1 commit intoJingMatrix:masterfrom
kiber-io:master

Conversation

@kiber-io
Copy link
Copy Markdown
Contributor

I hit this issue while testing CorePatch on Pixel 7, build CP1A.260305.018 (Android 16), and I also saw similar behavior on older ROMs / older LSPosed versions.

This is specifically related to XSharedPreferences path resolution in hooked processes.

The module process itself could read/update settings and correctly wrote XML into /data/misc/.
But when a hooked process initialized XSharedPreferences, getPrefsPath() could return an uninitialized/invalid path state (effectively null path behavior), so preferences failed to load.
So the problem is initialization order around miscPath availability at the time XSharedPreferences requests preference path resolution.

@kiber-io
Copy link
Copy Markdown
Contributor Author

kiber-io commented Apr 16, 2026

I think the initialization of miscPath should not depend on PackageManager, as they don’t seem related. That’s why the initialization was moved out of the condition to the beginning of the method. The root cause is that the condition was not met at the moment when CorePatch attempted to read via XSharedPreferences.

Here is the problematic line in the CorePatch repository. At the moment it is invoked, the condition in ensureCacheReady is not satisfied.
https://github.com/LSPosed/CorePatch/blob/c691400c070f45e1fb8f0dcfcf8c136868e81eb1/app/src/main/java/toolkit/coderstory/CorePatchForR.java#L60

@JingMatrix
Copy link
Copy Markdown
Owner

@kiber-io Please upload the Vector log, for me to understand better why it only happens on certain device but not all.

@kiber-io
Copy link
Copy Markdown
Contributor Author

@JingMatrix Of course, but it won’t bring any benefit, since the error "Fatal: miscPath not initialized" is suppressed in VectorServiceClient.getPrefsPath and doesn’t appear in the logs.

override fun getPrefsPath(packageName: String): String? {
    return runCatching { service?.getPrefsPath(packageName) }.getOrNull()
}

I’m attaching two log archives - one from the latest CI build of vector (before my PR, of course), and logs from my debug build with the issue described above fixed, along with some additional logging.

diff --git a/xposed/src/main/kotlin/org/matrix/vector/impl/core/VectorServiceClient.kt b/xposed/src/main/kotlin/org/matrix/vector/impl/core/VectorServiceClient.kt
index fc70e909..f56cdb41 100644
--- a/xposed/src/main/kotlin/org/matrix/vector/impl/core/VectorServiceClient.kt
+++ b/xposed/src/main/kotlin/org/matrix/vector/impl/core/VectorServiceClient.kt
@@ -47,7 +47,23 @@ object VectorServiceClient : ILSPApplicationService, IBinder.DeathRecipient {
     }
 
     override fun getPrefsPath(packageName: String): String? {
-        return runCatching { service?.getPrefsPath(packageName) }.getOrNull()
+        val appService = service
+        if (appService == null) {
+            Log.w(
+                TAG,
+                "getPrefsPath called with null service, package=$packageName, process=$processName",
+            )
+            return null
+        }
+        return runCatching { appService.getPrefsPath(packageName) }
+            .onFailure {
+                Log.e(
+                    TAG,
+                    "getPrefsPath failed for package=$packageName, process=$processName, binderAlive=${appService.asBinder()?.isBinderAlive == true}",
+                    it,
+                )
+            }
+            .getOrNull()
     }
diff --git a/legacy/src/main/java/de/robv/android/xposed/XSharedPreferences.java b/legacy/src/main/java/de/robv/android/xposed/XSharedPreferences.java
index e3976013..08430e3b 100644
--- a/legacy/src/main/java/de/robv/android/xposed/XSharedPreferences.java
+++ b/legacy/src/main/java/de/robv/android/xposed/XSharedPreferences.java
@@ -153,8 +153,14 @@ public final class XSharedPreferences implements SharedPreferences {
      * @param prefFileName The file name without ".xml".
      */
     public XSharedPreferences(String packageName, String prefFileName) {
+        if (BuildConfig.DEBUG) {
+            Log.d(TAG, "ctor(packageName=" + packageName + ", prefFileName=" + prefFileName + ")");
+        }
         boolean newModule = false;
         var m = XposedInit.getLoadedModules().getOrDefault(packageName, Optional.empty());
+        if (BuildConfig.DEBUG) {
+            Log.d(TAG, "loadedModuleEntry present=" + m.isPresent() + (m.isPresent() ? ", apkPath=" + m.get() : ""));
+        }
         if (m.isPresent()) {
             boolean isModule = false;
             int xposedminversion = -1;
@@ -171,17 +177,41 @@ public final class XSharedPreferences implements SharedPreferences {
                     }
                     xposedsharedprefs = metaData.containsKey("xposedsharedprefs");
                 }
+                if (BuildConfig.DEBUG) {
+                    Log.d(TAG, "moduleMeta: isModule=" + isModule
+                            + ", xposedminversion=" + xposedminversion
+                            + ", xposedsharedprefs=" + xposedsharedprefs);
+                }
             } catch (NumberFormatException | IOException e) {
                 Log.w(TAG, "Apk parser fails: " + e);
             }
             newModule = isModule && (xposedminversion > 92 || xposedsharedprefs);
+            if (BuildConfig.DEBUG) {
+                Log.d(TAG, "newModule decision=" + newModule
+                        + " (isModule=" + isModule
+                        + ", minVersion>92=" + (xposedminversion > 92)
+                        + ", sharedPrefsFlag=" + xposedsharedprefs + ")");
+            }
+        } else if (BuildConfig.DEBUG) {
+            Log.d(TAG, "moduleMeta unavailable for package " + packageName + ", falling back to legacy path");
         }
         if (newModule) {
-            mFile = new File(VectorServiceClient.INSTANCE.getPrefsPath(packageName), prefFileName + ".xml");
+            String prefsDir = VectorServiceClient.INSTANCE.getPrefsPath(packageName);
+            if (BuildConfig.DEBUG) {
+                Log.d(TAG, "using VectorService prefs dir: " + prefsDir);
+            }
+            mFile = new File(prefsDir, prefFileName + ".xml");
         } else {
             mFile = new File(Environment.getDataDirectory(), "data/" + packageName + "/shared_prefs/" + prefFileName + ".xml");
         }
         mFilename = mFile.getAbsolutePath();
+        if (BuildConfig.DEBUG) {
+            Log.d(TAG, "resolved prefs file=" + mFilename
+                    + ", exists=" + mFile.exists()
+                    + ", canRead=" + mFile.canRead()
+                    + ", parent=" + (mFile.getParentFile() != null ? mFile.getParentFile().getAbsolutePath() : "null")
+                    + ", parentExists=" + (mFile.getParentFile() != null && mFile.getParentFile().exists()));
+        }
         init();
     }

Here is the actual error log from the second archive:

[ 2026-04-17T10:52:51.813     1000:  1788:  1788 E/VectorServiceClient ] getPrefsPath failed for package=com.coderstory.toolkit, process=system, binderAlive=true
java.lang.IllegalStateException: Fatal: miscPath not initialized!
	at android.os.Parcel.createExceptionOrNull(Parcel.java:3378)
	at android.os.Parcel.createException(Parcel.java:3354)
	at android.os.Parcel.readException(Parcel.java:3337)
	at android.os.Parcel.readException(Parcel.java:3279)
	at org.lsposed.lspd.service.ILSPApplicationService$Stub$Proxy.getPrefsPath(ILSPApplicationService.java:202)
	at org.matrix.vector.impl.core.VectorServiceClient.getPrefsPath(VectorServiceClient.kt:58)
	at uBaxYHar.JCcqPoOi.I.Fb.XSharedPreferences.<init>(XSharedPreferences.java:199)
	at toolkit.coderstory.CorePatchForR.<init>(CorePatchForR.java:50)
	at toolkit.coderstory.CorePatchForS.<init>(CorePatchForS.java:10)
	at toolkit.coderstory.CorePatchForT.<init>(CorePatchForT.java:15)
	at toolkit.coderstory.CorePatchForU.<init>(CorePatchForU.java:17)
	at toolkit.coderstory.CorePatchForV.<init>(CorePatchForV.java:9)
	at toolkit.coderstory.MainHook.handleLoadPackage(MainHook.java:22)
	at uBaxYHar.JCcqPoOi.I.Fb.IXposedHookLoadPackage$Wrapper.handleLoadPackage(IXposedHookLoadPackage.java:34)
	at uBaxYHar.JCcqPoOi.I.Fb.callbacks.XC_LoadPackage.call(XC_LoadPackage.java:83)
	at uBaxYHar.JCcqPoOi.I.Fb.callbacks.XCallback.callAll(XCallback.java:128)
	at org.matrix.vector.legacy.LegacyDelegateImpl.onSystemServerLoaded(LegacyDelegateImpl.java:62)
	at org.matrix.vector.impl.hookers.StartBootstrapServicesHooker.dispatchSystemServerLoaded(SystemServerHookers.kt:75)
	at org.matrix.vector.impl.hookers.StartBootstrapServicesHooker.intercept(SystemServerHookers.kt:65)
	at org.matrix.vector.impl.hooks.VectorChain.internalProceed(VectorChain.kt:67)
	at org.matrix.vector.impl.hooks.VectorChain.proceed(VectorChain.kt:45)
	at org.matrix.vector.impl.hooks.VectorNativeHooker.callback(VectorNativeHooker.kt:99)
	at Vector_.startBootstrapServices(Unknown Source:11)
	at com.android.server.SystemServer.run(SystemServer.java:1037)
	at com.android.server.SystemServer.main(SystemServer.java:717)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:566)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:907)

P.S. My Pixel also decided to update itself again, ignoring disabled automatic updates, so now my system build is higher, but the issue from the PR is still present.

LSPosed_2026-04-17T10_53_27.419731_my_custom_debug_build.zip
LSPosed_2026-04-17T10_36_59.707112_original_ci_build_3039.zip

@kiber-io
Copy link
Copy Markdown
Contributor Author

As I suspected, the issue isn’t limited to one device - on my second device, a Xiaomi 14 running HyperOS 3 on Android 16, I’m experiencing the same problem with the same error.

LSPosed_2026-04-17T11_20_47.869798_xiaomi.zip

@JingMatrix
Copy link
Copy Markdown
Owner

I see, on some devices, the package service takes relatively longer time to set up than my testing devices.

The `ensureCacheReady` method waits for the 'package' service, which is often unavailable when modules inject into `system_server`. This leads to an uninitialized `miscPath` when `getPrefsPath` is called early.

We thus call `setupMiscPath` directly in `getPrefsPath` to bypass the package service dependency, and add a null check to avoid redundant initialization.
@JingMatrix
Copy link
Copy Markdown
Owner

@kiber-io I made a simpler change, please test if it solves the issue.

@kiber-io
Copy link
Copy Markdown
Contributor Author

@kiber-io I made a simpler change, please test if it solves the issue.

Yes, it works

@JingMatrix JingMatrix merged commit 846527e into JingMatrix:master Apr 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants