언제 시간날 때 아래 내용에 대해서 정리가 필요할 듯....


* Android(JB)

http://processors.wiki.ti.com/index.php?title=TI-Android-JB-PortingGuide


* Android(ICS)

http://processors.wiki.ti.com/index.php/TI-Android-ICS-PortingGuide



Posted by 깍수
,

Android.mk 파일에 $(warning xxxx) 를 사용하면 해당 메세지가 출력 되는 것을 볼 수 있다.


예) 단순히 내용 출력

- Android.mk

$(warning ######  Start)


- 출력 결과

######  Start


예) 환경 변수를 확인할 경우

- Android.mk

ENABLE_WEBGL := true


$(warning ######  Check 1 WEBGL : $(ENABLE_WEBGL)  )


- 출력 결과

######  Check 1 WEBGL : true


'android > Platform' 카테고리의 다른 글

TI의 안드로이드 PortingGuide 링크  (0) 2014.12.02
Posted by 깍수
,

* 2012.07 버젼의 u-boot

해당 버젼의 u-boot의 경우 GPT를 인식 못 하기 때문에 MBR을 사용해야 하면 LK Boot의 경우 MBR과 GPT를

모두 인식 하기 때문에 어떤 것을 사용해도 무방하다.


'android' 카테고리의 다른 글

ramdisk-uboot.img 수정  (0) 2014.06.30
특정 모듈에 대해서만 build 하는 방법  (0) 2014.02.05
ODROID-A T-flash map  (0) 2013.12.24
android platform 빌드 하기  (0) 2013.12.23
[Tip] android platform version 정보 확인  (0) 2013.12.11
Posted by 깍수
,

- MTU

아래와 같이 set/getsockeopt() 함수를 이용하면 MTU(Maximum Transmission Unit) 값을 변경할 수 있으며

해당 Local socket에서만 적용이 된다. Server or Client 각각 이를 설정해야 값을 지정할 수 있다.

기본 MTU 값은 672 Bytes이며 최소 48  Bytes에서 최대 65,535 Bytes까지 적용하 수 있다.

struct l2cap_options {

uint16_t omtu;

uint16_t imtu;

uint16_t flush_to;

uint8_t mode;

};

 

int set_l2cap_mtu( int sock, uint16_t mtu ) { struct l2cap_options opts; int optlen = sizeof(opts), err; err = getsockopt( s, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen ); if( ! err ) { opts.omtu = opts.imtu = mtu; err = setsockopt( s, SOL_L2CAP, L2CAP_OPTIONS, &opts, optlen ); } return err; };


* 용어 정리

PSM : Protocol Service Multiplexer

L2CAP socket에서 사용가능한 Port는 4097~32,767(0x1001-0x7FFF)까지 홀수 번호이다.


ex) L2CAP Server&Client

1) Server

#include <stdio.h>
#include <string.h>

#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>

int main(int argc, char **argv)
{
    struct sockaddr_l2 loc_addr = { 0 }, rem_addr = { 0 };
//  struct l2cap_options opts;

    char buf[1024] = { 0 };
    int s, client, bytes_read, optlen;
    socklen_t opt = sizeof(rem_addr);

    // allocate socket
    s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);

    // bind socket to port 0x1001 of the first available
    // bluetooth adapter
    loc_addr.l2_family = AF_BLUETOOTH;
    loc_addr.l2_bdaddr = *BDADDR_ANY;
    loc_addr.l2_psm = htobs(0x1001);

    bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));

//  optlen = sizeof(opts);
//  getsockopt(s, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen);
//  printf("before omtu = %d, imtu = %d\n", opts.omtu, opts.imtu);

    // put socket into listening mode
    listen(s, 1);

    // accept one connection
    client = accept(s, (struct sockaddr *)&rem_addr, &opt);

//  optlen = sizeof(opts);
//  getsockopt(s, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen);
//  printf("after omtu = %d, imtu = %d\n", opts.omtu, opts.imtu);

    ba2str( &rem_addr.l2_bdaddr, buf );
    fprintf(stderr, "accepted connection from %s\n", buf);

    memset(buf, 0, sizeof(buf));

    // read data from the client
    bytes_read = read(client, buf, sizeof(buf));
    if( bytes_read > 0 ) {
        printf("received [%s]\n", buf);
    }

    // close connection
    close(client);
    close(s);


2) Client

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>

int set_l2cap_mtu( int sock, uint16_t mtu );

