Skip to content

Commit c0e6a3e

Browse files
xaionaro@dx.centerxaionaro@dx.center
authored andcommitted
feat: add ServiceManager.AddService for publishing Go services
Add RegisterReceiver to the Transport interface so that ServiceManager.AddService can register a TransactionReceiver with the driver, obtain a cookie, and include the local binder object in the addService parcel sent to the ServiceManager.
1 parent 60768e6 commit c0e6a3e

4 files changed

Lines changed: 187 additions & 8 deletions

File tree

binder/transport.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ type Transport interface {
2020
AcquireHandle(ctx context.Context, handle uint32) (_err error)
2121
ReleaseHandle(ctx context.Context, handle uint32) (_err error)
2222

23+
RegisterReceiver(
24+
ctx context.Context,
25+
receiver TransactionReceiver,
26+
) uintptr
27+
2328
RequestDeathNotification(
2429
ctx context.Context,
2530
handle uint32,

binder/versionaware/transport.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,13 @@ func (t *Transport) ReleaseHandle(
644644
return t.inner.ReleaseHandle(ctx, handle)
645645
}
646646

647+
func (t *Transport) RegisterReceiver(
648+
ctx context.Context,
649+
receiver binder.TransactionReceiver,
650+
) uintptr {
651+
return t.inner.RegisterReceiver(ctx, receiver)
652+
}
653+
647654
func (t *Transport) RequestDeathNotification(
648655
ctx context.Context,
649656
handle uint32,

servicemanager/servicemanager.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,51 @@ func (sm *ServiceManager) IsDeclared(
186186
return val, nil
187187
}
188188

189+
// AddService publishes a Go service under the given name so that other
190+
// processes can discover it via GetService/CheckService.
191+
func (sm *ServiceManager) AddService(
192+
ctx context.Context,
193+
name ServiceName,
194+
service binder.TransactionReceiver,
195+
allowIsolated bool,
196+
dumpPriority int32,
197+
) (_err error) {
198+
logger.Tracef(ctx, "AddService(%q)", name)
199+
defer func() { logger.Tracef(ctx, "/AddService(%q): %v", name, _err) }()
200+
201+
transport := sm.transport()
202+
203+
cookie := transport.RegisterReceiver(ctx, service)
204+
205+
code, err := sm.remote.ResolveCode(serviceManagerDescriptor, "addService")
206+
if err != nil {
207+
return fmt.Errorf("servicemanager: AddService(%q): %w", name, err)
208+
}
209+
210+
data := parcel.New()
211+
data.WriteInterfaceToken(serviceManagerDescriptor)
212+
data.WriteString16(string(name))
213+
data.WriteLocalBinder(cookie)
214+
215+
var allowIsolatedInt int32
216+
if allowIsolated {
217+
allowIsolatedInt = 1
218+
}
219+
data.WriteInt32(allowIsolatedInt)
220+
data.WriteInt32(dumpPriority)
221+
222+
reply, err := sm.remote.Transact(ctx, code, 0, data)
223+
if err != nil {
224+
return fmt.Errorf("servicemanager: AddService(%q): %w", name, err)
225+
}
226+
227+
if err := binder.ReadStatus(reply); err != nil {
228+
return fmt.Errorf("servicemanager: AddService(%q): %w", name, err)
229+
}
230+
231+
return nil
232+
}
233+
189234
// transport extracts the VersionAwareTransport from the ProxyBinder.
190235
func (sm *ServiceManager) transport() binder.VersionAwareTransport {
191236
pb, ok := sm.remote.(*binder.ProxyBinder)

servicemanager/servicemanager_test.go

Lines changed: 130 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ import (
1313

1414
// mockTransport captures the last Transact call and returns a predetermined reply.
1515
type mockTransport struct {
16-
lastHandle uint32
17-
lastCode binder.TransactionCode
18-
lastFlags binder.TransactionFlags
19-
lastData *parcel.Parcel
20-
replyFunc func() *parcel.Parcel
21-
err error
16+
lastHandle uint32
17+
lastCode binder.TransactionCode
18+
lastFlags binder.TransactionFlags
19+
lastData *parcel.Parcel
20+
replyFunc func() *parcel.Parcel
21+
err error
22+
lastReceiver binder.TransactionReceiver
23+
nextCookie uintptr
2224
}
2325

2426
func (m *mockTransport) Transact(
@@ -38,8 +40,13 @@ func (m *mockTransport) Transact(
3840
return m.replyFunc(), nil
3941
}
4042

41-
func (m *mockTransport) AcquireHandle(_ context.Context, _ uint32) error { return nil }
42-
func (m *mockTransport) ReleaseHandle(_ context.Context, _ uint32) error { return nil }
43+
func (m *mockTransport) AcquireHandle(_ context.Context, _ uint32) error { return nil }
44+
func (m *mockTransport) ReleaseHandle(_ context.Context, _ uint32) error { return nil }
45+
func (m *mockTransport) RegisterReceiver(_ context.Context, receiver binder.TransactionReceiver) uintptr {
46+
m.lastReceiver = receiver
47+
m.nextCookie++
48+
return m.nextCookie
49+
}
4350

4451
func (m *mockTransport) RequestDeathNotification(
4552
_ context.Context,
@@ -262,3 +269,118 @@ func TestGetService_TransportError(t *testing.T) {
262269
assert.Nil(t, result)
263270
assert.ErrorIs(t, err, assert.AnError)
264271
}
272+
273+
// mockReceiver is a minimal TransactionReceiver for testing.
274+
type mockReceiver struct{}
275+
276+
func (r *mockReceiver) OnTransaction(
277+
_ context.Context,
278+
_ binder.TransactionCode,
279+
_ *parcel.Parcel,
280+
) (*parcel.Parcel, error) {
281+
return parcel.New(), nil
282+
}
283+
284+
func TestAddService(t *testing.T) {
285+
ctx := context.Background()
286+
287+
mt := &mockTransport{
288+
replyFunc: buildSuccessReply(func(_ *parcel.Parcel) {}),
289+
}
290+
291+
receiver := &mockReceiver{}
292+
sm := New(mt)
293+
err := sm.AddService(ctx, ServiceName("my.new.service"), receiver, true, 4)
294+
require.NoError(t, err)
295+
296+
// Verify the receiver was registered.
297+
assert.Equal(t, binder.TransactionReceiver(receiver), mt.lastReceiver)
298+
299+
// Verify we sent to handle 0 (ServiceManager) with correct code.
300+
assert.Equal(t, uint32(0), mt.lastHandle)
301+
assert.Equal(t, binder.FirstCallTransaction, mt.lastCode)
302+
assert.Equal(t, binder.TransactionFlags(0), mt.lastFlags)
303+
304+
// Verify the sent parcel data.
305+
mt.lastData.SetPosition(0)
306+
307+
token, err := mt.lastData.ReadInterfaceToken()
308+
require.NoError(t, err)
309+
assert.Equal(t, serviceManagerDescriptor, token)
310+
311+
svcName, err := mt.lastData.ReadString16()
312+
require.NoError(t, err)
313+
assert.Equal(t, "my.new.service", svcName)
314+
315+
// Read the local binder object (flat_binder_object).
316+
handle, err := mt.lastData.ReadStrongBinder()
317+
require.NoError(t, err)
318+
// The cookie is 1 (first call to RegisterReceiver on this mock).
319+
assert.Equal(t, uint32(1), handle)
320+
321+
// allowIsolated = true -> 1
322+
allowIsolated, err := mt.lastData.ReadInt32()
323+
require.NoError(t, err)
324+
assert.Equal(t, int32(1), allowIsolated)
325+
326+
// dumpPriority = 4
327+
dumpPriority, err := mt.lastData.ReadInt32()
328+
require.NoError(t, err)
329+
assert.Equal(t, int32(4), dumpPriority)
330+
}
331+
332+
func TestAddService_NotIsolated(t *testing.T) {
333+
ctx := context.Background()
334+
335+
mt := &mockTransport{
336+
replyFunc: buildSuccessReply(func(_ *parcel.Parcel) {}),
337+
}
338+
339+
sm := New(mt)
340+
err := sm.AddService(ctx, ServiceName("isolated.service"), &mockReceiver{}, false, 0)
341+
require.NoError(t, err)
342+
343+
// Verify allowIsolated is written as 0.
344+
mt.lastData.SetPosition(0)
345+
_, _ = mt.lastData.ReadInterfaceToken()
346+
_, _ = mt.lastData.ReadString16()
347+
_, _ = mt.lastData.ReadStrongBinder()
348+
349+
allowIsolated, err := mt.lastData.ReadInt32()
350+
require.NoError(t, err)
351+
assert.Equal(t, int32(0), allowIsolated)
352+
}
353+
354+
func TestAddService_StatusError(t *testing.T) {
355+
ctx := context.Background()
356+
357+
mt := &mockTransport{
358+
replyFunc: func() *parcel.Parcel {
359+
p := parcel.New()
360+
binder.WriteStatus(p, &aidlerrors.StatusError{
361+
Exception: aidlerrors.ExceptionSecurity,
362+
Message: "not allowed",
363+
})
364+
p.SetPosition(0)
365+
return p
366+
},
367+
}
368+
369+
sm := New(mt)
370+
err := sm.AddService(ctx, ServiceName("blocked.service"), &mockReceiver{}, false, 0)
371+
require.Error(t, err)
372+
assert.Contains(t, err.Error(), "not allowed")
373+
}
374+
375+
func TestAddService_TransportError(t *testing.T) {
376+
ctx := context.Background()
377+
378+
mt := &mockTransport{
379+
err: assert.AnError,
380+
}
381+
382+
sm := New(mt)
383+
err := sm.AddService(ctx, ServiceName("fail.service"), &mockReceiver{}, false, 0)
384+
require.Error(t, err)
385+
assert.ErrorIs(t, err, assert.AnError)
386+
}

0 commit comments

Comments
 (0)