@@ -851,6 +851,183 @@ func newRegistryResolver(
851851 return r
852852}
853853
854+ // generateServiceNamesFile generates servicemanager/service_names_gen.go from
855+ // the Services field of the servicemanager package spec. Each ServiceMapping
856+ // produces a typed constant: ConstantName (SCREAMING_SNAKE) is converted to
857+ // PascalCase, and the value is the service_name string.
858+ func generateServiceNamesFile (
859+ specs map [string ]* spec.PackageSpec ,
860+ outputDir string ,
861+ ) error {
862+ smSpec := specs ["servicemanager" ]
863+ if smSpec == nil || len (smSpec .Services ) == 0 {
864+ return nil
865+ }
866+
867+ // Build sorted constant entries.
868+ type constEntry struct {
869+ GoName string
870+ ServiceName string
871+ }
872+
873+ entries := make ([]constEntry , 0 , len (smSpec .Services ))
874+ for _ , svc := range smSpec .Services {
875+ goName := codegen .ScreamingSnakeToPascal (svc .ConstantName )
876+ entries = append (entries , constEntry {
877+ GoName : goName ,
878+ ServiceName : svc .ServiceName ,
879+ })
880+ }
881+ sort .Slice (entries , func (i , j int ) bool {
882+ return entries [i ].GoName < entries [j ].GoName
883+ })
884+
885+ // Find the longest GoName for column alignment.
886+ maxLen := 0
887+ for _ , e := range entries {
888+ if len (e .GoName ) > maxLen {
889+ maxLen = len (e .GoName )
890+ }
891+ }
892+
893+ var buf bytes.Buffer
894+ buf .WriteString ("// Code generated by spec2go. DO NOT EDIT.\n \n " )
895+ buf .WriteString ("package servicemanager\n \n " )
896+ buf .WriteString ("// Service name constants extracted from android.content.Context.\n " )
897+ buf .WriteString ("const (\n " )
898+
899+ for _ , e := range entries {
900+ padding := strings .Repeat (" " , maxLen - len (e .GoName ))
901+ fmt .Fprintf (& buf , "\t %s%s ServiceName = %q\n " , e .GoName , padding , e .ServiceName )
902+ }
903+
904+ buf .WriteString (")\n " )
905+
906+ formatted , err := format .Source (buf .Bytes ())
907+ if err != nil {
908+ return fmt .Errorf ("formatting service names: %w\n \n Raw source:\n %s" , err , buf .String ())
909+ }
910+
911+ outPath := filepath .Join (outputDir , "servicemanager" , "service_names_gen.go" )
912+ if err := os .MkdirAll (filepath .Dir (outPath ), 0o755 ); err != nil {
913+ return fmt .Errorf ("creating directory: %w" , err )
914+ }
915+ if err := os .WriteFile (outPath , formatted , 0o644 ); err != nil {
916+ return fmt .Errorf ("writing %s: %w" , outPath , err )
917+ }
918+
919+ fmt .Fprintf (os .Stderr , "Wrote %s (%d constants)\n " , outPath , len (entries ))
920+ return nil
921+ }
922+
923+ // generateAccessorFiles generates get_*.go files for services whose AIDL
924+ // interface has a generated proxy in the output directory. Each file contains
925+ // a GetXxxManager function that calls ServiceManager.GetService and wraps
926+ // the result in the typed proxy constructor.
927+ func generateAccessorFiles (
928+ specs map [string ]* spec.PackageSpec ,
929+ outputDir string ,
930+ ) error {
931+ smSpec := specs ["servicemanager" ]
932+ if smSpec == nil || len (smSpec .Services ) == 0 {
933+ return nil
934+ }
935+
936+ // Build a descriptor -> InterfaceSpec index across all specs so we can
937+ // look up the interface definition for each service's descriptor.
938+ type ifaceInfo struct {
939+ GoPackage string
940+ InterfaceName string
941+ }
942+ descriptorIndex := map [string ]ifaceInfo {}
943+ for _ , ps := range specs {
944+ for _ , iface := range ps .Interfaces {
945+ desc := ps .AIDLPackage + "." + iface .Name
946+ descriptorIndex [desc ] = ifaceInfo {
947+ GoPackage : ps .GoPackage ,
948+ InterfaceName : iface .Name ,
949+ }
950+ }
951+ }
952+
953+ count := 0
954+ for _ , svc := range smSpec .Services {
955+ info , ok := descriptorIndex [svc .Descriptor ]
956+ if ! ok {
957+ continue
958+ }
959+
960+ interfaceGoName := codegen .AIDLToGoName (info .InterfaceName )
961+ proxyName := deriveProxyName (interfaceGoName )
962+ constructorName := "New" + proxyName
963+
964+ // Verify the proxy constructor exists in the output directory by
965+ // checking that the generated interface file is present.
966+ goFileName := codegen .AIDLToGoFileName (info .InterfaceName )
967+ ifaceFilePath := filepath .Join (outputDir , info .GoPackage , goFileName )
968+ if _ , err := os .Stat (ifaceFilePath ); err != nil {
969+ continue
970+ }
971+
972+ // Determine the Go package name (last segment of the package path).
973+ goPkg := filepath .Base (info .GoPackage )
974+
975+ // Build the constant name for the servicemanager constant.
976+ constName := codegen .ScreamingSnakeToPascal (svc .ConstantName )
977+
978+ // The base name without Proxy suffix is used for the Get function.
979+ baseName := interfaceGoName
980+ if strings .HasPrefix (interfaceGoName , "I" ) && len (interfaceGoName ) > 1 {
981+ baseName = interfaceGoName [1 :]
982+ }
983+ funcName := "Get" + baseName
984+
985+ var buf bytes.Buffer
986+ buf .WriteString ("// Code generated by spec2go. DO NOT EDIT.\n \n " )
987+ fmt .Fprintf (& buf , "package %s\n \n " , goPkg )
988+ buf .WriteString ("import (\n " )
989+ buf .WriteString ("\t \" context\" \n " )
990+ buf .WriteString ("\t \" fmt\" \n \n " )
991+ buf .WriteString ("\t \" github.com/xaionaro-go/binder/servicemanager\" \n " )
992+ buf .WriteString (")\n \n " )
993+ fmt .Fprintf (& buf , "// %s retrieves the %s service and returns a typed proxy.\n " , funcName , constName )
994+ fmt .Fprintf (& buf , "func %s(\n " , funcName )
995+ buf .WriteString ("\t ctx context.Context,\n " )
996+ buf .WriteString ("\t sm *servicemanager.ServiceManager,\n " )
997+ fmt .Fprintf (& buf , ") (*%s, error) {\n " , proxyName )
998+ fmt .Fprintf (& buf , "\t svc, err := sm.GetService(ctx, servicemanager.%s)\n " , constName )
999+ buf .WriteString ("\t if err != nil {\n " )
1000+ fmt .Fprintf (& buf , "\t \t return nil, fmt.Errorf(\" %s: %%w\" , err)\n " , funcName )
1001+ buf .WriteString ("\t }\n " )
1002+ fmt .Fprintf (& buf , "\t return %s(svc), nil\n " , constructorName )
1003+ buf .WriteString ("}\n " )
1004+
1005+ formatted , fmtErr := format .Source (buf .Bytes ())
1006+ if fmtErr != nil {
1007+ return fmt .Errorf ("formatting accessor for %s: %w\n \n Raw source:\n %s" , svc .ServiceName , fmtErr , buf .String ())
1008+ }
1009+
1010+ outPath := filepath .Join (outputDir , info .GoPackage , "get_" + svc .ServiceName + ".go" )
1011+ if err := os .WriteFile (outPath , formatted , 0o644 ); err != nil {
1012+ return fmt .Errorf ("writing %s: %w" , outPath , err )
1013+ }
1014+ count ++
1015+ }
1016+
1017+ fmt .Fprintf (os .Stderr , "Generated %d accessor files\n " , count )
1018+ return nil
1019+ }
1020+
1021+ // deriveProxyName derives a proxy struct name from an interface name.
1022+ // IFoo -> FooProxy, Foo -> FooProxy. This mirrors the codegen convention.
1023+ func deriveProxyName (interfaceName string ) string {
1024+ base := interfaceName
1025+ if strings .HasPrefix (interfaceName , "I" ) && len (interfaceName ) > 1 {
1026+ base = interfaceName [1 :]
1027+ }
1028+ return base + "Proxy"
1029+ }
1030+
8541031// generateCodesFile builds codes_gen.go from version_codes embedded in
8551032// interface specs.
8561033func generateCodesFile (
0 commit comments