int main(int argc, char **argv)
{
    struct sockaddr_l2 addr = { 0 };
    int s, status;
    char *message = "hello!";
    char dest[18] = "01:23:45:67:89:AB";

    struct l2cap_options opts;
    int optlen;

    if(argc < 2)
    {
        fprintf(stderr, "usage: %s <bt_addr>\n", argv[0]);
        exit(2);
    }

    strncpy(dest, argv[1], 18);

    // allocate a socket
    s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);

    set_l2cap_mtu(s, 65535);

    // set the connection parameters (who to connect to)
    addr.l2_family = AF_BLUETOOTH;
    addr.l2_psm = htobs(0x1001);
    str2ba( dest, &addr.l2_bdaddr );

    // connect to server
    status = connect(s, (struct sockaddr *)&addr, sizeof(addr));

    optlen = sizeof(opts);
    getsockopt(s, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen);
    printf("after omtu = %d, imtu = %d\n", opts.omtu, opts.imtu);

    // send a message
    if( status == 0 ) {
        status = write(s, "hello!", 6);
    }

    if( status < 0 ) perror("uh oh");

    close(s);
}

int set_l2cap_mtu( int sock, uint16_t mtu ) {
    struct l2cap_options opts;
    int optlen = sizeof(opts), err;

    err = getsockopt( sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen );
    if( ! err ) {
        printf(" current omtu = %d, imtu = %d\n", opts.omtu, opts.imtu);
        opts.omtu = opts.imtu = mtu;
        err = setsockopt( sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, optlen );
    }

    return err;
}


춮처 : Bluetooth Essentials


Posted by 깍수
,

* 개발 기본 패키지

gcc, manpages-dev


* BlueZ 개발 환경

libblutooth-dev libbluetooth3 libbluetooth3-dbg, bluez-dbg

bluez-alsa bluez-gstreamer

bluez bluetooth

bluez-hcidump (디버깅)


* Old Package

bluez-utils, bluez-audio : 해당 패키지는 4.x 버전 이전에 설치한 Package이지만 이후 부터 bluez Package에 포함되었다.

Posted by 깍수
,

- RFCOMM Channel

RFCOMM의 Channel은 1~30까지 사용할 수 있으나 이상하게 1~13번 채널을 사용하면 Server에 연결 되지 않았다.

테스트 환경은 Server의 경우 PC Ubuntu을 사용하였으며 Client는 Android ICS 4.0.4 번젼의 BlueZ v4.93을 사용

하였다. 현상을 보면 Client에서 Socket 생성까지는 무리 없이 동작하며 connect() 함수를 호출하였을 경우 에러를

리턴 하였다.


- bind 함수

bind() 함수 호출시 커널 2.6.7 버전 이상에서는 Channel값을 0으로 설정하면 자동으로 값을 설정한다고 하는데… 정확히 어떤말을 하는지 모르겠음.

=> kernel source를 보면 rfcomm_sock_getname() 함수를 보면 struct sockaddr을 반환해 주는 함수가 있음.(미검토)


ex) RFCOMM Server&Client

1) Server

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>

#define USE_DYNAMIC_BIND    0   

int dynamic_bind_rc(int sock, struct sockaddr_rc *sockaddr, uint8_t *port);

int main(int argc, char **argv)
{
    struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
    char buf[1024] = { 0 };
    int s, client, bytes_read;
    uint8_t ch;
    socklen_t opt = sizeof(rem_addr);

    if (argc != 2) {
        printf("Usage:\n");
        printf("\nrfcomm-server Channel\n");
        exit(1);
    }

    ch = (uint8_t)strtol(argv[1], NULL, 10);

#if(USE_DYNAMIC_BIND == 1)
    uint8_t port;
#endif

    // allocate socket
    s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);

    // bind socket to port 1 of the first available
    // local bluetooth adapter
    loc_addr.rc_family = AF_BLUETOOTH;
    loc_addr.rc_bdaddr = *BDADDR_ANY;

#if(USE_DYNAMIC_BIND == 1)    
    dynamic_bind_rc(s, &loc_addr, &port);
    printf("Available Ch : %d\n", port);

    // put socket into listening mode
    listen(s, 1);
#else
    loc_addr.rc_channel = (uint8_t) ch;
    bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));

    // put socket into listening mode
    listen(s, 1);
#endif

    // accept one connection
    client = accept(s, (struct sockaddr *)&rem_addr, &opt);

    ba2str( &rem_addr.rc_bdaddr, buf );
    fprintf(stderr, "accepted connection from %s\n", buf);
    memset(buf, 0, sizeof(buf));

    // read data from the client
    bytes_read = read(client, buf, sizeof(buf));
    if( bytes_read > 0 ) {
        printf("received [%s]\n", buf);
    }

    // close connection
    close(client);
    close(s);
    return 0;
}


