포럼 회원으로 등록하신분만 다운로드가 가능합니다. 최대 업로드 가능한 용량은 20MB 입니다.

AudioPolicyService와 AudioFlinger 및 HAL의 연결 구조

 

글쓴이 : 김재훈 (이솝 임베디드 포럼)

 

안드로이드 AudioPolicyService와 AudioFlinger의 연결 구조에 대한 코드를 트레이스하여 간단하게 정리해 봤습니다. :)

도움되시길 바랍니다.

 

1. main_mediaserver.cpp => AudioPolicyService::instantiate(); 명령으로 서비스가 초기화 된다.
   frameworksbasemediamediaservermain_mediaserver.cpp
  
2. frameworksbaselibsaudioflingerAudioPolicyService.cpp // 424라인
   void AudioPolicyService::instantiate(); 함수에서 서비스가 활성화 된다.

 

AudioPolicyService의 바인더 구조

 

1. AudioPolicyService는 AudioSystem에서 Binder를 통해 호출한다.


2. AudioSystem.cpp에서 Binder를 통해 AudioPolicyService를 호출하면, 

    IAudioPolicyService.cpp의 onTranct(); 함수를 통하여, Binder에 대한 응답이 이루어진다.

 

- 참고소스 : frameworks/base/media/AudioSystem.cpp : 447라인

 

// client singleton for AudioPolicyService binder interface
sp<IAudioPolicyService> AudioSystem::gAudioPolicyService
sp<AudioSystem::AudioPolicyServiceClient> AudioSystem::gAudioPolicyServiceClient;

// establish binder interface to AudioPolicy service
const sp<IAudioPolicyService>& AudioSystem::get_audio_policy_service()
{
    gLock.lock();
    if (gAudioPolicyService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16("media.audio_policy"));
            if (binder != 0)
                break;
            LOGW("AudioPolicyService not published, waiting...");
            usleep(500000); // 0.5 s
        } while(true);
        if (gAudioPolicyServiceClient == NULL) {
            gAudioPolicyServiceClient = new AudioPolicyServiceClient();
        }
        binder->linkToDeath(gAudioPolicyServiceClient);
        gAudioPolicyService = interface_cast<IAudioPolicyService>(binder);
        gLock.unlock();
    } else {
        gLock.unlock();
    }
    return gAudioPolicyService;
}


3. AudioSystem에서의 Binder에 대한 처리는 다음과 같은 형식으로 이루어진다.

 

status_t AudioSystem::setDeviceConnectionState(audio_devices device,
                                                  device_connection_state state,
                                                  const char *device_address)
{
    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    if (aps == 0) return PERMISSION_DENIED;

    return aps->setDeviceConnectionState(device, state, device_address);
}


 4. 위와 같이 AudioSystem::get_audio_policy_service();에 대한 리턴형을 <IAudioPolicyService>으로 캐스팅하여,
   사용하기 때문에, 소스코드 추적이 잘 되지 않는다.
   실제적인 AudioPolicyService에 대한 Binder onTranct(); 함수는 frameworks/base/medis/IAudioPolicyService.cpp에

   위치하게 된다.

 

- 참고소스 : frameworks/base/media/libmedia/IAudioPolicyService.cpp : 58라인

 

    virtual status_t setDeviceConnectionState(
                                    AudioSystem::audio_devices device,
                                    AudioSystem::device_connection_state state,
                                    const char *device_address)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
        data.writeInt32(static_cast <uint32_t>(device));
        data.writeInt32(static_cast <uint32_t>(state));
        data.writeCString(device_address);
        remote()->transact(SET_DEVICE_CONNECTION_STATE, data, &reply);
        return static_cast <status_t> (reply.readInt32());
    }

 5. 결론적으로 AudioSystem.cpp에서 setDeviceConnectionState();를 호출했을 경우, IAudioPolicyService.cpp의
   setDeviceConnectionState(); 함수에서 Binder를 이용한 실제 Transaction이 발생하게 되며, 이것을 AudioPolicySerivce에서
   받아서 처리하게 된다.

 

