구글 애드센스에 로그인 했더니...

흔치않은일상 2015. 4. 3. 01:06

 블로그를 오픈한지 어느덧 9달이 되어가고 있습니다. 처음에는 개인의 공부 자료실로 만들어 보고자 시작하였던 블로그였습니다. 처음엔 일주일에 두세 분 정도 오실까 말까 할 정도로 평범했던 블로그였습니다만 제가 안드로이드에 입문하면서 제가 배우게 된 내용들을 정리해서 글을 올리기 시작하니 하루하루 방문객을 늘어나기 시작하더니 현재는 평일에 하루 100분이 다녀가는 블로그가 되어버렸습니다.. 사실 처음엔 별 생각 없었습니다만 하루하루 손님이 늘어나니 기분이 좋아지더군요 ㅎㅎ



 블로그를 통해 약간의 부수익을 나름 창출해 보고자 구글의 애드센스를 시작한지 약 반년이 되어가는 시점이었습니다. 마침 방문하시는 분들도 늘어가니 애드센스의 수익류를 확인해 보고자 애드센스에 로그인을 해보았습니다. 그러자 이전에는 본 적도 없던 붉은 색의 바가 나타나며 마치 경고문을 보는 듯한 메시지가 눈에 띄더군요.


 '주소를 확인하지 않아 지급이 보류 중입니다 [확인]'



 비록 파워블로거 분들 처럼 하루 1만명이 오는 규모의 블로그는 아니기에 큰 수익을 기대하고 지내지는 않았습니다. 나중에 블로그 문 닫을 때 남는 돈으로 안주나 해볼까 했었는데 어느덧 10달러를 돌파해 있는 광경을 보게 되었습니다. 그렇습니다. 구글 애드센스는 수익이 10달러를 넘기는 시점 즈음부터 수익금을 받을 수 있게 되는 것이지요. 이를 위해 주소 인증을 해야 하는데 제가 드디어 그 과정에 돌입하게 된 것이지요!


 정황을 보니 구글에서는 2015년 3월 30일 저에게 편지를 보낸 것으로 보입니다. 이제 구글에서 오게 될 쪽지를 하루하루 기다리는 낙도 참 재미있을 것만 같습니다. ^^


 무엇보다도 제게 이러한 기쁨을 선사해주신 제 블로그 방문자 분들께 진심으로 감사 말씀 드립니다! 비록 필력이 모자라 설명하는데에 가끔 한계를 경험하기도 합니다만 꾸준히 글을 쓰면서 연습해 나가겠습니다. 아무쪼록 제 부족한 정보들에 도움이 되섰다면 저야말로 여러분들께 감사드리고 싶습니다!

 

300x250

안드로이드 프레임워크 프로그래밍(18) [Native에서 원하는 ServiceManager 불러오기]

 요즘 안드로이드 Framework를 공부하면서 드는 생각이 있습니다. 정말 구글의 안드로이드 제작자들이 경이롭다고 생각될 정도로 안드로이드 운영체제는 심오하면서도 동작원리가 매우 신기하다는 생각이 듭니다.

 이번 포스팅에서는 Native에서 등록하였던 Service를 ServiceManager에서 불러들이는 과정에 대해 살펴보도록 하겠습니다.


 먼저 예제로 CameraService를 불러들이는 예제를 살펴보도록 하겠습니다.


/frameworks/av/camera/CameraBase.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace {
    sp<ICameraService>        gCameraService;
    const int                 kCameraServicePollDelay = 500000// 0.5s
    const char*               kCameraServiceName      = "media.camera";
 
    Mutex                     gLock;
 
    class DeathNotifier : public IBinder::DeathRecipient
    {
    public:
        DeathNotifier() {
        }
 
        virtual void binderDied(const wp<IBinder>& who) {
            ALOGV("binderDied");
            Mutex::Autolock _l(gLock);
            gCameraService.clear();
            ALOGW("Camera service died!");
        }
    };
 
    sp<DeathNotifier>         gDeathNotifier;
}; // namespace anonymous
cs

/frameworks/av/camera/CameraBase.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
// establish binder interface to camera service
template <typename TCam, typename TCamTraits>
const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService()
{
    Mutex::Autolock _l(gLock);
    if (gCameraService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16(kCameraServiceName));
            if (binder != 0) {
                break;
            }
            ALOGW("CameraService not published, waiting...");
            usleep(kCameraServicePollDelay);
        } while(true);
        if (gDeathNotifier == NULL) {
            gDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(gDeathNotifier);
        gCameraService = interface_cast<ICameraService>(binder);
    }
    ALOGE_IF(gCameraService == 0"no CameraService!?");
    return gCameraService;
}
cs

 위에서 보시는 바와 같이 ServiceManager를 통해 getService() 함수로 CameraService의 바인더를 불러오고 있는 모습을 보실 수 있습니다. 그렇다면 위의 바인더가 어떠한 방식으로 호출이 되는지 자세히 알아보도록 하겠습니다.