int dynamic_bind_rc(int sock, struct sockaddr_rc *sockaddr, uint8_t *port)
{
    int err;
    for( *port = 1; *port <= 31; *port++ ) {
        sockaddr->rc_channel = *port;
        err = bind(sock, (struct sockaddr *)sockaddr, sizeof(sockaddr));
        if( ! err ) break;
    }
    if( *port == 31 ) {
        err = -1;
    }
    return err;
}


2) Client

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>

int main(int argc, char **argv)
{
    struct sockaddr_rc addr = { 0 };
    int s, status;
    char dest[18] = "00:15:83:6A:02:F2";
    uint8_t ch;

    if (argc != 2) {
        printf("Usage:\n");
        printf("\nrfcomm-client Channel\n");
        exit(1);
    }

    ch = (uint8_t)strtol(argv[1], NULL, 10);

    // allocate a socket
    s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);

    // set the connection parameters (who to connect to)
    addr.rc_family = AF_BLUETOOTH;
    addr.rc_channel = (uint8_t) ch;
    str2ba( dest, &addr.rc_bdaddr );

    // connect to server
    status = connect(s, (struct sockaddr *)&addr, sizeof(addr));
    printf("status %d \n", status);

    // send a message
    if( status == 0 ) {
        status = write(s, "hello!", 6);
    }

    if( status < 0 ) perror("uh oh");

    close(s);
    return 0;
}


춮처 : Bluetooth Essentials




Posted by 깍수
,

int hci_get_route(bdaddr_t *bdaddr)

: bdaddr의 값으로 NULL값을 주면 사용가능한 Local의 첫 번째 Adapter의 Index값을 반환해 준다.

: local에 2개 이상의 Adapter가 존재하며 해당 bdaddr의 값을 알고 있을 경우 다음과 같은 함수를 사용하여 ID값을 반환 받을 수 있다.


ex) int dev_id = hci_devid("01:23:45:67:89:AB")


typedef struct {
    uint8_t b[ 6 ];
} --attribute--( (packed) ) bdaddr_t;


int hci_devid ( const char *addr ) ;


int hci_open_dev(int dev_id)

: socket을 생성하고 성공시 device descriptor를 리턴하게 된다. Error일 경우 -1을 리턴하게 된다.


int hci_inquiry(int dev_id, int len, int max_rsp, const uint8_t *lap, inquiry_info **ii, long flags)

 #2 len : inquiry 동작 시간을 설정하는 것이며 약 1.28s * len 이 된다.

 #6 flags : IREQ_CACHE_FLUSH(1)의 값이 들어가면 기존의 cache에 있는 값을 flush한 다음 다시 inquiry 하게

    되며 0의 값을 가지게 되면 Remote Device가 범위내에 없더라도 cache에 그 정보가 있다면 출력하게 된다.


typedef struct {
    bdaddr_t bdaddr;
    uint8_t pscan_rep_mode;
    uint8_t pscan_period_mode;
    uint8_t pscan_mode;
    uint8_t dev_class[3];
    uint16_t clock_offset;
} --attribute-- ( (packed) ) inquiry_info;



int hci_read_remote(int dd, const bdaddr_t *bdaddr, int len, char *name, int timout)

마지막 Paramter인 timeout의 단위는 ms 이다.


* Compile

arm-none-linux-gnueabi-gcc -o simplescan simplescan.c --static -lbluetooth


* simplescan.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

int main(int argc, char **argv)
{
    inquiry_info *ii = NULL;
    int max_rsp, num_rsp;
    int dev_id, sock, len, flags;
    int i;
    char addr[19] = { 0 };
    char name[248] = { 0 };

    dev_id = hci_get_route(NULL);
    if (dev_id < 0) {
        fprintf(stderr, "error code %d: %s\n", errno, strerror(errno));
        exit(1);
    }

    sock = hci_open_dev( dev_id );
    if (dev_id < 0 || sock < 0) {
        perror("opening socket");
        exit(1);
    }

    len  = 8;
    max_rsp = 255;
    flags = IREQ_CACHE_FLUSH;
    ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));

    num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);
    if( num_rsp < 0 ) perror("hci_inquiry");

    for (i = 0; i < num_rsp; i++) {
        ba2str(&(ii+i)->bdaddr, addr);
        memset(name, 0, sizeof(name));
        if (hci_read_remote_name(sock, &(ii+i)->bdaddr, sizeof(name), name, 0) < 0)
            strcpy(name, "[unknown]");

        printf("%s  %s\n", addr, name);
    }

    free( ii );
    close( sock );
    return 0;
}


