mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 00:00:21 +02:00
Fix the cross-provider review's findings on VR APK support
patch() turns corrupt-manifest struct/index errors into FrameError; a missing layer build says so; the signing key falls back to a rename where hard links aren't supported and explains how to recover from a bad cached key; the layer nulls an instance it can't destroy and logs xrLocateSpaces once. Not changed: the 1.1 Meta profile names match xr.xml's promoted names (meta/touch_pro_controller, meta/touch_plus_controller), and grip_surface is palm_ext renamed, so the rewrite stays (now commented). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
50d5e14539
commit
f10282294d
7 files changed
+37
-11
No files matched your search
@@ -238,7 +238,7 @@ from the binary; runtime execution of those paths remains unverified.
|
||||
Prebuilt library SHA-256:
|
||||
|
||||
```
|
||||
aace99b7bcdb3c79fa844d75a9483f8f4b16bd6e669fb3003515df8c673ce54b
|
||||
479d31c374f137906e03f73209b581d65d7e6a41b8476e6425fa55a6aa40bd68
|
||||
```
|
||||
|
||||
APK SHA-256:
|
||||
|
||||
@@ -35,6 +35,7 @@ inline ExtensionPlan extensions(const Names& requested, const Names& available,
|
||||
}
|
||||
return p;
|
||||
}
|
||||
// 1.1's grip_surface is XR_EXT_palm_pose's palm_ext renamed (same pose), not the grip pose.
|
||||
inline std::string rewritePath(std::string p, bool palm) {
|
||||
const std::string suffix = "/input/grip_surface/pose";
|
||||
if (palm && (p == "/user/hand/left" + suffix || p == "/user/hand/right" + suffix))
|
||||
|
||||
@@ -121,7 +121,8 @@ XrResult XRAPI_CALL destroySession(XrSession s) try {
|
||||
XrResult XRAPI_CALL locateSpaces(XrSession s, const XrSpacesLocateInfo* in, XrSpaceLocations* out) try {
|
||||
auto d = get(s); if (!d) return XR_ERROR_HANDLE_INVALID;
|
||||
if (!d->locate) return XR_ERROR_FUNCTION_UNSUPPORTED;
|
||||
LOG("xrLocateSpaces -> xrLocateSpacesKHR (core/KHR type aliases)");
|
||||
static std::once_flag logged; // called every frame
|
||||
std::call_once(logged, [] { LOG("xrLocateSpaces -> xrLocateSpacesKHR (core/KHR type aliases)"); });
|
||||
return frame::locate(d->locate, s, in, out);
|
||||
} GUARD_END
|
||||
XrResult XRAPI_CALL threadSettings(XrSession s, XrAndroidThreadTypeKHR type, uint32_t tid) try {
|
||||
@@ -226,7 +227,9 @@ XrResult XRAPI_CALL createLayer(const XrInstanceCreateInfo* info, const XrApiLay
|
||||
d->gipa = next; d->instance = *instance;
|
||||
d->destroy = proc<PFN_xrDestroyInstance>(next, *instance, "xrDestroyInstance");
|
||||
// A successful runtime instance must expose xrDestroyInstance.
|
||||
if (!d->destroy) { LOG("invalid downstream: missing xrDestroyInstance"); return XR_ERROR_INITIALIZATION_FAILED; }
|
||||
if (!d->destroy) { // nothing could ever destroy it, so don't hand it out
|
||||
LOG("invalid downstream: missing xrDestroyInstance"); *instance = XR_NULL_HANDLE; return XR_ERROR_INITIALIZATION_FAILED;
|
||||
}
|
||||
try {
|
||||
d->enabled = std::move(plan.downstream); d->stubbed = std::move(plan.stubbed);
|
||||
d->palm = frame::has(d->enabled, "XR_EXT_palm_pose");
|
||||
|
||||
Binary file not shown.
@@ -202,6 +202,20 @@ class VRTests(unittest.TestCase):
|
||||
z.writestr('classes.dex', b'')
|
||||
self.assertEqual(frame_android.xr_compat_files(str(flat)), {})
|
||||
|
||||
def test_xr_compat_layer_missing(self):
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
apk = Path(d) / 'a.apk'
|
||||
with zipfile.ZipFile(apk, 'w') as z:
|
||||
z.writestr('lib/arm64-v8a/libopenxr_loader.so', b'')
|
||||
with patch.object(frame_android, 'XR_COMPAT', d):
|
||||
with self.assertRaisesRegex(frame_android.FrameError, 'build.sh'):
|
||||
frame_android.xr_compat_files(str(apk))
|
||||
|
||||
def test_patch_rejects_corrupt_manifest_cleanly(self):
|
||||
with patch.object(frame_android, 'apk_info', side_effect=struct.error('bad')):
|
||||
with self.assertRaises(frame_android.FrameError):
|
||||
frame_android.patch('x.apk', 'y.apk')
|
||||
|
||||
def test_install_adds_layer_to_vr_apps(self):
|
||||
base = {'package': 'org.test.vr', 'label': 'VR', 'abis': [], 'min_sdk': None,
|
||||
'vr': True, 'vr_activity': True, 'launchable': True}
|
||||
|
||||
+7
-4
@@ -10,7 +10,7 @@ Python stdlib only. CLI: python3 ui/frame_android.py
|
||||
install APK [--vr|--flat] [--no-xr-compat] | info APK | patch SRC DST [--add NAME=PATH ...]
|
||||
list | launch PKG | stop PKG | remove PKG | probe PKG
|
||||
"""
|
||||
import json, os, re, shlex, shutil, subprocess, sys, threading, time, zlib
|
||||
import json, os, re, shlex, shutil, struct, subprocess, sys, threading, time, zlib
|
||||
|
||||
import frame_apk
|
||||
import frame_host
|
||||
@@ -86,8 +86,11 @@ def xr_compat_files(apk_path):
|
||||
return {}
|
||||
add = {}
|
||||
for entry, rel in XR_COMPAT_FILES.items():
|
||||
with open(os.path.join(XR_COMPAT, rel), 'rb') as f:
|
||||
add[entry] = f.read()
|
||||
try:
|
||||
with open(os.path.join(XR_COMPAT, rel), 'rb') as f:
|
||||
add[entry] = f.read()
|
||||
except OSError:
|
||||
raise FrameError("the OpenXR compatibility layer isn't built; run frame/openxr-compat/build.sh")
|
||||
return add
|
||||
|
||||
|
||||
@@ -326,7 +329,7 @@ def patch(src, dst, add=None):
|
||||
result['patched'] = (['launcher'] if manifest != original else []) + \
|
||||
(['openxr-compat'] if add and set(XR_COMPAT_FILES) <= set(add) else [])
|
||||
return result
|
||||
except (OSError, ValueError, zipfile.BadZipFile, frame_apk.ApkError) as e:
|
||||
except (OSError, ValueError, IndexError, struct.error, zipfile.BadZipFile, frame_apk.ApkError) as e:
|
||||
raise FrameError(str(e)) from e
|
||||
|
||||
|
||||
|
||||
@@ -126,15 +126,20 @@ def signing_key(path=None):
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
try:
|
||||
os.link(tmp, path)
|
||||
os.link(tmp, path) # never replaces a key another process wrote first
|
||||
except FileExistsError:
|
||||
pass
|
||||
except OSError: # no hard links (FAT/exFAT): plain rename
|
||||
if not path.exists():
|
||||
os.replace(tmp, path)
|
||||
finally:
|
||||
os.unlink(tmp)
|
||||
os.chmod(path, 0o600)
|
||||
if os.path.exists(tmp):
|
||||
os.unlink(tmp)
|
||||
os.chmod(path, 0o600) # Windows ignores this; the per-user app-data folder is the protection there
|
||||
key = json.loads(path.read_text())
|
||||
if key['n'].bit_length() != 2048 or key['e'] != 65537:
|
||||
raise ValueError('invalid cached APK signing key')
|
||||
raise ValueError(f'invalid cached APK signing key; delete {path} to make a new one '
|
||||
'(re-signed apps then need reinstalling)')
|
||||
rsa_verify(b'key check', rsa_sign(b'key check', key), key['n'], key['e'])
|
||||
return key
|
||||
|
||||
|
||||
Reference in new issue
Block a user