6. AudioPolicyService에서의 Binder Transaction의 처리

   AudioPolicyService에서의 Binder Transaction의 처리는 다음의 코드와 같으며,
   reply->writeInt32(static_cast <uint32_t>(setDeviceConnectionState(device, state, device_address)));를 호출하며,
   이것은 실제로, AudioPolicyService.cpp의 setDeviceConnectionState(); 함수가 호출되는 것이다.    
  
- 참고소스 1 : frameworks/base/medis/libmedia/IAudioPolicyService.cpp : 252라인

 

status_t BnAudioPolicyService::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        case SET_DEVICE_CONNECTION_STATE: {
            CHECK_INTERFACE(IAudioPolicyService, data, reply);
            AudioSystem::audio_devices device = static_cast <AudioSystem::audio_devices>(data.readInt32());
            AudioSystem::device_connection_state state = static_cast <AudioSystem::device_connection_state>(data.readInt32());
            const char *device_address = data.readCString();
            reply->writeInt32(static_cast <uint32_t>(setDeviceConnectionState(device, state, device_address)));
            return NO_ERROR;
        } break;
.
.
.
}

 - 참고소스 2 : frameworksbaselibsaudioflingerAudioPolicyService.cpp // 107라인

 

status_t AudioPolicyService::setDeviceConnectionState(AudioSystem::audio_devices device,
                                                  AudioSystem::device_connection_state state,
                                                  const char *device_address)
{
    if (mpPolicyManager == NULL) {
        return NO_INIT;
    }
    if (!checkPermission()) {
        return PERMISSION_DENIED;
    }
    if (!AudioSystem::isOutputDevice(device) && !AudioSystem::isInputDevice(device)) {
        return BAD_VALUE;
    }
    if (state != AudioSystem::DEVICE_STATE_AVAILABLE && state != AudioSystem::DEVICE_STATE_UNAVAILABLE) {
        return BAD_VALUE;
    }

    LOGV("setDeviceConnectionState() tid %d", gettid());
    Mutex::Autolock _l(mLock);
    return mpPolicyManager->setDeviceConnectionState(device, state, device_address);
}


 7. 위의 참고소스 2에서 유의해야 할 점은 시스템의 퍼미션을 검사하는 부분이며, 접근 퍼미션이 거부될 경우
   시스템 부팅이 다운되게 된다. 오디오에 대한 퍼미션 설정은 system/core/init/devices.cpp 에서 설정을 해줘야 한다.

 

static struct perms_ devperms[] = {
    { "/dev/null",          0666,   AID_ROOT,       AID_ROOT,       0 },
    { "/dev/zero",          0666,   AID_ROOT,       AID_ROOT,       0 },
    { "/dev/full",          0666,   AID_ROOT,       AID_ROOT,       0 },
    { "/dev/ptmx",          0666,   AID_ROOT,       AID_ROOT,       0 },
    { "/dev/tty",           0666,   AID_ROOT,       AID_ROOT,       0 },
    { "/dev/random",        0666,   AID_ROOT,       AID_ROOT,       0 },
    { "/dev/urandom",       0666,   AID_ROOT,       AID_ROOT,       0 },
    { "/dev/ashmem",        0666,   AID_ROOT,       AID_ROOT,       0 },
    { "/dev/binder",        0666,   AID_ROOT,       AID_ROOT,       0 },
     .
     .
     .
    { "/dev/snd/pcmC0D0c",  0660,   AID_SYSTEM,     AID_AUDIO,      0 }, // ghcstop fix for ALSA
    { "/dev/snd/pcmC0D0p",  0660,   AID_SYSTEM,     AID_AUDIO,      0 },
    { "/dev/snd/controlC0", 0660,   AID_SYSTEM,     AID_AUDIO,      0 },
    { "/dev/snd/timer",     0660,   AID_SYSTEM,     AID_AUDIO,      0 },
    { NULL, 0, 0, 0, 0 },
};

 8. 위의 참고소스 2에서 return mpPolicyManager->setDeviceConnectionState(device, state, device_address);의 경우,
   HAL 부분을 호출하는 부분이며, Audio Device의 종류에 따라 mpPolicyManager에 붙는 HAL의 종류를 선택하여 할당한다.
   mpPolicyManager를 할당하는 부분은 다음과 같으며, AudioPolicyService 클래스를 생성할 때 초기화된다.

 

