Skip to content

Commit 36c48fe

Browse files
xaionaro@dx.centerxaionaro@dx.center
authored andcommitted
fix: resolve method signatures from device DEX for version-aware proxies
The transport cache stored ScannedJARs (marking JARs as already processed) but only cached transaction codes, not method signatures. When ResolveMethodSignature was called, the JAR scan was skipped because ScannedJARs said the JARs were already processed. Added loadSignatures() that scans JARs for $Stub$Proxy method prototypes independently of the transaction code cache. This enables the generated proxy to detect that API 35 registerClient has 4 params (no transport) vs API 36's 5 params and adapt the marshaling accordingly. Verified: TestBluetoothGATT_FullPipeline/GATTClientLifecycle now passes with OnClientRegistered status=0 (GATT_SUCCESS) on API 35 emulator.
1 parent 4dcd8f6 commit 36c48fe

1 file changed

Lines changed: 59 additions & 4 deletions

File tree

binder/versionaware/transport.go

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ type Transport struct {
5858
// (parameter type descriptor lists). Populated alongside table
5959
// during lazy JAR extraction.
6060
signatures map[string]dex.MethodSignatures
61+
// signaturesLoaded tracks whether signatures have been extracted
62+
// from device JARs. Separate from ScannedJARs because the cache
63+
// stores transaction codes but not signatures — JARs marked as
64+
// scanned in the cache need re-scanning for signatures.
65+
signaturesLoaded bool
6166
// ScannedJARs tracks which framework JARs have been fully scanned
6267
// for $Stub classes. Keyed by JAR filename (e.g. "framework.jar").
6368
// Persisted in the cache to avoid re-scanning across runs.
@@ -317,15 +322,30 @@ func (t *Transport) ResolveMethodSignature(
317322
) []string {
318323
// Fast path: check if signatures are already loaded.
319324
t.mu.RLock()
320-
sigs, loaded := t.signatures[descriptor]
325+
loaded := t.signaturesLoaded
326+
sigs := t.signatures[descriptor]
321327
t.mu.RUnlock()
322328

323-
if loaded {
329+
if loaded && sigs != nil {
324330
return sigs[method]
325331
}
326332

327-
// Descriptor not in the signatures map yet — trigger lazy
328-
// extraction (which populates both table and signatures).
333+
if !loaded {
334+
// Signatures haven't been extracted yet. The cache only stores
335+
// transaction codes, not signatures, so JARs marked as scanned
336+
// may need re-scanning for $Stub$Proxy method prototypes.
337+
t.loadSignatures(ctx)
338+
339+
t.mu.RLock()
340+
sigs = t.signatures[descriptor]
341+
t.mu.RUnlock()
342+
343+
if sigs != nil {
344+
return sigs[method]
345+
}
346+
}
347+
348+
// Trigger lazy extraction which may find new JARs.
329349
t.lazyExtractDescriptor(ctx, descriptor, method)
330350

331351
t.mu.RLock()
@@ -339,6 +359,41 @@ func (t *Transport) ResolveMethodSignature(
339359
return nil
340360
}
341361

362+
// loadSignatures scans all known framework JARs for method signatures,
363+
// regardless of whether they were already scanned for transaction codes.
364+
func (t *Transport) loadSignatures(ctx context.Context) {
365+
t.mu.Lock()
366+
defer t.mu.Unlock()
367+
368+
if t.signaturesLoaded {
369+
return // another goroutine loaded them
370+
}
371+
372+
for _, dir := range jarDirectories() {
373+
entries, err := os.ReadDir(dir)
374+
if err != nil {
375+
continue
376+
}
377+
for _, entry := range entries {
378+
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".jar") {
379+
continue
380+
}
381+
jarPath := dir + "/" + entry.Name()
382+
sigs, err := dex.ExtractSignaturesFromJAR(jarPath)
383+
if err != nil {
384+
continue
385+
}
386+
for iface, ms := range sigs {
387+
if t.signatures[iface] == nil {
388+
t.signatures[iface] = ms
389+
}
390+
}
391+
}
392+
}
393+
394+
t.signaturesLoaded = true
395+
}
396+
342397
// lazyExtractDescriptor attempts on-demand extraction of a single
343398
// interface descriptor. Uses a two-phase procedure:
344399
//

0 commit comments

Comments
 (0)