안드로이드 프레임워크 프로그래밍(28) [System Service의 proxy와 native의 Interface 상속 구조]

 안드로이드 프레임워크를 공부하는 데 있어 다루게 되는 내용 중 가장 중요한 것을 꼽아본다면 각 프로세스간의 통신 방식인 Binder의 활용이 아닐까 생각합니다. 실제로 안드로이드 운영체제 내에서 동작하는 System Service가 Android 애플리케이션 프로세스 상호간에 통신하기 위해서는 IPC(Interprocess Communication)통신의 일종인 Binder 통신을 사용합니다. 안드로이드 운영체제 또한 리눅스를 기반으로 만들어졌으므로 프로세스의 구조 또한 리눅스의 그것과 비슷하다고 할 수 있습니다. 다만, 기존 리눅스에서 사용하는 IPC 뿐 아니라 RPC(Remote Process Communication)를 사용해 AIDL(Android Interface Definition Language)를 통해 좀 더 간단하게 프로세스간 통신을 할 수 있게 하고 있습니다.



 위의 그램은 Application에서의 프로세스와 System Service에서의 프로세스가 Binder 드라이버를 통해 커널 상으로 통신하고 있는 모습을 그림으로 나타낸 것입니다. 본 포스팅에서는 위와 같은 Binder를 사용하기 위해 proxy와 native에서 Interface를 구성하는 과정에 대해 간단히 설명드리겠습니다. Android에서 proxy에 해당되는 프로세스는 앞부분에 bp를, native에 해당되는 프로세스는 bn을 붙여줍니다. 아마도 약자는 각각 binder proxy, binder native로 추정됩니다.


 먼저 System Service중 하나인 CameraService를 예를 들어 설명해 보겠습니다. 실제로 CameraService는 System Service의 일부로서 이를 사용하기 위해서는 Interface로 구성된 ICameraService를 통해 Binder를 통한 프로세스 통신을 해야 합니다.


/frameworks/av/include/camera/ICameraService.h

1
2
3
4
5
6
7
8
9
10
11
12
class ICameraService : public IInterface
{
public:
....
    virtual status_t connect(const sp<ICameraClient>& cameraClient,
            int cameraId,
            const String16& clientPackageName,
            int clientUid,
            /*out*/
            sp<ICamera>& device) = 0;
....
}
cs


 ICameraService 클래스는 IInterface 클래스를 상속받고 있으며 내부에는 virtual 함수 connect()가 있습니다. 차후 connect()함수는 ICameraService 클래스를 상속하는 클래스에서 정의될 것입니다.


/frameworks/native/include/binder/IInterface.h

1
2
3
4
5
6
7
8
9
10
11
class IInterface : public virtual RefBase
{
public:
            IInterface();
            static sp<IBinder>  asBinder(const IInterface*);
            static sp<IBinder>  asBinder(const sp<IInterface>&);
 
protected:
    virtual                     ~IInterface();
    virtual IBinder*            onAsBinder() = 0;
};
cs


 IInterface 클래스가 RefBase 클래스를 상속받고 


/frameworks/rs/cpp/util/RefBase.h

1
2
3
4
5
6
7
class RefBase
{
public:
 
...
 
}
cs


 RefBase 클래스는 안드로이드 native 단계에서의 framework 내에 있는 거의 대다수의 Class의 최상 부모입니다. 안드로이드에서는 이를 통해 각 클래스의 성질을 설정하고 있다고 생각해주시면 되겠습니다.


 그렇다면 위에서 설명해드렸던 ICameraService를 상속받는 클래스는 어떤 것이 있을까요? Application 프로세스가 System Service 프로세스와 통신하기 위해서는 총 2가지의 클래스를 사용하게 됩니다. Application 프로세스에서는 BpCameraService 클래스를, System Service 프로세스에서는 BnCameraService를 통해 Application 프로세스에서 요구하는 명령을 수행하게 됩니다.

 아래 소스코드는 ICameraService 클래스를 상속받는 BpCameraService의 모습을 나타냅니다.


/frameworks/av/camera/ICameraService.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class BpCameraService: public BpInterface<ICameraService>
{
public:
    BpCameraService(const sp<IBinder>& impl)
        : BpInterface<ICameraService>(impl)
    {
    }
....
    // connect to camera service (android.hardware.Camera)
    virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId,
                             const String16 &clientPackageName, int clientUid,
                             /*out*/
                             sp<ICamera>& device)
    {
        Parcel data, reply;
        data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
        data.writeStrongBinder(cameraClient->asBinder());
        data.writeInt32(cameraId);
        data.writeString16(clientPackageName);
        data.writeInt32(clientUid);
        remote()->transact(BnCameraService::CONNECT, data, &reply);
 
        if (readExceptionCode(reply)) return -EPROTO;
        status_t status = reply.readInt32();
        if (reply.readInt32() != 0) {
            device = interface_cast<ICamera>(reply.readStrongBinder());
        }
        return status;
    }
....
}
cs

 소스코드를 보시면 아시듯이 BpCameraService는 BpInterface 클래스를 수식하는데 BpInterface 클래스는 ICameraService 클래스를 template로 설정하고 있는 것을 볼 수 있습니다. 그렇다면 BpInterface는 어떻게 생겼을까요? 


/frameworks/native/include/binder/IInterface.h
1
2
3
4
5
6
7
8
9
10
template<typename INTERFACE>
class BpInterface : public INTERFACEpublic BpRefBase
{
public:
                                BpInterface(const sp<IBinder>& remote);
 
protected:
    virtual IBinder*            onAsBinder();
};
 
cs

 놀랍게도 BpInterface는 template로 설저하였던 INTERFACE를 그대로 상속받는 것으로 설정하고 있습니다. 즉, 위의 경우 template인 INTERFACE는 ICameraService인 것이지요. 이렇게 BpInterface는 ICameraService와 BpRefBase 클래스를 다중상속 받고 있는 것입니다.

/frameworks/native/include/binder/Binder.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class BpRefBase : public virtual RefBase
{
protected:
                            BpRefBase(const sp<IBinder>& o);
    virtual                 ~BpRefBase();
    virtual void            onFirstRef();
    virtual void            onLastStrongRef(const void* id);
    virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
 
    inline  IBinder*        remote()                { return mRemote; }
    inline  IBinder*        remote() const          { return mRemote; }
 
private:
                            BpRefBase(const BpRefBase& o);
    BpRefBase&              operator=(const BpRefBase& o);
 
    IBinder* const          mRemote;
    RefBase::weakref_type*  mRefs;
    volatile int32_t        mState;
};
cs


 BpRefBase 클래스 또한 RefBase를 상속받고 있습니다. 결과적으로 BpCameraService는 RefBase를 다이아몬드 상속으로 두 번 이상 상속받고 있는 것입니다. 그 때문인지 RefBase는 virtual 클래스로 상속받는 것으로 이해할 수 있습니다.


 위에서 설명한 소스코드의 상속 구조도를 이미지로 표현하면 다음과 같습니다. native에서의 상속 구조도 나타내 보았으니 여러분들께서 BnCameraService를 직접 확인해보시면 원리를 파악하는데 큰 도움이 되실 겁니다. :)