package Ġcom . ste ves ol ty s . seed vault . transport . backup Ċ Ċ import Ġandroid . content . pm . Application Info . FLAG _ SYSTEM Ċ import Ġandroid . content . pm . Application Info . FLAG _ TEST _ ONLY Ċ import Ġandroid . content . pm . Application Info . FLAG _ UPDATED _ SYSTEM _ APP Ċ import Ġandroid . content . pm . Install Source Info Ċ import Ġandroid . content . pm . Package Info Ċ import Ġandroid . content . pm . PackageManager Ċ import Ġandroid . content . pm . Signature Ċ import Ġandroid . util . Package Utils Ċ import Ġcom . ste ves ol ty s . seed vault . MAGIC _ PACKAGE _ MANAGER Ċ import Ġcom . ste ves ol ty s . seed vault . getRandom String Ċ import Ġcom . ste ves ol ty s . seed vault . metadata . A pk Split Ċ import Ġcom . ste ves ol ty s . seed vault . metadata . Package Metadata Ċ import Ġcom . ste ves ol ty s . seed vault . metadata . Package State . UNKNOWN _ ERROR Ċ import Ġio . mock k . co Every Ċ import Ġio . mock k . every Ċ import Ġio . mock k . mock k Ċ import Ġio . mock k . mock k Static Ċ import Ġkotlinx . coroutines . run Blocking Ċ import Ġorg . junit . jupiter . api . Assertions . assertArrayEquals Ċ import Ġorg . junit . jupiter . api . Assertions . assertEquals Ċ import Ġorg . junit . jupiter . api . Assertions . assertNull Ċ import Ġorg . junit . jupiter . api . Assertions . assert Throws Ċ import Ġorg . junit . jupiter . api . Assertions . assertTrue Ċ import Ġorg . junit . jupiter . api . Test Ċ import Ġorg . junit . jupiter . api . io . TempDir Ċ import Ġjava . io . ByteArray OutputStream Ċ import Ġjava . io . File Ċ import Ġjava . io . IOException Ċ import Ġjava . io . OutputStream Ċ import Ġjava . nio . file . Path Ċ import Ġkotlin . random . Random Ċ Ċ @ Suppress (" Blocking Method In Non Blocking Context ") Ċ internal Ġclass ĠA pk Backup Test Ġ: ĠBackup Test () Ġ{ ĊĊĠĠĠ Ġprivate Ġval Ġpm : ĠPackage Manager Ġ= Ġmock k () ĊĠĠĠ Ġprivate Ġval Ġstream Getter : Ġsuspend Ġ( name : ĠString ) Ġ-> ĠOutputStream Ġ= Ġmock k () ĊĊĠĠĠ Ġprivate Ġval Ġapk Backup Ġ= ĠA pk Backup ( pm , Ġcrypto , Ġsettings Manager , Ġmetadata Manager ) ĊĊĠĠĠ Ġprivate Ġval Ġsignature Bytes Ġ= Ġbyte ArrayOf ( 0 x 0 1 , Ġ 0 x 0 2 , Ġ 0 x 0 3 ) ĊĠĠĠ Ġprivate Ġval Ġsignature Hash Ġ= Ġbyte ArrayOf ( 0 x 0 3 , Ġ 0 x 0 2 , Ġ 0 x 0 1 ) ĊĠĠĠ Ġprivate Ġval Ġsig s Ġ= ĠarrayOf ( Signature ( signature Bytes )) ĊĠĠĠ Ġprivate Ġval Ġpackage Metadata Ġ= ĠPackage Metadata ( ĊĠĠĠĠĠĠĠ Ġtime Ġ= ĠRandom . next Long (), ĊĠĠĠĠĠĠĠ Ġversion Ġ= Ġpackage Info . long Version Code Ġ- Ġ 1 , ĊĠĠĠĠĠĠĠ Ġsignatures Ġ= ĠlistOf (" Aw IB ") ĊĠĠĠ Ġ) ĊĊĠĠĠ Ġinit Ġ{ ĊĠĠĠĠĠĠĠ Ġmock k Static ( Package Utils :: class ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` does Ġnot Ġback Ġup Ġ@ pm @ `() Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġpackage Info Ġ= ĠPackage Info (). apply Ġ{ ĠpackageName Ġ= ĠMAG IC _ PACKAGE _ MANAGER Ġ} ĊĠĠĠĠĠĠĠ ĠassertNull ( apk Backup . backup A pk If Ne cessary ( package Info , ĠUNKNOWN _ ERROR , Ġstream Getter )) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` does Ġnot Ġback Ġup Ġwhen Ġsetting Ġdisabled `() Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġsettings Manager . backup A pk s () Ġ} Ġreturns Ġfalse ĊĊĠĠĠĠĠĠĠ ĠassertNull ( apk Backup . backup A pk If Ne cessary ( package Info , ĠUNKNOWN _ ERROR , Ġstream Getter )) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` does Ġnot Ġback Ġup Ġtest - only Ġapps `() Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġpackage Info . application Info . flags Ġ= ĠFLAG _ TEST _ ONLY ĊĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġsettings Manager . backup A pk s () Ġ} Ġreturns Ġtrue ĊĊĠĠĠĠĠĠĠ ĠassertNull ( apk Backup . backup A pk If Ne cessary ( package Info , ĠUNKNOWN _ ERROR , Ġstream Getter )) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` does Ġnot Ġback Ġup Ġsystem Ġapps `() Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġpackage Info . application Info . flags Ġ= ĠFLAG _ SYSTEM ĊĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġsettings Manager . backup A pk s () Ġ} Ġreturns Ġtrue ĊĊĠĠĠĠĠĠĠ ĠassertNull ( apk Backup . backup A pk If Ne cessary ( package Info , ĠUNKNOWN _ ERROR , Ġstream Getter )) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` does Ġnot Ġback Ġup Ġthe Ġsame Ġversion `() Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġpackage Info . application Info . flags Ġ= ĠFLAG _ UPDATED _ SYSTEM _ APP ĊĠĠĠĠĠĠĠ Ġval Ġpackage Metadata Ġ= Ġpackage Metadata . copy ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġversion Ġ= Ġpackage Info . long Version Code ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġexpect Checks ( package Metadata ) ĊĊĠĠĠĠĠĠĠ ĠassertNull ( apk Backup . backup A pk If Ne cessary ( package Info , ĠUNKNOWN _ ERROR , Ġstream Getter )) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` does Ġback Ġup Ġthe Ġsame Ġversion Ġwhen Ġsignatures Ġchanges `() Ġ{ ĊĠĠĠĠĠĠĠ Ġpackage Info . application Info . source Dir Ġ= Ġ"/ tmp / does NotExist " ĊĊĠĠĠĠĠĠĠ Ġexpect Checks () ĊĊĠĠĠĠĠĠĠ ĠassertThrows ( IOException :: class . java ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠassertNull ( apk Backup . backup A pk If Ne cessary ( package Info , ĠUNKNOWN _ ERROR , Ġstream Getter )) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` do Ġnot Ġaccept Ġempty Ġsignature `() Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġsettings Manager . backup A pk s () Ġ} Ġreturns Ġtrue ĊĠĠĠĠĠĠĠ Ġevery Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmetadata Manager . get Package Metadata ( package Info . packageName ) ĊĠĠĠĠĠĠĠ Ġ} Ġreturns Ġpackage Metadata ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġsig Info . has Multiple Sign ers () Ġ} Ġreturns Ġfalse ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġsig Info . signing Certificate History Ġ} Ġreturns Ġempty Array () ĊĊĠĠĠĠĠĠĠ ĠassertNull ( apk Backup . backup A pk If Ne cessary ( package Info , ĠUNKNOWN _ ERROR , Ġstream Getter )) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` test Ġsuccessful ĠA PK Ġbackup ` (@ TempDir Ġtmp Dir : ĠPath ) Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġapk Bytes Ġ= Ġbyte ArrayOf ( 0 x 0 4 , Ġ 0 x 0 5 , Ġ 0 x 0 6 ) ĊĠĠĠĠĠĠĠ Ġval Ġtmp File Ġ= ĠFile ( tmp Dir . to AbsolutePath (). toString ()) ĊĠĠĠĠĠĠĠ Ġpackage Info . application Info . source Dir Ġ= ĠFile ( tmp File , Ġ" test . apk "). apply Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertTrue ( createNew File ()) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġwrite Bytes ( apk Bytes ) ĊĠĠĠĠĠĠĠ Ġ}. absolute Path ĊĠĠĠĠĠĠĠ Ġval Ġapk OutputStream Ġ= ĠByteArrayOutputStream () ĊĠĠĠĠĠĠĠ Ġval Ġupdated Metadata Ġ= ĠPackage Metadata ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtime Ġ= Ġ 0 L , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstate Ġ= ĠUNKNOWN _ ERROR , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġversion Ġ= Ġpackage Info . long Version Code , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġinstaller Ġ= ĠgetRandom String (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsha 2 5 6 Ġ= Ġ" e Hx 5 jj ml v Bk Q NV u ub Qz Y ej ay 4 Q _ Q IC q D 4 7 tr AF 2 o N HI ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsignatures Ġ= Ġpackage Metadata . signatures ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġexpect Checks () ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġmetadata Manager . salt Ġ} Ġreturns Ġsalt ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġcrypto . getName For A pk ( salt , Ġpackage Info . packageName ) Ġ} Ġreturns Ġname ĊĠĠĠĠĠĠĠ Ġco Every Ġ{ Ġstream Getter . invoke ( name ) Ġ} Ġreturns Ġapk OutputStream ĊĠĠĠĠĠĠĠ Ġevery Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpm . get Install Source Info ( package Info . packageName ) ĊĠĠĠĠĠĠĠ Ġ} Ġreturns ĠInstall Source Info ( null , Ġnull , Ġnull , Ġupdated Metadata . installer ) ĊĊĠĠĠĠĠĠĠ ĠassertEquals ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġupdated Metadata , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapk Backup . backup A pk If Ne cessary ( package Info , ĠUNKNOWN _ ERROR , Ġstream Getter ) ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġassert ArrayEquals ( apk Bytes , Ġapk OutputStream . toByteArray ()) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġ@ Test ĊĠĠĠ Ġfun Ġ` test Ġsuccessful ĠA PK Ġbackup Ġwith Ġtwo Ġsplits ` (@ TempDir Ġtmp Dir : ĠPath ) Ġ= Ġrun Blocking Ġ{ ĊĠĠĠĠĠĠĠ Ġ// Ġcreate Ġbase ĠA PK ĊĠĠĠĠĠĠĠ Ġval Ġapk Bytes Ġ= Ġbyte ArrayOf ( 0 x 0 4 , Ġ 0 x 0 5 , Ġ 0 x 0 6 ) Ġ// Ġnot Ġrandom Ġbecause Ġof Ġhash ĊĠĠĠĠĠĠĠ Ġval Ġtmp File Ġ= ĠFile ( tmp Dir . to AbsolutePath (). toString ()) ĊĠĠĠĠĠĠĠ Ġpackage Info . application Info . source Dir Ġ= ĠFile ( tmp File , Ġ" test . apk "). apply Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠassertTrue ( createNew File ()) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġwrite Bytes ( apk Bytes ) ĊĠĠĠĠĠĠĠ Ġ}. absolute Path ĊĠĠĠĠĠĠĠ Ġ// Ġset Ġsplit Ġnames ĊĠĠĠĠĠĠĠ Ġval Ġsplit 1 Name Ġ= Ġ" config . arm 6 4 _ v 8 a " ĊĠĠĠĠĠĠĠ Ġval Ġsplit 2 Name Ġ= Ġ" config . xxx hdpi " ĊĠĠĠĠĠĠĠ Ġpackage Info . split Names Ġ= ĠarrayOf ( split 1 Name , Ġsplit 2 Name ) ĊĠĠĠĠĠĠĠ Ġ// Ġcreate Ġtwo Ġsplit ĠA PK s ĊĠĠĠĠĠĠĠ Ġval Ġsplit 1 Bytes Ġ= Ġbyte ArrayOf ( 0 x 0 7 , Ġ 0 x 0 8 , Ġ 0 x 0 9 ) ĊĠĠĠĠĠĠĠ Ġval Ġsplit 1 Sha 2 5 6 Ġ= Ġ" Zq Z 1 c VH 4 7 l Xb Enc Wx - Pc 4 L 6 Ad L ZO IO 2 l Qu XB 5 Gy px B 4 " ĊĠĠĠĠĠĠĠ Ġval Ġsplit 2 Bytes Ġ= Ġbyte ArrayOf ( 0 x 0 1 , Ġ 0 x 0 2 , Ġ 0 x 0 3 ) ĊĠĠĠĠĠĠĠ Ġval Ġsplit 2 Sha 2 5 6 Ġ= Ġ" A 5 BY xv LA y 0 ks U zs K TR T vd 8 w Pe Kv M zt U of Y Sh og Ec - 4 E " ĊĠĠĠĠĠĠĠ Ġpackage Info . application Info . split Source Dirs Ġ= ĠarrayOf ( ĊĠĠĠĠĠĠĠĠĠĠĠ ĠFile ( tmp File , Ġ" test -$ split 1 Name . apk "). apply Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠassertTrue ( createNew File ()) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġwrite Bytes ( split 1 Bytes ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}. absolute Path , ĊĠĠĠĠĠĠĠĠĠĠĠ ĠFile ( tmp File , Ġ" test -$ split 2 Name . apk "). apply Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠassertTrue ( createNew File ()) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġwrite Bytes ( split 2 Bytes ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ}. absolute Path ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġ// Ġcreate Ġstreams ĊĠĠĠĠĠĠĠ Ġval Ġapk OutputStream Ġ= ĠByteArrayOutputStream () ĊĠĠĠĠĠĠĠ Ġval Ġsplit 1 OutputStream Ġ= ĠByteArrayOutputStream () ĊĠĠĠĠĠĠĠ Ġval Ġsplit 2 OutputStream Ġ= ĠByteArrayOutputStream () ĊĠĠĠĠĠĠĠ Ġ// Ġexpected Ġnew Ġmetadata Ġfor Ġpackage ĊĠĠĠĠĠĠĠ Ġval Ġupdated Metadata Ġ= ĠPackage Metadata ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġtime Ġ= Ġ 0 L , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstate Ġ= ĠUNKNOWN _ ERROR , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġversion Ġ= Ġpackage Info . long Version Code , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġinstaller Ġ= ĠgetRandom String (), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsplits Ġ= ĠlistOf ( ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠA pk Split ( split 1 Name , Ġsplit 1 Sha 2 5 6 ), ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠA pk Split ( split 2 Name , Ġsplit 2 Sha 2 5 6 ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ), ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsha 2 5 6 Ġ= Ġ" e Hx 5 jj ml v Bk Q NV u ub Qz Y ej ay 4 Q _ Q IC q D 4 7 tr AF 2 o N HI ", ĊĠĠĠĠĠĠĠĠĠĠĠ Ġsignatures Ġ= Ġpackage Metadata . signatures ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġval Ġsuffix Name 1 Ġ= ĠgetRandom String () ĊĠĠĠĠĠĠĠ Ġval Ġsuffix Name 2 Ġ= ĠgetRandom String () ĊĊĠĠĠĠĠĠĠ Ġexpect Checks () ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġmetadata Manager . salt Ġ} Ġreturns Ġsalt ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġcrypto . getName For A pk ( salt , Ġpackage Info . packageName ) Ġ} Ġreturns Ġname ĊĠĠĠĠĠĠĠ Ġevery Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġcrypto . getName For A pk ( salt , Ġpackage Info . packageName , Ġsplit 1 Name ) ĊĠĠĠĠĠĠĠ Ġ} Ġreturns Ġsuffix Name 1 ĊĠĠĠĠĠĠĠ Ġevery Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġcrypto . getName For A pk ( salt , Ġpackage Info . packageName , Ġsplit 2 Name ) ĊĠĠĠĠĠĠĠ Ġ} Ġreturns Ġsuffix Name 2 ĊĠĠĠĠĠĠĠ Ġco Every Ġ{ Ġstream Getter . invoke ( name ) Ġ} Ġreturns Ġapk OutputStream ĊĠĠĠĠĠĠĠ Ġco Every Ġ{ Ġstream Getter . invoke ( suffix Name 1 ) Ġ} Ġreturns Ġsplit 1 OutputStream ĊĠĠĠĠĠĠĠ Ġco Every Ġ{ Ġstream Getter . invoke ( suffix Name 2 ) Ġ} Ġreturns Ġsplit 2 OutputStream ĊĊĠĠĠĠĠĠĠ Ġevery Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġpm . get Install Source Info ( package Info . packageName ) ĊĠĠĠĠĠĠĠ Ġ} Ġreturns ĠInstall Source Info ( null , Ġnull , Ġnull , Ġupdated Metadata . installer ) ĊĊĠĠĠĠĠĠĠ ĠassertEquals ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġupdated Metadata , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġapk Backup . backup A pk If Ne cessary ( package Info , ĠUNKNOWN _ ERROR , Ġstream Getter ) ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġassert ArrayEquals ( apk Bytes , Ġapk OutputStream . toByteArray ()) ĊĠĠĠĠĠĠĠ Ġassert ArrayEquals ( split 1 Bytes , Ġsplit 1 OutputStream . toByteArray ()) ĊĠĠĠĠĠĠĠ Ġassert ArrayEquals ( split 2 Bytes , Ġsplit 2 OutputStream . toByteArray ()) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġexpect Checks ( package Metadata : ĠPackage Metadata Ġ= Ġthis . package Metadata ) Ġ{ ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġsettings Manager . backup A pk s () Ġ} Ġreturns Ġtrue ĊĠĠĠĠĠĠĠ Ġevery Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmetadata Manager . get Package Metadata ( package Info . packageName ) ĊĠĠĠĠĠĠĠ Ġ} Ġreturns Ġpackage Metadata ĊĠĠĠĠĠĠĠ Ġevery Ġ{ ĠPackage Utils . compute Sha 2 5 6 Digest Bytes ( signature Bytes ) Ġ} Ġreturns Ġsignature Hash ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġsig Info . has Multiple Sign ers () Ġ} Ġreturns Ġfalse ĊĠĠĠĠĠĠĠ Ġevery Ġ{ Ġsig Info . signing Certificate History Ġ} Ġreturns Ġsig s ĊĠĠĠ Ġ} Ċ Ċ } Ċ