춮처 : Bluetooth Essentials

Posted by 깍수
,

============ BlueZ ============

Bluetooth (package, service&JNI)

ㄴlibbluetooth_jni (slib) : Bluetooth JNI


* Executable

bluetoothd(bluetoothd-main) (exec) : Android BlueZ daemon, user mode일 경우 bluetoothd-main아니면 bluetoothd로 빌드 됨

bluetoothd (exec) : daemon의 wrapper로 user mode로 빌드시 생성

bluetoothd-snoop (exec)

hciattach, (exec) => vendro에 따른 코드가 따로 있음.


* Shared Lib

bluetooth.default (slib)

audio.a2dp.default (slib)

audio.sco.default (slib)

libsbc (slib)


* Debug

halttest, dbg (exec) 

mcaptest, dbg (exec)

btmon, dbg (exec)

btproxy, dbg (exec)

l2test, dbg (exec)

btmgmt, dbg (exec)

hcitool, dbg (exec)

l2ping, dbg (exec)

avtest, dbg (exec)

avinfo, dbg (exec)


* Etc

init.bluetooth.rc


============ Bluedroid ============

libbt-vendor (slib) : vendor에 따른 HW 설정관련된 코드들이 있다.


Bluetooth (package, service&JNI)

ㄴlibbluetooth_jni (slib) : Bluetooth JNI


* Bluedroid

audio.a2dp.default (slib)

libbt-hci (slib)

bluetooth.default (slib)

libbt-utils (slib)

libbt-brcm_bta (static_lib)

libbt-brcm_gki (static_lib)

libbt-brcm_stack (static_lib)


* Debug

bdt, eng (exec)


* Etc

bt_stack.conf

bt_did.conf

auto_pair_devlist.conf

Posted by 깍수
,

ramdisk-uboot.img 수정

android 2014. 6. 30. 10:43

AIL(Android Initial Language) 파일들을 수정하려면 동작 상태를 확인하려면 full android를 build하는것 보다 직접 이미지 파일(ramdisk.img)을 풀어서 수정하여 보자.


위의 ramdisk.img 파일의 위치는 out/target/product/$(product Name)/ 에서 찾아 볼 수 있다.

file ramdisk.img 하여 확인해 보면 gzip으로 압축 되어있는 것을 확인할 수 있다.

$file ramdisk.img
ramdisk.img: gzip compressed data, from Unix 


압축을 풀기 위해서 확장자를 변경한 다음 gzip을 이용하여 압축을 아래와 같이 해제 한다.

$mv ramdisk.img ramdisk.img.gz

$gzip -d ramdisk.img.gz 


이것을 다시 cpio를 이용하여 다시 풀어 내용을 확인 및 수정 한다.

 $cpio -i -F ramdisk.img


수정 완료후 다시 img 파일로 묶고 압축을 한다.

$find . | cpio -o -H newc | gzip > ../ramdisk.img


위 명령에서 gzip만 빼면 압축 없는 cpio 이미지가 만들어 진다.

$find . | cpio --quiet -o -H newc > ../rootfs.cpio


* cpio 파일을 풀기

$cd system <= 저장될 위치

 $cpio -i < (이미지 위치)/rootfs.cpio



'android' 카테고리의 다른 글

Boot loader에서 인식(GPT/MBR)  (0) 2014.10.15
특정 모듈에 대해서만 build 하는 방법  (0) 2014.02.05
ODROID-A T-flash map  (0) 2013.12.24
android platform 빌드 하기  (0) 2013.12.23
[Tip] android platform version 정보 확인  (0) 2013.12.11
Posted by 깍수
,

android$source build/envsetup.sh를 실행 하여 현재 환경을 로딩해 준다.

envsetup.sh 스크립트에서 제공해 주는 함수 중 m, mm, mmm 세 가지를 사용할 수 있다.


m : 현재 경로를 기준으로 소스 트리의 최상위 경로로 이동한 후 make를 실행해준다.

mm : 현재 경로를 기준으로 가장 가까운 단위 모듈을 찾아서 그 모듈만 build 해준다.

mmm : 파라미터로 주어진 경로들에 대해 단위 모듈 build를 해준다. 


$ mmm packages/apps/Contacts와 같이 시행하면 Contacts부분만 다시 빌드됩니다. Android.mk가 존재하는

 Path만 적용할 수 있습니다.


