@@ -676,3 +676,343 @@ func TestAidlcli_Bluetooth_Close(t *testing.T) {
676676 assert .Equal (t , "ok" , val )
677677 t .Logf ("bluetooth close: status=%s" , val )
678678}
679+
680+ // runAidlcliHALOrSkip runs aidlcli for a HAL service and skips the test
681+ // when the service is unavailable or returns any error.
682+ // HAL services are typically absent on emulators, so all errors are treated as skip-worthy.
683+ func runAidlcliHALOrSkip (
684+ t * testing.T ,
685+ serviceName string ,
686+ args ... string ,
687+ ) string {
688+ t .Helper ()
689+ fullArgs := append ([]string {serviceName }, args ... )
690+ stdout , stderr , err := runAidlcli (fullArgs ... )
691+ if err != nil {
692+ combined := stderr + stdout
693+ switch {
694+ case strings .Contains (combined , "not found" ),
695+ strings .Contains (combined , "no service with descriptor" ):
696+ t .Skipf ("%s not available: %s" , serviceName , strings .TrimSpace (combined ))
697+ }
698+ t .Skipf ("%s unavailable: %v\n stdout: %s\n stderr: %s" , serviceName , err , stdout , stderr )
699+ }
700+ return stdout
701+ }
702+
703+ // --- Core service commands (additional) ---
704+
705+ func TestAidlcli_ServiceMethods (t * testing.T ) {
706+ stdout := runAidlcliOrSkip (t , "service" , "methods" , "activity" )
707+
708+ var envelope map [string ]any
709+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
710+
711+ descriptor , ok := envelope ["descriptor" ].(string )
712+ require .True (t , ok , "response missing 'descriptor' string" )
713+ assert .NotEmpty (t , descriptor , "descriptor should not be empty" )
714+
715+ methods , ok := envelope ["methods" ].([]any )
716+ require .True (t , ok , "response missing 'methods' array" )
717+ require .NotEmpty (t , methods , "expected at least one method" )
718+
719+ // Verify the first method has a name field.
720+ firstMethod , ok := methods [0 ].(map [string ]any )
721+ require .True (t , ok , "method entry should be an object" )
722+ name , ok := firstMethod ["name" ].(string )
723+ require .True (t , ok , "method should have a 'name' string" )
724+ assert .NotEmpty (t , name , "method name should not be empty" )
725+
726+ t .Logf ("activity interface %s has %d methods, first: %s" , descriptor , len (methods ), name )
727+ }
728+
729+ func TestAidlcli_ServiceTransact (t * testing.T ) {
730+ // Transaction code 64 on SurfaceFlinger queries active color mode.
731+ stdout := runAidlcliOrSkip (t , "service" , "transact" , "SurfaceFlinger" , "64" )
732+
733+ var envelope map [string ]any
734+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
735+
736+ replyHex , ok := envelope ["reply_hex" ].(string )
737+ require .True (t , ok , "response missing 'reply_hex' string" )
738+ assert .NotEmpty (t , replyHex , "reply_hex should not be empty" )
739+
740+ replySize , ok := envelope ["reply_size" ].(float64 )
741+ require .True (t , ok , "response missing 'reply_size' number" )
742+ assert .Greater (t , replySize , float64 (0 ), "reply_size should be > 0" )
743+
744+ t .Logf ("transact SurfaceFlinger code=64: reply_size=%.0f reply_hex=%s" , replySize , replyHex )
745+ }
746+
747+ // --- Location (additional) ---
748+
749+ func TestAidlcli_Location_GetGnssYearOfHardware (t * testing.T ) {
750+ stdout , stderr , err := runAidlcli (
751+ "android.location.ILocationManager" , "get-gnss-year-of-hardware" ,
752+ )
753+ if err != nil {
754+ combined := stderr + stdout
755+ switch {
756+ case strings .Contains (combined , "not found" ),
757+ strings .Contains (combined , "no service with descriptor" ):
758+ t .Skipf ("location service not available: %s" , strings .TrimSpace (combined ))
759+ }
760+ // GNSS year might not be available on emulator.
761+ t .Skipf ("GNSS year of hardware unavailable: %v\n stdout: %s\n stderr: %s" , err , stdout , stderr )
762+ }
763+
764+ var envelope map [string ]any
765+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
766+
767+ raw , ok := envelope ["result" ]
768+ require .True (t , ok , "response missing 'result' key" )
769+
770+ val , ok := raw .(float64 )
771+ require .True (t , ok , "result should be numeric, got %T" , raw )
772+
773+ // Sanity check: year should be a reasonable value (0 means unknown, otherwise 2000+).
774+ year := int (val )
775+ if year != 0 {
776+ assert .GreaterOrEqual (t , year , 2000 , "year should be >= 2000 if set" )
777+ }
778+ t .Logf ("getGnssYearOfHardware: %d" , year )
779+ }
780+
781+ func TestAidlcli_Location_GetLastLocation (t * testing.T ) {
782+ stdout , stderr , err := runAidlcli (
783+ "android.location.ILocationManager" , "get-last-location" ,
784+ "--provider=gps" ,
785+ "--packageName=com.android.shell" ,
786+ "--attributionTag=none" ,
787+ )
788+ if err != nil {
789+ combined := stderr + stdout
790+ switch {
791+ case strings .Contains (combined , "not found" ),
792+ strings .Contains (combined , "no service with descriptor" ):
793+ t .Skipf ("location service not available: %s" , strings .TrimSpace (combined ))
794+ case strings .Contains (combined , "NullPointer" ),
795+ strings .Contains (combined , "null" ):
796+ t .Skipf ("no last location available (NullPointer): %s" , strings .TrimSpace (combined ))
797+ }
798+ t .Skipf ("get-last-location unavailable: %v\n stdout: %s\n stderr: %s" , err , stdout , stderr )
799+ }
800+
801+ var envelope map [string ]any
802+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
803+
804+ // Result may be null if no location has been recorded.
805+ raw , ok := envelope ["result" ]
806+ require .True (t , ok , "response missing 'result' key" )
807+ t .Logf ("getLastLocation: %v" , raw )
808+ }
809+
810+ // --- PackageManager (additional) ---
811+
812+ func TestAidlcli_PackageManager_GetInstallerPackageName (t * testing.T ) {
813+ stdout := runAidlcliOrSkip (t ,
814+ "android.content.pm.IPackageManager" , "get-installer-package-name" ,
815+ "--packageName=com.android.settings" ,
816+ )
817+
818+ var envelope map [string ]any
819+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
820+
821+ _ , ok := envelope ["result" ]
822+ require .True (t , ok , "response missing 'result' key" )
823+
824+ // The result may be null/empty if Settings was not installed via a store.
825+ t .Logf ("getInstallerPackageName(com.android.settings): %v" , envelope ["result" ])
826+ }
827+
828+ // --- Telephony (additional) ---
829+
830+ func TestAidlcli_Telephony_GetNetworkCountryIso (t * testing.T ) {
831+ stdout , stderr , err := runAidlcli (
832+ "com.android.internal.telephony.ITelephony" , "get-network-country-iso-for-phone" ,
833+ "--phoneId=0" ,
834+ "--callingPackage=com.android.shell" ,
835+ "--callingFeatureId=none" ,
836+ )
837+ if err != nil {
838+ combined := stderr + stdout
839+ switch {
840+ case strings .Contains (combined , "not found" ),
841+ strings .Contains (combined , "no service with descriptor" ):
842+ t .Skipf ("telephony service not available: %s" , strings .TrimSpace (combined ))
843+ }
844+ t .Skipf ("telephony unavailable: %v\n stdout: %s\n stderr: %s" , err , stdout , stderr )
845+ }
846+
847+ var envelope map [string ]any
848+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
849+
850+ raw , ok := envelope ["result" ]
851+ require .True (t , ok , "response missing 'result' key" )
852+
853+ // Result should be a string (ISO country code, may be empty).
854+ val , ok := raw .(string )
855+ require .True (t , ok , "result should be string, got %T" , raw )
856+
857+ if val != "" {
858+ assert .Len (t , val , 2 , "country ISO should be a 2-letter code" )
859+ }
860+ t .Logf ("getNetworkCountryIsoForPhone(0): %q" , val )
861+ }
862+
863+ // --- WiFi supplicant (additional) ---
864+
865+ func TestAidlcli_WiFi_AddNetwork (t * testing.T ) {
866+ stdout := runAidlcliHALOrSkip (t ,
867+ "android.hardware.wifi.supplicant.ISupplicantStaIface" , "add-network" ,
868+ )
869+
870+ var envelope map [string ]any
871+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
872+ t .Logf ("addNetwork: %v" , envelope )
873+ }
874+
875+ func TestAidlcli_WiFi_Disconnect (t * testing.T ) {
876+ stdout := runAidlcliHALOrSkip (t ,
877+ "android.hardware.wifi.supplicant.ISupplicantStaIface" , "disconnect" ,
878+ )
879+
880+ var envelope map [string ]any
881+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
882+ t .Logf ("disconnect: %v" , envelope )
883+ }
884+
885+ func TestAidlcli_WiFi_SetSsid (t * testing.T ) {
886+ // "test" in hex = 74657374
887+ stdout := runAidlcliHALOrSkip (t ,
888+ "android.hardware.wifi.supplicant.ISupplicantStaNetwork" , "set-ssid" ,
889+ "--ssid=74657374" ,
890+ )
891+
892+ var envelope map [string ]any
893+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
894+ t .Logf ("setSsid: %v" , envelope )
895+ }
896+
897+ func TestAidlcli_WiFi_SetPskPassphrase (t * testing.T ) {
898+ stdout := runAidlcliHALOrSkip (t ,
899+ "android.hardware.wifi.supplicant.ISupplicantStaNetwork" , "set-psk-passphrase" ,
900+ "--psk=testpassphrase" ,
901+ )
902+
903+ var envelope map [string ]any
904+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
905+ t .Logf ("setPskPassphrase: %v" , envelope )
906+ }
907+
908+ func TestAidlcli_WiFi_SetKeyMgmt (t * testing.T ) {
909+ stdout := runAidlcliHALOrSkip (t ,
910+ "android.hardware.wifi.supplicant.ISupplicantStaNetwork" , "set-key-mgmt" ,
911+ "--keyMgmtMask=2" ,
912+ )
913+
914+ var envelope map [string ]any
915+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
916+ t .Logf ("setKeyMgmt: %v" , envelope )
917+ }
918+
919+ func TestAidlcli_WiFi_Enable (t * testing.T ) {
920+ stdout := runAidlcliHALOrSkip (t ,
921+ "android.hardware.wifi.supplicant.ISupplicantStaNetwork" , "enable" ,
922+ "--noConnect=false" ,
923+ )
924+
925+ var envelope map [string ]any
926+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
927+ t .Logf ("enable: %v" , envelope )
928+ }
929+
930+ // --- Camera (additional) ---
931+
932+ func TestAidlcli_Camera_GetCameraCharacteristics (t * testing.T ) {
933+ stdout := runAidlcliHALOrSkip (t ,
934+ "android.hardware.camera.device.ICameraDevice" , "get-camera-characteristics" ,
935+ )
936+
937+ var envelope map [string ]any
938+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
939+
940+ _ , ok := envelope ["result" ]
941+ require .True (t , ok , "response missing 'result' key" )
942+ t .Logf ("getCameraCharacteristics: result present" )
943+ }
944+
945+ func TestAidlcli_Camera_SetTorchMode (t * testing.T ) {
946+ stdout := runAidlcliHALOrSkip (t ,
947+ "android.hardware.camera.provider.ICameraProvider" , "set-torch-mode" ,
948+ "--cameraDeviceName=0" ,
949+ "--enabled=true" ,
950+ )
951+
952+ var envelope map [string ]any
953+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
954+ t .Logf ("setTorchMode: %v" , envelope )
955+ }
956+
957+ // --- Audio (HAL) ---
958+
959+ func TestAidlcli_Audio_GetActiveMicrophones (t * testing.T ) {
960+ stdout := runAidlcliHALOrSkip (t ,
961+ "android.hardware.audio.core.IStreamIn" , "get-active-microphones" ,
962+ )
963+
964+ var envelope map [string ]any
965+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
966+
967+ _ , ok := envelope ["result" ]
968+ require .True (t , ok , "response missing 'result' key" )
969+ t .Logf ("getActiveMicrophones: %v" , envelope ["result" ])
970+ }
971+
972+ func TestAidlcli_Audio_SetMicrophoneDirection (t * testing.T ) {
973+ // Direction: 1 = FRONT.
974+ stdout := runAidlcliHALOrSkip (t ,
975+ "android.hardware.audio.core.IStreamIn" , "set-microphone-direction" ,
976+ "--direction=1" ,
977+ )
978+
979+ var envelope map [string ]any
980+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
981+ t .Logf ("setMicrophoneDirection: %v" , envelope )
982+ }
983+
984+ func TestAidlcli_Audio_SetMicrophoneFieldDimension (t * testing.T ) {
985+ stdout := runAidlcliHALOrSkip (t ,
986+ "android.hardware.audio.core.IStreamIn" , "set-microphone-field-dimension" ,
987+ "--zoom=1.0" ,
988+ )
989+
990+ var envelope map [string ]any
991+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
992+ t .Logf ("setMicrophoneFieldDimension: %v" , envelope )
993+ }
994+
995+ // --- Bluetooth (additional) ---
996+
997+ func TestAidlcli_Bluetooth_Initialize (t * testing.T ) {
998+ stdout := runAidlcliHALOrSkip (t ,
999+ "android.hardware.bluetooth.IBluetoothHci" , "initialize" ,
1000+ )
1001+
1002+ var envelope map [string ]any
1003+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
1004+ t .Logf ("bluetooth initialize: %v" , envelope )
1005+ }
1006+
1007+ func TestAidlcli_Bluetooth_SendHciCommand (t * testing.T ) {
1008+ // HCI Reset command: OGF=0x03, OCF=0x0003 => opcode 0x0C03, param_len=0
1009+ // Wire bytes: 01 03 0c 00
1010+ stdout := runAidlcliHALOrSkip (t ,
1011+ "android.hardware.bluetooth.IBluetoothHci" , "send-hci-command" ,
1012+ "--command=01030c00" ,
1013+ )
1014+
1015+ var envelope map [string ]any
1016+ require .NoError (t , json .Unmarshal ([]byte (stdout ), & envelope ), "unmarshal response" )
1017+ t .Logf ("bluetooth sendHciCommand: %v" , envelope )
1018+ }
0 commit comments