166 lines
5.0 KiB
Markdown
166 lines
5.0 KiB
Markdown
# Binder Principles
|
|
|
|
### IPC - Inter-process communication
|
|
|
|
An API for IPC does not exist; however, a communication paradigm between separate process components that everything uses at some level does exist
|
|
|
|
- Each process has its own address space
|
|
- Provides data isolation
|
|
- Prevents direct interaction between different processes
|
|
- We have to use **binders** for communication
|
|
- Binder is fundamentally a kernel driver
|
|
- Provides lightweight remote procedure calls (RPC)
|
|
- Reading and writing parcels between processes
|
|
- Per-process **thread pools** for handling requests
|
|
- **Synchronous** calls between processes
|
|
|
|

|
|
|
|
Note: binder is a kernel driver
|
|
|
|
##### Ideal IPC
|
|
|
|

|
|
|
|
This is an idealised abstraction
|
|
|
|
##### Binder as Intermediary
|
|
|
|

|
|
|
|
The client asks the binder driver to do something; it'll then use one of its threads to talk to the service. It needs a thread pool, as if another client makes a request, it needs to be handled sequentially.
|
|
|
|
All communication is mediated by the binder driver. This is possible because of the privileged position of the kernel in an operating system, e.g. access to kernel space.
|
|
|
|
#### Binder Facilities
|
|
|
|
###### Calls
|
|
|
|
- Simple inter-process messaging system
|
|
- one-way or two-way
|
|
|
|
###### Identifying
|
|
|
|
- Via PID or UID (user ID)
|
|
- This is down to the privileged position of the kernel
|
|
- Cannot be deceived as it has access to all memory
|
|
|
|
###### Notification
|
|
|
|
- Link to death
|
|
- Leaked service connections
|
|
|
|
###### Managing
|
|
|
|
- Reference counting
|
|
- Object mapping across processes
|
|
- Sleeping and waking worker threads
|
|
|
|
###### Indirect Functionality
|
|
|
|
- Can use a binder object as a globally unique token
|
|
- Sharing a file descriptor to shared memory
|
|
|
|
#### Binder Implementation
|
|
|
|
- API for apps
|
|
- AIDL
|
|
- Java API Wrapper
|
|
- Exposes the IBinder interface
|
|
- Wraps the middleware layer
|
|
- Parcelable object marshalling interface
|
|
- Native Middleware
|
|
- Implements the user space (within a process) facilities of the Binder framework
|
|
- Marshalling and un-marshalling of specific data to primitives
|
|
- Provides interaction with the Binder kernel driver
|
|
- Kernel Driver
|
|
- Supports ioctl (input/output system controls) from middleware
|
|
- Supports cross-process file operations, memory mapping
|
|
- Thread pool for each service application for IPC
|
|
- Mapping of objects between processes via `copy_from_user`, `copy_to_user`
|
|
|
|

|
|
|
|
`/dev/binder` - appears as a device, as it's a driver
|
|
|
|
### Binder Transactions
|
|
|
|
The proxy binder will call `IBinder.transact()` and then the binder driver will call `Binder.onTransact()` on the stub
|
|
|
|

|
|
|
|
Binder maintains a pool of transaction threads in each process
|
|
|
|
- To dispatch all IPC coming from other processes
|
|
|
|
- If process A calls process B
|
|
|
|
> - Calling thread in A blocks in `transact()`
|
|
> - Sends the transaction to process B
|
|
> - Next available thread in process B receives the incoming transaction, calls `onTransact()` on the target object, replies with the resultant parcel
|
|
> - Thread in process A returns, resumes execution
|
|
|
|
- Reliant on Service B responding in a timely manner
|
|
|
|
- Hence catching remote exceptions & transaction failures
|
|
- Developer-defined worker threads
|
|
- Handling multiple calls from multiple transaction threads
|
|
|
|
Proxy object that's making the `transact` call and the stub object that makes the `onTransact()` call.
|
|
|
|

|
|
|
|
#### IPC Abstractions
|
|
|
|
###### Intent
|
|
|
|
- Highest-level abstraction
|
|
- Asynchronous message passing
|
|
- Messenger
|
|
|
|
###### Inter-process method invocation
|
|
|
|
- AIDL
|
|
|
|
###### Binder
|
|
|
|
- Kernel Driver
|
|
|
|

|
|
|
|
### Binder Security
|
|
|
|
Binder doesn't deal with security
|
|
|
|
- Enables a **trusted** execution environment
|
|
- Transactions via the kernel
|
|
- Client identity is managed by the kernel
|
|
- `Binder.getCallingUid()`, `Binder.getCallingPid()`
|
|
- Access controlled in two ways:
|
|
1. Limit who can obtain a reference to a binder object
|
|
- Interface reference security
|
|
- Client cannot guess *address* of a service
|
|
- The binder driver *knows* who has knowledge of which objects
|
|
2. Check caller identity before performing an action on the binder objects
|
|
- Service asks package number manager about UID permissions
|
|
- Check whether it holds a permission we want to enforce via `PackageManager.getPackageInfo(...)`
|
|
|
|
### Binder Performance
|
|
|
|
###### Explicit limitations
|
|
|
|
- Transactional buffer size 1 Mb per process for all concurrent transactions in a process
|
|
- Many moderately sized transactions could exhaust its limit
|
|
- Arguments and return values are too large
|
|
- Keep transaction data small
|
|
|
|
###### Implicit Limitation
|
|
|
|
- Data is copied
|
|
- Duplication of memory resources
|
|
- Native binary marshalling
|
|
- Better than reflection-based serialisation
|
|
- Still has overhead of parcel marshalling
|
|
- Not ideal for large data streams
|
|
- You wouldn't want to pass videos through bundles
|
|
- Instead, we pass file descriptors to shared memory regions |