/frameworks/native/libs/binder/IServiceManager.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
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
 
    virtual sp<IBinder> getService(const String16& name) const
    {
        unsigned n;
        for (n = 0; n < 5; n++){
            sp<IBinder> svc = checkService(name);
            if (svc != NULL) return svc;
            ALOGI("Waiting for service %s...\n", String8(name).string());
            sleep(1);
        }
        return NULL;
    }
 
    virtual sp<IBinder> checkService( const String16& name) const
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
        return reply.readStrongBinder();
    }
....
}
cs

 IServiceManager.cpp 내에 있는 BpServiceManager를 통해 Binder를 받는 과정을 나타내고 있습니다. 위 과정을 마치게 되면 Binder를 CameraService의 Binder를 얻을 수 있게 됩니다.

 getService() 함수를 통해 부르고자 하는 Service의 명을 전달 받은 후 그 안에 있는 checkService()함수를 호출하여 Parcel을 통해 원하는 Service의 Binder를 얻게 됩니다.


혹시 위에서 Binder를 받기 위해 활용되는 Parcel에 대해 자세히 알고 싶으신 분께서는 아래 포스팅을 참조해 주시기 바랍니다.

안드로이드 프레임워크 프로그래밍(15) [Native 단계에서의 Parcel 전송(1)]

안드로이드 프레임워크 프로그래밍(16) [Native 단계에서의 Parcel 전송(2)]


여기서 chechService() 함수의 동작 과정에 대해 살펴보도록 하겠습니다.


Parcel data, reply;

전송보낼 data와 전송을 받을 reply인 Parcel 클래스 변수를 선언합니다.


data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());

data Parcel에 인터페이스 토큰을 작성합니다. 여기서 인자값으로 들어오는 값은 "android.os.IServiceManager"입니다.

위 함수에 대해 좀 더 자세히 알고 싶으신 분은 아래 포스팅을 참조해 주시기 바랍니다.


http://elecs.tistory.com/87


data.writeString16(name);

찾고자 하는 System Service의 이름을 입력합니다. 여기에서는 "media.camera"가 되겠군요.


remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);

지금까지 data에 기록하였던 내용을 Binder로 전송합니다. 이 과정을 통해 reply로  data의 내용에 대한 응답인 Parcel을 전송받게 됩니다.


return reply.readStrongBinder();

전달 받은 Parcel인 reply를 통해 Service의 BpBinder를 반환합니다.

여기서 수신된 Parcel로부터 binder를 얻는 과정을 살펴보도록 합니다.


/frameworks/native/libs/binder/Parcel.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
32
33
34
35
sp<IBinder> Parcel::readStrongBinder() const
{
    sp<IBinder> val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;
}
 
....
 
status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);
    
    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = static_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }        
    }
    return BAD_TYPE;
}
 
....
 
inline static status_t finish_unflatten_binder(
    BpBinder* proxy, const flat_binder_object& flat, const Parcel& in)
{
    return NO_ERROR;
}
cs

 위 코드의 주요 내용들을 하나씩 살펴보도록 하겠습니다.


const flat_binder_object* flat = in.readObject(false);

수신된 Parcel로부터 값을 읽어들입니다. 이 과정에서 flat_binder_object 구조체값이 넘어오게 됩니다.

이렇게 해서 넘어온 구조체값의 type는 BINDER_TYPE_HANDLE이 설정되어 있습니다.


*out = proc->getStrongProxyForHandle(flat->handle);

flat_binder_object에 저장된 서비스 핸들을 전달하여 BpBinder를 얻습니다. 위 함수의 구조는 다음과 같이 되어 있음을 확인하실 수 있습니다.

아래는 해당 함수가 동작하는 과정에 대해 확인해 보고자 하는 목적으로 첨부합니다.

위 함수에 대해 좀 더 자세한 내용을 참조하고 싶으신 분께서는 아래 포스팅을 참조해 주시길 바랍니다.


http://elecs.tistory.com/89


/frameworks/native/libs/binder/ProcessState.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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;
 
    AutoMutex _l(mLock);
 
    handle_entry* e = lookupHandleLocked(handle);
 
    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.
 
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
 
            = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }
 
    return result;
}
 
 
cs

return finish_unflatten_binder(

                    static_cast<BpBinder*>(out->get()), *flat, in);

