@@ -23,6 +23,7 @@ import {
2323export class AndroidApplicationManager extends ApplicationManagerBase {
2424 public PID_CHECK_INTERVAL = 100 ;
2525 public PID_CHECK_TIMEOUT = 10000 ; // 10 secs
26+ private listPackagesPerUser = false ;
2627
2728 constructor (
2829 private adb : Mobile . IDeviceAndroidDebugBridge ,
@@ -42,61 +43,55 @@ export class AndroidApplicationManager extends ApplicationManagerBase {
4243 }
4344
4445 public async getInstalledApplications ( ) : Promise < string [ ] > {
45- let result = "" ;
46- try {
47- result = await this . adb . executeShellCommand ( [ "pm" , "list" , "packages" ] ) ;
48- } catch ( err ) {
49- /**
50- * on some devices (Samsung) listing packages is prevented by a permission error
51- * notably, some system apps (bloatware) is installed under user 150
52- * and listing these packages results in a permission error.
53- * if this happens, we have to first list all the users, and then loop through
54- * all the users and trying to list packages for that specific user, ignoring
55- * any errors. These are all then concatenated together and parsed normally.
56- * This is a slower operation, so we only do it in case listing failed in the first place.
57- */
58- const userIDs : string [ ] = [ ] ;
59- const users = await this . adb . executeShellCommand ( [ "pm" , "list" , "users" ] ) ;
60- /**
61- * Users:
62- * UserInfo{0:Owner:c13} running
63- */
64-
65- const userIDRegex = / U s e r I n f o { ( \d + ) [: } ] / ;
66- users . split ( EOL ) . forEach ( ( line : string ) => {
67- const [ , userID ] = line . match ( userIDRegex ) ?? [ ] ;
68-
69- if ( userID ) {
70- userIDs . push ( userID ) ;
71- }
72- } ) ;
73-
74- for ( let id of userIDs ) {
75- try {
76- result +=
77- EOL +
78- ( await this . adb . executeShellCommand ( [
79- "pm" ,
80- "list" ,
81- "packages" ,
82- "--user" ,
83- id ,
84- ] ) ) ;
85- } catch ( err ) {
86- // ignore - likely permission denied.
87- }
46+ if ( ! this . listPackagesPerUser ) {
47+ const packages = this . parsePackageList (
48+ await this . adb . executeShellCommand ( [ "pm" , "list" , "packages" ] ) ,
49+ ) ;
50+ if ( packages . length ) {
51+ return packages ;
8852 }
8953 }
54+
55+ // Listing without `--user` walks every user and prints nothing when shell
56+ // is denied access to one of them (e.g. Samsung's Secure Folder, user 150)
57+ // without rejecting. Listing per user only loses the inaccessible ones.
58+ this . listPackagesPerUser = true ;
59+ const packages : string [ ] = [ ] ;
60+ for ( const userId of await this . getUserIds ( ) ) {
61+ packages . push (
62+ ...this . parsePackageList (
63+ await this . adb . executeShellCommand ( [
64+ "pm" ,
65+ "list" ,
66+ "packages" ,
67+ "--user" ,
68+ userId ,
69+ ] ) ,
70+ ) ,
71+ ) ;
72+ }
73+
74+ return _ . uniq ( packages ) ;
75+ }
76+
77+ private parsePackageList ( output : string ) : string [ ] {
9078 const regex = / p a c k a g e : ( .+ ) / ;
91- return result
79+ return ( output || "" )
9280 . split ( EOL )
93- . map ( ( packageString : string ) => {
94- const match = packageString . match ( regex ) ;
81+ . map ( ( line : string ) => {
82+ const match = line . match ( regex ) ;
9583 return match ? match [ 1 ] : null ;
9684 } )
9785 . filter ( ( parsedPackage : string ) => parsedPackage !== null ) ;
9886 }
9987
88+ private async getUserIds ( ) : Promise < string [ ] > {
89+ const output : string =
90+ ( await this . adb . executeShellCommand ( [ "pm" , "list" , "users" ] ) ) || "" ;
91+ const regex = / U s e r I n f o \{ ( \d + ) : / g;
92+ return Array . from ( output . matchAll ( regex ) , ( match ) => match [ 1 ] ) ;
93+ }
94+
10095 @hook ( "install" )
10196 public async installApplication (
10297 packageFilePath : string ,
0 commit comments