- 참고소스 : frameworksbaselibsaudioflingerAudioPolicyService.cpp // 64라인

 

AudioPolicyService::AudioPolicyService()
    : BnAudioPolicyService() , mpPolicyManager(NULL)
{
    char value[PROPERTY_VALUE_MAX];

    // start tone playback thread
    mTonePlaybackThread = new AudioCommandThread();
    // start audio commands thread
    mAudioCommandThread = new AudioCommandThread();

#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)  // BoardConfig.mk에서 GENERIC_AUDIO를 선택했을 경우
    mpPolicyManager = new AudioPolicyManagerGeneric(this);
    LOGV("build for GENERIC_AUDIO - using generic audio policy");
#else
    // if running in emulation - use the emulator driver
    if (property_get("ro.kernel.qemu", value, 0)) {
        LOGV("Running in emulation - using generic audio policy");
        mpPolicyManager = new AudioPolicyManagerGeneric(this);  // System Property에서 qemu가 선택되었을 경우
    }
    else {
        LOGV("Using hardware specific audio policy");   // Audio 관련 HAL을 사용하는 경우
        mpPolicyManager = createAudioPolicyManager(this);
    }
#endif

    // load properties
    property_get("ro.camera.sound.forced", value, "0");
    mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value);
}


 9. mpPolicyManager = createAudioPolicyManager(this); 에서 createAudioPolicyManager();는 ALSA HAL에 위치해 있으며,
   다음의 소스코드를 참조한다.

 

- 참고소스 : hardware/alsa_sound/AudioPolicyManager // 1207라인

 

extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
{
    return new AudioPolicyManagerALSA(clientInterface); // AudioPolicyClientInterface 클래스를 상속 받아서, ALSA에서
                                                           AudioPolicyManagerALSA 클래스 초기에, 각종 오디오 인터페이스에 대한
                                                           설정사항을 등록한다.
}

 

AudioPolicyManagerALSA::AudioPolicyManagerALSA(AudioPolicyClientInterface *clientInterface)
: mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0)
{
    mpClientInterface = clientInterface;

    for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
        mForceUse[i] = AudioSystem::FORCE_NONE;
    }

    // devices available by default are speaker, ear piece and microphone
    mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
                        AudioSystem::DEVICE_OUT_SPEAKER;
    mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;

    mA2dpDeviceAddress = String8("");
    mScoDeviceAddress = String8("");

    // open hardware output
    AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
    outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
    mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
                                    &outputDesc->mSamplingRate,
                                    &outputDesc->mFormat,
                                    &outputDesc->mChannels,
                                    &outputDesc->mLatency,
                                    outputDesc->mFlags);

    if (mHardwareOutput == 0) {
        LOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d",
                outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
    } else {
        mOutputs.add(mHardwareOutput, outputDesc);
        setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
    }

    mA2dpOutput = 0;
    mDuplicatedOutput = 0;
}


 10. AudioPolicyManager와 AudioPolicySerivce 사이의 함수 매핑

 

다음 AudioPolicyServe는 참고소스1 에서 mpPolicyManager 클래스 포인터를 할당받아, 사용하며,
AudioPolicyManagerALSA.h와 1:1 매핑이 된다.

 

