@@ -5,6 +5,7 @@ import { resolveFileOverridePath, runCmd, whichCmd } from '../../utils/exec.ts';
55import { AppError } from '../../utils/errors.ts' ;
66import type { DeviceInfo } from '../../utils/device.ts' ;
77import { isDeepLinkTarget } from '../../core/open-target.ts' ;
8+ import { createAppResolutionCache , type AppResolutionCacheScope } from '../app-resolution-cache.ts' ;
89import { waitForAndroidBoot } from './devices.ts' ;
910import { adbArgs } from './adb.ts' ;
1011import { classifyAndroidAppTarget } from './open-target.ts' ;
@@ -34,16 +35,28 @@ const ANDROID_APPS_DISCOVERY_HINT =
3435const ANDROID_AMBIGUOUS_APP_HINT =
3536 'Run agent-device apps --platform android to see the exact installed package names before retrying open.' ;
3637
38+ type AndroidAppResolution = { type : 'intent' | 'package' ; value : string } ;
39+
40+ const androidAppResolutionCache = createAppResolutionCache < AndroidAppResolution > ( ) ;
41+
42+ function androidAppResolutionScope ( device : DeviceInfo ) : AppResolutionCacheScope {
43+ return { platform : 'android' , deviceId : device . id , variant : device . target ?? '' } ;
44+ }
45+
3746export async function resolveAndroidApp (
3847 device : DeviceInfo ,
3948 app : string ,
40- ) : Promise < { type : 'intent' | 'package' ; value : string } > {
49+ ) : Promise < AndroidAppResolution > {
4150 const trimmed = app . trim ( ) ;
4251 if ( classifyAndroidAppTarget ( trimmed ) === 'package' ) return { type : 'package' , value : trimmed } ;
4352
4453 const alias = ALIASES [ trimmed . toLowerCase ( ) ] ;
4554 if ( alias ) return alias ;
4655
56+ const cacheScope = androidAppResolutionScope ( device ) ;
57+ const cached = androidAppResolutionCache . get ( cacheScope , trimmed ) ;
58+ if ( cached ) return cached ;
59+
4760 const result = await runCmd ( 'adb' , adbArgs ( device , [ 'shell' , 'pm' , 'list' , 'packages' ] ) ) ;
4861 const packages = result . stdout
4962 . split ( '\n' )
@@ -54,7 +67,10 @@ export async function resolveAndroidApp(
5467 pkg . toLowerCase ( ) . includes ( trimmed . toLowerCase ( ) ) ,
5568 ) ;
5669 if ( matches . length === 1 ) {
57- return { type : 'package' , value : matches [ 0 ] } ;
70+ return androidAppResolutionCache . set ( cacheScope , trimmed , {
71+ type : 'package' ,
72+ value : matches [ 0 ] ,
73+ } ) ;
5874 }
5975
6076 if ( matches . length > 1 ) {
@@ -560,10 +576,17 @@ export async function installAndroidInstallablePath(
560576 device : DeviceInfo ,
561577 installablePath : string ,
562578) : Promise < void > {
579+ const cacheScope = androidAppResolutionScope ( device ) ;
580+ androidAppResolutionCache . clear ( cacheScope ) ;
563581 if ( ! device . booted ) {
564582 await waitForAndroidBoot ( device . id ) ;
565583 }
566- await installAndroidAppFiles ( device , installablePath ) ;
584+ try {
585+ await installAndroidAppFiles ( device , installablePath ) ;
586+ } finally {
587+ // A concurrent name lookup can finish after the initial clear and repopulate stale data.
588+ androidAppResolutionCache . clear ( cacheScope ) ;
589+ }
567590}
568591
569592export async function installAndroidInstallablePathAndResolvePackageName (
@@ -617,18 +640,24 @@ export async function reinstallAndroidApp(
617640 app : string ,
618641 appPath : string ,
619642) : Promise < { package : string } > {
643+ const cacheScope = androidAppResolutionScope ( device ) ;
644+ androidAppResolutionCache . clear ( cacheScope ) ;
620645 if ( ! device . booted ) {
621646 await waitForAndroidBoot ( device . id ) ;
622647 }
623- const { package : pkg } = await uninstallAndroidApp ( device , app ) ;
624- const prepared = await prepareAndroidInstallArtifact (
625- { kind : 'path' , path : appPath } ,
626- { resolveIdentity : false } ,
627- ) ;
628648 try {
629- await installAndroidInstallablePath ( device , prepared . installablePath ) ;
649+ const { package : pkg } = await uninstallAndroidApp ( device , app ) ;
650+ const prepared = await prepareAndroidInstallArtifact (
651+ { kind : 'path' , path : appPath } ,
652+ { resolveIdentity : false } ,
653+ ) ;
654+ try {
655+ await installAndroidInstallablePath ( device , prepared . installablePath ) ;
656+ } finally {
657+ await prepared . cleanup ( ) ;
658+ }
630659 return { package : pkg } ;
631660 } finally {
632- await prepared . cleanup ( ) ;
661+ androidAppResolutionCache . clear ( cacheScope ) ;
633662 }
634663}
0 commit comments