@@ -13,6 +13,7 @@ import (
1313 "github.com/spf13/cobra"
1414
1515 "github.com/AndroidGoLab/binder/binder"
16+ "github.com/AndroidGoLab/binder/binder/versionaware"
1617 "github.com/AndroidGoLab/binder/cmd/bindercli/discovery"
1718 "github.com/AndroidGoLab/binder/parcel"
1819 "github.com/AndroidGoLab/binder/servicemanager"
@@ -28,6 +29,7 @@ func newServiceCmd() *cobra.Command {
2829 cmd .AddCommand (newServiceInspectCmd ())
2930 cmd .AddCommand (newServiceTransactCmd ())
3031 cmd .AddCommand (newServiceMethodsCmd ())
32+ cmd .AddCommand (newServiceResolveCmd ())
3133
3234 return cmd
3335}
@@ -125,16 +127,21 @@ func newServiceInspectCmd() *cobra.Command {
125127
126128func newServiceTransactCmd () * cobra.Command {
127129 return & cobra.Command {
128- Use : "transact <name> <code> [hex-data]" ,
130+ Use : "transact <name> <code-or-method > [hex-data]" ,
129131 Short : "Send a raw transaction to a binder service" ,
130132 Args : cobra .RangeArgs (2 , 3 ),
131133 RunE : func (cmd * cobra.Command , args []string ) error {
132134 ctx := cmd .Context ()
133135 name := args [0 ]
136+ codeArg := args [1 ]
134137
135- code , err := strconv .ParseUint (args [1 ], 0 , 32 )
136- if err != nil {
137- return fmt .Errorf ("parsing transaction code: %w" , err )
138+ var txCode binder.TransactionCode
139+ isNumeric := false
140+
141+ // Try parsing as numeric transaction code first.
142+ if parsed , parseErr := strconv .ParseUint (codeArg , 0 , 32 ); parseErr == nil {
143+ txCode = binder .TransactionCode (parsed )
144+ isNumeric = true
138145 }
139146
140147 var data * parcel.Parcel
@@ -159,12 +166,28 @@ func newServiceTransactCmd() *cobra.Command {
159166 return err
160167 }
161168
162- reply , err := svc .Transact (
163- ctx ,
164- binder .TransactionCode (code ),
165- 0 ,
166- data ,
167- )
169+ // If not numeric, resolve method name to code.
170+ if ! isNumeric {
171+ descriptor , descErr := descriptorForBinder (ctx , svc , name )
172+ if descErr != nil {
173+ return fmt .Errorf ("resolving method name %q: %w" , codeArg , descErr )
174+ }
175+
176+ table , tableErr := getActiveTable (conn )
177+ if tableErr != nil {
178+ return fmt .Errorf ("resolving method name %q: %w" , codeArg , tableErr )
179+ }
180+
181+ methodName := kebabToMethod (codeArg , descriptor )
182+
183+ resolved , ok := resolveMethodToCode (table , descriptor , methodName )
184+ if ! ok {
185+ return fmt .Errorf ("method %q not found on interface %s" , codeArg , descriptor )
186+ }
187+ txCode = resolved
188+ }
189+
190+ reply , err := svc .Transact (ctx , txCode , 0 , data )
168191 if err != nil {
169192 return fmt .Errorf ("transact failed: %w" , err )
170193 }
@@ -236,17 +259,25 @@ func newServiceMethodsCmd() *cobra.Command {
236259 return fmt .Errorf ("unknown interface %q for service %q — not in registry" , descriptor , name )
237260 }
238261
262+ table , tableErr := getActiveTable (conn )
263+
239264 f := NewFormatter (mode , os .Stdout )
240265 switch f .Mode {
241266 case "json" :
242267 f .WriteJSON (map [string ]any {
243268 "descriptor" : descriptor ,
244- "methods" : methodsToJSON (info .Methods ),
269+ "methods" : methodsToJSONWithCodes (info .Methods , table , descriptor ),
245270 })
246271 default :
247272 fmt .Fprintf (f .W , "Interface: %s (%d methods)\n \n " , descriptor , len (info .Methods ))
248273 for _ , m := range info .Methods {
249- fmt .Fprintf (f .W , " %s\n " , formatMethodSignature (m ))
274+ codeStr := " ???? "
275+ if tableErr == nil {
276+ if code , ok := resolveMethodToCode (table , descriptor , m .Name ); ok {
277+ codeStr = fmt .Sprintf (" 0x%04x" , code )
278+ }
279+ }
280+ fmt .Fprintf (f .W , "%s %s\n " , codeStr , formatMethodSignature (m ))
250281 }
251282 }
252283
@@ -305,3 +336,110 @@ func methodsToJSON(methods []MethodInfo) []map[string]any {
305336 }
306337 return result
307338}
339+
340+ func methodsToJSONWithCodes (
341+ methods []MethodInfo ,
342+ table versionaware.VersionTable ,
343+ descriptor string ,
344+ ) []map [string ]any {
345+ result := make ([]map [string ]any , 0 , len (methods ))
346+ for _ , m := range methods {
347+ entry := map [string ]any {
348+ "name" : camelToKebab (m .Name ),
349+ }
350+
351+ if table != nil {
352+ if code , ok := resolveMethodToCode (table , descriptor , m .Name ); ok {
353+ entry ["code" ] = fmt .Sprintf ("0x%04x" , code )
354+ }
355+ }
356+
357+ if len (m .Params ) > 0 {
358+ params := make ([]map [string ]string , 0 , len (m .Params ))
359+ for _ , p := range m .Params {
360+ params = append (params , map [string ]string {
361+ "name" : p .Name ,
362+ "type" : p .Type ,
363+ })
364+ }
365+ entry ["params" ] = params
366+ }
367+
368+ if m .ReturnType != "" {
369+ entry ["return_type" ] = m .ReturnType
370+ }
371+
372+ result = append (result , entry )
373+ }
374+ return result
375+ }
376+
377+ func newServiceResolveCmd () * cobra.Command {
378+ return & cobra.Command {
379+ Use : "resolve <name> <method-or-code>" ,
380+ Short : "Resolve a transaction code or method name for a binder service" ,
381+ Long : `Resolve translates between method names and transaction codes.
382+
383+ If the second argument is a number, it is treated as a transaction code and
384+ resolved to a method name. Otherwise it is treated as a method name (in
385+ camelCase or kebab-case) and resolved to a transaction code.` ,
386+ Args : cobra .ExactArgs (2 ),
387+ RunE : func (cmd * cobra.Command , args []string ) error {
388+ ctx := cmd .Context ()
389+ name := args [0 ]
390+ query := args [1 ]
391+
392+ conn , err := OpenConn (ctx , cmd )
393+ if err != nil {
394+ return err
395+ }
396+ defer conn .Close (ctx )
397+
398+ descriptor , err := resolveDescriptor (ctx , conn , name )
399+ if err != nil {
400+ return err
401+ }
402+
403+ table , err := getActiveTable (conn )
404+ if err != nil {
405+ return err
406+ }
407+
408+ mode , err := cmd .Root ().PersistentFlags ().GetString ("format" )
409+ if err != nil {
410+ return fmt .Errorf ("reading --format flag: %w" , err )
411+ }
412+
413+ f := NewFormatter (mode , os .Stdout )
414+
415+ // Try parsing as numeric transaction code first.
416+ if code , parseErr := strconv .ParseUint (query , 0 , 32 ); parseErr == nil {
417+ methodName , ok := resolveCodeToMethod (table , descriptor , binder .TransactionCode (code ))
418+ if ! ok {
419+ return fmt .Errorf ("no method found for code %#x on %s" , code , descriptor )
420+ }
421+ f .Result (map [string ]any {
422+ "descriptor" : descriptor ,
423+ "method" : camelToKebab (methodName ),
424+ "code" : fmt .Sprintf ("0x%04x" , code ),
425+ })
426+ return nil
427+ }
428+
429+ // Treat as method name -- support kebab-case via registry lookup.
430+ methodName := kebabToMethod (query , descriptor )
431+
432+ code , ok := resolveMethodToCode (table , descriptor , methodName )
433+ if ! ok {
434+ return fmt .Errorf ("method %q not found on %s" , query , descriptor )
435+ }
436+
437+ f .Result (map [string ]any {
438+ "descriptor" : descriptor ,
439+ "method" : camelToKebab (methodName ),
440+ "code" : fmt .Sprintf ("0x%04x" , code ),
441+ })
442+ return nil
443+ },
444+ }
445+ }
0 commit comments