- 참고소스1 : frameworksbaselibsaudioflingerAudioPolicyService.cpp // 64라인

 

AudioPolicyService::AudioPolicyService()
    : BnAudioPolicyService() , mpPolicyManager(NULL)
{
    char value[PROPERTY_VALUE_MAX];

    // start tone playback thread
    mTonePlaybackThread = new AudioCommandThread();
    // start audio commands thread
    mAudioCommandThread = new AudioCommandThread();

#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)  // BoardConfig.mk에서 GENERIC_AUDIO를 선택했을 경우
    mpPolicyManager = new AudioPolicyManagerGeneric(this);
    LOGV("build for GENERIC_AUDIO - using generic audio policy");
#else
    // if running in emulation - use the emulator driver
    if (property_get("ro.kernel.qemu", value, 0)) {
        LOGV("Running in emulation - using generic audio policy");
        mpPolicyManager = new AudioPolicyManagerGeneric(this);  // System Property에서 qemu가 선택되었을 경우
    }
    else {
        LOGV("Using hardware specific audio policy");   // Audio 관련 HAL을 사용하는 경우
        mpPolicyManager = createAudioPolicyManager(this);
    }
#endif

    // load properties
    property_get("ro.camera.sound.forced", value, "0");
    mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value);
}


 참고소스2 : hardware/alsa_sound/AudioPolicyManagerALSA.h // 45라인

 

        // AudioPolicyInterface
        virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device,
                                                          AudioSystem::device_connection_state state,
                                                          const char *device_address);
        virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device,
                                                                              const char *device_address);
        virtual void setPhoneState(int state);
        virtual void setRingerMode(uint32_t mode, uint32_t mask);
        virtual void setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config);
        virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage);
        virtual void setSystemProperty(const char* property, const char* value);
        virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream,
                                            uint32_t samplingRate,
                                            uint32_t format,
                                            uint32_t channels,
                                            AudioSystem::output_flags flags);
        virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
        virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
        virtual void releaseOutput(audio_io_handle_t output);
        virtual audio_io_handle_t getInput(int inputSource,
                                            uint32_t samplingRate,
                                            uint32_t format,
                                            uint32_t channels,
                                            AudioSystem::audio_in_acoustics acoustics);
        // indicates to the audio policy manager that the input starts being used.
        virtual status_t startInput(audio_io_handle_t input);
        // indicates to the audio policy manager that the input stops being used.
        virtual status_t stopInput(audio_io_handle_t input);
        virtual void releaseInput(audio_io_handle_t input);
        virtual void initStreamVolume(AudioSystem::stream_type stream,
                                                    int indexMin,
                                                    int indexMax);
        virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index);
        virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index);

        virtual status_t dump(int fd);


 11. hardware/alsa_sound/AudioPolicyManagerALSA.h에서 AudioPolicyClientInterface 클래스에 대한 각종 함수들은
    AudioPolicyInterface에서 AudioPolicyService의 요청사항을 대응할 때 내부적으로 이용하게 된다.
    따라서, 실제 Android쪽의 인터페이스는 AudioPolicyClientInterface 클래스이며, AudioPolicyClientInterface
    부분 부터는 H/W에 의존적인 완전한 ALSA HAL단이 된다.
    따라서, H/W를 디버깅하려면 이 부분을 수정하면 된다.

 

- 참고소스 : hardware/alsa_sound/AudioPolicyManagerALSA.cpp

 

1) 내부적으로 사용되는 AudioPolicyClientInterface 클래스의 getOutputForDevice(uint32_t device) 함수의 경우 다음과 같다.

 