위 과정을 통해 이 함수의 수행이 무사히 수행되었음을 NO_ERROR을 반환함으로서 종료합니다.



이제 이 즈음에서 우리들이 처음부터 연구하였던 소스 CameraBase.cpp의 getService() 이후의 동작들에 대해 파악해 보도록 합시다.


/frameworks/av/camera/CameraBase.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
// establish binder interface to camera service
template <typename TCam, typename TCamTraits>
const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService()
{
    Mutex::Autolock _l(gLock);
    if (gCameraService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16(kCameraServiceName));
            if (binder != 0) {
                break;
            }
            ALOGW("CameraService not published, waiting...");
            usleep(kCameraServicePollDelay);
        } while(true);
        if (gDeathNotifier == NULL) {
            gDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(gDeathNotifier);
        gCameraService = interface_cast<ICameraService>(binder);
    }
    ALOGE_IF(gCameraService == 0"no CameraService!?");
    return gCameraService;
}
cs


이제 위에서 남은 두 함수들에 대해 분석을 해 보도록 하겠습니다.


binder->linkToDeath(gDeathNotifier);

 getService() 함수를 통해 얻은 binder에 gDeathNotifier를 틍록합니다. linkToDeath() 함수는 해당 binder가 의도치 않은 상황에서 종료가 되었을 때 해당 클래스를 실행하라는 의도로 생각하시면 되겠습니다.


/frameworks/av/camera/CameraBase.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace {
    sp<ICameraService>        gCameraService;
    const int                 kCameraServicePollDelay = 500000// 0.5s
    const char*               kCameraServiceName      = "media.camera";
 
    Mutex                     gLock;
 
    class DeathNotifier : public IBinder::DeathRecipient
    {
    public:
        DeathNotifier() {
        }
 
        virtual void binderDied(const wp<IBinder>& who) {
            ALOGV("binderDied");
            Mutex::Autolock _l(gLock);
            gCameraService.clear();
            ALOGW("Camera service died!");
        }
    };
 
    sp<DeathNotifier>         gDeathNotifier;
}; // namespace anonymous
cs


gCameraService = interface_cast<ICameraService>(binder);

 위 코드를 통해 CameraService가 binder를 통해 등록됩니다. 이 함수가 어떻게 동작하는지 확인해 보도록 하겠습니다. 해당 함수에서 사용되는 기술에 대해 자세히 알고 싶으신 분은 아래 포스팅을 참조해 주시길 바랍니다.


http://elecs.tistory.com/87

http://elecs.tistory.com/88


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

1
2
3
4
5
6
7
8
9
#include <binder/IInterface.h>
 
class ICameraService : public IInterface
{
....
public:
    DECLARE_META_INTERFACE(CameraService);
....
}
cs

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

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
32
33
34
35
36
37
38
39
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}
....
#define DECLARE_META_INTERFACE(INTERFACE)                               \
    static const android::String16 descriptor;                          \
    static android::sp<I##INTERFACE> asInterface(                       \
            const android::sp<android::IBinder>& obj);                  \
    virtual const android::String16& getInterfaceDescriptor() const;    \
    I##INTERFACE();                                                     \
    virtual ~I##INTERFACE();                                            \
 
 
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const android::String16 I##INTERFACE::descriptor(NAME);             \
    const android::String16&                                            \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }                                   \
....
 
cs


/frameworks/av/camera/ICameraService.cpp

1
2
3
#include <binder/IInterface.h>
 
IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");
cs


 위 내용을 종합하면 다음과 같은 내용임을 알아낼 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    static android::sp<ICameraService> asInterface(
            const android::sp<android::IBinder>& obj);
 
    const android::String16 ICameraService::descriptor("android.hardware.ICameraService");
    android::sp<ICameraService> ICameraService::asInterface(
            const android::sp<android::IBinder>& obj)
    {
        android::sp<ICameraService> intr;
        if (obj != NULL) {
            intr = static_cast<ICameraService*>(
                obj->queryLocalInterface(
                        ICameraService::descriptor).get());
            if (intr == NULL) {
                intr = new BpCameraService(obj);
            }
        }
        return intr;
    } 
cs

위 코드를 통해 gCameraService 내에 BpCameraService 클래스가 선언되는 것을 확인할 수 있다.

300x250

