This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

fix: stub MLKit Clearcut at LogWriter level + synthesize missing class method (v4.15.3)

v4.15.2 only stubbed -[MLKITx_CCTClearcutLogger log:completion:]. Kima's
crash stack actually entered telemetry via
-[MLKITx_CCTLogWriter writeLog:pseudonymousID:logDirectory:clock:
logTransformers:completionQueue:completion:]
directly — bypassing the Logger entry point we'd stubbed. The crash then
propagates down to the missing
+[MLKITx_CCTClearcutFileUtility computeUrlForLogContextDir:context:bundleId:]
class method.

v4.15.3 adds two more defenses:

1. Stub -[MLKITx_CCTLogWriter writeLog:...] to a no-op that invokes
the completion handler (dispatching onto completionQueue if the
caller supplied one, to preserve semantics).

2. As a belt-and-braces fallback, if callers reach the FileUtility
anyway, install a class method on MLKITx_CCTClearcutFileUtility for
computeUrlForLogContextDir:context:bundleId: that returns the input
directory unchanged — satisfies the selector lookup so we don't
raise NSInvalidArgumentException even if the rest of the chain runs.

Each stub has its own installed-flag so partial success on the first
attempt still leaves the other stubs to retry on the second attempt
(+load vs mlkit_set_resource_dir).