audio_io_handle_t AudioPolicyManagerALSA::getOutputForDevice(uint32_t device)
{
    audio_io_handle_t output = 0;
    uint32_t lDevice;

    for (size_t i = 0; i < mOutputs.size(); i++) {
        lDevice = mOutputs.valueAt(i)->device();
        LOGV("getOutputForDevice() output %d devices %x", mOutputs.keyAt(i), lDevice);

        // We are only considering outputs connected to a mixer here => exclude direct outputs
        if ((lDevice == device) &&
           !(mOutputs.valueAt(i)->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) {
            output = mOutputs.keyAt(i);
            LOGV("getOutputForDevice() found output %d for device %x", output, device);
            break;
        }
    }
    return output;
}


 2) 이것은 AudioPolicyInterface 클래스의 setDeviceConnectionState(); 함수에서, 다음과 같이 사용하는 것을 볼 수있다.

 

status_t AudioPolicyManagerALSA::setDeviceConnectionState(AudioSystem::audio_devices device,
                                                  AudioSystem::device_connection_state state,
                                                  const char *device_address)
{

    LOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address);

    // connect/disconnect only 1 device at a time
    if (AudioSystem::popCount(device) != 1) return BAD_VALUE;

    if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) {
        LOGE("setDeviceConnectionState() invalid address: %s", device_address);
        return BAD_VALUE;
    }

    // handle output devices
    if (AudioSystem::isOutputDevice(device)) {

#ifndef WITH_A2DP
        if (AudioSystem::isA2dpDevice(device)) {
            LOGE("setDeviceConnectionState() invalid device: %x", device);
            return BAD_VALUE;
        }
#endif

        switch (state)
        {
        // handle output device connection
        case AudioSystem::DEVICE_STATE_AVAILABLE:
            if (mAvailableOutputDevices & device) {
                LOGW("setDeviceConnectionState() device already connected: %x", device);
                return INVALID_OPERATION;
            }
            LOGW_IF((getOutputForDevice((uint32_t)device) != 0), "setDeviceConnectionState(): output using unconnected device %x", device);

            LOGV("setDeviceConnectionState() connecting device %x", device);
 .
 .
 .
 .
 .


 12. 따라서 AudioPolicyInterface에 대한 호출 구조는 다음과 같다.
    AudioPolicyManagerALSA => AudioPolicyInterface => AudioPolicyService => AudioSystem.cpp
    AudioPolicyManagerALSA의 경우, ALSA 오디오에 대한 라우팅 경로를 설정하는데 사용되며,

    실제 소리의 출력등은 AudioFlinger에서 수행한다.

 

13. AudioPolicyService와 AudioFlingerService의 연관성

    다음은 HAL을 연동하는 AudioPolicyServiceManagerALSA에서, 샘플링 레이트와, 포멧, 체널, 주기 및 플래그를 사용할 때,
    AudioFlinger와 연동되는 예이다.
    참고소스1에서과 같이 mpClientInterface->openOutput을 사용하고 있으며,
    이것은 AudioPolicyService.cpp에서 참고소스2에서와 같이 사용된다.

 

- 참고소스1 : hardware/alsa_sound/AudioPolicyManagerALSA.cpp // 1217라인

 

AudioPolicyManagerALSA::AudioPolicyManagerALSA(AudioPolicyClientInterface *clientInterface)
: mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0)
{
    mpClientInterface = clientInterface;

    for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
        mForceUse[i] = AudioSystem::FORCE_NONE;
    }

    // devices available by default are speaker, ear piece and microphone
    mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
                        AudioSystem::DEVICE_OUT_SPEAKER;
    mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;

    mA2dpDeviceAddress = String8("");
    mScoDeviceAddress = String8("");

    // open hardware output
    AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
    outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
    mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
                                    &outputDesc->mSamplingRate,
                                    &outputDesc->mFormat,
                                    &outputDesc->mChannels,
                                    &outputDesc->mLatency,
                                    outputDesc->mFlags);

    if (mHardwareOutput == 0) {
        LOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d",
                outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
    } else {
        mOutputs.add(mHardwareOutput, outputDesc);
        setOutputDevice(mHardwareOutput, (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER, true);
    }

    mA2dpOutput = 0;
    mDuplicatedOutput = 0;
}