안드로이드 프레임워크 프로그래밍(17) [Binder 드라이버 호출과정]

 안드로이드 Binder 부분을 연구하는데 상당히 많은 지식이 필요하군요. 이제는 안드로이드 Application 단계에서는 상상도 못했던 linux 시스템 프로그래밍 단계까지 내려와보니 분석 난이도도 많이 높아졌음을 실감하고 있습니다.


 이번 포스팅에서는 안드로이드 IPC 통신의 핵심이라 할 수 있는 Binder 드라이버가 호출되는 과정을 살펴보겠습니다. 본 포스팅을 이해하기 위해서는 linux 단계에서의 시스템 프로그래밍을 이해하셔야 이해가 수월하실 듯 합니다. 그럼 분석을 시작해 보도록 하겠습니다.


 Binder 드라이버는 PrecessState가 생성될 때 함께 생성되는 구조로 되어 있습니다. 아래 코드를 보면서 이해해보도록 합시다.


/frameworks/native/libs/binder/ProcessState.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
ProcessState::ProcessState()
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
        // XXX Ideally, there should be a specific define for whether we
        // have mmap (or whether we could possibly have the kernel module
        // availabla).
#if !defined(HAVE_WIN32_IPC)
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
            close(mDriverFD);
            mDriverFD = -1;
        }
#else
        mDriverFD = -1;
#endif
    }
 
    LOG_ALWAYS_FATAL_IF(mDriverFD < 0"Binder driver could not be opened.  Terminating.");
}
cs

  현재 ProcessSate가 생성되면서 Binder 드라이버와 System Service를 등록하기 위한 mVMStart 부분을 보고 계십니다. 여기서 우리는 바인더를 호출하는 open_driver() 부분을 살펴볼 것입니다.


mDriverFD(open_driver())

 open_driver() 함수는 binder의 file descriptor을 반환해줍니다. 이 함수를 통해 Binder가 호출됩니다.


/frameworks/native/libs/binder/ProcessState.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
static int open_driver()
{
    int fd = open("/dev/binder", O_RDWR);
    if (fd >= 0) {
        fcntl(fd, F_SETFD, FD_CLOEXEC);
        int vers;
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        if (result == -1) {
            ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
            close(fd);
            fd = -1;
        }
        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
            ALOGE("Binder driver protocol does not match user space protocol!");
            close(fd);
            fd = -1;
        }
        size_t maxThreads = 15;
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
        if (result == -1) {
            ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
        }
    } else {
        ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
    }
    return fd;
}
cs

 오늘 제가 주로 설명하게 될 코드입니다. 중요한 부분마다 자세하게 설명을 해 보도록 하겠습니다.


int fd = open("/dev/binder", O_RDWR);

 open() 함수를 통해 binder 파일을 생성합니다. 보시면 아시듯이 binder는 하나의 파일에 불과했다는 것을 아실 수 있습니다.


fcntl(fd, F_SETFD, FD_CLOEXEC);

 fd에 설정된 Binder를 Control 하는 함수 입니다. F_SETFD는 Binder인 fd에 FD_CLOEXEC 플래그를 설정하라는 의미입니다. 이를 설정해주었을 경우 exec를 통해 프로그램이 실행될 때 Binder가 close 되도록 해주는 역할을 합니다.


 FD_CLOEXEC 설정에 대해 좀 더 자세히 알고 싶으신 분은 아래 링크를 통해 확인바랍니다.(영문)

http://stackoverflow.com/questions/6125068/what-does-the-fd-cloexec-fcntl-flag-do


status_t result = ioctl(fd, BINDER_VERSION, &vers);

 Binder 의 입출력 설정을 바꾸어주는 역할을 합니다. BINDER_VERSION은 요청 코드 번호를 말하며, &vers는 포인터를 보내 값을 얻기 위해 사용합니다.


/bionic/libc/kernel/common/linux/binder.h

1
2
3
4
5
6
struct binder_version {
 signed long protocol_version;
};
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
....
#define BINDER_VERSION _IOWR('b'9struct binder_version)
cs


/bionic/libc/kernel/common/asm-generic/ioctl.h

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
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define _IOC_SIZEBITS 14
#define _IOC_DIRBITS 2
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
#define _IOC_NONE 0U
#define _IOC_WRITE 1U
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define _IOC_READ 2U
#define _IOC(dir,type,nr,size)   (((dir) << _IOC_DIRSHIFT) |   ((type) << _IOC_TYPESHIFT) |   ((nr) << _IOC_NRSHIFT) |   ((size) << _IOC_SIZESHIFT))
extern unsigned int __invalid_size_argument_for_IOC;
#define _IOC_TYPECHECK(t)   ((sizeof(t) == sizeof(t[1]) &&   sizeof(t) < (1 << _IOC_SIZEBITS)) ?   sizeof(t) : __invalid_size_argument_for_IOC)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
....
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
cs

size_t maxThreads = 15;
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
 Binder의 최대 쓰레드 수를 15개로 한 후 이를 ioctl을 통해 설정해주는 모습입니다.

return fd;

return을 통해 Binder의 File Description을 반환해 줍니다.

300x250