Version 4.15.3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+95 -12
+1 -1
posedetection/build.gradle.kts
··· 4 4 5 5 mavenPublishing { 6 6 publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) 7 - coordinates("com.performancecoachlab.posedetection", "posedetection-compose", "4.15.2") 7 + coordinates("com.performancecoachlab.posedetection", "posedetection-compose", "4.15.3") 8 8 9 9 pom { 10 10 name.set("Pose Detection")
+94 -11
posedetection/src/nativeInterop/mlkitRedirect/MLKitResourceRedirect.m
··· 16 16 17 17 static NSString *_mlkitResourceDir = nil; 18 18 19 - // Install the Clearcut-telemetry no-op swizzle. Safe to call multiple times — 20 - // once it actually lands the stub, a flag short-circuits future attempts. 19 + // Install MLKit telemetry stubs. Safe to call multiple times. 21 20 // Runs at two points: during +load (MLKit classes may not be registered yet, 22 21 // in which case we silently skip and retry), and from mlkit_set_resource_dir 23 22 // (called by Kotlin after app init — all classes definitely loaded by then). 23 + // Each stub has its own "installed" flag so partial success on the first 24 + // attempt still leaves work for the second. 24 25 static void _pd_mlkit_noop_log(id, SEL, id, id); 25 - static BOOL _pd_clearcut_stubbed = NO; 26 + static void _pd_mlkit_noop_writelog(id, SEL, id, id, id, id, id, id, id); 27 + static id _pd_mlkit_compute_url(Class, SEL, id, id, id); 28 + 29 + static BOOL _pd_logger_stubbed = NO; 30 + static BOOL _pd_writer_stubbed = NO; 31 + static BOOL _pd_fileutil_stubbed = NO; 32 + 26 33 static void _pd_install_clearcut_stub(void) { 27 - if (_pd_clearcut_stubbed) return; 28 - Class clearcut = NSClassFromString(@"MLKITx_CCTClearcutLogger"); 29 - if (!clearcut) return; 30 - Method logM = class_getInstanceMethod(clearcut, 31 - @selector(log:completion:)); 32 - if (!logM) return; 33 - method_setImplementation(logM, (IMP)_pd_mlkit_noop_log); 34 - _pd_clearcut_stubbed = YES; 34 + // 1. -[MLKITx_CCTClearcutLogger log:completion:] (high-level entry) 35 + if (!_pd_logger_stubbed) { 36 + Class logger = NSClassFromString(@"MLKITx_CCTClearcutLogger"); 37 + if (logger) { 38 + Method m = class_getInstanceMethod(logger, 39 + @selector(log:completion:)); 40 + if (m) { 41 + method_setImplementation(m, (IMP)_pd_mlkit_noop_log); 42 + _pd_logger_stubbed = YES; 43 + } 44 + } 45 + } 46 + // 2. -[MLKITx_CCTLogWriter writeLog:pseudonymousID:logDirectory:clock: 47 + // logTransformers:completionQueue:completion:] 48 + // Some callers bypass the Logger and hit the LogWriter directly — 49 + // which is the path kima's crash stack showed. 50 + if (!_pd_writer_stubbed) { 51 + Class writer = NSClassFromString(@"MLKITx_CCTLogWriter"); 52 + if (writer) { 53 + Method m = class_getInstanceMethod(writer, 54 + @selector(writeLog:pseudonymousID:logDirectory:clock:logTransformers:completionQueue:completion:)); 55 + if (m) { 56 + method_setImplementation(m, (IMP)_pd_mlkit_noop_writelog); 57 + _pd_writer_stubbed = YES; 58 + } 59 + } 60 + } 61 + // 3. Fallback: if neither stub fires and something reaches 62 + // +[MLKITx_CCTClearcutFileUtility computeUrlForLogContextDir:context: 63 + // bundleId:], make that class method respond instead of throwing. 64 + if (!_pd_fileutil_stubbed) { 65 + Class fileutil = NSClassFromString(@"MLKITx_CCTClearcutFileUtility"); 66 + if (fileutil) { 67 + SEL sel = @selector(computeUrlForLogContextDir:context:bundleId:); 68 + // Try to override the existing method impl if there IS one; if 69 + // not (this is what causes the original crash — the class method 70 + // table has lost the entry), add it via the metaclass. 71 + Method existing = class_getClassMethod(fileutil, sel); 72 + if (existing) { 73 + method_setImplementation(existing, (IMP)_pd_mlkit_compute_url); 74 + _pd_fileutil_stubbed = YES; 75 + } else { 76 + Class meta = object_getClass((id)fileutil); 77 + if (meta && class_addMethod(meta, sel, 78 + (IMP)_pd_mlkit_compute_url, 79 + "@@:@@@")) { 80 + _pd_fileutil_stubbed = YES; 81 + } 82 + } 83 + } 84 + } 35 85 } 36 86 37 87 __attribute__((visibility("default"))) ··· 70 120 void (^block)(BOOL, NSError *) = completion; 71 121 block(YES, nil); 72 122 } 123 + } 124 + 125 + // No-op replacement for the 7-arg LogWriter writeLog. Some callers bypass 126 + // CCTClearcutLogger and call CCTLogWriter directly — stubbing only the 127 + // Logger isn't enough. This signature matches the actual method: 128 + // -[MLKITx_CCTLogWriter writeLog:pseudonymousID:logDirectory:clock: 129 + // logTransformers:completionQueue:completion:] 130 + static void _pd_mlkit_noop_writelog(id self, SEL _cmd, 131 + id log, id pid, id logDir, id clock, 132 + id transformers, id completionQueue, 133 + id completion) { 134 + if (completion) { 135 + void (^block)(BOOL, NSError *) = completion; 136 + if (completionQueue) { 137 + // Preserve semantics: callers typically want the completion 138 + // delivered on their queue. dispatch_async into it, else call 139 + // inline. 140 + dispatch_async((dispatch_queue_t)completionQueue, ^{ 141 + block(YES, nil); 142 + }); 143 + } else { 144 + block(YES, nil); 145 + } 146 + } 147 + } 148 + 149 + // Fallback for the missing +[MLKITx_CCTClearcutFileUtility 150 + // computeUrlForLogContextDir:context:bundleId:] class method. Return the 151 + // logContextDir NSURL unchanged — good enough to satisfy any caller that 152 + // slips past our writeLog stub. 153 + static id _pd_mlkit_compute_url(Class self, SEL _cmd, 154 + id logContextDir, id context, id bundleId) { 155 + return logContextDir; 73 156 } 74 157 75 158 @interface NSBundle (PoseDetectionMLKitRedirect)