- 참고소스2 : frameworks/base/libs/audioflinger/AudioPolicyService.cpp // 435라인

 

# 아래 부분에서와 같이 AudioSystem::get_audio_flinger();를 할당하여 audioFlinger 측으로 Binder를 이용해

   함수를 호출하게 된다.

 

audio_io_handle_t AudioPolicyService::openOutput(uint32_t *pDevices,
                                uint32_t *pSamplingRate,
                                uint32_t *pFormat,
                                uint32_t *pChannels,
                                uint32_t *pLatencyMs,
                                AudioSystem::output_flags flags)
{
    sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
    if (af == 0) {
        LOGW("openOutput() could not get AudioFlinger");
        return 0;
    }

    return af->openOutput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, pLatencyMs, flags);
}


 14. 결론적으로 AudioPolicyManager에서의 ALSA H/W에 대한 접근은 AudioFlinger를 이용하게 된다.              

 

profile

인생은 연극이고 세상은 무대이다!

이솝 임베디드 포럼 운영 및 비즈니스와 관련된 것 이외에 E-Mail이나 메신저 및 휴대폰 등을 통한 개인적인 질문 및 답변은 받지 않습니다. 문의 사항은 이솝 임베디드 포럼 게시판을 이용해 주시면 감사하겠습니다.

엮인글 :
http://www.aesop.or.kr/index.php?mid=Board_Documents_Android_Frameworks&document_srl=34865&act=trackback&key=392
List of Articles
번호 제목 글쓴이 날짜 조회 수
64 Google 의 새로운 코덱 VP8 Video Codec 규격자료를 올려 봅니다... file [1] 장석원 2010-05-24 14955
63 [실습-03] android source compile시 battery 부분 patch하기 [7] 고도리 2010-05-24 15897
62 [실습-02] android에서 new product 만들고 compile하기 file [7] 고도리 2010-05-18 13223
61 [이론-00] Android의 개요와 구성 part0 [2] 고도리 2010-05-17 12471
60 [실습-01] android 개발환경 세팅과 source download [2] 고도리 2010-05-17 13781
59 [실습-00] android eclair porting을 위한 ubuntu 설치하기 file [6] 고도리 2010-05-17 12321
58 AudioSystem과 AudioSystem.java를 이용한 JNI 단에서의 연결 [1] JhoonKim 2010-04-14 10871
» AudioPolicyService와 AudioFlinger 및 HAL의 연결 구조 JhoonKim 2010-04-08 11294
56 Android 2.x AudioFlinger와 HAL의 연결 구조 분석 [2] JhoonKim 2010-04-08 19343
55 busybox에 대한 질문 [3] 득드로이드 2010-03-24 8881
54 누가 Android 에 tslib를 포팅해 놓은듯 합니다. file 최종환 2009-11-11 11062
53 Android Battery 부분 조금 정리해 놓은것 file [2] 최종환 2009-11-04 10726
52 Power Management from Linux Kernel to Android file [10] 최종환 2009-11-01 14996
51 이솝 임베디드 포럼 - 10월 31일 Google Android Seminar 발표 ... file [16] 관리자 2009-10-28 10898
50 Dummy Battery 드라이버 입니다. file [3] 관리자 2009-10-10 10263
49 [참고] Android wifi howto - 아직 테스트는 다 못했습니다. file [10] 고도리 2009-09-22 21723
48 [번역] Android Camera Framework 번역 file [7] 고도리 2009-09-10 19665
47 Android home key is not working(home key 동작 안 할경우 ) [1] 고현철 2009-09-10 10597
46 S3C6410 프로세서용 Andoird Kernel 2.6.29 이식 방법 #1 [2] 김재훈 2009-09-04 15823
45 삼성 안드로이드폰(갤럭시) 리눅스 커널 소스코드 file [3] 유형목 2009-09-01 15909

사용자 로그인