예) bluetooth의 Power On/Off HAL 부분 이며 아래와 같이 mm을 사용할 경우 libbluedroid.so가 생성 됨을 알 수 있다.

 tiva@luxia:/android/system/bluetooth/bluedroid$ mm -j8
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.0.4
TARGET_PRODUCT=full_luxia
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=IMM76I
============================================
make: Entering directory `/android'
target thumb C: libbluedroid <= system/bluetooth/bluedroid/bluetooth_bcm4329.c
system/bluetooth/bluedroid/bluetooth_bcm4329.c: In function 'str2ba':
system/bluetooth/bluedroid/bluetooth_bcm4329.c:271: warning: implicit declaration of function 'strtoul'
system/bluetooth/bluedroid/bluetooth_bcm4329.c: In function 'set_bluetooth_power':
system/bluetooth/bluedroid/bluetooth_bcm4329.c:111: warning: 'bt_wake' may be used uninitialized in this function
system/bluetooth/bluedroid/bluetooth_bcm4329.c:112: warning: 'bt_enable' may be used uninitialized in this function
system/bluetooth/bluedroid/bluetooth_bcm4329.c: In function 'bt_is_enabled':
system/bluetooth/bluedroid/bluetooth_bcm4329.c:84: warning: 'bt_wake' may be used uninitialized in this function
system/bluetooth/bluedroid/bluetooth_bcm4329.c:62: note: 'bt_wake' was declared here
system/bluetooth/bluedroid/bluetooth_bcm4329.c:85: warning: 'bt_enable' may be used uninitialized in this function
system/bluetooth/bluedroid/bluetooth_bcm4329.c:62: note: 'bt_enable' was declared here
target SharedLib: libbluedroid (out/target/product/luxia/obj/SHARED_LIBRARIES/libbluedroid_intermediates/LINKED/libbluedroid.so)
target Symbolic: libbluedroid (out/target/product/luxia/symbols/system/lib/libbluedroid.so)
target Strip: libbluedroid (out/target/product/luxia/obj/lib/libbluedroid.so)
Install: out/target/product/luxia/system/lib/libbluedroid.so


* make snod

앞에서 빌드된 모듈을 이용하여 system.img를 바로 생성할 수 있다.

$android make snod

tiva@luxia:~/mywork/android$ make snod
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.0.4
TARGET_PRODUCT=full_luxia
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=IMM76I
============================================
==================================================
stagefright -> Android.mk : USE_PIXTREE_STAGEFRIGHT = false
==================================================
make snod: ignoring dependencies
Target system fs image: out/target/product/luxia/system.img
in mkuserimg.sh PATH=out/host/linux-x86/bin/:/opt/java/jdk1.6.0_26/bin:/opt/Qt-4.8.4/bin:/home/tiva/bin:/opt/android/android-sdk-linux/platform-tools:/opt/openocd/bin:/opt/Xilinx/13.4/ISE_DS/ISE/bin/lin64:/opt/toolchains/arm-2010q1-linux-gnueabi/bin:/opt/java/jdk1.6.0_26/bin:/home/tiva/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/opt/workspace/odroidA/0622/android/out/host/linux-x86/bin:/opt/workspace/odroidA/0622/android/prebuilt/linux-x86/toolchain/arm-linux-androideabi-4.4.x/bin:/opt/workspace/odroidA/0622/android/development/emulator/qtools:/opt/workspace/odroidA/0622/android/prebuilt/linux-x86/toolchain/arm-linux-androideabi-4.4.x/bin:/opt/workspace/odroidA/0622/android/prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin
make_ext4fs -s -l 524288000 -a system out/target/product/luxia/system.img out/target/product/luxia/system
Creating filesystem with parameters:
    Size: 524288000
    Block size: 4096
    Blocks per group: 32768
    Inodes per group: 8000
    Inode size: 256
    Journal blocks: 2000
    Label:
    Blocks: 128000
    Block groups: 4
    Reserved block group size: 31
Created filesystem with 977/32000 inodes and 42956/128000 blocks
out/target/product/luxia/system.img total size is 169561012


'android' 카테고리의 다른 글

Boot loader에서 인식(GPT/MBR)  (0) 2014.10.15
ramdisk-uboot.img 수정  (0) 2014.06.30
ODROID-A T-flash map  (0) 2013.12.24
android platform 빌드 하기  (0) 2013.12.23
[Tip] android platform version 정보 확인  (0) 2013.12.11
Posted by 깍수
,