git 요약

old/Git 2010. 7. 27. 18:19

# Permission Request
# If you want to join p500_froyo Project, plz follow permission process.
[Korean] http://cc.lge.com/newcc/xe/9204
[English] http://cc.lge.com/newcc/xe/12239

# Registering Public key
# 1.  $ ssh-keygen -t rsa
# 2.  $ cat ~/.ssh/id_rsa.pub  (copy contents)
# 3. log on to gerrit site http://165.243.137.39:8091/p500_froyo
# 4. Settings -> SSH Keys -> Add Key -> Paste your publick key -> Add
# 5. copy Server Host Key and register known_hosts

# 사전 설정
git config --global user.name "hong.hyunjun"
git config --global user.email "hong.hyunjun@lge.com"

# 권한 확인
# ssh -p 29439 hong.hyunjun@165.243.137.26
ssh -p 29421 hong.hyunjun@165.243.137.39
("Connection to 165.243.137.26 closed."  is OK result.)

# 로그인
# repo init -u ssh://hong.hyunjun@165.243.137.26:29439/p500/manifest.git -b lge_p500
repo init -u ssh://hong.hyunjun@165.243.137.39:29421/p500_froyo/manifest.git -b p500_froyo_master

# 최신 소스 다운로드
repo sync

# lge_gw740 <= branch 명
# git pull 및 push 가능
# repo start lge_p500 --all
repo start p500_froyo_master --all

# 소스 위치
# /home/hong.hyunjun/git/vs660/.repo/project.list 참조
cd /home/hong.hyunjun/git/p500/android/packages/apps/FmRadio

# 소스 반영 전에 build를 해주는 습관을 기르자!!
# IFmRadioControlService.java copy
# src/*
# res/*
# Android.mk
# AndroidManifest.xml

# 다수의 git 프로젝트로 구성된 경우 각 git 프로젝트를 검색하여 변경파일을 출력함
repo status

# 현재 Add / Commit 또는 새로 추가된 파일들의 목록을 보여 줌
git status

# 수정된 소스 가져오기 (push, commit 사전 절차)
git pull

# 수정된 소스를 인덱스에 올리기
git add .
git rm aaa.png
git rm . (현 폴더 모든거)
git rm -r 폴더이름(해당 폴더 전체)

# 반영된 소스 승인 ("comment")
git commit -m "20100722 fixed BroadCom Stack, bind FM_RECEIVER_SERVICE and seekStation available"
git commit -m "20100726 enable to volume control for FmRadio on Lockscreen and sleep"
git commit -m "20100726 fixed ON/OFF and modify seek thresh to 105 and add string"
git commit -m "20100726 modify ON/OFF button image"

# 수정된 소스 올리기 (수정된 소스 덮어치기)
git push

# 빌드
cd /home/kim.taejung/C710/gw740/android

# optional
make clean

# build
# make update-api
# ./build_target.sh
./build_target.sh -j16

# classes : out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar

# 변경된 file들을 Head 상태로 원복
git checkout -f

# 해당 테그로 소스 다운로드
repo forall -c git checkout P500_20100718_V08e_pre1_QCT6013
repo forall -c git checkout P500_20100720_V08e_final
repo forall -c git reset --hard P500_20100720_V08e_final
repo forall -c git reset --hard P500_20100718_V08e_pre1_QCT6013
repo forall -c git reset --hard P500_20100717_V08d_pre2_temp


adb shell
su
cat /proc/kmsg

'old > Git' 카테고리의 다른 글

Repo 설치 및 설정  (0) 2011.01.14
git 작업취소  (0) 2010.08.07
Subversion Git 사용법  (0) 2010.05.14
Eclipse Plugins  (0) 2010.02.01
Git과 SVN 통합  (0) 2010.02.01
Posted by jazzlife
,
; 핸들러로 지연시키지 않으면 안 뜬다...이상해...

Handler keyPadHandler = new Handler();
            keyPadHandler.postDelayed(new Runnable() {
                public void run() {
                    if(mRecipientsEditor!= null){
                    if(mRecipientsEditor.getVisibility()== View.VISIBLE){
                        InputMethodManager inputMethodManager =
                            (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);       
                     inputMethodManager.showSoftInput(mRecipientsEditor, 0);
                    }
                    }
                }
            }, 100);

'old > sms&mms' 카테고리의 다른 글

SMS solicite_eclair  (0) 2010.07.22
Internals of Telephony subsystem  (0) 2010.07.09
The Radio Interface Layer (RIL)  (0) 2010.07.09
Android's Radio Interface Layer(RIL)  (0) 2010.07.08
Complete End to End Call Flow of Short Message Service(SMS)  (0) 2010.07.08
Posted by jazzlife
,

SMS solicite_eclair

old/sms&mms 2010. 7. 22. 12:12
[ComposeMessageActivity.java]
confirmSendMessageIfNeeded()
sendMessage()

[WorkingMessage.java]
send()
sendSmsWorker(conv, msgText)

[SmsMessageSender.java]
sendMessage(threadId)

- frameworks
[SmsManager.java]
1.sendMultipartTextMessage(mDests[i], mServiceCenter, messages, sentIntents, deliveryIntents)
2.sendTextMessage()

[IccSmsInterfaceManager.java] - ISms.aidl (ISms_Stub.java)
권한승인 -  
PhoneBase.getContext().enforceCallingPermission("android.permission.SEND_SMS","Sending SMS message");
1.sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents)
2.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent)

[GsmSMSDispatcher.java] - extended SMSDispatcher
1.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,(ArrayList<PendingIntent>) sentIntents,
(ArrayList<PendingIntent>) deliveryIntents)
   SmsMessage.getSubmitPdu(scAddress, destinationAddress,parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader),encoding)
   sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent)

2.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent)
   SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddr, destAddr, text, (deliveryIntent != null))
   SMSDispatcher.sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent)


Posted by jazzlife
,

sms

old/용어정리 2010. 7. 13. 13:44
TPDU is an acronym for Transport Protocol Data Unit.

UCS : Universal Character Set characters
AT Command : AT 명령어의 원래 명칭은 헤이즈 명령어(Hayes command) 입니다.
미국의 헤이즈 마이크로컴퓨터(Hayes Microcomputer products)사의 스마트 모뎀 및 그 호환 모뎀을 제어하기 위하여 사용되는 명령어 랍니다.
현재 사실상의 표준으로 되어있는 거의 모든 모뎀이 사용하고 있습니다.
통상 명령어가 AT로 시작하기 때문에 AT 명령어라 불리우게 되었습니다.
여기서 AT는 "주목"의 의미를 가지는 attention의 약자 라고 합니다.

'old > 용어정리' 카테고리의 다른 글

BPR (Business Process Re-engineering) ; 업무 재설계  (0) 2010.11.04
bottleneck ; 병목현상  (0) 2010.11.04
1  (0) 2010.07.07
Refactoring - Martin P.  (0) 2010.04.26
Network  (0) 2010.03.24
Posted by jazzlife
,

Internals of Telephony Subsystem

1.1. Part I, the key items

Assumption: We are in the top level of Android source code repository

  • Files
    frameworks\base\telephony  the core of the telephony subsystem implementation, GSM phone and so on
    packages\apps\Phone        Phone application implementation, UI related stuffs reside
    hardware\ril               Radio reference implementation

  • The big picture

  • The key interfaces and classes

    1. Phone interfaces
      Phone.java:Base abstraction of the phone

      frameworks\base\telephony\java\com\android\internal\telephony\Phone.java

      PhoneBase.java:An abstract class which extends Phone interface and implements the Event registration and notification

      frameworks\base\telephony\java\com\android\internal\telephony\PhoneBase.java

      GSMPhone.java:A concrete phone implementation

      frameworks\base\telephony\java\com\android\internal\telephony\gsm\GSMPhone.java

      CommandsInterface.java:Radio operation abstraction

      frameworks\base\telephony\java\com\android\internal\telephony\gsm\CommandsInterface.java

      BaseCommands.java:Radio event registration and notification

      frameworks\base\telephony\java\com\android\internal\telephony\gsm\BaseCommands.java

      RIL.java: Radio interface library implementation, responsible for sending request to Radio layer and
      distribute the response from Radio layer to request sender
       

      frameworks\base\telephony\java\com\android\internal\telephony\RIL.java

      Native stub for RIL.java: communicates with RIL.java through local socket

      hardware\ril\libril\*

      Reference implementation of Radio library:communicates with Radio hardware through AT command

      hardware\ril\reference-ril\*

      Radio interface lib daemon runs as a stand alone process

      hardware\ril\rild\*

    2. RIL request and response
      1, Solicited and Unsolicited response
      Solicited response response to the upper layer request, this is a kind of passive response,
      for instance, response the request of Singal strength request
      Unsolicited response a response triggered by the underlying hardware, for instance, the state change
      Radio will trigger an RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED response to notify the upper layer

    3. Request sending and handling
      RIL_Sender thread
      gets the reqeust from mRequestsList and send it to Radio reference library through local socket
      RIL Receiver thread
      receives the response from Radio reference library and process it with processUnsolicited()/processSolicited()
      according to response type RESPONSE_UNSOLICITED/RESPONSE_SOLICITED
      processUnsolicited() is responsible for processing active event from Radio layer
      processSolicited() is responsible for processing passive response to upper layer request

    4. Message identity localization and Parcel
      The mechanism used here is localize the identity of message and put it into a Parcel, send it to the target,
      the target receives the Parcel, put the wanted stuffs into Parcel and return back it to the sender, it is
      the sender's responsibility to classify and handle the response corresponds to the specific request.

      We explain this trick by an example, User dialed a number
      *A user's dialing activity runs a long trip to get to the point dial() in RIL.java
      * RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
           send(rr);
           put the RIL_REQUEST_DIAL ID into Parcel embedded in rr and send it to the target, the target here is      reference-ril.c
      * refernece-ril.c receives the request and handle it with requestDial()
      * requestDial() completes it work and send response back by RIL_onRequestComplete()
      * The implementation of RIL_onRequestComplete() resides in ril.cpp
           p.writeInt32 (RESPONSE_SOLICITED);
           p.writeInt32 (pRI->token);
           put the response type to RESPONSE_SOLICITED, because the request is initiated by upper level, and it      is a passive response, put the RIL_REQUEST_DIAL into the Parcel (pRI->token == RIL_REQUEST_DIAL) for      upper layer to handle the response to the RIL_REQUEST_DIAL request.
      * thanks to the local socket, the control flow arrived at RIL.java again, this time it hits at
           RIL_Receiver(),for the reason of RIL_Receiver's listing on SOCKET_NAME_RIL socket
           the RIL_Receiver delegates the response to the processSolicited(), it was responsibility of
           processSolicited() to notify the registrarant according the content of the response.

'old > sms&mms' 카테고리의 다른 글

soft keyboard 출력 방법  (0) 2010.07.22
SMS solicite_eclair  (0) 2010.07.22
The Radio Interface Layer (RIL)  (0) 2010.07.09
Android's Radio Interface Layer(RIL)  (0) 2010.07.08
Complete End to End Call Flow of Short Message Service(SMS)  (0) 2010.07.08
Posted by jazzlife
,

The Radio Interface Layer (RIL)

Introduction

Android's Radio Interface Layer (RIL) provides an abstraction layer between Android telephony services (android.telephony) and radio hardware. The RIL is radio agnostic, and includes support for Global System for Mobile communication (GSM)-based radios.

The diagram below illustrates the RIL in the context of Android's Telephony system architecture.

Solid elements represent Android blocks and dashed elements represent partner-specific proprietary blocks.

The RIL consists of two primary components:

  • RIL Daemon: The RIL daemon initializes the Vendor RIL, processes all communication from Android telephony services, and dispatches calls to the Vendor RIL as solicited commands.
  • Vendor RIL: The radio-specific Vendor RIL of ril.h that processes all communication with radio hardware and dispatches calls to the RIL Daemon (rild) through unsolicited commands.

The RIL Shim Layer

Radio Interface Layer (RIL)

  • HAL interface between the Android TelephonyManager and the baseband modem
  • Voice, Data, SMS, SIM, SIMToolkit
  • Android includes a reference AT command based RIL

RIL API

  • Currently 75 requests (e.g. Dial) and 17 unsolicited responses (e.g. signal strength)
  • Android RIL inspired by GSM TS 27.007
  • Can implement as AT command based or proprietary protocol (e.g. RPCs via shared memory)

Enhancing the RIL

  • Carriers may require specific features (e.g. JCDMA, O2 Homezone)
  • Qualcomm working on adding CDMA and Multi-mode support

Certification

  • GCF
  • CDG

Tips

  • SIM and SIM Toolkit is complex, get an expert
  • RIL needs to provide neighboring cell id (non-standard). Used by Google Maps.
  • Data: suggest packet interface, no DHCP, no PPP.
  • Depending on modem interface, RIL may get complicated with lots of logic/state.
  • Android data is always-on, modem should support dormancy

Architecture overview

Linux手机打电话代码分析(Android) - clarkluo2004 - 天翼的博客

 
RIL
Initialization

Android initializes the telephony stack and the Vendor RIL at startup as described in the sequence below:

  1. RIL daemon reads rild.lib path and rild.libargs system properties to determine the Vendor RIL library to use and any initialization arguments to provide to the Vendor RIL
  2. RIL daemon loads the Vendor RIL library and calls RIL_Init to initialize the RIL and obtain a reference to RIL functions
  3. RIL daemon calls RIL_register on the Android telephony stack, providing a reference to the Vendor RIL functions

See the RIL Daemon source code at /device/commands/rild/rild.c for details.

RIL Interaction

There are two forms of communication that the RIL handles:

  • Solicited commands: Solicited commands originated by RIL lib, such as DIAL and HANGUP.
  • Unsolicited responses: Unsolicited responses that originate from the baseband, such as CALL_STATE_CHANGED and NEW_SMS.

Solicited

The following snippet illustrates the interface for solicited commands:

void OnRequest (int request_id, void *data, size_t datalen, RIL_Token t);
void OnRequestComplete (RIL_Token t, RIL_Error e, void *response, size_t responselen);

There are over sixty solicited commands grouped by the following families:

  • SIM PIN, IO, and IMSI/IMEI (11)
  • Call status and handling (dial, answer, mute…) (16)
  • Network status query (4)
  • Network setting (barring, forwarding, selection…) (12)
  • SMS (3)
  • PDP connection (4)
  • Power and reset (2)
  • Supplementary Services (5)
  • Vendor defined and support (4)

The following diagram illustrates a solicited call in Android:

Unsolicited

The following snippet illustrates the interface for unsolicited commands:

void OnUnsolicitedResponse (int unsolResponse, void *data, size_t datalen);

There are over ten unsolicited commands grouped by the following families:

  • Network status changed (4)
  • New SMS notify (3)
  • New USSD notify (2)
  • Signal strength or time changed (2)

The following diagram illustrates an unsolicited call in Android:

Implementing the RIL

To implement a radio-specific RIL, create a shared library that implements a set of functions required by Android to process radio requests. The required functions are defined in the RIL header (/include/telephony/ril.h).

The Android radio interface is radio-agnostic and the Vendor RIL can use any protocol to communicate with the radio. Android provides a reference Vendor RIL, using the Hayes AT command set, that you can use as a quick start for telephony testing and a guide for commercial vendor RILs. The source code for the reference RIL is found at /commands/reference-ril/.

Compile your Vendor RIL as a shared library using the convention libril-<companyname>-<RIL version>.so, for example, libril-acme-124.so, where:

  • libril: all vendor RIL implementations start with 'libril'
  • <companyname>: a company-specific abbreviation
  • <RIL version>: RIL version number
  • so: file extension

RIL_Init

Your Vendor RIL must define a RIL_Init function that provides a handle to the functions which will process all radio requests. RIL_Init will be called by the Android RIL Daemon at boot time to initialize the RIL.

RIL_RadioFunctions *RIL_Init (RIL_Env* env, int argc, char **argv);

RIL_Init should return a RIL_RadioFunctions structure containing the handles to the radio functions:

type structure {
	int RIL_version;
	RIL_RequestFunc onRequest;
	RIL_RadioStateRequest onStateRequest;      
	RIL_Supports supports;
	RIL_Cancel onCancel;
	RIL_GetVersion getVersion;
} 
RIL_RadioFunctions;

RIL Functions

ril.h defines RIL states and variables, such as RIL_UNSOL_STK_CALL_SETUP, RIL_SIM_READY, RIL_SIM_NOT_READY, as well as the functions described in the tables below. Skim the header file (/device/include/telephony/ril.h) for details.

RIL Solicited Command Requests

The vendor RIL must provide the functions described in the table below to handle solicited commands. The RIL solicited command request types are defined in ril.h with the RIL_REQUEST_ prefix. Check the header file for details.

void (*RIL_RequestFunc)(int request, void *data, size_t datalen, RIL_Token t);

This is the RIL entry point for solicited commands and must be able to handle the various RIL solicited request types defined in ril.h with the RIL_REQUEST_ prefix.

  • request is one of RIL_REQUEST_*
  • data is pointer to data defined for that RIL_REQUEST_*
  • t should be used in subsequent call to RIL_onResponse
  • datalen is owned by caller, and should not be modified or freed by callee

Must be completed with a call to RIL_onRequestComplete(). RIL_onRequestComplete() may be called from any thread before or after this function returns. This will always be called from the same thread, so returning here implies that the radio is ready to process another command (whether or not the previous command has completed).|

RIL_RadioState (*RIL_RadioStateRequest)();

This function should return the current radio state synchronously.

int (*RIL_Supports)(int requestCode);

This function returns “1” if the specified RIL_REQUEST code is supported and 0 if it is not.

void (*RIL_Cancel)(RIL_Token t);

This function is used to indicate that a pending request should be canceled. This function is called from a separate thread–not the thread that calls RIL_RequestFunc.

On cancel, the callee should do its best to abandon the request and call RIL_onRequestComplete with RIL_Errno CANCELLED at some later point.

Subsequent calls to RIL_onRequestComplete for this request with other results will be tolerated but ignored (that is, it is valid to ignore the cancellation request).

RIL_Cancel calls should return immediately and not wait for cancellation.

const char* (*RIL_GetVersion)(void);

Return a version string for your Vendor RIL

The vendor RIL uses the following callback methods to communicate back to the Android RIL daemon.

void RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen);

* t is parameter passed in on previous call to RIL_Notification routine.

  • If e != SUCCESS, then response can be null and is ignored
  • response is owned by caller, and should not be modified or freed by callee
  • RIL_onRequestComplete will return as soon as possible

void RIL_requestTimedCallback (RIL_TimedCallback callback, void *param, const struct timeval *relativeTime);

Call user-specified callback function on the same thread that RIL_RequestFunc is called. If relativeTime is specified, then it specifies a relative time value at which the callback is invoked. If relativeTime is NULL or points to a 0-filled structure, the callback will be invoked as soon as possible.

RIL Unsolicited Commands

The functions listed in the table below are call-back functions used by the Vendor RIL to invoke unsolicited commands on the Android platform. See ril.h for details.

void RIL_onUnsolicitedResponse(int unsolResponse, const void *data, size_t datalen);

* unsolResponse is one of RIL_UNSOL_RESPONSE_*

  • data is pointer to data defined for that RIL_UNSOL_RESPONSE_*
  • data is owned by caller, and should not be modified or freed by callee

The ril source code in userspace

Usefull links

'old > sms&mms' 카테고리의 다른 글

SMS solicite_eclair  (0) 2010.07.22
Internals of Telephony subsystem  (0) 2010.07.09
Android's Radio Interface Layer(RIL)  (0) 2010.07.08
Complete End to End Call Flow of Short Message Service(SMS)  (0) 2010.07.08
Android SMS call analysis  (0) 2010.07.06
Posted by jazzlife
,
RIL 이란 무엇인가?
 
개요.
 
안드로이드 RIL은 android.telephony 서비스들과 radio h/w간의 추상화된 계층을 제공한다.
RIL은 radio agnostic(존재하지만 정확하게 표현하기는 어려운 실체)이며, GSM 기반의 radio들에 대한 지원을 포함하고
있다.
 
아래의 그림은 안드로이드 전화통화 시스템 구조를 설명한다.
 
   
 
위의 그림에서 Radio Daemon과 Vender RIL은 파트너의 특수성을 가진 사적소유영역에 속하며, 나머지 블록은
안드로이드 영역에 속한다고 할 수 있다.
 
RIL은 두가지의 기본적인 컴포넌트로 구성된다.
 
RIL 데몬(Daemon) : 
 
RIL 데몬은 Vender RIL을 초기화하며, 안드로이드 telephony 서비스들로부터의 모든 통신을 프로세스하며,
solicited 명령어를 통해 Vender RIL로 콜을 넘긴다.
 
Vender RIL :
 
ril.h의 radio 특수성을 포함하는 Vender RIL은 radio h/w와의 모든 통신을 처리하며, unsolicited 명령어들을 통해서
RIL 데몬(rild)에 콜을 넘긴다.



 
RIL 초기화
 
안드로이드는 구동시점에 아래에 언급된 순서대로, telephony 스택과 Vender RIL 을 초기화한다.
 
1. RIL 데몬은 Vender RIL에 제공해야할 초기화 argument와 그것을 사용하기 위한 Vender RIL 라이브러리를
   결정하기 위한 rild.lib 경로와 rild.libargs 시스템 속성들을 읽어들인다.
 
2. RIL 데몬은 Vender RIL 라이브러리를 load(탑재)하며, RIL을 초기화하기 위해 RIL_Init을 호출하며,
    RIL 함수에 대한 참조를 얻는다.
 
3. RIL 데몬은 Vender RIL 함수들에 대한 하나의 참조를 제공하는 안드로이드 Telephony 스택상의  
    RIL_register를 호출한다.
 
RIL 상호작용
 
RIL 제어 관련 통신에는 두가지 형식이 존재한다.
 
Solicited commands : Solicited 명령어들은 전화걸기(DIAL) 과 HANGUP(전화끊기)와 같은 RIL 라이브러리에 의해
                                만들어 진다.
Unsolicited responses : Unsolicited responses는 CALL_STATE_CHNGED(전화 상태 변화)와 NEW_SMS(새 문자
                                   메시지)와 같은 baseband로 부터 만들어 진다.

 
Solicited
 
아래의 인용은 solicited 명령어들에 대한 인터페이스를 보여주고 있다.
 
void OnRequest (int request_id, void *data, size_t datalen, RIL_Token t);
void OnRequestComplete (RIL_Token t, RIL_Error e, void *response, size_t responselen);

 
그리고 아래와 같이 계열들로 묶여질 수 있는 60개 이상의 solicited 명령어들이 존재한다.
 
  • SIM PIN, IO, and IMSI/IMEI (11)
  • Call status and handling (dial, answer, mute…) (16)
  • Network status query (4)
  • Network setting (barring, forwarding, selection…) (12)
  • SMS (3)
  • PDP connection (4)
  • Power and reset (2)
  • Supplementary Services (5)
  • Vendor defined and support (4)
아래의 다이어그램은 안드로이드에서의 solicited call을 설명하고 있다.
 
 
 
Unsolicited
 
아래의 인용은 unsolicited 명령어들에 대한 인터페이스를 설명하고 있다.
 
void OnUnsolicitedResponse (int unsolResponse, void *data, size_t datalen);

 
그리고 아래와 같이 계열들로 묶여질 수 있는 10개 이상의 unsolicited 명령어들이 존재한다.
 
  • Network status changed (4)
  • New SMS notify (3)
  • New USSD notify (2)
  • Signal strength or time changed (2)
아래의 다이어그램은 안드로이드에서의 unsolicited call을 설명하고 있다.
 
 
 
RIL 구현하기
 
특정 radio RIL을 구현하기 위해서는, 라디오 요청을 처리하기 위해 안드로이드에서 요구되는 함수의 집합을 구현하는
공유 라이브러리를 만들어야 한다. 요구되는 함수들은 RIL header (/include/telephony/ril.h)에 정의된다.
 
안드로이드 radio 인터페이스는 radio-agnostic이며, Vender RIL은 radio를 통해 통신하기 위한 어떤 프로토콜도
사용할 수 있다. 안드로이드는 하나의 참조적 구현형태로써의 Vender RIL을 제공하고 있는데, 그것은 Hayes AT
command 집합을 사용하고 있으며, 여러분들은 telephony 테스트를 위한 쉬운 시작과 상업적 Vender RIL 들을 위한
가이드로써 사용할 수 있다. 참조적으로 구현된 RIL 소스 코드는 /commands/reference-ril/ 에서 볼 수 있다.
 
libril-<companyname>-<RIL version>.so 규약을 사용해서 공유 라이브러리 형태로 여러분의 Vender RIL을 컴파일
해라. 여기에 예로써, libril-acme-124.so가 있다.
  • libril: 모든 vender RIL 구현은 'libril' 로 시작
  • <companyname>: 특정 회사의 약어
  • <RIL version>: RIL 버전 숫자
  • so: 파일 확장자
RIL_Init
 
여러분의 Vender RIL은 모든 radio 요청들을 처리할 함수들에 대한 제어를 제공하는 RIL_Init 함수를 정의해야 한다.
RIL_Init은 RIL을 초기화하기 위해, 부트시점에서 안드로이드 RIL Daemon에 의해 호출된다.
 
RIL_RadioFunctions *RIL_Init (RIL_Env* env, int argc, char **argv);

 
RIL_Init은 radio 함수들에 대한 제어를 포함하는 RIL_RadioFuncions 구조체를 반환해야 한다.
 
type structure {
       int RIL_version;
       RIL_RequestFunc onRequest;
       RIL_RadioStateRequest onStateRequest;      
       RIL_Supports supports;
       RIL_Cancel onCancel;
       RIL_GetVersion getVersion;
}
RIL_RadioFunctions;

 
 
RIL 함수들
 
ril.h는 아래의 테이블에 설명된 함수들 같은, RIL_UNSOL_STK_CALL_SETUP, RIL_SIM_READY, RIL_SIM_NOT_READY 과
같은 RIL 상태들과 변수들을 정의한다. 더 상세하게는 헤더파일(/device/incldue/telephony/ril.h)을 살펴보길.
 
RIL Solicited 명령어 요청들.
 
Vender RIL은 solicited 명령어들을 처리하기 위해 아래의 테이블에 설명된 함수들을 제공해야 한다.
RIL solicited 명령어 요청 타입들은 RIL_REQUEST_ 라는 접두사를 가지고 ril.h에 정의된다.
상세하게는 헤더 파일을 체크하길.
 
함수명 설명
void (*RIL_RequestFunc) (int request, void *data, size_t datalen, RIL_Token t); 이것은 solicited 명령어들에 대한 시작 포인트이며, RIL_REQUST_ 접두사를 가지고 ril.h에 정의된 다양한 RIL solicited 요청 타입들을 제어할 수 있어야 한다.
  • request 는 RIL_REQUEST_* 중 하나임.
  • data 는 RIL_REQUST_* 위해 정의된 data에 대한 포인터임.
  • t 는 RIL_onResponse 에 수반되는 호출에서 사용되어야 함.
  • datalen 은 caller 소유이며, callee에 의해 수정되거나 free 되어서는 안됨.
RIL_onRequestComplete()에 대한 호출을 통해 완료되어야 함.
RIL_onRequestComplete()는 임의의 쓰레드로부터 이 함수의 반환 이전 또는 이후에 호출될 수 있다. 이것은 항상 동일한 쓰레드로부터 호출될 것이며, 그러므로 여기에서 반환이라함은 radio가 다른 명령을 수행할 준비하 되어있음을 의미한다.(이전 명령이 완료여부와 상관없이)
RIL_RadioState (*RIL_RadioStateRequest)(); 이 함수는 현재의 radio 상태를 동기적으로 반환해야 한다.
int (*RIL_Supports)(int requestCode); 이 함수는 명시된 RIL_REQUEST 코드가 지원된다면 "1"을 아니라면 "0"을 반환한다.
void (*RIL_Cancel)(RIL_Token t); 이 함수는 멈춰져 있는 요청을 취소시키기 위해 사용된다.
이 함수는 다른 쓰레드, 즉 RIL_RequestFunc 을 호출한 쓰레드가 아닌 쓰레드에 의해 호출된다.
 
취소시, callee는 요청을 포기하기 위해 최선을 다해야하며, 잠시후 RIL_Errno CALCELLED 를 가지고 RIL_onRequestComplete를 호출한다.
 
다른 결과들을 가진 이 요청들에 대한 RIL_onRequestComplete에 대한 수반되는 호출들은 묵인되지만, 무시된다.(그것은 취소 요청을 무시하기 위해 유효한 것이다)
 
RIL_Cancel 호출들은 즉시 반환되어야 하며, 취소를 기다려서는 안된다.
const char * (*RIL_GetVersion) (void); 여러분의 Vender RIL에 대한 버젼 문자열을 반환한다.
 
Vender RIL은 안드로이드 RIL 데몬에 역으로 통신하기 위해 다음과 같은 callback 메쏘드를 사용한다.
 
함수명 설명
void RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen);
  • t 는 이전 호출에서 RIL_Notification 루틴에 전달된 파라메터이다.
  • 만약 e != SUCCESS 라면, response 는 null 이 될것이며, 무시된다.
  • RIL_onRequestComplete 는 가능한 빨리 return 할 것이다.
void RIL_requestTimedCallback (RIL_TimedCallback callback, void *param, const struct timeval *relativeTime); RIL_RequestFunc 이 호출된 동일한 쓰레드에서 사용자 정의의 callback 함수를 호출한다. 만약 relativeTime 이 명시되었다면, 그것은 callback이 발생해야하는 상대적인 시각을 명시한다. 만약 relativeTime 이 NULL 이거나 0 으로 채워진 구조체에 대한 포인터라면, callback은 최대한 빨리 발생한다.

 
RIL Unsolicited 명령어들
 
아래의 테이블에 열거된 함수들은 안드로이드 플랫폼에서 unsolicited 명령어들을 발생시키기 위해 Vendor RIL에 의해
사용되는 call-back 함수들이다. 자세하게는 ril.h 를 보길.
 
함수명 설명
void RIL_onUnsolicitedResponse(int unsolResponse, const void *data, size_t datalen);
  • unsolResponse 는 RIL_UNSOL_RESPONSE_* 중 하나임.
  • data 는 RIL_UNSOL_RESPONSE_* 를 위해 정의된 data에 대한 포인터임.
  • data 는 caller 소유이며, callee 에 의해 수정되거나 free 되어서는 안됨.

이건 뭐 간단히 말해서 안드로이드 소프트웨어(Telephony service)와 하드웨어(Radio) 사이에 존재하는 놈(추상레이어)으로  인터페이스 역할을 한다.
  • Ex) SMS Sending/Receiving, Incomming/Outgoing call, Data, Network and so on.
  • 두가지 Command로 나눈다.

       1. Solicited : User가 요청한 command (SMS Sending, Outgoing call 등)

          - 101 개 정도 commands : (Ril_commands.h 에 정의)

       2. Unsolicited : User의 의도와 상관없이 network나 h/w에서 받는 command (SMS Retriving, Incomming call 등)

          - 31개 정도 (Ril_unsol_commands.h에 정의)

     

    getIMSI() : ril.java - {RIL_REQUEST_GET_IMSI, dispatchVoid, responseString}

    - dispatchVoid() : ril.cpp

    -- s_callbacks.onRequest(pRI->pCI->requestNumber, NULL, 0, pRI); //Vendor ril

    -- RIL_onRequestComplete : ril.cpp

    ---- responseFunction() : responseString()

    ---- sendResponse(p)

    - processResponse : ril.java

    processSolicited : ril.java

     

    Posted by jazzlife
    ,
     Complete End to End Call Flow of Short Message Service(SMS)

    [ad#co-1]

     We will use above picture as refference.

    For example,  Subscriber A(MS A) want to send Short message to subscriber B(MS B)

    1.  Then subscriber A will type the message on the phone and select subscriber B as the destination address.
    2.  After subsriber A, hit send button, MS terminal will request channel assignment to the BTS and BSC and then forward the MS A Information to MSC A
    3.  MSC A will check MS A(subscriber A) data from VLR, if the telecommunication operator has many SMSC, it will identified which SMSC this MS A belong to.
    4.  After that MS A will transfer the short message to MSC A, at this point on MS A phone will display “sending…” notification
    5.  MSC A routes the message to the interworking MSC(IW MSC), beside use IW MSC usually it can use STP
    6.  IW MSC will route the short message to the SMSC.
    7.  SMSC Send a notification report to IW MSC and then to MSC A, MSC B and come to subscriber. At this point MS A phone will display “Message Sent” but not yet delivered to MS B
    8.  All Above step called MO(Message Originating), we will come on MT(Message Terminating) on the next step
    9.  SMSC Transfer the short message to the Gateway MSC(GW MSC)
    10.  The GW MSC ask information from the HLR for routing the short message
    11.  GW MSC will forward the message to MSC B, and MSC B will check MS B data from VLR
    12.  MSC B Transfer the short message to MS B through BSC and BTS
    13.  MSC B Send delivery report to GW MSC, and GW MSC will forward to HLR and SMSC. At this point MS B receive the message and can read it.
    14.  Delivery report will send by SMSC to MS A after MS B receive the message.

    Quite log story  :) . but this simple thing need so many interworking between quite alot of telecommunication network element.

    'old > sms&mms' 카테고리의 다른 글

    The Radio Interface Layer (RIL)  (0) 2010.07.09
    Android's Radio Interface Layer(RIL)  (0) 2010.07.08
    Android SMS call analysis  (0) 2010.07.06
    기본 안드로이드 서비스 구조  (0) 2010.05.12
    SMS framework 소스 참고  (0) 2010.05.10
    Posted by jazzlife
    ,

    1

    old/용어정리 2010. 7. 7. 12:09

    glibc : GNU C 라이브러리(줄여서 glibc)는 GNU 프로젝트의 일환으로 만든 C 표준 라이브러리의 한
                 종류이다.

    EABI : EABI(임베디드 응용 프로그램 이진 인터페이스, embedded-application binary
                interface)는 임베디드 소프트웨어 프로그램을 수행하는 파일 형식, 자료형, 레지스터 사
                용, 스택 프레임 조직, 명령 변수를 위한 표준 변환을 정의하고 있다.

                 EABI를 지원하는 컴파일러는 이와 같은 또다른 컴파일러가 만든 코드와 호환되는 목적코드를
                 만드므로 개발자들이 다른 컴파일러가 만들어 낸 목적 코드와 한 컴파일로로 만
                 든 라이브러리를 연결할 수 있게 한다. 자체적인 어셈블리어 코드를 작성하는 개발자
                 들은 EABI를 사용하여 호환 컴파일러가 만들어낸 어셈블리 코드와 상호 작용하게 할 수
                 있다.

                 일반 목적의 운영 체제에서 ABI와 EABI의 추된 차이점은 권한 명령이 응용 프로그램 코
                 드 내에서 실행되는 점, 동적 링크가 요구되지 않는다는 점(심지어는 아예 동적 링크를
                 허용하지 않는 경우도 있음), 메모리 저장을 위해 더 작아진 스택 프레임 조직을 갖춘다
                 는 점이 있다.[1]

                 널리 쓰이는 EABI로는 파워피씨[2]ARM 아키텍처[3][4]가 있다.
                
    primitive : 컴퓨터 프로그램 작성에서 어떤 복잡한 프로그램을 만드는 데 사용될 수 있는 언어의 가장 기본적인 
                    단위, 즉 문자, 숫자, 요소 등.

    marshalling : 마샬링은 하나 이상의 프로그램 또는 연속되어 있지 않은 저장 공간으로부터 데이터를 모은 
                          다음, 데이터들을 메시지
    버퍼에 집어넣고, 특정 수신기나 프로그래밍 인터페이스에 맞도록 그 
                          데이터를 조직화하거나, 미리 정해진 다른 형식으로 변환하는 과정을 말한다.


    Daemon (computer software): a background process

    'old > 용어정리' 카테고리의 다른 글

    bottleneck ; 병목현상  (0) 2010.11.04
    sms  (0) 2010.07.13
    Refactoring - Martin P.  (0) 2010.04.26
    Network  (0) 2010.03.24
    MMC/MNC 란?  (0) 2010.02.10
    Posted by jazzlife
    ,

    Android Flow Diagram

    old/Basic 2010. 7. 6. 14:59
     
    Android Anatomy
    Linux Kernel
    • Android는 Linux Kernel을 기반으로 하고 있다.  왜 Linux System이 아닌 Kernel인가?
      • 어찌보면 당연하다. Mobile Device 상에서 full linux 필요없다.  하지만 Android를 넷북이나 UMPC 등으로 영역을 확장한다면 좀 다른 얘기가 될지도 모른다
      • Android is not Linux
        • Android는 Linux Kernel을 기반으로 하는 Mobile Platform이라고 정의하길 선호한다.
      • Native windowing system, glibc support(gnu c library), 표준 linux utilities 등을 포함하지 않고 있다.
        • 일종의 file system 및 하드웨어를 제어하기 위한 드라이버 등, 최소한의 Linux를 채용하고 있다고 볼 수 있다.
        • Android는 Linux Kernel의 안정적인 System 운영 측면을 가져온 것이다.  Linux의 지속적인 업데이트 또한 장점이 되었을 것이다.
        •       


      • Linux 2.6.24를 기반으로 하였으나 최근 발표된 Android 1.5에서는 Linux 2.6.29로 업그레이드 됨
      • Kernel enhancements를 통해 Android를 지원
        • Alarm / Ashmen(Android shared memory) / Binder / Power Management / Low Memory Killer / Kernel Debugger / Logger
      • Why Linux kernel?
        • Great memory and process management
        • Permissions-based security model
        • Proven driver model
        • Support for shared libraries
        • It's already open source
      • Application과 Service는 별개의 process에서 동작을 하지만 상호 communicate 하거나 data를 공유할 필요가 있다.  이는 IPC (Inter Process Communication)를 통해 지원 : Binder
        • High performance through shared memory
        • Per-process thread pool for processing requests
        • Reference counting, and mapping of object reference across processes
        • Synchronous calls between processes
        • AIDL(Android Interface Definition Language)
      • PM (Power Management) Solution
        • 기본적으로 Linux Power Management를 기반으로 구성 (on top of it)
        • More aggressive power management policy - 결국 좀 더 타이트한 policy를 통해 pm을 한다는 내용
        • Components make requests to keep the power on throught "wake locks"
        • Support different types of wake locks
        • Android.os.PowerManager - use wake locks carefully!

    Native Libraries
    • Bionic Libc
      •  What is - 일종의 c library로 android에서는 다음과 같은 이유로 standard c lib가 아닌 bionic libc를 쓰기로 함
        • Bionic은 custom libc implementation, optimized for embedded use
        • License 문제 - standard c lib는 GPL이므로 사용자는 자신의 code를 open 해야 함으로 이로부터 자유롭게 하기 위해
          • BSD License
        • Size - android에서는 will load in each process 해야 함으로, so it needs to be small
          • Small size and fast code paths
        • Fast - mobile device와 같은 한정된 CPU에 최적화되어 빠르다
          • Very fast and small custom pthread implementation
        • 단점 or 장점?
          • Doesn't support certain POSIX features
          • Not compatible with Gnu Libc (glibc)
          • All native code must be compiled against bionic
    • Function Libraries
      • WebKit - 현재까지 알려진 Web 엔진 가운데 가장 괜찮음 : 애플사파리(아이폰포함), Nokia 등이 WebKit 기반 Web 엔진 사용
        • Based on open source WebKit browser
        • Renders pages in full (desktop) view
        • Full CSS, Javascript, DOM, AJAX support
        • Support for single-column and adative view rendering
      • Media Framework
        • Based on PacketVideo OpenCORE platform
        • Supports standard video, audio, still-frame formats
        • Support for hardware/software codec plug-ins - 기본 지원외에 format은 plug-in을 통해 또는 hardware accelerator등이 장착된 mobile device에도 plug-in을 사용하여 fully 지원할 수 있다.
      • SQLite
        • Light-weight transactional data store
        • Back end for most platform data storage
    • Native Servers
      • Surface Flinger
        • Provides system-wide surface "composer", handling all surface rendering to frame buffer device
        • Can combine 2D and 3D surfaces and surfaces from multiple applications
        • Surfaces passed as buffers via Binder IPC calls
        • Can use OpenGL ES and 2D hardware accelerator for its compositions
        • Double-buffering using page-flip
      • Audio Flinger
        • Manages all audio output devices
        • Processes multiple audio streams into PCM audio out paths
        • Handles audio routing to various outputs
    • Hardware Abstraction Libraries
      • User space C/C++ library layer
      • Defines the interface that Android requires hardware "drivers" to implement
      • Separates the Android platform logic from the hardware interface
      • Why do we need a user-space HAL? - HAL 영역이 왜 필요한가 : 당연 - Linux에서 kernel driver가 존재할 텐데 왜 굳이 Android용 HAL을 제공하는가에 대한 문제
        • Not all components have standardized kernel driver interface - 현재 제공되는 Linux system 상에서 모든 component의 driver interface에 대한 표준화가 되어있는 것은 아니다
        • Kernel drivers are GPL which exposes any proprietary IP - kernel driver는 현재 GPL로 되어 있어 그대로 사용하게 되면 연계 소스코드에 대해 오픈을 해야 한다
        • Android has specific requirements for hardware drivers

    Android Runtime
    • Dalvik Virtual Machine
      • 사용자에게 Java를 이용해 app을 작성하게 하고 이러한 Java platform 기반 app을 모바일 device상에서 동작하게 하기 위한 최적의 환경을 제공하기 위해 기존의 Java VM과는 별도로 Google이 제공하는 VM이라고 할 수 있다
      • 일반 VM과는 다음과 같은 다른 특징을 가지고 있다
        • The VM was slimmed down to use less space
        • Dalvik has no Just-in-time compiler
        • The constant pool has been modified to use only 32-bit indexes to simplify the interpreter
        • It uses its own bytecode, not Java bytecode
      • Android's custom clean-room implementation virtual machine
        • Provides application portability and runtime consistency
        • Runs optimized file format (.dex) and Dalvik bytecode
        • Java .class/.jar files converted to .dex at build time
      • Designed for embedded environment
        • Supports multiple virtual machine processes per device
        • Highly CPU-optimized bytecode interpreter
        • Uses runtime memory very efficiently
    • Core Libraries
      • Core APIs for Java language provide a powerful, yet simple and familiar development platform
      • Data structures, Utilities, File access, Network Access, Graphics, …

    Application Framework
    • Core Platform Services
      • Services that are essential to the Android platform
      • Behind the scenes - applications typically don’t access them directly
      • Activity Manager, Package Manager, Window Manager, Resource Manager, Content Providers, View System
    • Hardware Services
      • Provide access to lower-level hardware APIs
      • Typically accessed through local Manager object
        • LocationManager lm = (LocationManager) Context.getSystemService(Context.LOCATION_SERVICE);
      • Telephony Service, Location Service, Bluetooth Service, WiFi Service, USB Service, Sensor Service

    Android Physiology

    Start-up Walkthrough

    • Runtime Walkthrough
      • It all starts with init… - similar to most Linux-based systems at startup, the bootloader loads the Linux kernel and starts the init process.
      • Init starts Linux daemons, including:
        • USB Daemon (usbd) to manage USB connections
        • Android Debug Bridge (adbd) to manage ADB connections
        • Debugger Daemon (debuggerd) to manage debug processes requests (dump memory, etc.)
        • Radio Interface Layer Daemon (rild) to manage communication with the radio
      • Init process starts the zygote process:
        • A nascent process which initializes a Dalvik VM instance
        • Loads classes and listens on socket for requests to spawn VMs
        • Forks on request to create VM instances for managed processes
        • Copy-on-write to maximize re-use and minimize footprint
      • Init starts runtime process:
        • Initializes Service Manager - the context manager for Binder that handles service registration and lookup
        • Registers Service Manager as default context manager for Binder services
      • Runtime process sends request for Zygote to start System Service
        • Zygote forks a new VM instance for the System Service process and starts the service
        • System Service starts the native system servers, including:
          • Surface Flinger, Audio Flinger
      • Native system servers register with Service Manager as IPC service targets:
      • System Service starts the Android managed services:
      • Android managed Services register with Service Manager:
      • After system server loads all services, the system is ready..
      • Each subsequent application is launched in it's own process

    Layer Interaction
    • There are 3 main flavors of Android layer cake:
      • App -> Runtime Service -> lib
      • App -> Runtime Service -> Native Service -> lib
      • App -> Runtime Service -> Native Daemon -> lib
    • App -> Runtime Service -> lib
    • App -> Runtime Service -> Native Service -> lib
    • App -> Runtime Service -> Native Daemon -> lib

    'old > Basic' 카테고리의 다른 글

    안드로이드 AIDL 문법(Android AIDL Syntax)  (0) 2010.09.07
    JNI  (0) 2010.07.06
    Android Bitmap Object Resizing Tip  (0) 2010.07.06
    Handler  (0) 2010.07.06
    aidl  (0) 2010.07.06
    Posted by jazzlife
    ,

    JNI

    old/Basic 2010. 7. 6. 14:58
    이글은, 구글 안드로이드 상의 Java 애플리케이션에서 native c/c++ 모듈을 Call 할 수 있는 가에
    대한 것이다. 결론적으로 말하면 쉽지 않을 것 같지만 불가능 하지는 않을 듯 하다.
    직접 JNI를 통해 결과를 검증해 보지는 않았지만, 대략적인 방법들과 문제점들을 정리해 보고자
    한다.
     
     
    1. JNI란 무엇인가? - JNI example 중심으로
     
    This section presents the ReadFile example program. This example shows how you can use
    the Java Native Interface (JNI) to invoke a native method that makes C function calls to map a
    file into memory.

    About the Example

    You can call code written in any programming language from a program written in the Java
    language by declaring a native Java method, loading the library that contains the native code,
    and then calling the native method. The ReadFile source code below does exactly this.
    However, successfully running the program requires a few additional steps beyond compiling
    the Java language source file. After you compile, but before you run the example, you have to
    generate a header file. The native code implements the function defintions contained in the
    generated header file and implements the business logic as well. The following sections walk
    through all the steps.
    import java.util.*;
    class ReadFile {
     //Native method declaration
       native byte[] loadFile(String name);
     //Load the library
       static {
         System.loadLibrary("nativelib");
       }
     
       public static void main(String args[]) {
          byte buf[];
     //Create class instance
          ReadFile mappedFile=new ReadFile();
     //Call native method to load ReadFile.java
          buf=mappedFile.loadFile("ReadFile.java");
     //Print contents of ReadFile.java
          for(int i=0;i<buf.length;i++) {
              System.out.print((char)buf[i]);
          }
       }
    }
    Native Method Declaration 

    The native declaration provides the bridge to run the native function in the Java virtual machine.
    In this example, the loadFile function maps onto a C function called Java_ReadFile_loadFile.
    The function implementation accepts a String that represents a file name and returns the
    contents of that file in the byte array.
    native byte[] loadFile(String name); Load the Library
    The library containing the native code implementation is loaded by a call to System.loadLibrary().
    Placing this call in a static initializer ensures this library is only loaded once per class.
    The library can be loaded outside of the static block if your application requires it. You might
    need to configure your environment so the loadLibrary method can find your native code library.
    static {
         System.loadLibrary("nativelib");
    }
    Compile the Program
     
    To compile the program, just run the javac compiler command as you normally would:
    javac ReadFile.java
    Next, you need to generate a header file with the native method declaration and implement the
    native method to call the C functions for loading and reading a file.

    Generate the Header File 

    To generate a a header file, run the javah command on the ReadFile class. In this example,
    the generated header file is named ReadFile.h. It provides a method signature that you have
    to use when you implement the loadfile native function.
    javah -jni ReadFile
    Note: When running javah on your own classes, be sure to use the fully-qualified class name.
    Method Signature 

    The ReadFile.h header file defines the interface to map the Java language method to the native
    C function. It uses a method signature to map the arguments and return value of the Java
    language mappedfile.loadFile method to the loadFile native method in the nativelib library.
    Here is the loadFile native method mapping (method signature):
    /* * Class: ReadFile * Method: loadFile * Signature: (Ljava/lang/String;)[B */
    JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile (JNIEnv *, jobject, jstring);
    The method signature parameters function as follows:
    JNIEnv *: A pointer to the JNI environment. This pointer is a handle to the current thread
    in the Java virtual machine, and contains mapping and other hosuekeeping information.
    jobject: A reference to the method that called this native code. If the calling method is
    static, this parameter would be type jclass instead of jobject.
    jstring: The parameter supplied to the native method. In this example, it is the name of
    the file to be read.
     
    Implement the Native Method
     
    In this native C source file, the loadFile definition is a copy and paste of the C declaration
    contained in ReadFile.h. The definition is followed by the native method implementation.
    JNI provides a mapping for both C and C++ by default.
    #include <jni.h>
    #include <sys/types.h>
    #include <sys/
    ipc
    .h>
    #include <sys/shm.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
     
    JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile
      (JNIEnv * env, jobject jobj, jstring name) {
        caddr_t m;
        jbyteArray jb;
        jboolean iscopy;
        struct stat finfo;
        const char *mfile = (*env)->GetStringUTFChars(
                    env, name, &iscopy);
        int fd = open(mfile, O_RDONLY);
        if (fd == -1) {
          printf("Could not open %sn", mfile);
        }
        lstat(mfile, &finfo);
        m = mmap((caddr_t) 0, finfo.st_size,
                    PROT_READ, MAP_PRIVATE, fd, 0);
        if (m == (caddr_t)-1) {
          printf("Could not mmap %sn", mfile);
          return(0);
        }
        jb=(*env)->NewByteArray(env, finfo.st_size);
        (*env)->SetByteArrayRegion(env, jb, 0,
     finfo.st_size, (jbyte *)m);
        close(fd);
        (*env)->ReleaseStringUTFChars(env, name, mfile);
        return (jb);
    }
     
    You can approach calling an existing C function instead of implementing one, in one of two ways:
    Map the name generated by JNI to the existing C function name. The Language Issues
    section shows how to map between Xbase database functions and Java language code
    Use the shared stubs code available from the JNI page on the java.sun.com web site.

    Compile the Dynamic or Shared Object Library 

    The library needs to be compiled as a dynamic or shared object library so it can be loaded at
    runtime. Static or archive libraries are compiled into an executable and cannot be loaded at
    runtime. The shared object or dynamic library for the loadFile example is compiled as follows:
    Gnu C/Linux:
    gcc  -o libnativelib.so -shared -Wl,-soname,libnative.so  
         -I/export/home/jdk1.2/include
         -I/export/home/jdk1.2/include/linux nativelib.c  
         -static -lc
    Gnu C++/Linux with Xbase
    g++ -o libdbmaplib.so -shared -Wl,-soname,libdbmap.so  
        -I/export/home/jdk1.2/include
        -I/export/home/jdk1.2/include/linux
        dbmaplib.cc -static -lc -lxbase

    Win32/WinNT/Win2000
    cl -Ic:/jdk1.2/include
       -Ic:/jdk1.2/include/win32
       -LD nativelib.c -Felibnative.dll

    Run the Example
     
    To run the example, the Java virtual machine needs to be able to find the native library. To do this,
    set the library path to the current directory as follows:

    Unix or Linux: LD_LIBRARY_PATH=`pwd` export LD_LIBRARY_PATH
    Windows NT/2000/95: set PATH=%path%;.

    With the library path properly specified for your platform, invoke the program as you normally would
    with the interpreter command:
     java ReadFile
     
     
    2. 구글 안드로이드 상에서 JNI 지원 API

    아래의 두가지 함수에 대한 안드로이드 상에서의 특별한 제약이 없다면 JNI 을 지원하기 위한 최소한의
    조건은 만족된 듯하다
    The core functional component is the use of the 'native' keyword and the bindings to the
    java.lang.System.loadLibary() API, which, does seem to be documented within the android reference:
     
    java.lang.System
    java.lang
    public final class
    java.lang.System
    java.lang.Object  
           java.lang.System

    Class System provides a standard place for programs to find system related information.
    All System API is static.
    static  void  load(String pathName) : Loads the specified file as a dynamic library.
    static  void  loadLibrary(String libName) : Loads and links the library specified by the argument.

    3. Dynamic & Shard Object Library 만들기
     
    the question here is how to build a native .so (shared library), but we can refer to the JNI example
    above for instructions. we have also been informed, through this list that you can build native code
    on the device, but without UI bindings:
     
    이 부분 중, 가장 어렵게 생각되는 부분은 Cross Compiler를 통해 shard object library를 만들 수 있느냐 일듯.
    현재 안드로이드의 /system/lib 아래에는 안드로이드 Library들이 .so 형태로 존재함.

    'old > Basic' 카테고리의 다른 글

    안드로이드 AIDL 문법(Android AIDL Syntax)  (0) 2010.09.07
    Android Flow Diagram  (0) 2010.07.06
    Android Bitmap Object Resizing Tip  (0) 2010.07.06
    Handler  (0) 2010.07.06
    aidl  (0) 2010.07.06
    Posted by jazzlife
    ,

    [Intro]


    Android에서 사용하는 이미지는 Bitmap이라는 클래스에서 다~ 알아서 해줍니다.
    그리고 이런 Bitmap Object를 쉽게 만들 수 있도록 도와주는
    BitmapFactory 클래스 라는 것도 있습니다.


    BitmapFactory는 여러가지 소스로 부터 Bitmap Object를 만들어 주는 일을 하는데,
    전부 static이며 decodeXXX 라는 이름을 가진 메소드들로 이루어져 있습니다.

    XXX에는 어떤 것으로 부터 decode를 하여
    Bitmap Object를 만들어 낼지에 대한 말들이 들어 가겠죠.



    [Decoding Methods]


    BitmapFactory.decodeByteArray() 메소드는 Camera.PictureCallback 으로 부터 받은
    Jpeg 사진 데이터를 가지고 Bitmap으로 만들어 줄 때 많이 사용 합니다.
    Camera.PictureCallback에서 들어오는 데이터가 byte[] 형식이기 때문에
    저 메소드를 사용 해야 하는 것이죠.


    BitmapFactory.decodeFile() 메소드는 파일을 그대로 읽어 옵니다.
    내부적으로는 파일 경로를 가지고 FileInputStream을 만들어서 decodeStream을 합니다.
    그냥 파일 경로만 쓰면 다 해주는게 편리 한 것이죠.


    BitmapFactory.decodeResource() 메소드는 Resource로 부터 Bitmap을 만들어 내며
    BitmapFactory.decodeStream() 메소드는 InputStream으로 부터 Bitmap을 만들어 냅니다.
    뭐 그냥 이름만 봐도 알 수 있는 것들이지요.



    [OutOfMemoryError??]


    보통 이미지 파일을 읽어서 Resizing을 해야 할 때가 있는데,
    그럴때는 BitmapFactory로 읽어서 Bitmap.createScaledBitmap() 메소드를 사용하여 줄이면

    간단하게 처리 할 수 있습니다.


    그런데 BitmapFactory를 사용할 때 주의해야 할 점이 있습니다.
    아래의 예를 한번 보시죠.

    Bitmap src = BitmapFactory.decodeFile("/sdcard/image.jpg");
    Bitmap resized = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, true);

    이미지 파일로부터 Bitmap을 만든 다음에

    다시 dstWidth, dstHeight 만큼 줄여서 resized 라는 Bitmap을 만들어 냈습니다.
    보통이라면 저렇게 하는게 맞습니다.


    읽어서, 줄인다.


    그런데 만약 이미지 파일의 크기가 아주 크다면 어떻게 될까요?
    지금 Dev Phone에서 카메라로 촬영하면
    기본적으로 2048 x 1536 크기의 Jpeg 이미지가 촬영된 데이터로 넘어옵니다.
    이것을 decode 하려면 3MB 정도의 메모리가 필요 할 텐데,

    과연 어떤 모바일 디바이스에서 얼마나 처리 할 수 있을까요?


    실제로 촬영된 Jpeg 이미지를 여러번 decoding 하다보면

    아래와 같은 황당한 메세지를 발견 할 수 있습니다.

    java.lang.OutOfMemoryError: bitmap size exceeds VM budget

    네... OutOfMemory 입니다.
    더 이상 무슨 말이 필요 하겠습니까...
    메모리가 딸려서 처리를 제대로 못합니다.


    이것이 실제로 decoding 후 메모리 해제가 제대로 되지 않아서 그런 것인지,
    하더라도 어디서 Leak이 발생 하는지에 대한 정확한 원인은 알 수 없습니다.
    이것은 엔지니어들이 해결해야 할 문제 겠죠...


    하지만 메모리 에러를 피할 수 있는 방법이 있습니다.



    [BitmapFactory.Options.inSampleSize]


    BitmapFactory.decodeXXX 시리즈는 똑같은 메소드가 두 개씩 오버로딩 되어 있습니다.
    같은 이름이지만 Signature가 다른 메소드의 차이점은
    BitmapFactory.Options를 파라메터로 받느냐 안받느냐의 차이죠.

    BitmapFactory.Options를 사용하게 되면 decode 할 때 여러가지 옵션을 줄 수 있습니다.


    여러가지 많지만 저희가 지금 사용할 것은 inSampleSize 옵션 입니다.


    inSampleSize 옵션은,
    애초에 decode를 할 때 얼마만큼 줄여서 decoding을 할 지 정하는 옵션 입니다.


    inSampleSize 옵션은 1보다 작은 값일때는 무조건 1로 세팅이 되며,
    1보다 큰 값, N일때는 1/N 만큼 이미지를 줄여서 decoding 하게 됩니다.
    즉 inSampleSize가 4라면 1/4 만큼 이미지를 줄여서 decoding 해서 Bitmap으로 만들게 되는 것이죠.


    2의 지수만큼 비례할 때 가장 빠르다고 합니다.
    2, 4, 8, 16... 정도 되겠죠?


    그래서 만약 내가 줄이고자 하는 이미지가 1/4보다는 작고 1/8보다는 클 때,
    inSampleSize 옵션에 4를 주어서 decoding 한 다음에,

    Bitmap.createScaledBitmap() 메소드를 사용하여 한번 더 줄이면 됩니다.

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = 4;
    Bitmap src = BitmapFactory.decodeFile("/sdcard/image.jpg", options);
    Bitmap resized = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, true);

    당연한 이야기 이겠지만,
    내가 원하고자 하는 사이즈가 딱 1/4 크기라면

    Bitmap.createScaledBitmap() 메소드를 쓸 필요가 없지요.


    inSampleSize 옵션을 잘 활용하면 메모리 부족 현상을 대략적으로 해소 할 수 있습니다.
    참고로 제가 저 옵션을 사용한 뒤로는 메모리 에러를 본적이 한~번도 없답니다.



    [Appendix]


    inSampleSize 옵션을 사용하면

    SkScaledBitmapSampler Object (Library Level) 를 생성 하게 되는데,
    Object를 만들때 정해진 SampleSize 만큼 축소하여 width와 height를 정한 뒤에 만들게 됩니다.
    그러니까 애초에 축소된 사이즈로 이미지를 decoding 하는 것이죠.



    [Outro]


    Android의 기본 어플리케이션 소스를 분석 하다보면
    상당히 테크니컬한 기법들을 많이 얻을 수 있습니다.
    어떻게 이런 방법으로 만들었나 싶을 정도로 매우 정교하고 복잡하게 만들어져 있지요.
    참 대단한 것 같습니다.


    아 그리고 왜 dstWidth와 dstHeight 변수 선언이 없냐고 따지시는 분들 설마 없겠죠?

    'old > Basic' 카테고리의 다른 글

    Android Flow Diagram  (0) 2010.07.06
    JNI  (0) 2010.07.06
    Handler  (0) 2010.07.06
    aidl  (0) 2010.07.06
    zygote & Dalvik VM  (0) 2010.07.06
    Posted by jazzlife
    ,

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.iftitah.android.contact">
      <application android:icon="@drawable/icon">
       <activity class=".Contact" android:label="@string/app_name">
        <intent-filter>
        <action android:value="android.intent.action.MAIN" />
        <category android:value="android.intent.category.LAUNCHER" />
       </intent-filter>
      </activity>
     </application>
    .
    .
    </manifest>

    위와 같이 activity가 정의되어 있을 때... 아래와 같이 실행하면 됨

    am start -a android.intent.action.MAIN -n
    com.iftitah.android.contact/com.iftitah.android.contact.Contact

    'old > UI Design' 카테고리의 다른 글

    shape layout  (0) 2010.08.04
    cutom title bar  (0) 2010.08.04
    PowerManager에서 Screen ON/OFF 통지  (0) 2010.07.06
    Resources - Menus, Searchable  (0) 2010.07.01
    Resources - Dimen, Color, 9-patch, Animation  (0) 2010.07.01
    Posted by jazzlife
    ,
    PowerManager는 Screen을 On/Off할때 WindowManager에 통지를 하고 이는 KeyguardViewMediator를 통해 LockScreen에도 전달된다.

    PowerManagerService.mNotificationTask
        -> WindowManagerPolicy.screenTurnedOn()
            -> KeyguardViewMediator.onScreenTurnedOn();
        -> WindowManagerPolicy.screenTurnedOff()
            -> KeyguardViewMediator.onScreenTurnedOff();

    'old > UI Design' 카테고리의 다른 글

    cutom title bar  (0) 2010.08.04
    adb shell에서 특정 activity실행하기  (0) 2010.07.06
    Resources - Menus, Searchable  (0) 2010.07.01
    Resources - Dimen, Color, 9-patch, Animation  (0) 2010.07.01
    String & StyledText  (0) 2010.06.30
    Posted by jazzlife
    ,

    Command Line Shell For SQLite

    The SQLite library includes a simple command-line utility named sqlite3 (or sqlite3.exe on windows) that allows the user to manually enter and execute SQL commands against an SQLite database. This document provides a brief introduction on how to use the sqlite3 program.

    Getting Started

    To start the sqlite3 program, just type "sqlite3" followed by the name the file that holds the SQLite database. If the file does not exist, a new one is created automatically. The sqlite3 program will then prompt you to enter SQL. Type in SQL statements (terminated by a semicolon), press "Enter" and the SQL will be executed.

    For example, to create a new SQLite database named "ex1" with a single table named "tbl1", you might do this:

    $ sqlite3 ex1
    SQLite version 3.6.11
    Enter ".help" for instructions
    Enter SQL statements terminated with a ";"
    sqlite> create table tbl1(one varchar(10), two smallint);
    sqlite> insert into tbl1 values('hello!',10);
    sqlite> insert into tbl1 values('goodbye', 20);
    sqlite> select * from tbl1;
    hello!|10
    goodbye|20
    sqlite>

    You can terminate the sqlite3 program by typing your systems End-Of-File character (usually a Control-D). Use the interrupt character (usually a Control-C) to stop a long-running SQL statement.

    Make sure you type a semicolon at the end of each SQL command! The sqlite3 program looks for a semicolon to know when your SQL command is complete. If you omit the semicolon, sqlite3 will give you a continuation prompt and wait for you to enter more text to be added to the current SQL command. This feature allows you to enter SQL commands that span multiple lines. For example:

    sqlite> CREATE TABLE tbl2 (
       ...>   f1 varchar(30) primary key,
       ...>   f2 text,
       ...>   f3 real
       ...> );
    sqlite>

    Aside: Querying the SQLITE_MASTER table

    The database schema in an SQLite database is stored in a special table named "sqlite_master". You can execute "SELECT" statements against the special sqlite_master table just like any other table in an SQLite database. For example:

    $ sqlite3 ex1
    SQLite vresion 3.6.11
    Enter ".help" for instructions
    sqlite> select * from sqlite_master;
        type = table
        name = tbl1
    tbl_name = tbl1
    rootpage = 3
         sql = create table tbl1(one varchar(10), two smallint)
    sqlite>

    But you cannot execute DROP TABLE, UPDATE, INSERT or DELETE against the sqlite_master table. The sqlite_master table is updated automatically as you create or drop tables and indices from the database. You can not make manual changes to the sqlite_master table.

    The schema for TEMPORARY tables is not stored in the "sqlite_master" table since TEMPORARY tables are not visible to applications other than the application that created the table. The schema for TEMPORARY tables is stored in another special table named "sqlite_temp_master". The "sqlite_temp_master" table is temporary itself.

    Special commands to sqlite3

    Most of the time, sqlite3 just reads lines of input and passes them on to the SQLite library for execution. But if an input line begins with a dot ("."), then that line is intercepted and interpreted by the sqlite3 program itself. These "dot commands" are typically used to change the output format of queries, or to execute certain prepackaged query statements.

    For a listing of the available dot commands, you can enter ".help" at any time. For example:

    sqlite> .help
    .backup ?DB? FILE      Backup DB (default "main") to FILE
    .bail ON|OFF           Stop after hitting an error.  Default OFF
    .databases             List names and files of attached databases
    .dump ?TABLE? ...      Dump the database in an SQL text format
    .echo ON|OFF           Turn command echo on or off
    .exit                  Exit this program
    .explain ON|OFF        Turn output mode suitable for EXPLAIN on or off.
    .genfkey ?OPTIONS?     Options are:
                             --no-drop: Do not drop old fkey triggers.
                             --ignore-errors: Ignore tables with fkey errors
                             --exec: Execute generated SQL immediately
                           See file tool/genfkey.README in the source 
                           distribution for further information.
    .header(s) ON|OFF      Turn display of headers on or off
    .help                  Show this message
    .import FILE TABLE     Import data from FILE into TABLE
    .indices TABLE         Show names of all indices on TABLE
    .iotrace FILE          Enable I/O diagnostic logging to FILE
    .load FILE ?ENTRY?     Load an extension library
    .mode MODE ?TABLE?     Set output mode where MODE is one of:
                             csv      Comma-separated values
                             column   Left-aligned columns.  (See .width)
                             html     HTML <table> code
                             insert   SQL insert statements for TABLE
                             line     One value per line
                             list     Values delimited by .separator string
                             tabs     Tab-separated values
                             tcl      TCL list elements
    .nullvalue STRING      Print STRING in place of NULL values
    .output FILENAME       Send output to FILENAME
    .output stdout         Send output to the screen
    .prompt MAIN CONTINUE  Replace the standard prompts
    .quit                  Exit this program
    .read FILENAME         Execute SQL in FILENAME
    .restore ?DB? FILE     Restore content of DB (default "main") from FILE
    .schema ?TABLE?        Show the CREATE statements
    .separator STRING      Change separator used by output mode and .import
    .show                  Show the current values for various settings
    .tables ?PATTERN?      List names of tables matching a LIKE pattern
    .timeout MS            Try opening locked tables for MS milliseconds
    .timer ON|OFF          Turn the CPU timer measurement on or off
    .width NUM NUM ...     Set column widths for "column" mode
    sqlite>

    Changing Output Formats

    The sqlite3 program is able to show the results of a query in eight different formats: "csv", "column", "html", "insert", "line", "list", "tabs", and "tcl". You can use the ".mode" dot command to switch between these output formats.

    The default output mode is "list". In list mode, each record of a query result is written on one line of output and each column within that record is separated by a specific separator string. The default separator is a pipe symbol ("|"). List mode is especially useful when you are going to send the output of a query to another program (such as AWK) for additional processing.

    sqlite> .mode list
    sqlite> select * from tbl1;
    hello|10
    goodbye|20
    sqlite>

    You can use the ".separator" dot command to change the separator for list mode. For example, to change the separator to a comma and a space, you could do this:

    sqlite> .separator ", "
    sqlite> select * from tbl1;
    hello, 10
    goodbye, 20
    sqlite>

    In "line" mode, each column in a row of the database is shown on a line by itself. Each line consists of the column name, an equal sign and the column data. Successive records are separated by a blank line. Here is an example of line mode output:

    sqlite> .mode line
    sqlite> select * from tbl1;
    one = hello
    two = 10
    
    one = goodbye
    two = 20
    sqlite>

    In column mode, each record is shown on a separate line with the data aligned in columns. For example:

    sqlite> .mode column
    sqlite> select * from tbl1;
    one         two       
    ----------  ----------
    hello       10        
    goodbye     20        
    sqlite>

    By default, each column is at least 10 characters wide. Data that is too wide to fit in a column is truncated. You can adjust the column widths using the ".width" command. Like this:

    sqlite> .width 12 6
    sqlite> select * from tbl1;
    one           two   
    ------------  ------
    hello         10    
    goodbye       20    
    sqlite>

    The ".width" command in the example above sets the width of the first column to 12 and the width of the second column to 6. All other column widths were unaltered. You can gives as many arguments to ".width" as necessary to specify the widths of as many columns as are in your query results.

    If you specify a column a width of 0, then the column width is automatically adjusted to be the maximum of three numbers: 10, the width of the header, and the width of the first row of data. This makes the column width self-adjusting. The default width setting for every column is this auto-adjusting 0 value.

    The column labels that appear on the first two lines of output can be turned on and off using the ".header" dot command. In the examples above, the column labels are on. To turn them off you could do this:

    sqlite> .header off
    sqlite> select * from tbl1;
    hello         10    
    goodbye       20    
    sqlite>

    Another useful output mode is "insert". In insert mode, the output is formatted to look like SQL INSERT statements. You can use insert mode to generate text that can later be used to input data into a different database.

    When specifying insert mode, you have to give an extra argument which is the name of the table to be inserted into. For example:

    sqlite> .mode insert new_table
    sqlite> select * from tbl1;
    INSERT INTO 'new_table' VALUES('hello',10);
    INSERT INTO 'new_table' VALUES('goodbye',20);
    sqlite>

    The last output mode is "html". In this mode, sqlite3 writes the results of the query as an XHTML table. The beginning <TABLE> and the ending </TABLE> are not written, but all of the intervening <TR>s, <TH>s, and <TD>s are. The html output mode is envisioned as being useful for CGI.

    Writing results to a file

    By default, sqlite3 sends query results to standard output. You can change this using the ".output" command. Just put the name of an output file as an argument to the .output command and all subsequent query results will be written to that file. Use ".output stdout" to begin writing to standard output again. For example:

    sqlite> .mode list
    sqlite> .separator |
    sqlite> .output test_file_1.txt
    sqlite> select * from tbl1;
    sqlite> .exit
    $ cat test_file_1.txt
    hello|10
    goodbye|20
    $

    Querying the database schema

    The sqlite3 program provides several convenience commands that are useful for looking at the schema of the database. There is nothing that these commands do that cannot be done by some other means. These commands are provided purely as a shortcut.

    For example, to see a list of the tables in the database, you can enter ".tables".

    sqlite> .tables
    tbl1
    tbl2
    sqlite>

    The ".tables" command is similar to setting list mode then executing the following query:

    SELECT name FROM sqlite_master 
    WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'
    UNION ALL 
    SELECT name FROM sqlite_temp_master 
    WHERE type IN ('table','view') 
    ORDER BY 1
    

    In fact, if you look at the source code to the sqlite3 program (found in the source tree in the file src/shell.c) you'll find exactly the above query.

    The ".indices" command works in a similar way to list all of the indices for a particular table. The ".indices" command takes a single argument which is the name of the table for which the indices are desired. Last, but not least, is the ".schema" command. With no arguments, the ".schema" command shows the original CREATE TABLE and CREATE INDEX statements that were used to build the current database. If you give the name of a table to ".schema", it shows the original CREATE statement used to make that table and all if its indices. We have:

    sqlite> .schema
    create table tbl1(one varchar(10), two smallint)
    CREATE TABLE tbl2 (
      f1 varchar(30) primary key,
      f2 text,
      f3 real
    )
    sqlite> .schema tbl2
    CREATE TABLE tbl2 (
      f1 varchar(30) primary key,
      f2 text,
      f3 real
    )
    sqlite>

    The ".schema" command accomplishes the same thing as setting list mode, then entering the following query:

    SELECT sql FROM 
       (SELECT * FROM sqlite_master UNION ALL
        SELECT * FROM sqlite_temp_master)
    WHERE type!='meta'
    ORDER BY tbl_name, type DESC, name
    

    Or, if you give an argument to ".schema" because you only want the schema for a single table, the query looks like this:

    SELECT sql FROM
       (SELECT * FROM sqlite_master UNION ALL
        SELECT * FROM sqlite_temp_master)
    WHERE type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'
    ORDER BY substr(type,2,1), name
    

    You can supply an argument to the .schema command. If you do, the query looks like this:

    SELECT sql FROM
       (SELECT * FROM sqlite_master UNION ALL
        SELECT * FROM sqlite_temp_master)
    WHERE tbl_name LIKE '%s'
      AND type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'
    ORDER BY substr(type,2,1), name
    

    The "%s" in the query is replace by your argument. This allows you to view the schema for some subset of the database.

    sqlite> .schema %abc%

    Along these same lines, the ".table" command also accepts a pattern as its first argument. If you give an argument to the .table command, a "%" is both appended and prepended and a LIKE clause is added to the query. This allows you to list only those tables that match a particular pattern.

    The ".databases" command shows a list of all databases open in the current connection. There will always be at least 2. The first one is "main", the original database opened. The second is "temp", the database used for temporary tables. There may be additional databases listed for databases attached using the ATTACH statement. The first output column is the name the database is attached with, and the second column is the filename of the external file.

    sqlite> .databases

    Converting An Entire Database To An ASCII Text File

    Use the ".dump" command to convert the entire contents of a database into a single ASCII text file. This file can be converted back into a database by piping it back into sqlite3.

    A good way to make an archival copy of a database is this:

    $ echo '.dump' | sqlite3 ex1 | gzip -c >ex1.dump.gz

    This generates a file named ex1.dump.gz that contains everything you need to reconstruct the database at a later time, or on another machine. To reconstruct the database, just type:

    $ zcat ex1.dump.gz | sqlite3 ex2

    The text format is pure SQL so you can also use the .dump command to export an SQLite database into other popular SQL database engines. Like this:

    $ createdb ex2
    $ sqlite3 ex1 .dump | psql ex2

    Other Dot Commands

    The ".explain" dot command can be used to set the output mode to "column" and to set the column widths to values that are reasonable for looking at the output of an EXPLAIN command. The EXPLAIN command is an SQLite-specific SQL extension that is useful for debugging. If any regular SQL is prefaced by EXPLAIN, then the SQL command is parsed and analyzed but is not executed. Instead, the sequence of virtual machine instructions that would have been used to execute the SQL command are returned like a query result. For example:

    sqlite> .explain
    sqlite> explain delete from tbl1 where two<20;
    addr  opcode        p1     p2     p3          
    ----  ------------  -----  -----  -------------------------------------   
    0     ListOpen      0      0                  
    1     Open          0      1      tbl1        
    2     Next          0      9                  
    3     Field         0      1                  
    4     Integer       20     0                  
    5     Ge            0      2                  
    6     Key           0      0                  
    7     ListWrite     0      0                  
    8     Goto          0      2                  
    9     Noop          0      0                  
    10    ListRewind    0      0                  
    11    ListRead      0      14                 
    12    Delete        0      0                  
    13    Goto          0      11                 
    14    ListClose     0      0

    The ".timeout" command sets the amount of time that the sqlite3 program will wait for locks to clear on files it is trying to access before returning an error. The default value of the timeout is zero so that an error is returned immediately if any needed database table or index is locked.

    And finally, we mention the ".exit" command which causes the sqlite3 program to exit.

    Using sqlite3 in a shell script

    One way to use sqlite3 in a shell script is to use "echo" or "cat" to generate a sequence of commands in a file, then invoke sqlite3 while redirecting input from the generated command file. This works fine and is appropriate in many circumstances. But as an added convenience, sqlite3 allows a single SQL command to be entered on the command line as a second argument after the database name. When the sqlite3 program is launched with two arguments, the second argument is passed to the SQLite library for processing, the query results are printed on standard output in list mode, and the program exits. This mechanism is designed to make sqlite3 easy to use in conjunction with programs like "awk". For example:

    $ sqlite3 ex1 'select * from tbl1' |
    >  awk '{printf "<tr><td>%s<td>%s\n",$1,$2 }'
    <tr><td>hello<td>10
    <tr><td>goodbye<td>20
    $

    Ending shell commands

    SQLite commands are normally terminated by a semicolon. In the shell you can also use the word "GO" (case-insensitive) or a slash character "/" on a line by itself to end a command. These are used by SQL Server and Oracle, respectively. These won't work insqlite3_exec(), because the shell translates these into a semicolon before passing them to that function.

    Compiling the sqlite3 program from sources

    The source code to the sqlite3 command line interface is in a single file named "shell.c" which you can download from the SQLite website. Compile this file (together with the sqlite3 library source code to generate the executable. For example:

    gcc -o sqlite3 shell.c sqlite3.c -ldl -lpthread

    Command Line Shell For SQLite

    The SQLite library includes a simple command-line utility named sqlite3 (or sqlite3.exe on windows) that allows the user to manually enter and execute SQL commands against an SQLite database. This document provides a brief introduction on how to use the sqlite3 program.

    Getting Started

    To start the sqlite3 program, just type "sqlite3" followed by the name the file that holds the SQLite database. If the file does not exist, a new one is created automatically. The sqlite3 program will then prompt you to enter SQL. Type in SQL statements (terminated by a semicolon), press "Enter" and the SQL will be executed.

    For example, to create a new SQLite database named "ex1" with a single table named "tbl1", you might do this:

    $ sqlite3 ex1
    SQLite version 3.6.11
    Enter ".help" for instructions
    Enter SQL statements terminated with a ";"
    sqlite> create table tbl1(one varchar(10), two smallint);
    sqlite> insert into tbl1 values('hello!',10);
    sqlite> insert into tbl1 values('goodbye', 20);
    sqlite> select * from tbl1;
    hello!|10
    goodbye|20
    sqlite>

    You can terminate the sqlite3 program by typing your systems End-Of-File character (usually a Control-D). Use the interrupt character (usually a Control-C) to stop a long-running SQL statement.

    Make sure you type a semicolon at the end of each SQL command! The sqlite3 program looks for a semicolon to know when your SQL command is complete. If you omit the semicolon, sqlite3 will give you a continuation prompt and wait for you to enter more text to be added to the current SQL command. This feature allows you to enter SQL commands that span multiple lines. For example:

    sqlite> CREATE TABLE tbl2 (
       ...>   f1 varchar(30) primary key,
       ...>   f2 text,
       ...>   f3 real
       ...> );
    sqlite>

    Aside: Querying the SQLITE_MASTER table

    The database schema in an SQLite database is stored in a special table named "sqlite_master". You can execute "SELECT" statements against the special sqlite_master table just like any other table in an SQLite database. For example:

    $ sqlite3 ex1
    SQLite vresion 3.6.11
    Enter ".help" for instructions
    sqlite> select * from sqlite_master;
        type = table
        name = tbl1
    tbl_name = tbl1
    rootpage = 3
         sql = create table tbl1(one varchar(10), two smallint)
    sqlite>

    But you cannot execute DROP TABLE, UPDATE, INSERT or DELETE against the sqlite_master table. The sqlite_master table is updated automatically as you create or drop tables and indices from the database. You can not make manual changes to the sqlite_master table.

    The schema for TEMPORARY tables is not stored in the "sqlite_master" table since TEMPORARY tables are not visible to applications other than the application that created the table. The schema for TEMPORARY tables is stored in another special table named "sqlite_temp_master". The "sqlite_temp_master" table is temporary itself.

    Special commands to sqlite3

    Most of the time, sqlite3 just reads lines of input and passes them on to the SQLite library for execution. But if an input line begins with a dot ("."), then that line is intercepted and interpreted by the sqlite3 program itself. These "dot commands" are typically used to change the output format of queries, or to execute certain prepackaged query statements.

    For a listing of the available dot commands, you can enter ".help" at any time. For example:

    sqlite> .help
    .backup ?DB? FILE      Backup DB (default "main") to FILE
    .bail ON|OFF           Stop after hitting an error.  Default OFF
    .databases             List names and files of attached databases
    .dump ?TABLE? ...      Dump the database in an SQL text format
    .echo ON|OFF           Turn command echo on or off
    .exit                  Exit this program
    .explain ON|OFF        Turn output mode suitable for EXPLAIN on or off.
    .genfkey ?OPTIONS?     Options are:
                             --no-drop: Do not drop old fkey triggers.
                             --ignore-errors: Ignore tables with fkey errors
                             --exec: Execute generated SQL immediately
                           See file tool/genfkey.README in the source 
                           distribution for further information.
    .header(s) ON|OFF      Turn display of headers on or off
    .help                  Show this message
    .import FILE TABLE     Import data from FILE into TABLE
    .indices TABLE         Show names of all indices on TABLE
    .iotrace FILE          Enable I/O diagnostic logging to FILE
    .load FILE ?ENTRY?     Load an extension library
    .mode MODE ?TABLE?     Set output mode where MODE is one of:
                             csv      Comma-separated values
                             column   Left-aligned columns.  (See .width)
                             html     HTML <table> code
                             insert   SQL insert statements for TABLE
                             line     One value per line
                             list     Values delimited by .separator string
                             tabs     Tab-separated values
                             tcl      TCL list elements
    .nullvalue STRING      Print STRING in place of NULL values
    .output FILENAME       Send output to FILENAME
    .output stdout         Send output to the screen
    .prompt MAIN CONTINUE  Replace the standard prompts
    .quit                  Exit this program
    .read FILENAME         Execute SQL in FILENAME
    .restore ?DB? FILE     Restore content of DB (default "main") from FILE
    .schema ?TABLE?        Show the CREATE statements
    .separator STRING      Change separator used by output mode and .import
    .show                  Show the current values for various settings
    .tables ?PATTERN?      List names of tables matching a LIKE pattern
    .timeout MS            Try opening locked tables for MS milliseconds
    .timer ON|OFF          Turn the CPU timer measurement on or off
    .width NUM NUM ...     Set column widths for "column" mode
    sqlite>

    Changing Output Formats

    The sqlite3 program is able to show the results of a query in eight different formats: "csv", "column", "html", "insert", "line", "list", "tabs", and "tcl". You can use the ".mode" dot command to switch between these output formats.

    The default output mode is "list". In list mode, each record of a query result is written on one line of output and each column within that record is separated by a specific separator string. The default separator is a pipe symbol ("|"). List mode is especially useful when you are going to send the output of a query to another program (such as AWK) for additional processing.

    sqlite> .mode list
    sqlite> select * from tbl1;
    hello|10
    goodbye|20
    sqlite>

    You can use the ".separator" dot command to change the separator for list mode. For example, to change the separator to a comma and a space, you could do this:

    sqlite> .separator ", "
    sqlite> select * from tbl1;
    hello, 10
    goodbye, 20
    sqlite>

    In "line" mode, each column in a row of the database is shown on a line by itself. Each line consists of the column name, an equal sign and the column data. Successive records are separated by a blank line. Here is an example of line mode output:

    sqlite> .mode line
    sqlite> select * from tbl1;
    one = hello
    two = 10
    
    one = goodbye
    two = 20
    sqlite>

    In column mode, each record is shown on a separate line with the data aligned in columns. For example:

    sqlite> .mode column
    sqlite> select * from tbl1;
    one         two       
    ----------  ----------
    hello       10        
    goodbye     20        
    sqlite>

    By default, each column is at least 10 characters wide. Data that is too wide to fit in a column is truncated. You can adjust the column widths using the ".width" command. Like this:

    sqlite> .width 12 6
    sqlite> select * from tbl1;
    one           two   
    ------------  ------
    hello         10    
    goodbye       20    
    sqlite>

    The ".width" command in the example above sets the width of the first column to 12 and the width of the second column to 6. All other column widths were unaltered. You can gives as many arguments to ".width" as necessary to specify the widths of as many columns as are in your query results.

    If you specify a column a width of 0, then the column width is automatically adjusted to be the maximum of three numbers: 10, the width of the header, and the width of the first row of data. This makes the column width self-adjusting. The default width setting for every column is this auto-adjusting 0 value.

    The column labels that appear on the first two lines of output can be turned on and off using the ".header" dot command. In the examples above, the column labels are on. To turn them off you could do this:

    sqlite> .header off
    sqlite> select * from tbl1;
    hello         10    
    goodbye       20    
    sqlite>

    Another useful output mode is "insert". In insert mode, the output is formatted to look like SQL INSERT statements. You can use insert mode to generate text that can later be used to input data into a different database.

    When specifying insert mode, you have to give an extra argument which is the name of the table to be inserted into. For example:

    sqlite> .mode insert new_table
    sqlite> select * from tbl1;
    INSERT INTO 'new_table' VALUES('hello',10);
    INSERT INTO 'new_table' VALUES('goodbye',20);
    sqlite>

    The last output mode is "html". In this mode, sqlite3 writes the results of the query as an XHTML table. The beginning <TABLE> and the ending </TABLE> are not written, but all of the intervening <TR>s, <TH>s, and <TD>s are. The html output mode is envisioned as being useful for CGI.

    Writing results to a file

    By default, sqlite3 sends query results to standard output. You can change this using the ".output" command. Just put the name of an output file as an argument to the .output command and all subsequent query results will be written to that file. Use ".output stdout" to begin writing to standard output again. For example:

    sqlite> .mode list
    sqlite> .separator |
    sqlite> .output test_file_1.txt
    sqlite> select * from tbl1;
    sqlite> .exit
    $ cat test_file_1.txt
    hello|10
    goodbye|20
    $

    Querying the database schema

    The sqlite3 program provides several convenience commands that are useful for looking at the schema of the database. There is nothing that these commands do that cannot be done by some other means. These commands are provided purely as a shortcut.

    For example, to see a list of the tables in the database, you can enter ".tables".

    sqlite> .tables
    tbl1
    tbl2
    sqlite>

    The ".tables" command is similar to setting list mode then executing the following query:

    SELECT name FROM sqlite_master 
    WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'
    UNION ALL 
    SELECT name FROM sqlite_temp_master 
    WHERE type IN ('table','view') 
    ORDER BY 1
    

    In fact, if you look at the source code to the sqlite3 program (found in the source tree in the file src/shell.c) you'll find exactly the above query.

    The ".indices" command works in a similar way to list all of the indices for a particular table. The ".indices" command takes a single argument which is the name of the table for which the indices are desired. Last, but not least, is the ".schema" command. With no arguments, the ".schema" command shows the original CREATE TABLE and CREATE INDEX statements that were used to build the current database. If you give the name of a table to ".schema", it shows the original CREATE statement used to make that table and all if its indices. We have:

    sqlite> .schema
    create table tbl1(one varchar(10), two smallint)
    CREATE TABLE tbl2 (
      f1 varchar(30) primary key,
      f2 text,
      f3 real
    )
    sqlite> .schema tbl2
    CREATE TABLE tbl2 (
      f1 varchar(30) primary key,
      f2 text,
      f3 real
    )
    sqlite>

    The ".schema" command accomplishes the same thing as setting list mode, then entering the following query:

    SELECT sql FROM 
       (SELECT * FROM sqlite_master UNION ALL
        SELECT * FROM sqlite_temp_master)
    WHERE type!='meta'
    ORDER BY tbl_name, type DESC, name
    

    Or, if you give an argument to ".schema" because you only want the schema for a single table, the query looks like this:

    SELECT sql FROM
       (SELECT * FROM sqlite_master UNION ALL
        SELECT * FROM sqlite_temp_master)
    WHERE type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'
    ORDER BY substr(type,2,1), name
    

    You can supply an argument to the .schema command. If you do, the query looks like this:

    SELECT sql FROM
       (SELECT * FROM sqlite_master UNION ALL
        SELECT * FROM sqlite_temp_master)
    WHERE tbl_name LIKE '%s'
      AND type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%'
    ORDER BY substr(type,2,1), name
    

    The "%s" in the query is replace by your argument. This allows you to view the schema for some subset of the database.

    sqlite> .schema %abc%

    Along these same lines, the ".table" command also accepts a pattern as its first argument. If you give an argument to the .table command, a "%" is both appended and prepended and a LIKE clause is added to the query. This allows you to list only those tables that match a particular pattern.

    The ".databases" command shows a list of all databases open in the current connection. There will always be at least 2. The first one is "main", the original database opened. The second is "temp", the database used for temporary tables. There may be additional databases listed for databases attached using the ATTACH statement. The first output column is the name the database is attached with, and the second column is the filename of the external file.

    sqlite> .databases

    Converting An Entire Database To An ASCII Text File

    Use the ".dump" command to convert the entire contents of a database into a single ASCII text file. This file can be converted back into a database by piping it back into sqlite3.

    A good way to make an archival copy of a database is this:

    $ echo '.dump' | sqlite3 ex1 | gzip -c >ex1.dump.gz

    This generates a file named ex1.dump.gz that contains everything you need to reconstruct the database at a later time, or on another machine. To reconstruct the database, just type:

    $ zcat ex1.dump.gz | sqlite3 ex2

    The text format is pure SQL so you can also use the .dump command to export an SQLite database into other popular SQL database engines. Like this:

    $ createdb ex2
    $ sqlite3 ex1 .dump | psql ex2

    Other Dot Commands

    The ".explain" dot command can be used to set the output mode to "column" and to set the column widths to values that are reasonable for looking at the output of an EXPLAIN command. The EXPLAIN command is an SQLite-specific SQL extension that is useful for debugging. If any regular SQL is prefaced by EXPLAIN, then the SQL command is parsed and analyzed but is not executed. Instead, the sequence of virtual machine instructions that would have been used to execute the SQL command are returned like a query result. For example:

    sqlite> .explain
    sqlite> explain delete from tbl1 where two<20;
    addr  opcode        p1     p2     p3          
    ----  ------------  -----  -----  -------------------------------------   
    0     ListOpen      0      0                  
    1     Open          0      1      tbl1        
    2     Next          0      9                  
    3     Field         0      1                  
    4     Integer       20     0                  
    5     Ge            0      2                  
    6     Key           0      0                  
    7     ListWrite     0      0                  
    8     Goto          0      2                  
    9     Noop          0      0                  
    10    ListRewind    0      0                  
    11    ListRead      0      14                 
    12    Delete        0      0                  
    13    Goto          0      11                 
    14    ListClose     0      0

    The ".timeout" command sets the amount of time that the sqlite3 program will wait for locks to clear on files it is trying to access before returning an error. The default value of the timeout is zero so that an error is returned immediately if any needed database table or index is locked.

    And finally, we mention the ".exit" command which causes the sqlite3 program to exit.

    Using sqlite3 in a shell script

    One way to use sqlite3 in a shell script is to use "echo" or "cat" to generate a sequence of commands in a file, then invoke sqlite3 while redirecting input from the generated command file. This works fine and is appropriate in many circumstances. But as an added convenience, sqlite3 allows a single SQL command to be entered on the command line as a second argument after the database name. When the sqlite3 program is launched with two arguments, the second argument is passed to the SQLite library for processing, the query results are printed on standard output in list mode, and the program exits. This mechanism is designed to make sqlite3 easy to use in conjunction with programs like "awk". For example:

    $ sqlite3 ex1 'select * from tbl1' |
    >  awk '{printf "<tr><td>%s<td>%s\n",$1,$2 }'
    <tr><td>hello<td>10
    <tr><td>goodbye<td>20
    $

    Ending shell commands

    SQLite commands are normally terminated by a semicolon. In the shell you can also use the word "GO" (case-insensitive) or a slash character "/" on a line by itself to end a command. These are used by SQL Server and Oracle, respectively. These won't work insqlite3_exec(), because the shell translates these into a semicolon before passing them to that function.

    Compiling the sqlite3 program from sources

    The source code to the sqlite3 command line interface is in a single file named "shell.c" which you can download from the SQLite website. Compile this file (together with the sqlite3 library source code to generate the executable. For example:

    gcc -o sqlite3 shell.c sqlite3.c -ldl -lpthread

    'old > SQL' 카테고리의 다른 글

    외부 키  (0) 2010.03.18
    열 구성 변경  (0) 2010.03.18
    용어  (0) 2010.03.17
    TRANSACTION  (0) 2010.03.17
    INDEX  (0) 2010.03.17
    Posted by jazzlife
    ,

    Handler

    old/Basic 2010. 7. 6. 14:45
     

    Android(안드로이드) 개발시 사용되는 Handler(핸들러)와 통신에 사용되는 Message(메시지)에 대해서 알아보자.

     

    핸들러란?

     

     시스템은 사용자가 작성한 UI에서 빠른 응답을 요구하고 있다.

    만약  이상의 응당이 없을 경우 최악의 경우 작성된 프로그램이 강제로 종료가  경우도 발생할 수 있다.

    이런 상황을 방지하기 위해서  오래걸리는 작업이 필요한 경우 두가지의 방법으로 해결을 할 수 있다.

    첫번째는 시간이 오래걸리는 작업은 서비스로 만들어서 처리하는 방법

     새로운 쓰레드로 처리를 하는 방법

     

    두번째 방법으로  생성해서 데이터 처리등의 시간이 오래걸리는 작업을 지원하기 위한 클래스가 존재하는데  핸들러(Handdler)이다.

     

    간략하게 요약을 해보면

    • 백그라운드  생성을 위한 가장 유연한 방법이다.
    • 인스턴스 생성시 자동으로 안드로이드가  관리한다.
    • 메시지를 전달받게 되면 호출 되는 handlerMessage()에 실제 처리내용을
    • post(), postDelayed()를 통해서 인자로 실행하고 자하는 Runnable객체를 전달할 수 있다.
    • View단에도 Runnable객처를 인자로 전달가능하고 이럴경우 코드가 심플해지는 경우도  있지만 Handler를 추천한다.

     

     

    메시지(Message)란?

     

    UI등에서 실제로 처리를 담당하는  데이터를 전송하거나 작업을 요청하기 위해서 전달하는 객체이다.

     

    • 핸들러로 전달되는 객체이다.
    • 전달된 메시지는 메시지 Queue를 통해서 핸들러가  사용한다.
    • 메시지를 전달하기 위해선 핸들러의 obtainMessage()호출해서 메시지 풀의 메시지를  전달해야한다.
    • 메시지를 전달하기 위해서는 sendMessage() 등을 사용한다.

     

    메시지 전달 방식의 종류

    • sendMessage() - 큐의  메시지를 삽입한다.
    • sendMessageAtFrontQueue() - 큐의 맨앞에 메시지를 삽입한다.(우선처리)
    •  - 장비기동시간을 기준으로 삽입한다.(SystemClock.uptimeMillis()참고)
    • sendMessageDelayed() - 특정시간만큼 지연 삽입한다.

    'old > Basic' 카테고리의 다른 글

    JNI  (0) 2010.07.06
    Android Bitmap Object Resizing Tip  (0) 2010.07.06
    aidl  (0) 2010.07.06
    zygote & Dalvik VM  (0) 2010.07.06
    안드로이드 HAL - RIL(Radio Interface Layer)  (0) 2010.07.06
    Posted by jazzlife
    ,
     

    Android SMS call analysis




    < android SDK1.5 cupcake emulator >


    solicited Message

    emulator HOME의 Messaging 아이콘 클릭
    ConversationList.java
    /open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ConversationList.java
    onCreate();
        View, Listener 초기화 작업    
    onResume();
        DraftCache.getInstance().addOnDraftChangedListener(this);
        getContentResolver().delete(Threads.OBSOLETE_THREADS_URI, null, null);
        DraftCache.getInstance().refresh();
        startAsyncQuery();
        ContactInfoCache.getInstance().invalidateCache();


    New message 클릭
    /open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ConversationList.java
    @Override
        protected void onListItemClick(ListView l, View v, int position, long id) {
            if (LOCAL_LOGV) {
                Log.v(TAG, "onListItemClick: position=" + position + ", id=" + id);
            }

            if (position == 0) {
                createNewMessage();
            } else if (v instanceof ConversationHeaderView) {
                ConversationHeaderView headerView = (ConversationHeaderView) v;
                ConversationHeader ch = headerView.getConversationHeader();

                // TODO: The 'from' view of the ConversationHeader was
                // repurposed to be the cached display value, rather than
                // the old raw value, which openThread() wanted.  But it
                // turns out openThread() doesn't need it:
                // ComposeMessageActivity will load it.  That's not ideal,
                // though, as it's an SQLite query.  So fix this later to
                // save some latency on starting ComposeMessageActivity.
                String somethingDelimitedAddresses = null;
                openThread(ch.getThreadId(), somethingDelimitedAddresses);
            }
        }



            
    createNewMesage()
    /open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ConversationList.java
    private void createNewMessage() {
            Intent intent = new Intent(this, ComposeMessageActivity.class);
            startActivity(intent);
        }


    ComposeMessageActivity.java
    /open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ComposeMessageActivity.java
    onCreate() {
        initResourceRefs(); // Initialize members for UI elements.
        initActivityState(savedInstanceState, getIntent()); // Read parameters or previously saved              
                                                                                 state of this activity.
        mRecipientList = RecipientList.from(mExternalAddress, this); // Parse the recipient list.
        initMessageList(); // Set up the message history ListAdapter
        markAsRead(mThreadId); // Mark the current thread as read.
        updateSendButtonState();
        // 이 밖에도 MMS 모드 체크, 전송 실패한 메시지 정보를 pop-up 등등의 초기화 작업을 함.
    }

    onStart() {
        updateWindowTitle();
        initFocus();
        registerReceiver(mHttpProgressReceiver, mHttpProgressFilter); // Register a  
                                                      // BroadcastReceiver to listen on HTTP I/O process.  
        startMsgListQuery();                                                      
        startQueryForContactInfo();
        updateSendFailedNotification();
    }

    onResume() {
        startPresencePollingRequest(); // enqueueMessage(Message msg, long when) 에서
                                                  // MessageQueue를 확인한다. 주기적 반복
    }

        
        
    Send 버튼 클릭
    /open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ComposeMessageActivity.java
    /*
    onCreate() {
    ...  
        initResourceRefs() {
        ...
            mSendButton = (Button) findViewById(R.id.send_button);
            mSendButton.setOnClickListener(this);
        }
        updateSendButtonState() {
        ...  
            mSendButton.setEnabled(enable);
            mSendButton.setFocusable(enable);
        }
    }
    */

    public void onClick(View v) {
        if ((v == mSendButton) && isPreparedForSending()) {  
            // if문의 두번째 메소드는 hasRecipient() && (hasAttachment() || hasText()) 를 확인한다.
            // 즉, 메시지(받는사람, 내용, 첨부)를 작성했는지를 확인
            confirmSendMessageIfNeeded();
        }
    }    

       
    confirmSendMessageIfNeeded()
    /open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ComposeMessageActivity.java
    //받는사람이 유효한지 확인한 후, 메시지 전송

    private void confirmSendMessageIfNeeded() {
        if (mRecipientList.hasInvalidRecipient()) {
            if (mRecipientList.hasValidRecipient()) {
                String title = getResourcesString(R.string.has_invalid_recipient,
                        mRecipientList.getInvalidRecipientString());
                new AlertDialog.Builder(this)
                    .setIcon(android.R.drawable.ic_dialog_alert)
                    .setTitle(title)
                    .setMessage(R.string.invalid_recipient_message)
                    .setPositiveButton(R.string.try_to_send,
                            new SendIgnoreInvalidRecipientListener())
                    .setNegativeButton(R.string.no, new CancelSendingListener())
                    .show();
            } else {
                new AlertDialog.Builder(this)
                    .setIcon(android.R.drawable.ic_dialog_alert)
                    .setTitle(R.string.cannot_send_message)
                    .setMessage(R.string.cannot_send_message_reason)
                    .setPositiveButton(R.string.yes, new CancelSendingListener())
                    .show();
            }
        } else {
            sendMessage();
        }
    }


    sendMessage()
    /open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ComposeMessageActivity.java
    private void sendMessage() {
            // Need this for both SMS and MMS.
            final String[] dests = mRecipientList.getToNumbers();
            
            // removeSubjectIfEmpty will convert a message that is solely an MMS
            // message because it has an empty subject back into an SMS message.
            // It doesn't notify the user of the conversion.
            removeSubjectIfEmpty();
            if (requiresMms()) {
                // Make local copies of the bits we need for sending a message,
                // because we will be doing it off of the main thread, which will
                // immediately continue on to resetting some of this state.
                final Uri mmsUri = mMessageUri;
                final PduPersister persister = mPersister;
                final SlideshowModel slideshow = mSlideshow;
                final SendReq sendReq = new SendReq();
                fillMessageHeaders(sendReq);
                
                // Make sure the text in slide 0 is no longer holding onto a reference to the text
                // in the message text box.
                slideshow.prepareForSend();

                // Do the dirty work of sending the message off of the main UI thread.
                new Thread(new Runnable() {
                    public void run() {
                        sendMmsWorker(dests, mmsUri, persister, slideshow, sendReq);
                    }
                }).start();
            } else {
                // Same rules apply as above.
                final String msgText = mMsgText.toString();
                new Thread(new Runnable() {
                    public void run() {
                        sendSmsWorker(dests, msgText);
                    }
                }).start();
            }
            
            if (mExitOnSent) {
                // If we are supposed to exit after a message is sent,
                // clear out the text and URIs to inhibit saving of any
                // drafts and call finish().
                mMsgText = "";
                mMessageUri = null;
                finish();
            } else {
                // Otherwise, reset the UI to be ready for the next message.
                resetMessage();
            }
    }


    sendSmsWorker()
    /open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Ui/ComposeMessageActivity.java
    private void sendSmsWorker(String[] dests, String msgText) {
            // Make sure we are still using the correct thread ID for our
            // recipient set.
            long threadId = getOrCreateThreadId(dests);

            MessageSender sender = new SmsMessageSender(this, dests, msgText, threadId);
            try {
                sender.sendMessage(threadId);
                setThreadId(threadId);
                startMsgListQuery();
            } catch (Exception e) {
                Log.e(TAG, "Failed to send SMS message, threadId=" + threadId, e);
            }
        }


    --- APPLICATION FRAMEWORK ↓ -------------------------------------------------------------

    sender.sendMessage(threadId)
    /open_src/Packages/Apps/Mms/Src/Com/Android/Mms/Transaction/SmsMessageSender.java
    /*
    위의 sender 인스턴스 생성시 SmsMessageSender(..) 생성자 실행
    public SmsMessageSender(Context context, String[] dests, String msgText,
                long threadId) {
            mContext = context;
            mMessageText = msgText;
            mNumberOfDests = dests.length;
            mDests = new String[mNumberOfDests];
            System.arraycopy(dests, 0, mDests, 0, mNumberOfDests);
            mTimestamp = System.currentTimeMillis();
            mThreadId = threadId > 0 ? threadId
                                                : Threads.getOrCreateThreadId(context,
                                                       new HashSet<String>(Arrays.asList(dests)));
            mServiceCenter = getOutgoingServiceCenter(mThreadId);
    }  
    */

    public boolean sendMessage(long token) throws MmsException {
            if ((mMessageText == null) || (mNumberOfDests == 0)) {
                // Don't try to send an empty message.
                throw new MmsException("Null message body or dest.");
            }

            SmsManager smsManager = SmsManager.getDefault(); //Get the default instance
                                                                                     // of the SmsManager

            for (int i = 0; i < mNumberOfDests; i++) {
                ArrayList<String> messages = smsManager.divideMessage(mMessageText);                      
                                                                          // Divide a text message into several
                                                                          // messages, none bigger than
                                                                          // the maximum SMS message size.      
                                                                          // SmsManager.java
                int messageCount = messages.size();
                ArrayList<PendingIntent> deliveryIntents =
                        new ArrayList<PendingIntent>(messageCount);
                ArrayList<PendingIntent> sentIntents =
                        new ArrayList<PendingIntent>(messageCount);
                SharedPreferences prefs =                         
                        PreferenceManager.getDefaultSharedPreferences(mContext);
                boolean requestDeliveryReport = prefs.getBoolean(
                        MessagingPreferenceActivity.SMS_DELIVERY_REPORT_MODE,
                        DEFAULT_DELIVERY_REPORT_MODE);
                Uri uri = null;
                try {
                    uri = Sms.Outbox.addMessage(mContext.getContentResolver(), mDests[i],
                                mMessageText, null, mTimestamp, requestDeliveryReport, mThreadId);
                } catch (SQLiteException e) {
                    SqliteWrapper.checkSQLiteException(mContext, e);
                }

                for (int j = 0; j < messageCount; j++) {
                    if (requestDeliveryReport) {
                        // TODO: Fix: It should not be necessary to
                        // specify the class in this intent.  Doing that
                        // unnecessarily limits customizability.
                        deliveryIntents.add(PendingIntent.getBroadcast(
                                mContext, 0,
                                new Intent(
                                        MessageStatusReceiver.MESSAGE_STATUS_RECEIVED_ACTION,
                                        uri,
                                        mContext,
                                        MessageStatusReceiver.class),
                                0));
                    }
                    sentIntents.add(PendingIntent.getBroadcast(
                            mContext, 0,
                            new Intent(SmsReceiverService.MESSAGE_SENT_ACTION,
                                    uri,
                                    mContext,
                                    SmsReceiver.class),
                            0));
                }
                smsManager.sendMultipartTextMessage(
                        mDests[i], mServiceCenter, messages, sentIntents,
                        deliveryIntents);
            }
            return false;
    }


    smsManager.sendMultipartTextmessage(...)
    /open_src/Frameworks/Base/Telephony/Gsm/SmsManager.java
    /**
      * Send a multi-part text based SMS.  The callee should have already
      * divided the message into correctly sized parts by calling
      * <code>divideMessage</code>.
      */

    public void sendMultipartTextMessage(
                String destinationAddress, String scAddress, ArrayList<String> parts,
                ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
            if (TextUtils.isEmpty(destinationAddress)) {
                throw new IllegalArgumentException("Invalid destinationAddress");
            }
            if (parts == null || parts.size() < 1) {
                throw new IllegalArgumentException("Invalid message body");
            }
            
            // divided된 메시지 parts에 따라 전송 방법이 2 가지로 나뉜다.
            if (parts.size() > 1) {
                try {
                    ISms simISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
                    if (simISms != null) {
                        simISms.sendMultipartText(destinationAddress, scAddress, parts,
                                sentIntents, deliveryIntents);
                    }
                } catch (RemoteException ex) {
                    // ignore it
                }
            } else {
                PendingIntent sentIntent = null;
                PendingIntent deliveryIntent = null;
                if (sentIntents != null && sentIntents.size() > 0) {
                    sentIntent = sentIntents.get(0);
                }
                if (deliveryIntents != null && deliveryIntents.size() > 0) {
                    deliveryIntent = deliveryIntents.get(0);
                }
                sendTextMessage(destinationAddress, scAddress, parts.get(0),
                        sentIntent, deliveryIntent);
            }
        }


    simISms.sendMultipartText(...)  
    /open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/
    SimSmsInterfaceManager.java
    /**
     * Send a multi-part text based SMS
     */
       
    private GSMPhone mPhone;

    public void sendMultipartText(String destinationAddress, String scAddress, List<String> parts,
                List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
            Context context = mPhone.getContext();

            context.enforceCallingPermission(
                    "android.permission.SEND_SMS",
                    "Sending SMS message");
            if (DBG) log("sendMultipartText");
            mPhone.mSMS.sendMultipartText(destinationAddress, scAddress,     
                    (ArrayList<String>) parts,
                    (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>)         
                    deliveryIntents);
    }


    mphone.mSMS.sendMultipartText(...)
    /open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/
    SMSDispatcher.java
    /**
     * Send a multi-part text based SMS
     */

    void sendMultipartText(String destinationAddress, String scAddress, ArrayList<String> parts,
                ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {

            PendingIntent sentIntent = null;
            
            int ss = mPhone.getServiceState().getState(); //Get current servcie state of phone
            
            if (ss == ServiceState.STATE_IN_SERVICE) {
                // Only check SMS sending limit while in service
                if (sentIntents != null && sentIntents.size() > 0) {
                    sentIntent = sentIntents.get(0);
                }
                String appName = getAppNameByIntent(sentIntent);
                if ( !mCounter.check(appName, parts.size())) {
                    HashMap<String, Object> map = new HashMap<String, Object>();
                    map.put("destination", destinationAddress);
                    map.put("scaddress", scAddress);
                    map.put("parts", parts);
                    map.put("sentIntents", sentIntents);
                    map.put("deliveryIntents", deliveryIntents);
                    
                    SmsTracker multipartParameter = new SmsTracker(map, null, null);

                    sendMessage(obtainMessage(EVENT_POST_ALERT, multipartParameter));
                    return;
                }
            }
            
            sendMultipartTextWithPermit(destinationAddress,
                    scAddress, parts, sentIntents, deliveryIntents);
    }


    sendMultipartTextWithPermit(...)
    /open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/
    SMSDispatcher.java
    /**
     * Send a multi-part text based SMS which already passed SMS control check.
     *
     * It is the working function for sendMultipartText().
     */

    private void sendMultipartTextWithPermit(String destinationAddress,
                String scAddress, ArrayList<String> parts,
                ArrayList<PendingIntent> sentIntents,
                ArrayList<PendingIntent> deliveryIntents) {
            
            PendingIntent sentIntent = null;
            PendingIntent deliveryIntent = null;
            
            // check if in service
            int ss = mPhone.getServiceState().getState();
            if (ss != ServiceState.STATE_IN_SERVICE) {
                for (int i = 0, count = parts.size(); i < count; i++) {
                    if (sentIntents != null && sentIntents.size() > i) {
                        sentIntent = sentIntents.get(i);
                    }
                    SmsTracker tracker = new SmsTracker(null, sentIntent, null);
                    handleNotInService(ss, tracker);
                }
                return;
            }

            int ref = ++sConcatenatedRef & 0xff;

            for (int i = 0, count = parts.size(); i < count; i++) {
                // build SmsHeader
                byte[] data = new byte[3];
                data[0] = (byte) ref;   // reference #, unique per message
                data[1] = (byte) count; // total part count
                data[2] = (byte) (i + 1);  // 1-based sequence
                SmsHeader header = new SmsHeader();
                header.add(new SmsHeader.Element(
                    SmsHeader.CONCATENATED_8_BIT_REFERENCE, data));
     
                if (sentIntents != null && sentIntents.size() > i) {
                    sentIntent = sentIntents.get(i);
                }
                if (deliveryIntents != null && deliveryIntents.size() > i) {
                    deliveryIntent = deliveryIntents.get(i);
                }
                

                /**
                 * Get an SMS-SUBMIT PDU for a destination address and a message
                 */
                                                                          
                SmsMessage.SubmitPdu pdus = // ↓ Encoding
                            SmsMessage.getSubmitPdu(scAddress, destinationAddress,
                                parts.get(i), deliveryIntent != null, header.toByteArray());

                HashMap<String, Object> map = new HashMap<String, Object>();
                map.put("smsc", pdus.encodedScAddress);
                map.put("pdu", pdus.encodedMessage);

                SmsTracker tracker = new SmsTracker(map, sentIntent, deliveryIntent);
                
                sendSms(tracker);
            }        
    }

        
    sendSms(tracker)
    /open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/
    SMSDispatcher.java
    /**
     * Send the message along to the radio.
     */

    private final CommandsInterface mCm;

    private void sendSms(SmsTracker tracker) {
        HashMap map = tracker.mData;

        byte smsc[] = (byte[]) map.get("smsc");
        byte pdu[] = (byte[]) map.get("pdu");

        Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
        
        mCm.sendSMS(SimUtils.bytesToHexString(smsc), SimUtils.bytesToHexString(pdu),
                 reply);
        // sendSMS,, interface CommandsInterface
    }


    --- RIL ↓ -----------------------------------------------------------------------------------

    mCm.sendSMS(...)
    /open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java
    public void
        sendSMS (String smscPDU, String pdu, Message result)
        {
            RILRequest rr
                    = RILRequest.obtain(RIL_REQUEST_SEND_SMS, result);

            rr.mp.writeInt(2);
            rr.mp.writeString(smscPDU);
            rr.mp.writeString(pdu);

            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
            // RILJ 로그는 여기서 프린트 된다. "[...] > SEND_SMS"
            send(rr);
        }


    send(rr)
    /open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java
    private void
        send(RILRequest rr)
        {
            Message msg;

            msg = mSender.obtainMessage(EVENT_SEND, rr); // 메시지큐에서 메시지 가져오기

            acquireWakeLock(); // Holds a PARTIAL_WAKE_LOCK whenever
                                           (a) There is outstanding RIL request sent to RIL deamon and no
                                                replied
                                           (b) There is a request waiting to be sent out. There is a
                                                WAKE_LOCK_TIMEOUT to release the lock, though
                                                it shouldn't happen often.

            msg.sendToTarget(); // Sends this Message to the Handler specified by getTarget().
                                         // Handler adroid.os.Message.getTarget()
                                         // Retrieve the a handler implementation that will receive this
                                            message. The object must implement
                             Handler.handleMessage().
                                            Each Handler has its own name-space for message codes,
                                            so you do not need to worry about yours conflicting with other
                                            handlers.
        }






    Unsolicited Message


    Vendor RIL 에서 New Message가 왔음을 알린다.

    processUnsolicited (Parcel P)
    /open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java
    private void
        processUnsolicited (Parcel p)
        {
            int response;
            Object ret;
            
            /**
             * Read an integer value from the parcel at the current dataPosition().
             */
            response = p.readInt();

            try {switch(response) {

            /*
                  cat libs/telephony/ril_unsol_commands.h \
                  | egrep "^ *{RIL_" \
                  | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: \2(rr, p); break;/'
             */

        ...      
                 /**
                  * Read a string value from the parcel at the current dataPosition().
                  */                                                                ↑ p.readString()  
                case RIL_UNSOL_RESPONSE_NEW_SMS: ret =  responseString(p); break;
                case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: ret =  responseString(p);
                                                                                                       break;
                case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: ret =  responseInts(p); break;
        ...
                default:
                    throw new RuntimeException("Unrecognized unsol response: " + response);
                //break; (implied)
            }} catch (Throwable tr) {
                Log.e(LOG_TAG, "Exception processing unsol response: " + response +
                    "Exception:" + tr.toString());
                return;
            }

            switch(response) {
                case RIL_UNSOL_RESPONSE_NEW_SMS: {
                    if (RILJ_LOGD) unsljLog(response);
                                       //  ↑   D/RILJ    (  610): [UNSL]< UNSOL_RESPONSE_NEW_SMS
                                       // 로그 프린트

                    // FIXME this should move up a layer
                    String a[] = new String[2];

                    a[1] = (String)ret;

                    SmsMessage sms;

                    sms = SmsMessage.newFromCMT(a);                
                    if (mSMSRegistrant != null) {  
                        mSMSRegistrant
                            .notifyRegistrant(new AsyncResult(null, sms, null));
                    }
                break;
                }
            }
        }


    SmsMessage.newFromCMT(a)
    /open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java
    /**
         * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
         * +CMT unsolicited response (PDU mode, of course)
         *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
         *
         * Only public for debugging
         *
         * {@hide}
         */
    /* package */ 
    public static SmsMessage newFromCMT(String[] lines) {
        try {
            SmsMessage msg = new SmsMessage();
            msg.parsePdu(SimUtils.hexStringToBytes(lines[1]));
            return msg;
        } catch (RuntimeException ex) {
            Log.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
            return null;
        }
    }


    msg.parsePdu(...)
    /open_src/Frameworks/Base/Telephony/Gsm/SmsMessage.java
    /**
         * TS 27.005 3.1, <pdu> definition "In the case of SMS: 3GPP TS 24.011 [6]
         * SC address followed by 3GPP TS 23.040 [3] TPDU in hexadecimal format:
         * ME/TA converts each octet of TP data unit into two IRA character long
         * hexad number (e.g. octet with integer value 42 is presented to TE as two
         * characters 2A (IRA 50 and 65))" ...in the case of cell broadcast,
         * something else...
         */

    int mti; // TP-Message-Type-Indicator
              // 9.2.3
    String scAddress; /* The address of the SMSC. May be null */
       
    private void parsePdu(byte[] pdu) {
            mPdu = pdu;
            // Log.d(LOG_TAG, "raw sms mesage:");
            // Log.d(LOG_TAG, s);

            PduParser p = new PduParser(pdu);

            scAddress = p.getSCAddress();  
                                      
            /* int android.telephony.gsm.SmsMessage.PduParser.getByte() 에 의해 SCAddress길이를
             *  알수 있고, 이 integer값으로 if문을 통해
             *  PhoneNumberUtils.calledPartyBCDToString(pdu, cur, len) 메소드가 실행되고,
             *  String 타입의 리턴값을 받는다.
             */
            
            if (scAddress != null) {
                if (Config.LOGD) Log.d(LOG_TAG, "SMS SC address: " + scAddress);
            }

            // TODO(mkf) support reply path, user data header indicator

            // TP-Message-Type-Indicator
            // 9.2.3
            int firstByte = p.getByte(); /* return pdu[cur++] & 0xff; */
                                                                      
            /* 위의 PduParser 인스턴스 p 생성시 생성자에 의해 PduParser 클래스의 integer 형 변수
             * cur, mUserDataSeptetPadding 는 0 으로 셋팅된다.
             */

            mti = firstByte & 0x3;
            switch (mti) {
            // TP-Message-Type-Indicator
            // 9.2.3
            case 0:
                parseSmsDeliver(p, firstByte);
                break;
            case 2:
                parseSmsStatusReport(p, firstByte);
                break;
            default:
                // TODO(mkf) the rest of these
                throw new RuntimeException("Unsupported message type");
            }
    }


    parseSmsDeliver(p, firstByte)
    /open_src/Frameworks/Base/Telephony/Gsm/SmsMessage.java

    boolean replyPathPresent = false; // TP-Reply-Path
                                                  // e.g. 23.040 9.2.2.1
    SmsAddress originatingAddress; /* The address of the sender */
    int protocolIdentifier; /* TP-Protocol-Identifier (TP-PID) */
    int dataCodingScheme; // TP-Data-Coding-Scheme
                                    // see TS 23.038
        
    private void parseSmsDeliver(PduParser p, int firstByte) {
            replyPathPresent = (firstByte & 0x80) == 0x80;

            originatingAddress = p.getAddress();

            if (originatingAddress != null) {
                if (Config.LOGV) Log.v(LOG_TAG, "SMS originating address: "
                        + originatingAddress.address);
            }

            // TP-Protocol-Identifier (TP-PID)
            // TS 23.040 9.2.3.9
            protocolIdentifier = p.getByte();

            // TP-Data-Coding-Scheme
            // see TS 23.038
            dataCodingScheme = p.getByte();

            if (Config.LOGV) {
                Log.v(LOG_TAG, "SMS TP-PID:" + protocolIdentifier
                        + " data coding scheme: " + dataCodingScheme);
            }

            scTimeMillis = p.getSCTimestampMillis();
                                              
            // Parses an SC timestamp and returns a currentTimeMillis()-style
            // timestamp

            if (Config.LOGD) Log.d(LOG_TAG, "SMS SC timestamp: " + scTimeMillis);
                                                                    
                                                // D/GSM     (  610): SMS SC timestamp: 1265783527000
                                                // 로그 프린트
            boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;

            parseUserData(p, hasUserDataHeader); //Parses the User Data of an SMS.
        }


    parseUserData(p, hasUserDataHeader)
    /open_src/Frameworks/Base/Telephony/Gsm/SmsMessage.java
    private void parseUserData(PduParser p, boolean hasUserDataHeader) {
            boolean hasMessageClass = false;
            boolean userDataCompressed = false;

            int encodingType = ENCODING_UNKNOWN;

            // Look up the data encoding scheme
            if ((dataCodingScheme & 0x80) == 0) {
                // Bits 7..4 == 0xxx
                automaticDeletion = (0 != (dataCodingScheme & 0x40));
                userDataCompressed = (0 != (dataCodingScheme & 0x20));
                hasMessageClass = (0 != (dataCodingScheme & 0x10));

                if (userDataCompressed) {
                    Log.w(LOG_TAG, "4 - Unsupported SMS data coding scheme "
                            + "(compression) " + (dataCodingScheme & 0xff));
                } else {
                    switch ((dataCodingScheme >> 2) & 0x3) {
                    case 0: // GSM 7 bit default alphabet
                        encodingType = ENCODING_7BIT;
                        break;

                    case 2: // UCS 2 (16bit)
                        encodingType = ENCODING_16BIT;
                        break;

                    case 1: // 8 bit data
                    case 3: // reserved
                        Log.w(LOG_TAG, "1 - Unsupported SMS data coding scheme "
                                + (dataCodingScheme & 0xff));
                        encodingType = ENCODING_8BIT;
                        break;
                    }
                }
            } else if ((dataCodingScheme & 0xf0) == 0xf0) {
                automaticDeletion = false;
                hasMessageClass = true;
                userDataCompressed = false;

                if (0 == (dataCodingScheme & 0x04)) {
                    // GSM 7 bit default alphabet
                    encodingType = ENCODING_7BIT;
                } else {
                    // 8 bit data
                    encodingType = ENCODING_8BIT;
                }
            } else if ((dataCodingScheme & 0xF0) == 0xC0
                    || (dataCodingScheme & 0xF0) == 0xD0
                    || (dataCodingScheme & 0xF0) == 0xE0) {
                // 3GPP TS 23.038 V7.0.0 (2006-03) section 4

                // 0xC0 == 7 bit, don't store
                // 0xD0 == 7 bit, store
                // 0xE0 == UCS-2, store

                if ((dataCodingScheme & 0xF0) == 0xE0) {
                    encodingType = ENCODING_16BIT;
                } else {
                    encodingType = ENCODING_7BIT;
                }

                userDataCompressed = false;
                boolean active = ((dataCodingScheme & 0x08) == 0x08);

                // bit 0x04 reserved

                if ((dataCodingScheme & 0x03) == 0x00) {
                    isMwi = true;
                    mwiSense = active;
                    mwiDontStore = ((dataCodingScheme & 0xF0) == 0xC0);
                } else {
                    isMwi = false;

                    Log.w(LOG_TAG, "MWI for fax, email, or other "
                            + (dataCodingScheme & 0xff));
                }
            } else {
                Log.w(LOG_TAG, "3 - Unsupported SMS data coding scheme "
                        + (dataCodingScheme & 0xff));
            }

            // set both the user data and the user data header.
            int count = p.constructUserData(hasUserDataHeader,
                    encodingType == ENCODING_7BIT);
            this.userData = p.getUserData();
            this.userDataHeader = p.getUserDataHeader();

            switch (encodingType) {
            case ENCODING_UNKNOWN:
            case ENCODING_8BIT:
                messageBody = null;
                break;

            case ENCODING_7BIT:
                messageBody = p.getUserDataGSM7Bit(count);
                break;                              
               // Convert a GSM alphabet 7 bit packed string (SMS string) into a java.lang.String.
                   See TS 23.038 6.1.2.1 for SMS Character Packing
            case ENCODING_16BIT:
                messageBody = p.getUserDataUCS2(count);
                break;
            }

            if (Config.LOGV) Log.v(LOG_TAG, "SMS message body (raw): '" + messageBody + "'");

            if (messageBody != null) {
                parseMessageBody();
            }

            if (!hasMessageClass) {
                messageClass = MessageClass.UNKNOWN;
            } else {
                switch (dataCodingScheme & 0x3) {
                case 0:
                    messageClass = MessageClass.CLASS_0;
                    break;
                case 1:
                    messageClass = MessageClass.CLASS_1;
                    break;
                case 2:
                    messageClass = MessageClass.CLASS_2;
                    break;
                case 3:
                    messageClass = MessageClass.CLASS_3;
                    break;
                }
            }
    }


    parseMessageBody()
    /open_src/Frameworks/Base/Telephony/Gsm/SmsMessage.java
    private void parseMessageBody() {  // return address.length() <= 4;
            if (originatingAddress.couldBeEmailGateway()) {
                extractEmailAddressFromMessageBody();
            }
    }


    extractEmailAddressFromMessageBody();
    /open_src/Frameworks/Base/Telephony/Gsm/SmsMessage.java
    /**
         * Try to parse this message as an email gateway message -> Neither
         * of the standard ways are currently supported: There are two ways
         * specified in TS 23.040 Section 3.8 (not supported via this mechanism) -
         * SMS message "may have its TP-PID set for internet electronic mail - MT
         * SMS format: [<from-address><space>]<message> - "Depending on the
         * nature of the gateway, the destination/origination address is either
         * derived from the content of the SMS TP-OA or TP-DA field, or the
         * TP-OA/TP-DA field contains a generic gateway address and the to/from
         * address is added at the beginning as shown above." - multiple addreses
         * separated by commas, no spaces - subject field delimited by '()' or '##'
         * and '#' Section 9.2.3.24.11
         */
    private void extractEmailAddressFromMessageBody() {

            /*
             * a little guesswork here. I haven't found doc for this.
             * the format could be either
             *
             * 1. [x@y][ ]/[subject][ ]/[body]
             * -or-
             * 2. [x@y][ ]/[body]
             */
            int slash = 0, slash2 = 0, atSymbol = 0;

            try {
                slash = messageBody.indexOf(" /");
                if (slash == -1) {
                    return;
                }

                atSymbol = messageBody.indexOf('@');
                if (atSymbol == -1 || atSymbol > slash) {
                    return;
                }

                emailFrom = messageBody.substring(0, slash);

                slash2 = messageBody.indexOf(" /", slash + 2);

                if (slash2 == -1) {
                    pseudoSubject = null;
                    emailBody = messageBody.substring(slash + 2);
                } else {
                    pseudoSubject = messageBody.substring(slash + 2, slash2);
                    emailBody = messageBody.substring(slash2 + 2);
                }

                isEmail = true;
            } catch (Exception ex) {
                Log.w(LOG_TAG,
                        "extractEmailAddressFromMessageBody: exception slash="
                        + slash + ", atSymbol=" + atSymbol + ", slash2="
                        + slash2, ex);
            }
    }


    여기까지 parse 작업이 완료되면,
    SmsMessage.newFromCMT(a) {} 부분의 return msg 부분이 실행되어
    processUnsolicited (Parcel P) {} 부분의 밑부분의 if문이 실행된다.
    /open_src/frameworks/base/telephony/java/com/android/internal/telephony/gsm/RIL.java
    private void processUnsolicited (Parcel p)
    {
    ...
       
        if (mSMSRegistrant != null) {  
            mSMSRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));
            
        /* mSMSRegistrant 는 Registrant의 인스턴스, 정의되어있는 곳은 RIL이 상속하고 있는
         * BaseCommands 이다. 인스턴스화된 곳을 찾아 올라가면
         * BaseCommands의 메소드인 setOnNewSMS()에서 생성됨을 알수 있고, setOnNewSMS()를
         * 호출한 곳을 역으로 올라가면,
         * setOnNewSMS()
    SMSDispatcher GSMPhone       *   ⇠ PhoneFactory.makeDefaultPhones() ⇠ PhoneApp.onCreate()  
          
    *    PhoneApp 는 system의 server thread가 생성하였다.
         */
            
    ...
    }


    mSMSRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));
    /open_src/frameworks/base/Core/Java/Os/Registrant.java
    /**
     * This makes a copy of @param ar
     */

    public void notifyRegistrant(AsyncResult ar)
    {
        internalNotifyRegistrant (ar.result, ar.exception);
    }

        


    internalNotifyRegistrant(ar.result, ar.exception);
    /open_src/frameworks/base/Core/Java/Os/Registrant.java
    /*package*/

    void internalNotifyRegistrant (Object result, Throwable exception)
    {
            Handler h = getHandler();

            if (h == null) {
                clear();
            } else {                         //   Return a new Message instance from the global pool.
                Message msg = Message.obtain();

                msg.what = what;
                
                msg.obj = new AsyncResult(userObj, result, exception);
                
                h.sendMessage(msg); 
           }    
    }


    h.sendMesage(msg)
    /open_src/frameworks/base/Core/Java/Android/Os/Handler.java
    /**
         * Pushes a message onto the end of the message queue after all pending messages
         * before the current time. It will be received in {@link #handleMessage},
         * in the thread attached to this handler.
         *  /

    public final boolean sendMessage(Message msg)
    {
            return sendMessageDelayed(msg, 0);
    }


    sendMessageDelayed(msg, 0)
    /open_src/frameworks/base/Core/Java/Android/Os/Handler.java
    /**
         * Enqueue a message into the message queue after all pending messages
         * before (current time + delayMillis). You will receive it in
         * {@link #handleMessage}, in the thread attached to this handler.
         *  /

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }


    sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    /open_src/frameworks/base/Core/Java/Android/Os/Handler.java
       /**
         * Enqueue a message into the message queue after all pending messages
         * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
         * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
         * You will receive it in {@link #handleMessage}, in the thread attached
         * to this handler.
         */ 

    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
            boolean sent = false;
            MessageQueue queue = mQueue;
            if (queue != null) {
                msg.target = this;
                sent = queue.enqueueMessage(msg, uptimeMillis);
                /* 메시지 전송 delay를 설정해주고, 메시지 큐에 넣는다.
                 * 메시지큐에 메시지가 들어오면 시스템에서 handleMassage()를 실행한다.
                 * handleMessage()는 Handler를 상속한 SMSDiapatcher에서 오버라이딩된
                 * handleMessage()가 실행된다.
                 * delay설정은 시스템의 스케줄링에 도움을 주기 위해서 이다.
            }
            else {
                RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
            }
            return sent;
    }


    handelMessage(msg)
    /open_src/frameworks/Base/Telephony/Java/Com/Android/Internal/Telephony/Gsm/
    SMSDispatcher.java
    /**
         * Handles events coming from the phone stack. Overridden from handler.
         *
         * @param msg the message to handle
         */

        @Override
        public void handleMessage(Message msg) {
            AsyncResult ar;

            switch (msg.what) {
            case EVENT_NEW_SMS:
                // A new SMS has been received by the device
                if (Config.LOGD) {
                    Log.d(TAG, "New SMS Message Received");

                                   //    D/GSM     (  610): New SMS Message Received

                }                 // 로그 프린트

                SmsMessage sms;

                ar = (AsyncResult) msg.obj;

                // FIXME unit test leaves cm == null. this should change
                if (mCm != null) {
                    // FIXME only acknowledge on store
                    mCm.acknowledgeLastIncomingSMS(true, null);
                }

                if (ar.exception != null) {
                    Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception);
                    return;
                }

                sms = (SmsMessage) ar.result;
                dispatchMessage(sms);

                break;

            case EVENT_SEND_SMS_COMPLETE:
                // An outbound SMS has been sucessfully transferred, or failed.
                handleSendComplete((AsyncResult) msg.obj);
                break;

            case EVENT_SEND_RETRY:
                sendSms((SmsTracker) msg.obj);
                break;

            case EVENT_NEW_SMS_STATUS_REPORT:
                handleStatusReport((AsyncResult)msg.obj);
                break;

            case EVENT_SIM_FULL:
                handleSimFull();
                break;

            case EVENT_POST_ALERT:
                handleReachSentLimit((SmsTracker)(msg.obj));
                break;

            case EVENT_ALERT_TIMEOUT:
                ((AlertDialog)(msg.obj)).dismiss();
                msg.obj = null;
                mSTracker = null;
                break;

            case EVENT_SEND_CONFIRMED_SMS:
                if (mSTracker!=null) {
                    if (isMultipartTracker(mSTracker)) {
                        sendMultipartSms(mSTracker);
                    } else {
                        sendSms(mSTracker);
                    }
                    mSTracker = null;
                }
                break;
            }
        }


    dispatchMessage(msg)
    /open_src/frameworks/Base/Telephony/Java/Com/Android/Internal/Telephony/Gsm/
    SMSDispatcher.java
    /**
         * Dispatches an incoming SMS messages.
         *
         * @param sms the incoming message from the phone
         */
       
     /* package */

    void dispatchMessage(SmsMessage sms) {
            // If sms is null, means there was a parsing error.
            // TODO: Should NAK this.
            if (sms == null) {
                return;
            }

            boolean handled = false;

            // Special case the message waiting indicator messages
            if (sms.isMWISetMessage()) {
                mPhone.updateMessageWaitingIndicator(true);

                if (sms.isMwiDontStore()) {
                    handled = true;
                }

                if (Config.LOGD) {
                    Log.d(TAG,
                            "Received voice mail indicator set SMS shouldStore="
                             + !handled);
                }
            } else if (sms.isMWIClearMessage()) {
                mPhone.updateMessageWaitingIndicator(false);

                if (sms.isMwiDontStore()) {
                    handled = true;
                }

                if (Config.LOGD) {
                    Log.d(TAG,
                            "Received voice mail indicator clear SMS shouldStore="
                            + !handled);
                }
            }

            if (handled) {
                return;
            }

            // Parse the headers to see if this is partial, or port addressed
            int referenceNumber = -1;
            int count = 0;
            int sequence = 0;
            int destPort = -1;

            SmsHeader header = sms.getUserDataHeader();
            if (header != null) {
                for (SmsHeader.Element element : header.getElements()) {
                    try {
                        switch (element.getID()) {
                            case SmsHeader.CONCATENATED_8_BIT_REFERENCE: {
                                byte[] data = element.getData();
                                
                                referenceNumber = data[0] & 0xff;
                                count = data[1] & 0xff;
                                sequence = data[2] & 0xff;
                                
                                // Per TS 23.040, 9.2.3.24.1: If the count is zero, sequence
                                // is zero, or sequence > count, ignore the entire element
                                if (count == 0 || sequence == 0 || sequence > count) {
                                    referenceNumber = -1;
                                }
                                break;
                            }
                            
                            case SmsHeader.CONCATENATED_16_BIT_REFERENCE: {
                                byte[] data = element.getData();
                                
                                referenceNumber = (data[0] & 0xff) * 256 + (data[1] & 0xff);
                                count = data[2] & 0xff;
                                sequence = data[3] & 0xff;
                                
                                // Per TS 23.040, 9.2.3.24.8: If the count is zero, sequence
                                // is zero, or sequence > count, ignore the entire element
                                if (count == 0 || sequence == 0 || sequence > count) {
                                    referenceNumber = -1;
                                }
                                break;
                            }
                            
                            case SmsHeader.APPLICATION_PORT_ADDRESSING_16_BIT: {
                                byte[] data = element.getData();
                                
                                destPort = (data[0] & 0xff) << 8;
                                destPort |= (data[1] & 0xff);
                                
                                break;
                            }
                        }
                    } catch (ArrayIndexOutOfBoundsException e) {
                        Log.e(TAG, "Bad element in header", e);
                        return;  // TODO: NACK the message or something, don't just discard.
                    }
                }
            }

            if (referenceNumber == -1) {
                // notify everyone of the message if it isn't partial
                byte[][] pdus = new byte[1][];
                pdus[0] = sms.getPdu();

                if (destPort != -1) {
                    if (destPort == SmsHeader.PORT_WAP_PUSH) {
                        mWapPush.dispatchWapPdu(sms.getUserData());
                    }
                    // The message was sent to a port, so concoct a URI for it
                    dispatchPortAddressedPdus(pdus, destPort);
                } else {
                    // It's a normal message, dispatch it
                    dispatchPdus(pdus);
                }
            } else {
                // Process the message part
                processMessagePart(sms, referenceNumber, sequence, count, destPort);
            }
        }


    dispatchPdus(pdus)
    /open_src/frameworks/Base/Telephony/Java/Com/Android/Internal/Telephony/Gsm/
    SMSDispatcher.java
    /**
         * Dispatches standard PDUs to interested applications
         *
         * @param pdus The raw PDUs making up the message
         */

    private void dispatchPdus(byte[][] pdus) {
            Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION);
            intent.putExtra("pdus", pdus);
            sendBroadcast(intent, "android.permission.RECEIVE_SMS");
    }


    sendBroadcast(intent, "android.permission.RECEIVE_SMS");
    /open_src/frameworks/Base/Telephony/Java/Com/Android/Internal/Telephony/Gsm/
    SMSDispatcher.java
    private void sendBroadcast(Intent intent, String permission) {
            // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any
            // receivers time to take their own wake locks.
            
            mWakeLock.acquire(WAKE_LOCK_TIMEOUT); 
            /* 메소드를 타고 들어가면, delay 주고, 핸들러의 메시지큐에 메시지를 넣는다.
             *  acquire mHandler.postDelayed() ⇠ sendMessageDelayed()                
             *  ⇠ sendMessageAtTime ⇠ enqueueMessage()
             */
            // 메시지큐에 메시지가 왔음으로 handleMessage()가 실행된다.
            mContext.sendBroadcast(intent, permission);
              // BroadcastReceiver 실행된다.
    }


    handleMessage(Message msg)
    /open_src/Packages/Apps/Src/Com/Android/Mms/Transactions/SmsReceiverService.java
    /**
             * Handle incoming transaction requests.
             * The incoming requests are initiated by the MMSC Server or by the
             * MMS Client itself.
             */
            
    @Override
    public void handleMessage(Message msg) {
                if (Log.isLoggable(MmsApp.LOG_TAG, Log.VERBOSE)) {
                    Log.v(TAG, "Handling incoming message: " + msg);
                }
                int serviceId = msg.arg1;
                Intent intent = (Intent)msg.obj;

                String action = intent.getAction();

                if (MESSAGE_SENT_ACTION.equals(intent.getAction())) {
                    handleSmsSent(intent);
                } else if (SMS_RECEIVED_ACTION.equals(action)) {
                    handleSmsReceived(intent);
                } else if (ACTION_BOOT_COMPLETED.equals(action)) {
                    handleBootCompleted();
                } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
                    handleServiceStateChanged(intent);
                }

                // NOTE: We MUST not call stopSelf() directly, since we need to
                // make sure the wake lock acquired by AlertReceiver is released.
                SmsReceiver.finishStartingService(SmsReceiverService.this, serviceId);
    }


    handleSmsReceived(intent)
    /open_src/Packages/Apps/Src/Com/Android/Mms/Transactions/SmsReceiverService.java
    private void handleSmsReceived(Intent intent) {
            SmsMessage[] msgs = Intents.getMessagesFromIntent(intent);
            Uri messageUri = insertMessage(this, msgs);

            if (messageUri != null) {
                MessagingNotification.updateNewMessageIndicator(this, true);
            }
    }


    MessagingNotification.updateNewMessageIndicator(this, true)
    Open_src/Package/Apps/Mms/Src/Com/Android/Mms/Transaction/MessagingNotification.java
    /**
         * Checks to see if there are any unread messages or delivery
         * reports.  Shows the most recent notification if there is one.
         *
         * @param context the context to use
         * @param isNew if notify a new message comes, it should be true, otherwise, false.
         */
    public static void updateNewMessageIndicator(Context context, boolean isNew) {
            SortedSet<MmsSmsNotificationInfo> accumulator =
                    new TreeSet<MmsSmsNotificationInfo>(INFO_COMPARATOR);
            Set<Long> threads = new HashSet<Long>(4);
            
            int count = 0;
            count += accumulateNotificationInfo(
                    accumulator, getMmsNewMessageNotificationInfo(context, threads));
            count += accumulateNotificationInfo(
                    accumulator, getSmsNewMessageNotificationInfo(context, threads));

            cancelNotification(context, NOTIFICATION_ID);
            if (!accumulator.isEmpty()) {
                accumulator.first().deliver(context, isNew, count, threads.size());
            }
        }


    accumulator.first().deliver(context, isNew, count, threads.size())
    Open_src/Package/Apps/Mms/Src/Com/Android/Mms/Transaction/MessagingNotification.java
    public void deliver(Context context, boolean isNew, int count, int uniqueThreads) {
                updateNotification(
                        context, mClickIntent, mDescription, mIconResourceId,
                        isNew, mTicker, mTimeMillis, mTitle, count, uniqueThreads);
            }


    updateNotification(...)
    Open_src/Package/Apps/Mms/Src/Com/Android/Mms/Transaction/MessagingNotification.java
    private static void updateNotification(
                Context context,
                Intent clickIntent,
                String description,
                int iconRes,
                boolean isNew,
                CharSequence ticker,
                long timeMillis,
                String title,
                int messageCount,
                int uniqueThreadCount) {
            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);

            if (!sp.getBoolean(
                        MessagingPreferenceActivity.NOTIFICATION_ENABLED, true)) {
                return;
            }

            Notification notification = new Notification(iconRes, ticker, timeMillis);

            // If we have more than one unique thread, change the title (which would
            // normally be the contact who sent the message) to a generic one that
            // makes sense for multiple senders, and change the Intent to take the
            // user to the conversation list instead of the specific thread.
            if (uniqueThreadCount > 1) {
                title = context.getString(R.string.notification_multiple_title);
                clickIntent = getAppIntent();
                clickIntent.setAction(Intent.ACTION_MAIN);
                clickIntent.setType("vnd.android-dir/mms-sms");
            }
            
            // If there is more than one message, change the description (which
            // would normally be a snippet of the individual message text) to
            // a string indicating how many unread messages there are.
            if (messageCount > 1) {
                description = context.getString(R.string.notification_multiple,
                        Integer.toString(messageCount));
            }

            // Make a startActivity() PendingIntent for the notification.
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, clickIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT);

            // Update the notification.
            notification.setLatestEventInfo(context, title, description, pendingIntent);

            if (isNew) {
                boolean vibrate =     
                        sp.getBoolean(MessagingPreferenceActivity.NOTIFICATION_VIBRATE, true);
                if (vibrate) {
                    notification.defaults |= Notification.DEFAULT_VIBRATE;
                }

                String ringtoneStr = sp
                        .getString(MessagingPreferenceActivity.NOTIFICATION_RINGTONE, null);
                notification.sound = TextUtils.isEmpty(ringtoneStr) ? null : Uri.parse(ringtoneStr);
            }

            notification.flags |= Notification.FLAG_SHOW_LIGHTS;
            notification.ledARGB = 0xff00ff00;
            notification.ledOnMS = 500;
            notification.ledOffMS = 2000;

            NotificationManager nm = (NotificationManager)
                context.getSystemService(Context.NOTIFICATION_SERVICE);

            nm.notify(NOTIFICATION_ID, notification);
    }

    Posted by jazzlife
    ,

    aidl

    old/Basic 2010. 7. 6. 14:25
    1. 인터페이스(aidl) 생성
    2. 구현클래스 생성
    3. 메니페스트에 적고 
    4. 엑티비티에서 해당 클래스를가져와서
    5. 구현된 함수를 사용.

    의 순서로 진행됩니다.

    사실 엑티비티 레벨이니 서비스 레벨이니 신경안써도 돼는 작은 어플에서는 샤용할일이 없을듯.


    1. aidl
    package hell.o;
    interface IPlusItService {
    int add(int a, int b);
    }


    2. impl class
    package hell.o;

    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.RemoteException;

    public class PlusItService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
    if (IPlusItService.class.getName().equals(intent.getAction())) {
    return plusItServiceIf;
    }
    return null;
    }

    private IPlusItService.Stub plusItServiceIf = new IPlusItService.Stub() {
    @Override
    public int add(int a, int b) throws RemoteException {
    return a + b;
    }
    };
    }


    3. manifest

            <service android:name="PlusItService">
    <intent-filter>
    <action android:name="hell.o.IPlusItService"></action>
    </intent-filter>
    </service>


    4. class load
     
        private IPlusItService plusItServiceIf
    먼저 서비스 인터페이스를 담을 변수를 선언
    	Intent intent = new Intent(IPlusItService.class.getName());
    bindService(intent, new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
    plusItServiceIf = IPlusItService.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
    plusItServiceIf = null;
    }
    }, BIND_AUTO_CREATE);
    온크리에이트에 넣을 내용. (상식이있는 인간이라면 ServiceConnection을 밖으로 빼겠지만 졸려죽겠는데 그런거없다능.)


    5. use
    	int sum = plusItServiceIf.add(a, b);
    요거 한줄하려고 이고생을!

    지금 생각해보니 평소하던데로
    XxxService , XxxServiceImpl이라는 편이 차라리 읽기 편하지 싶음.
    I를 앞에붙여서 인터페이스임을 표시하는게 어느동네 표기법이더라?

    'old > Basic' 카테고리의 다른 글

    Android Bitmap Object Resizing Tip  (0) 2010.07.06
    Handler  (0) 2010.07.06
    zygote & Dalvik VM  (0) 2010.07.06
    안드로이드 HAL - RIL(Radio Interface Layer)  (0) 2010.07.06
    안드로이드 초기화 (init 프로세스와 기타 서비스 등록)  (0) 2010.07.06
    Posted by jazzlife
    ,

    zygote & Dalvik VM

    old/Basic 2010. 7. 6. 14:23
    안드로이드의 사상입니다. 모든 플랫폼에서 가장 중요한 것이 사상인것 같습니다. 사상이 바르고 공정해야 그 플랫폼 위에서 움직이는 어플리케이션이 바르고 안정적으로 움직일 수 있습니다.

    안드로이드가 뛰어난 플랫폼이라고 생각되는 부분이 바로 이 바르고 공정한 플랫폼이라는 점입니다.

    안드로이드를 처음 구동하면 홈 스크린(Home Screen)이란 것을 구동합니다. 즉 바탕화면 관리자 정도 되나요? 여튼, 이 홈 스크린을 통하여 다른 어플리케이션에 접근할 수 있습니다. 바탕화면에서 아이콘을 통하여 자신이 원하는 어플리케이션에 접근하는 개념이란 동일하죠..

    헌데, 이 홈 스크린도 구글이 제공해주는 것을 사용할 수도 있고, 여러분들이 만든 것을 사용할 수 있습니다.

    출처: http://www.jco.or.kr/c?mc=conference&sc=pds 중 "[2009, 10회] Dolphin-4 Android Run-time (Dalvik VM & Core Lib) 분석" 에서


    이 스크린 샷이 컨퍼런스에서 본 화면인데요~ 잘 보시면 홈을 선택하게 되어 있습니다. 홈 스크린은 나름대로 중요한 위치에 있는 어플리케이션입니다. 홈 스크린이 고정되어 있다면, 사실 여러분들이 쓰고 싶지 않은 어플리케이션도 홈 스크린에 존재한다는 이유로 써야할 수 있습니다.

    대표적으로 원도우스(Windows)에 기본 탑재되었던 인터넷 익스플로러(Internet Explorer)입니다. 이 인터넷 익스플로어는 제 기억에 분명히 넷스케이프보다 성능이 떨어지는 웹 브라우져 어플리케이션이었습니다. 하지만 성능이 떨어져도 기본 탑재되어 있기 때문에 자연스럽게 사용하기 시작했습니다. 넷스케이프는 다시 깔아서 써야 했기 때문이죠. 그 덕분에 인터넷 익스플로러가 세계에서 가장 많이 사용되는 웹 브라우져가 된 것입니다.

    홈 스크린에 어플리케이션을 자연스럽게 껴서 넣는다면 자연스럽게 어플리케이션 사용율이 높아질 것이고 해당 분야에 세계 최고의 어플리케이션이 될 수도 있는 것입니다.

    하지만, 구글은 역시 구글답게 홈 스크린도 선택하도록 하였습니다. 여러분들이 좋아하는 어플리케이션을 구동하기 쉽도록 재 구성한 홈 스크린을 사용할 수 있다는 것입니다. 아... 얼마나 구글다운 오픈 마인드(Open Mind)입니까?

    사실 전 이 사실 하나만으로도 안드로이드의 장래가 매우 밝다고 생각합니다. 모든 것을 열어놓고 시작하는 이 정신이 안드로이드의 가치를 빛나게 해줍니다.



    두번째로 재미있는 개념이 Zygote란 개념입니다.


    출처: http://www.jco.or.kr/c?mc=conference&sc=pds 중 "[2009, 10회] Dolphin-4 Android Run-time (Dalvik VM & Core Lib) 분석" 에서



    상기 그림에서도 설명이 나와 있는데요, 미리 Fork된 프로세스이지만, 마치 빈 껍대기 정도의 상태로 존재하는 프로세스입니다. 그리고 어플리케이션의 Run될때 바로 이 Zygote를 통해서 안드로이드 기반의 프로세스를 바로 실행시키는 구조입니다. 아~ 멋집니다.

    미리 공통적으로 필요한 라이브러리들을 탑재한 상태로 반쯤 생성된 프로세스에 실제 핵심이 되는 어플리케이션의 로직을 태워, 해당 어플리케이션을 구동시키는 방법이라고 할 수 있습니다. 당근 어플리케이션 구동 속도가 엄청 빨라지겠죠~

    사실 안드로이드에 관심있는 분이라면 한번쯤 들어보셨을 것입니다. 저도 들어는 봤었지만, 정확한 개념이해가 되지 않았는데, 이번 세션을 통하여 명확하게 이해할 수 있었습니다. 아싸~ 하나 건졌다 :-)



    또 하나의 재미있는 부분이 달빅(Dalvik)이란 가상머신(Virtual Machine)에 관한 개념입니다.
    달빅 자체는 매우 아래와 같은 환경을 고려한 Bytecode Interpreter입니다.
    • Slow CPU (250-500 MHz)
    • RAM Usage : Low-level : 20M, High-level : 24M (system library : 10M)
    • Little RAM (64MB) : Available RAM : 20M
    • Bus speed : 100MHz
    • Data Cache : 16~32K
    • No swap space,  Battery power

    역시 혹독한 임베디드 환경을 고려하고 있네요.. 제가 모바일 어플리케이션을 작성할때 환경보다는 조금 나아졌습니다. 8 Gray 폰이나 16Gray 폰에 64Kbyte짜리 어플리케이션을 짜던 환경말이죠..

    이러한 느린 CPU위에 돌아간다는 가정하에 달빅이란 가상머신을 돌리기 위한 최적화의 노력이 눈물겹습니다.



    상기 Java 코드를 Bytecode로 변환하였을 경우에 Java VM과 Dalvik VM의 차이입니다.
    Dalvik VM의인스트럭션이 상당히 적습니다. 즉 CPU가 처리해야 할 일이 그만큼 적다는 의미입니다.

    일반적인 Java VM은 스택에 인스트럭션을 쌓아서 하나 하나씩 처리하는 반면, Dalvik VM은 ARM 프로세서에 최적화하여 인스트럭션 수를 줄였습니다. 즉 이터레이션(Iteration)을 한다는 의미인데요, 이터레이션을 할때 스택에 쌓을 것들을 변수로 처리하여, CPU의 연산횟수를 줄이는대신 인자는 늘어나게 됩니다. 결과적으로는 CPU의 처리 사이클이 줄어들게되지만, 스택대신 이터레이션을 사용하므로 Dalvik VM이 각 CPU 프로세서에 최적화되어 있어야 가능합니다.

    이런 일들을 멋지게 처리해 놓았습니다. 앞으로 안드로이드의 성능도 좋을 것 같습니다.
    이 외에도 여러 재미있는 이야기들을 들었습니다. 깊이있는 내용을 차근 차근히 잘 설명해주신 양정수님에게 감사드립니다.

    참고로 양정수님은 Korea Adroid(http://www.kandroid.org)의 운영자이시기도 합니다.



    'old > Basic' 카테고리의 다른 글

    Handler  (0) 2010.07.06
    aidl  (0) 2010.07.06
    안드로이드 HAL - RIL(Radio Interface Layer)  (0) 2010.07.06
    안드로이드 초기화 (init 프로세스와 기타 서비스 등록)  (0) 2010.07.06
    The Froyo Code Drop  (0) 2010.07.06
    Posted by jazzlife
    ,

    RIL 이란 무엇인가?

    개요.
     
    안드로이드 RIL은 android.telephony 서비스들과 radio h/w간의 추상화된 계층을 제공한다.
    RIL은 radio agnostic(존재하지만 정확하게 표현하기는 어려운 실체)이며, GSM 기반의 radio들에 대한 지원을 포함하고
    있다.
     
    아래의 그림은 안드로이드 전화통화 시스템 구조를 설명한다.
     
       
     
    위의 그림에서 Radio Daemon과 Vender RIL은 파트너의 특수성을 가진 사적소유영역에 속하며, 나머지 블록은
    안드로이드 영역에 속한다고 할 수 있다.
     
    RIL은 두가지의 기본적인 컴포넌트로 구성된다.
     
    RIL 데몬(Daemon) : 
     
    RIL 데몬은 Vender RIL을 초기화하며, 안드로이드 telephony 서비스들로부터의 모든 통신을 프로세스하며,
    solicited 명령어를 통해 Vender RIL로 콜을 넘긴다.
     
    Vender RIL :
     
    ril.h의 radio 특수성을 포함하는 Vender RIL은 radio h/w와의 모든 통신을 처리하며, unsolicited 명령어들을 통해서
    RIL 데몬(rild)에 콜을 넘긴다.
     
    RIL 초기화
     
    안드로이드는 구동시점에 아래에 언급된 순서대로, telephony 스택과 Vender RIL 을 초기화한다.
     
    1. RIL 데몬은 Vender RIL에 제공해야할 초기화 argument와 그것을 사용하기 위한 Vender RIL 라이브러리를
       결정하기 위한 rild.lib 경로와 rild.libargs 시스템 속성들을 읽어들인다.
     
    2. RIL 데몬은 Vender RIL 라이브러리를 load(탑재)하며, RIL을 초기화하기 위해 RIL_Init을 호출하며,
        RIL 함수에 대한 참조를 얻는다.
     
    3. RIL 데몬은 Vender RIL 함수들에 대한 하나의 참조를 제공하는 안드로이드 Telephony 스택상의  
        RIL_register를 호출한다.
     
    RIL 상호작용
     
    RIL 제어 관련 통신에는 두가지 형식이 존재한다.
     
    Solicited commands : Solicited 명령어들은 전화걸기(DIAL) 과 HANGUP(전화끊기)와 같은 RIL 라이브러리에 의해
                                    만들어 진다.
    Unsolicited responses : Unsolicited responses는 CALL_STATE_CHNGED(전화 상태 변화)와 NEW_SMS(새 문자
                                       메시지)와 같은 baseband로 부터 만들어 진다.
     
    Solicited
     
    아래의 인용은 solicited 명령어들에 대한 인터페이스를 보여주고 있다.
     
    void OnRequest (int request_id, void *data, size_t datalen, RIL_Token t);
    void OnRequestComplete (RIL_Token t, RIL_Error e, void *response, size_t responselen);

     
    그리고 아래와 같이 계열들로 묶여질 수 있는 60개 이상의 solicited 명령어들이 존재한다.
     
    • SIM PIN, IO, and IMSI/IMEI (11)
    • Call status and handling (dial, answer, mute…) (16)
    • Network status query (4)
    • Network setting (barring, forwarding, selection…) (12)
    • SMS (3)
    • PDP connection (4)
    • Power and reset (2)
    • Supplementary Services (5)
    • Vendor defined and support (4)
    아래의 다이어그램은 안드로이드에서의 solicited call을 설명하고 있다.
     
     
     
    Unsolicited
     
    아래의 인용은 unsolicited 명령어들에 대한 인터페이스를 설명하고 있다.
     
    void OnUnsolicitedResponse (int unsolResponse, void *data, size_t datalen);

     
    그리고 아래와 같이 계열들로 묶여질 수 있는 10개 이상의 unsolicited 명령어들이 존재한다.
     
    • Network status changed (4)
    • New SMS notify (3)
    • New USSD notify (2)
    • Signal strength or time changed (2)
    아래의 다이어그램은 안드로이드에서의 unsolicited call을 설명하고 있다.
     
     
     
    RIL 구현하기
     
    특정 radio RIL을 구현하기 위해서는, 라디오 요청을 처리하기 위해 안드로이드에서 요구되는 함수의 집합을 구현하는
    공유 라이브러리를 만들어야 한다. 요구되는 함수들은 RIL header (/include/telephony/ril.h)에 정의된다.
     
    안드로이드 radio 인터페이스는 radio-agnostic이며, Vender RIL은 radio를 통해 통신하기 위한 어떤 프로토콜도
    사용할 수 있다. 안드로이드는 하나의 참조적 구현형태로써의 Vender RIL을 제공하고 있는데, 그것은 Hayes AT
    command 집합을 사용하고 있으며, 여러분들은 telephony 테스트를 위한 쉬운 시작과 상업적 Vender RIL 들을 위한
    가이드로써 사용할 수 있다. 참조적으로 구현된 RIL 소스 코드는 /commands/reference-ril/ 에서 볼 수 있다.
     
    libril-<companyname>-<RIL version>.so 규약을 사용해서 공유 라이브러리 형태로 여러분의 Vender RIL을 컴파일
    해라. 여기에 예로써, libril-acme-124.so가 있다.
    • libril: 모든 vender RIL 구현은 'libril' 로 시작
    • <companyname>: 특정 회사의 약어
    • <RIL version>: RIL 버전 숫자
    • so: 파일 확장자
    RIL_Init
     
    여러분의 Vender RIL은 모든 radio 요청들을 처리할 함수들에 대한 제어를 제공하는 RIL_Init 함수를 정의해야 한다.
    RIL_Init은 RIL을 초기화하기 위해, 부트시점에서 안드로이드 RIL Daemon에 의해 호출된다.
     
    RIL_RadioFunctions *RIL_Init (RIL_Env* env, int argc, char **argv);

     
    RIL_Init은 radio 함수들에 대한 제어를 포함하는 RIL_RadioFuncions 구조체를 반환해야 한다.
     
    type structure {
           int RIL_version;
           RIL_RequestFunc onRequest;
           RIL_RadioStateRequest onStateRequest;      
           RIL_Supports supports;
           RIL_Cancel onCancel;
           RIL_GetVersion getVersion;
    }
    RIL_RadioFunctions;

     
     
    RIL 함수들
     
    ril.h는 아래의 테이블에 설명된 함수들 같은, RIL_UNSOL_STK_CALL_SETUP, RIL_SIM_READY, RIL_SIM_NOT_READY 과
    같은 RIL 상태들과 변수들을 정의한다. 더 상세하게는 헤더파일(/device/incldue/telephony/ril.h)을 살펴보길.
     
    RIL Solicited 명령어 요청들.
     
    Vender RIL은 solicited 명령어들을 처리하기 위해 아래의 테이블에 설명된 함수들을 제공해야 한다.
    RIL solicited 명령어 요청 타입들은 RIL_REQUEST_ 라는 접두사를 가지고 ril.h에 정의된다.
    상세하게는 헤더 파일을 체크하길.
     
    함수명 설명
    void (*RIL_RequestFunc) (int request, void *data, size_t datalen, RIL_Token t); 이것은 solicited 명령어들에 대한 시작 포인트이며, RIL_REQUST_ 접두사를 가지고 ril.h에 정의된 다양한 RIL solicited 요청 타입들을 제어할 수 있어야 한다.
    • request 는 RIL_REQUEST_* 중 하나임.
    • data 는 RIL_REQUST_* 위해 정의된 data에 대한 포인터임.
    • t 는 RIL_onResponse 에 수반되는 호출에서 사용되어야 함.
    • datalen 은 caller 소유이며, callee에 의해 수정되거나 free 되어서는 안됨.
    RIL_onRequestComplete()에 대한 호출을 통해 완료되어야 함.
    RIL_onRequestComplete()는 임의의 쓰레드로부터 이 함수의 반환 이전 또는 이후에 호출될 수 있다. 이것은 항상 동일한 쓰레드로부터 호출될 것이며, 그러므로 여기에서 반환이라함은 radio가 다른 명령을 수행할 준비하 되어있음을 의미한다.(이전 명령이 완료여부와 상관없이)
    RIL_RadioState (*RIL_RadioStateRequest)(); 이 함수는 현재의 radio 상태를 동기적으로 반환해야 한다.
    int (*RIL_Supports)(int requestCode); 이 함수는 명시된 RIL_REQUEST 코드가 지원된다면 "1"을 아니라면 "0"을 반환한다.
    void (*RIL_Cancel)(RIL_Token t); 이 함수는 멈춰져 있는 요청을 취소시키기 위해 사용된다.
    이 함수는 다른 쓰레드, 즉 RIL_RequestFunc 을 호출한 쓰레드가 아닌 쓰레드에 의해 호출된다.
     
    취소시, callee는 요청을 포기하기 위해 최선을 다해야하며, 잠시후 RIL_Errno CALCELLED 를 가지고 RIL_onRequestComplete를 호출한다.
     
    다른 결과들을 가진 이 요청들에 대한 RIL_onRequestComplete에 대한 수반되는 호출들은 묵인되지만, 무시된다.(그것은 취소 요청을 무시하기 위해 유효한 것이다)
     
    RIL_Cancel 호출들은 즉시 반환되어야 하며, 취소를 기다려서는 안된다.
    const char * (*RIL_GetVersion) (void); 여러분의 Vender RIL에 대한 버젼 문자열을 반환한다.
     
    Vender RIL은 안드로이드 RIL 데몬에 역으로 통신하기 위해 다음과 같은 callback 메쏘드를 사용한다.
     
    함수명 설명
    void RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen);
    • t 는 이전 호출에서 RIL_Notification 루틴에 전달된 파라메터이다.
    • 만약 e != SUCCESS 라면, response 는 null 이 될것이며, 무시된다.
    • RIL_onRequestComplete 는 가능한 빨리 return 할 것이다.
    void RIL_requestTimedCallback (RIL_TimedCallback callback, void *param, const struct timeval *relativeTime); RIL_RequestFunc 이 호출된 동일한 쓰레드에서 사용자 정의의 callback 함수를 호출한다. 만약 relativeTime 이 명시되었다면, 그것은 callback이 발생해야하는 상대적인 시각을 명시한다. 만약 relativeTime 이 NULL 이거나 0 으로 채워진 구조체에 대한 포인터라면, callback은 최대한 빨리 발생한다.

     
    RIL Unsolicited 명령어들
     
    아래의 테이블에 열거된 함수들은 안드로이드 플랫폼에서 unsolicited 명령어들을 발생시키기 위해 Vendor RIL에 의해
    사용되는 call-back 함수들이다. 자세하게는 ril.h 를 보길.
     
    함수명 설명
    void RIL_onUnsolicitedResponse(int unsolResponse, const void *data, size_t datalen);
    • unsolResponse 는 RIL_UNSOL_RESPONSE_* 중 하나임.
    • data 는 RIL_UNSOL_RESPONSE_* 를 위해 정의된 data에 대한 포인터임.
    • data 는 caller 소유이며, callee 에 의해 수정되거나 free 되어서는 안됨.

    'old > Basic' 카테고리의 다른 글

    aidl  (0) 2010.07.06
    zygote & Dalvik VM  (0) 2010.07.06
    안드로이드 초기화 (init 프로세스와 기타 서비스 등록)  (0) 2010.07.06
    The Froyo Code Drop  (0) 2010.07.06
    Parcelable Object 2  (0) 2010.07.06
    Posted by jazzlife
    ,
    안드로이드 초기화(init 프로세스와 기타 서비스 등록)



    안드로이드는 리눅스 커널을 이용한다. 따라서 리눅스의 시작 시퀀스 이후에 안드로이드 고유 시스템이 동작하게 된다.

    기본적인 동작을 잘 설명한 곳( http://samse.tistory.com/112?srchid=BR1http%3A%2F%2Fsamse.tistory.com%2F112 )이 있어서 일부 발췌해 온다.

     



    리눅스는 기본적으로 init이 가장먼저 실행된다. init.rc 라는 이름의 파일에는 init이 해야할 작업들이 기록되어 있다. 파일시스템 마운팅, 폴더 권한설정, 캐시폴더 삭제, 시작프로그램 동작 등이 기록되어 있다. 우선 데몬을 올린다. 데몬은 init에 의해 리눅스와 같이 시작되었다가 리눅스가 종료될 때 없어지는 프로그램으로서 데몬을 작성하는 규격에 따라 만들어져야 한다. Zygote가 Dalvik을 초기화 한다. C 밑에 있는 기본라이브러리들은 런타임을 통해 실행되고 상위 서비스들은 Dalvik을 통해 실행된다. 이러한 과정들을 위한 설정은 해당하는 config 파일을 수정하면 된다. 어떤 동작들을 바꾸고 싶으면 기본적으로 init.rc를 바꾸면 되고 Zygote를 바꾸고 싶으면 그 설정파일을 바꾸면 된다. 그리고 시스템서버, 서페이스매니저, 오디오매니저들이 올라간다. 그 다음에는 시스템 서비스들이 활성화 된다. 이들은 서비스이므로 서비스매니저에 등록된다.


    init은 /system/core/init 디렉토리에 관련 소스가 있다. init.rc 파일은 /system/core/rootdir/에 있다.

    init이 하는 일과 init.rc 파일을 작성하는 방법에 대해서는 여기( http://www.aesop.or.kr/?document_srl=46566 )를 참조한다.

     

    init이 하는 일을 정리하면 다음과 같다.

    1. log system 초기화

    2. /init.rc와 /init.%hardware$.rc 파일 파싱

    3. 2번 스텝의 2개 파일에서 초기 선동작(early-init action) 수행

    4. 디바이스 종속적인 초기화 작업 (예를 들어 /dev 에 모든 디바이스 노드 만듬)

    5. property system 초기화 (property system은 공유 메모리로 동작하며 윈도우의 레지스트리와 같은 역활이다)

    6. 2번 스텝의 2개 파일에서 초기 동작(init action) 수행

    7. property service 시작

    8. 2번 스텝의 2개 파일에서 선부팅(early boot), 부팅(boot) 동작 수행

    9. 2번 스텝의 2개 파일에서 property 동작 수행

    10.  device/property가 /child process를 셋할때까지 대기하면서 rc 파일에 정의된 명령어를 수행한다.



    위의 설명처럼 /out/target/product/XXX/init.rc 를 먼저 살펴 보자.  위에서 설명한 것처럼 폴더 권한 설정 등의 초기화 작업을 하고 나면

    Demon을 올리는 작업을 하도록 되어 있다. 이를 정리하면 다음과 같다.


    1) console : shell 을 시작 (system/core/sh/)

    ## Daemon processes to be run by init.
    ##
    service console /system/bin/sh
        console


    2) abdb : abdb daemon을 시작 (system/core/adb/)

    # adbd on at boot in emulator
    on property:ro.kernel.qemu=1
        start adbd


    on property:persist.service.adb.enable=1
        start adbd


    on property:persist.service.adb.enable=0
        stop adbd


    3) servicemanager : binder system을 시작 (framework/base/cmds/servicemanager/)

    service servicemanager /system/bin/servicemanager
        user system
        critical
        onrestart restart zygote
        onrestart restart media


    4) vold : volume daemon 시작 (system/core/vold/)

    service vold /system/bin/vold
        socket vold stream 0660 root mount

    #service mountd /system/bin/mountd
    #    socket mountd stream 0660 root mount


    5) debuggerd : debug system을 시작 (system/debuggerd/)

    service debuggerd /system/bin/debuggerd


    6) rild : radio interface layer daemon을 시작 (hardware/ril/rild/)

    service ril-daemon /system/bin/rild
        socket rild stream 660 root radio
        socket rild-debug stream 660 radio system
        user root
        group radio cache inet misc


    7) Zygote : (frameworks/base/cmds/app_process/)

    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
        socket zygote stream 666
        onrestart write /sys/android_power/request_state wake
        onrestart write /sys/power/state on


    8) media : AudioFlinger, MediaPlayerService, CameraService를 시작 (frameworks/base/media/mediaserver/)

    service media /system/bin/mediaserver
        user media
        group system audio camera graphics inet net_bt net_bt_admin


    관련 코드를 보면 굉장히 간단하다. (framework/base/media/mediaserver/main_mediaserver.cpp)

    int main(int argc, char** argv)
    {
        sp<ProcessState> proc(ProcessState::self());
        sp<IServiceManager> sm = defaultServiceManager();
        LOGI("ServiceManager: %p", sm.get());
        AudioFlinger::instantiate();
        MediaPlayerService::instantiate();
        CameraService::instantiate();

        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
    }


    9) bootsound : boot sound (system/media/audio/ui/boot.mp3)를 재생, (그런데 이 바이너리는 실제 찾을 수 없네...?. 그리고 샘플 파일도 안 만들어 지네)

    service bootsound /system/bin/playmp3
        user media
        group audio
        oneshot


    10) dbus : dbus daemon 시작 (BlueZ 즉 Bluetooth 관련 서비스) (external/dbus/bus/)

    service dbus /system/bin/dbus-daemon --system --nofork
        socket dbus stream 660 bluetooth bluetooth
        user bluetooth
        group bluetooth net_bt_admin


    11) hcid : hcid의 stdout, stdin을 안드로이드 logging sytstem으로 redirect (external/bluez/hcid/)

    service hcid /system/bin/hcid -s -n -f /etc/bluez/hcid.conf
        socket bluetooth stream 660 bluetooth bluetooth
        socket dbus_bluetooth stream 660 bluetooth bluetooth
        # init.rc does not yet support applying capabilities, so run as root and
        # let hcid drop uid to bluetooth with the right linux capabilities
        group bluetooth net_bt_admin misc
        disabled


    12) hfag : Bluetooth handsfree audio gateway 시작 (BlueZ 만 사용) (external/bluez/utils/tools/)

    service hfag /system/bin/sdptool add --channel=10 HFAG
        user bluetooth
        group bluetooth net_bt_admin
        disabled
        oneshot

    13) hsag : Bluetooth headset audio gateway 시작 (BlueZ 만 사용) (external/bluez/utils/tools/)

    service hsag /system/bin/sdptool add --channel=11 HSAG
        user bluetooth
        group bluetooth net_bt_admin
        disabled
        oneshot

    14) install : install package daemon 시작 (frameworks/cmds/installd/)

    service installd /system/bin/installd
        socket installd stream 600 system system


    15) flash_recovery : /system/recovery.img를 로드 (bootable/recovery/mtdutils/)

    service flash_recovery /system/bin/flash_image recovery /system/recovery.img
        oneshot


     

    강조한 것처럼 바와 같이 servicemanager을 이용해서 zygote와 media를 시작하고 SD 카드와 USB의 mount 등을 처리하는 vold 데몬을 올린다. 이후 부팅 사운드를 재생하거나 다른 필요한 데몬들을 올리게 된다. 이후 다시 강조한 부분을 보면 zygote를 이용해서 system server 를 시작한다. 이후 media를 다시 시작한다(?)


    안드로이드의 system server는 Native Service와 여러가지 기본 서비스(core system service 라고도 한다)들로 이루어진다.


    /framework/base/cmds/system_server/library/system_init.cpp 를 보면 system_init() 함수가 있다. 이 파일은 실제 사용한다기 보다는 초기화 흐름을 알게 해주는 코드로 보인다.


    extern "C" status_t system_init()
    {
        LOGI("Entered system_init()");
       
        sp<ProcessState> proc(ProcessState::self());
       
        sp<IServiceManager> sm = defaultServiceManager();
        LOGI("ServiceManager: %p\n", sm.get());
       
        sp<GrimReaper> grim = new GrimReaper();
        sm->asBinder()->linkToDeath(grim, grim.get(), 0);
       
        char propBuf[PROPERTY_VALUE_MAX];
        property_get("system_init.startsurfaceflinger", propBuf, "1");
        if (strcmp(propBuf, "1") == 0) {
            // Start the SurfaceFlinger
            SurfaceFlinger::instantiate();
        }

        // On the simulator, audioflinger et al don't get started the
        // same way as on the device, and we need to start them here
        if (!proc->supportsProcesses()) {

            // Start the AudioFlinger
            AudioFlinger::instantiate();

            // Start the media playback service
            MediaPlayerService::instantiate();

            // Start the camera service
            CameraService::instantiate();
        }

        // And now start the Android runtime.  We have to do this bit
        // of nastiness because the Android runtime initialization requires
        // some of the core system services to already be started.
        // All other servers should just start the Android runtime at
        // the beginning of their processes's main(), before calling
        // the init function.
        LOGI("System server: starting Android runtime.\n");
       
        AndroidRuntime* runtime = AndroidRuntime::getRuntime();

        LOGI("System server: starting Android services.\n");
        runtime->callStatic("com/android/server/SystemServer", "init2");
           
        // If running in our own process, just go into the thread
        // pool.  Otherwise, call the initialization finished
        // func to let this process continue its initilization.
        if (proc->supportsProcesses()) {
            LOGI("System server: entering thread pool.\n");
            ProcessState::self()->startThreadPool();
            IPCThreadState::self()->joinThreadPool();
            LOGI("System server: exiting thread pool.\n");
        }
        return NO_ERROR;
    }



    코드를 보면 알겠지만 크게 다음 순서로 초기화 과정이 이루어진다.

    1. Native Service 초기화

    SurfaceFlinger

    AudioFlinger

    MediaPlayerService

    CameraService


    2. Android RunTime 시작

    SystemServer


    안드로이드가 동작하기 위한 여러가지 기본 서비스들(또는 JAVA 서비스)을 시작하는 곳이 SystemServer이다.


    파일 위치는 다음과 같다.


    /frameworks/base/services/java/com/android/server/SystemServer.java


    SystemServer 클래스는 init2() 함수를 호출하게되면 ServerThread를 동작시키고 여기에서 다양한 서비스들을 시작한다.

    서비스 시작하는 순서대로 대충 정리하면 다음과 같다.(run 함수의 내용을 그냥 정리한 것이며 * 표시 없는 것은 주서비스이고 * 표시 있는 것은 부가 서비스이다)


    Power Manager 시작

    Activity Manager 생성


    ActivityManager를 생성해서 context를 받으면 이후 서비스들에는 context를 전달한다.

    (아직 이 구조는 잘 모르겠지만 서비스들에서 설정이나 특정 이벤트들을 브로드 캐스팅하기 위해서는 context를 이용해야 하는 것 같다. ActivityManger에서 이를 위한 초기화 과정이 이루어지고 이후 서비스들은 context를 가지고 서로간의 통신이나 Focus 이동이 생길 수 있도록 하고 있는 것 같다. 이것은 아직은 어디까지나 추측이다.)


    Telephony Registry 시작

    Package Manager 시작

    System Process로 Activity Manager 설정

    Context Manager 시작

    System Context Providers 시작

    Battery Service 시작

    * Hardware Service 시작

    Alarm Manager 시작

    Sensor Service 시작


    Window Manager 시작


    Bluetooth Service 시작

    BluetoothDeviceService

    BluetoothA2dpService


    *Status Bar Service 시작

    ClipboardService

    InputMethodManagerService


    *NetStat Service 시작

    *Connectivity Service 시작


    *Notification Manager 시작

    Mount Service 시작

    *DeviceStorageMonitor Service 시작


    *Location Manager 시작

    *SearchManager Service 시작

     *Clipboard Service 시작

    *Fallback Checkin Service 시작

    *WallpaperService 시작

    *Audio Service 시작

    *Headset Observer 시작

    (?)AppWidgetService


    *AdbSettingsObserver 시작

    (?)HDMISettingsObserver 시작

    'old > Basic' 카테고리의 다른 글

    zygote & Dalvik VM  (0) 2010.07.06
    안드로이드 HAL - RIL(Radio Interface Layer)  (0) 2010.07.06
    The Froyo Code Drop  (0) 2010.07.06
    Parcelable Object 2  (0) 2010.07.06
    Parcelable Object 1  (0) 2010.07.06
    Posted by jazzlife
    ,

    The Froyo Code Drop

    old/Basic 2010. 7. 6. 12:11

    The Froyo Code Drop

    Googleplex 안팎으로 소스 코드를 트럭으로 나르는 Jean-Baptiste Queru의 글
     
     
     
    오늘은 심장이 빨라지는 그런 날 중 하나다. 우리는 방금 안드로이드 2.2 소스 코드를 공개했다.
    이는 안드로이드 에코시스템 전체를 위해 큰 진보라고 할 수 있다.
    이 최신 소스 코드를 다운로드 하느라 또 한번 서버가 녹아버리는 일이 없기를…
     
    이 블로그는 대개 SDK와 NDK를 사용하여 안드로이드 애플리케이션을 개발하는 것에 대해 논의한다.
    그러나, 플랫폼 공헌자의 기술도 근본적으로는 애플리케이션 개발자들의 것과 다르지 않다.
    동일한 기술 집합을 사용하는데 단지 역할만 다를 뿐이다. 경험이 많은 전세계의 안드로이드 프로그래머들에게
    안드로이드 오픈 소스 프로젝트에서의 최근 개발 사항을 소개하고자 한다.
     
    안드로이드를 가지고 일하는 엔지니어들이라면 대부분 공개되는 각 버전을 그 코드 이름으로 안다.
    (안드로이드 코드명은 맛있게 차린 별미들 중에서 알파벳 순서로 선택하여 붙여진다.)
    필자는 이 글에서 안드로이드 2.2를 그 코드 이름인 “프로요”라고 부를 것이다.
    딱딱한 버전 숫자를 생각하는 것보다는 캘리포니아의 여름에 만끽하는 차가운 디저트를 생각하는 것이
    훨씬 군침 도는 일이다. 
     
    새로운 프로요 소스의 멋진 특성들에 대해 살펴본 후,
    안드로이드 오픈 소스 프로젝트의 몇 가지 주목할 만한 요소들로 다시 돌아가보자.
     
    필자는 안드로이드의 이전 오픈 소스 릴리스에 점점 더 많이 개입하여,
    처음 코드를 공개하는 것부터 이클레어에서 오픈소스 관련 git 레벨의 작업에 이르기까지 관여해왔다.
    그러한 경로를 따라, 프로요는 처음부터 끝까지 안드로이드 오픈 소스 프로젝트에 가장 초점을 맞추었던
    최초의 버전이다. 이 일의 많은 분량을 함께 하며 필자를 도와준 모든 안드로이드 팀에 감사를 표한다.
    여기, 필자가 자랑스러워하는 프로요의 특성 몇 가지를 소개하고자 한다.
    물론 덕분에 지난 몇 달 동안 계속 바빴지만 말이다.
    • 세계 각 곳에서 안드로이드 오픈 소스 프로젝트에 업로드 했던
      수백 가지의 플랫폼 변경 사항들이 채택되어 프로요에 병합되었다.
      컨트리뷰션을 수용하여 병합하는 과정은 이제는 기름칠이 잘 된 기계가 돌아가는 것처럼 순조롭고,
      앞으로 있을 많은 컨트리뷰션들도 잘 변환할 것으로 본다.

    • 오픈 소스 공개는 단일한 스텝으로 이루어졌다.
      이제 안드로이드 2.2 플랫폼 전체를 위한 모든 소스 트리를 사용할 수 있으며, 변경 이력들도 모두 공개되었다.
      이 내용들을 살펴본다면 개발자들이 이전 버전에서 프로요로 이동하는 데 가속도가 붙을 것이다.
      뿐만 아니라 프로요는 이미 오픈 소스 마스터 트리에도 전체가 병합되었다.
      그 결과, 우리는 당장에 리뷰를 하고 프로요를 기반으로 플랫폼 컨트리뷰션을 수용할 수 있게 되었다.
      따라서 오픈 소스 트리에 대한 컨트리뷰션과
      구글 내부의 마스터 트리(이는 컨트리뷰션들이 궁극적으로 반영될 목적지이다)의 변경 사항을 병합해야 하는
      위험요소가 줄어들 것이다.

    • 디바이스 제조사들과 커스텀 시스템 빌더들이 프로요를 보다 쉽게 사용할 수 있도록 하기 위해,
      우리의 소스 트리를 오픈 소스 트리에서, 별도의 클로즈드 소스 모듈로 재구성하였다.
      우리는 오픈 소스 코드 자체에 많은 수정을 가하여 원치 않게 클로즈드 소스 소프트웨어에 의존하는 부분들을 제거했다.
      또한 안드로이드 오픈 소스 프로젝트의 소스 코드를 그 자체로 빌드할 수 있도록,
      필요한 모든 컨피규레이션 파일들을 코어 플랫폼 내에 통합시켰다.
      여러분은 이제 Dream (ADP1), Sapphire (ADP2), Passion (Nexus One) 뿐만 아니라 에뮬레이터를 위해서도,
      곧바로 온전한 오픈 소스 시스템 이미지를 빌드하고 부팅시킬 수 있다.

    • 디바이스 지원에 대해 말하자면,
      우리는 이전 버전에서는 클로즈드 소스였던 하드웨어 관련 라이브러리들도 오픈 소스화 시켰다.
      이로 인해 보다 많은 컨트리뷰션들에게 문을 열게 되었다.
      예를 들면, Dream, Sapphire, Passion을 위한 복구 UI 코드, 그리고 미디어 프레임워크와 퀄컴 칩셋 간의
      인터페이스 등을 들 수 있다.
    프로요 소스 코드 공개 외에도, 필자는 안드로이드 오픈 소스 프로젝트에서의 몇 가지 개선된 사실들을 언급하고자 한다:
    • 우리는 그 동안 20 군데가 넘는 회사와 많은 개인 개발자들로부터 컨트리뷰션을 받았다.
      Gerrit 코드 리뷰 서버에는 거의 4천 명이나 되는 등록된 유저들이 있고,
      각 사람이 평균 두 건의 컨트리뷰션을 주었다.
      깊게는 C 라이브러리에서부터 락 스크린의 UI에 이르기까지, 시스템의 모든 영역에 대한 개선안들이었다.
      또한 문서의 오류를 고치거나 코드를 리포맷하는 것에서부터 개발자가 볼 수 있는 API들을 더하거나
      사용자가 볼 수 있는 기능에 이르기까지 다양한 수준의 컨트리뷰션들이었다.
      필자는 모든 공헌자들이 작업을 하며 들인 수고와 인내에 감사한다.  

    • 우리는 이제 플랫폼 컨트리뷰션들에 보다 빠르게 응대한다.
      대부분의 변경들은 업로드된 지 업무일 3, 4일 이내에 검토가 되며,
      몇 주가 지나도록 정체된 채 머무르는 변경은 얼마 되지 않는다.
      우리는 보다 빨리, 보다 자주 리뷰하려고 노력 중이다.
      이 글을 타이핑 하고 있는 지금도, 플랫폼 컨트리뷰션 중에 아직 살펴보지 못한 것은 여남은 개이고,
      그 중 사흘 된 것이 가장 오래 된 것이다.
      지난 2주 동안 플랫폼 코드에 관련된 컨트리뷰션들 중 90% 이상을 열심히 검토하였다.
      프로세스가 빠르게 전개되어 코드 리뷰 동안 상호작용이 더 활발하게 일어나길 희망한다.
      그럼에도 불구하고 전세계 공헌자들과 소통할 때 시차 문제 때문에 실시간으로 소통하는 것이 사뭇 어렵다는 것을 느낀다.

    • 지난 두 달 남짓, 우리는 Gerrit 서버에 업로드된 천 여 가지 변경 사항들에 대해 최종 결정을 내렸다.
      즉, 검토를 통해 ‘채택’ 혹은 ‘거절’ 여부가 판가름 났다는 것이다.
      안드로이드 오픈 소스 프로젝트의 모든 역사에 걸쳐 얻은 컨트리뷰션들이 매우 수준이 높아서
      그 중 80% 정도를 꾸준히 메인 저장소에 병합할 수 있었다.
      그 메인 저장소에서 공식 릴리스 대상으로 이동하는 것이다.
      말하자면, 지난 2개월 동안 매 업무일마다 평균 스무 가지 변경 사항들이 안드로이드 오픈 소스 프로젝트를 통해
      공용 git 저장소로 수용되었다는 것이다.

    • 우리는 최근 안드로이드 오픈 소스 프로젝트와 관련하여 공식 구글 그룹 두 개를 만들었다.
      Android-building은 특히 빌드 이슈를 다룰 의도로 만들어졌다(글을 포스팅 하기 전에 아카이브를 면밀히 검색해보라).
      Android-contrib은 실제적인 컨트리뷰션을 논의하는 데 사용된다(진짜로 오픈 소스에 공헌을 하고 리뷰 프로세스를
      따라갈 의도가 아니라면, 또한 여러분 스스로 한 두 시간 연구해보기 전에는 포스트하지 말아주시길.)

    • 우리는 개발자 툴을 오픈 소스 프로젝트에서 직접 만들고 있다.
      이 영역에는 닫힌 문 뒤로 몰래 일어나는 일이라고는 없다.
      여기에는 이클립스 플러그인과 에뮬레이터, 그리고 다른 많은 SDK 관련 툴들이 포함된다.

    • 특정 플랫폼 버전이 일단 오픈 소스화 되고 나면,
      그 버전과 관련된 모든 호환성 테스트 수트 (Compatibility Test Suite) 개선사항이 오픈 소스 트리에 직접 만들어진다.
      사실, 2.1 CTS의 버전 2는 100% 그러한 방식으로 이루어졌다. 개발과 테스팅, 릴리스 프로세스가
      곧장 오픈 소스 트리에서 이루어졌다. 프로요 역시 그렇다.
      그리고 우리는 현재 CTS 프로젝트의 프로요 부문으로 컨트리뷰션들을 수용하고 있다.
    필자는 마지막 언급한 두 가지 측면이 애플리케이션 개발자들에게 중요하다고 믿는다.
    여러분이 애플리케이션 개발자이고 여러분과 동료 개발자들이 사용하는 툴들을 개선하고 싶다면,
    그 분야에서의 변경을 이루어내는 과정들이 지금은 훨씬 투명해졌다.
    이와 유사하게, 애플리케이션 개발 도중 기기들 간에 호환이 안 된다는 것을 발견했을 때,
    그리고 그러한 비호환성이 문자적으로나 실제적으로 안드로이드의 호환성 의미 밖에 있다고 여길 때,
    그 영역을 위한 CTS 테스트로 공헌함으로써 상황을 개선하도록 도움을 줄 수 있다.
     
    이제 안드로이드 2.2가 오픈 소스 세상에 공개되었으므로, 그리고 리뷰 프로세스가 원활하게 작동하고 있으므로,
    필자는 수준 높은 컨트리뷰션들이 보다 많이 나와서 미래의 안드로이드 버전에 사용될 수 있기를 기대한다.
    필자의 가장 달콤한 꿈은 (또한 가장 끔찍한 악몽이기도 하지만) 좋은 컨트리뷰션들이 하도 많이 쏟아져서
    내가 도저히 따라잡을 수 없을 정도가 되는 것이다. 부디 이 단꿈에서 나를 깨우지 말아주시길.
    Posted by jazzlife
    ,

    Parcelable Object 2

    old/Basic 2010. 7. 6. 11:55

    [In Case of Inner Class]


    저번에는 아주 간단한 Rect 클래스로 parcelable하게 만들어 보았습니다.
    이번에는 좀 더 복잡한 형태의 클래스를 가지고 parcelable하게 만들어 보도록 하죠.

    이번에 parcelable하게 만들어 볼 클래스는 EyePoint 클래스 입니다.


    public class EyePoint {
        public Point left;
        public Point right;
       
        public class Point {
            public int x;
            public int y;
        }
    }


    눈 좌표를 나타내는 클래스 인데, 왼쪽 눈과 오른쪽 눈 좌표를 가지고 있습니다.
    그리고 다시 한쪽 눈 좌표는 x, y integer 값을 가지고 있죠.

    뭐 물론 주우욱 풀어서 leftX, leftY, rightX, rightY 4개의 필드를 만들어서 표현 할 수도 있겠습니다만,
    전 그냥 EyePoint 클래스 안에 다시 Point 클래스를 만들었습니다. 뭐 제 마음이죠.


    그럼 일단 안쪽에 있는 EyePoint$Point 클래스 부터 parcelable하게 만들어 봅시다.


    Step 1.


    public class EyePoint implements Parcelable {
        public Point left;
        public Point right;

       
       
        // EyePoint$Point Inner Class
        public static class Point implements Parcelable {
            public int x;
            public int y;
           
            public int describeContents() {
                return 0;
            }
            public void writeToParcel(Parcel dest, int flags) {
                dest.writeInt(x);
                dest.writeInt(y);
            }
            public static final Parcelable.Creator<Point> CREATOR = new Creator<Point>() {
                public Point createFromParcel(Parcel source) {
                    Point p = new Point();
                    p.x = source.readInt();
                    p.y = source.readInt();
                    return p;
                }
                public Point[] newArray(int size) {
                    return new Point[size];
                }
            };
        }
    }


    저번에 만들었던 Rect 클래스 보다 무려 필드의 수가 반 밖에 안됩니다.
    이쯤이야 금방 만들 수 있지요...


    한가지 주의할 점은 Point 클래스의 CREATOR가 static이니
    Point 클래스도 static이 되어야 EyePoint를 통해서 접근 할 수 있는것이 좀 다른 점입니다.
    어차피 Eclipse에서 다 잡아 주니까 부담없이 코딩 하시면 되겠습니다.



    [Write Parcelable And Read Parcelable]


    자... 그럼 이제 EyePoint 클래스를 parcelable하게 만들어 봅시다.
    Point 타입의 필드가 left, right 두개가 있으니 이것을 Parcel에다가 써야 하는데...

    어떤 타입으로 써야 할까요?


    눈치가 빠르신 분들은 이미 writeParcelable() 메소드를 사용하면 될거라 생각 하셨을 겁니다.


    이미 Point는 Parcelable Interface를 implements 했으니 이미 parcelable합니다.
    그래서 writeParcelable() 메소드를 사용해서 Parcel에다 쓸 수 있는 것이죠.


    그런데 writeParcelable() 메소드를 잘 살펴보면,

    다른 Primitive 타입과는 다른 인자가 하나 더 붙어 있습니다.

    public final void writeParcelable (Parcelable p, int parcelableFlags)

    parcelableFlags의 정체는 데체 무어란 말인가...
    근데 지금은 그냥 별 생각 없이 ZERO로 채워 주시면 됩니다. 아핫핫... 넘어갑시다.


    Step 2.


    public class EyePoint implements Parcelable {
        public Point left;
        public Point right;

       
        public int describeContents() {
            return 0;
        }
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeParcelable(left,  0);
            dest.writeParcelable(right, 0);
        }
       
       
        // EyePoint$Point Inner Class
        public static class Point implements Parcelable {
            public int x;
            public int y;
           
            public int describeContents() {
                return 0;
            }
            public void writeToParcel(Parcel dest, int flags) {
                dest.writeInt(x);
                dest.writeInt(y);
            }
            public static final Parcelable.Creator<Point> CREATOR = new Creator<Point>() {
                public Point createFromParcel(Parcel source) {
                    Point p = new Point();
                    p.x = source.readInt();
                    p.y = source.readInt();
                    return p;
                }
                public Point[] newArray(int size) {
                    return new Point[size];
                }
            };
        }
    }

     

    보시는 바와 같이 그냥 0을 썼습니다. 뭐 별거 없네요...

    그럼 이제 Parcel에다 쓰는 부분이 끝났으니 CREATOR를 만들어 봅시다.


    아까는 writeParcelable() 메소드를 사용 했으니

    반대로 readParcelable() 메소드를 사용 하면 됩니다.


    참 말은 쉽군요. 그럼 한번 살펴 봅시다.

    public final T readParcelable (ClassLoader loader)

    잉? 다른건 그냥 읽었는데 이건 ClassLoader가 필요하군요.
    데체 이 ClassLoader는 쌩뚱맞게 어디서...


    우리가 읽고 싶은것은 Point 타입입니다.
    그래서 Point의 ClassLoader를 지정해 주어야지 Point 값을 읽어 올 수 있습니다.


    ClassLoader를 얻어내는 방법은 의외로 간단합니다.
    Intent를 쓸 때와 비슷한 방법으로 얻어 오면 되는 것이죠.


    ClassLoader loader = EyePoint.Point.class.getClassLoader();


    Intent를 쓸 때는 Class까지만 넘겨주면 되는 거였는데,

    Class 객체에서 getClassLoader()를 호출하게 되면 ClassLoader를 쉽게 얻어 올 수 있습니다.


    Step 3.


    public class EyePoint implements Parcelable {
        public Point left;
        public Point right;

       
        public int describeContents() {
            return 0;
        }
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeParcelable(left,  0);
            dest.writeParcelable(right, 0);
        }
        public static final Parcelable.Creator<EyePoint> CREATOR = new Creator<EyePoint>() {
            public EyePoint createFromParcel(Parcel source) {                   
                EyePoint e = new EyePoint();
                       
                ClassLoader loader = EyePoint.Point.class.getClassLoader();
                e.left  = source.readParcelable(loader);
                e.right = source.readParcelable(loader);
               
                return e;
            }
            public EyePoint[] newArray(int size) {
                return new EyePoint[size];
            }
        };
       
       
        // EyePoint$Point Inner Class
        public static class Point implements Parcelable {
            public int x;
            public int y;
           
            public int describeContents() {
                return 0;
            }
            public void writeToParcel(Parcel dest, int flags) {
                dest.writeInt(x);
                dest.writeInt(y);
            }
            public static final Parcelable.Creator<Point> CREATOR = new Creator<Point>() {
                public Point createFromParcel(Parcel source) {
                    Point p = new Point();
                    p.x = source.readInt();
                    p.y = source.readInt();
                    return p;
                }
                public Point[] newArray(int size) {
                    return new Point[size];
                }
            };
        }
    }

     

    네.. 이런 것이죠.
    이제 왠만한 클래스들은 parcelable하게 만들 수 있겠죠?



    [Appendix]

    네... 위에서도 그냥 지나 쳤던 문제의 parcelableFlags가 아직 남아 있죠...
    역시 이것의 사용 용도에 대해서는 미궁입니다.

    이것역시 integer값으로, bit mask 형식으로 사용하기 위한 인자 입니다.


    지금 사용 가능한 플래그는

    Parcelable.PARCELABLE_WRITE_RETURN_VALUE (0x00000001) 뿐입니다.

    소스를 거의다 뒤져 보았는데, 이걸 사용하는 곳은 ParcelFileDescriptor 클래스 밖에 없었습니다.
    대부분은 0으로 채워져 있었지요.


    ParcelFileDescriptor는 또 데체 어디에 쓰이는건지 원...
    보면 볼 수록 알 수 없는 Android 인 것 같습니다.

    'old > Basic' 카테고리의 다른 글

    안드로이드 초기화 (init 프로세스와 기타 서비스 등록)  (0) 2010.07.06
    The Froyo Code Drop  (0) 2010.07.06
    Parcelable Object 1  (0) 2010.07.06
    안드로이드 기본 개념  (0) 2010.07.06
    JNI (Java Native Interface)  (0) 2010.07.06
    Posted by jazzlife
    ,

    Parcelable Object 1

    old/Basic 2010. 7. 6. 11:54
    [Parcel Class?]

    Android의 핵심 중에서 Binder Driver라는 녀석이 있습니다.
    Linux Kernel의 driver로 되어 있고, IPC이긴 하지만 기존의 IPC와는 살짝 다른 녀석 입니다.


    저도 어떻게 만들었는지는 잘 모릅니다만,
    shared memory를 통하여 오버헤드도 적고 프로세스 사이에 비동기로 호출도 이루어 진다고 합니다.
    그리고 Binder는 기존 IPC처럼 데이터만 전달 하는게 아니라,
    하나의 프로세스에서 다른 프로세스로 Object를 넘길 수도 있게끔 설계 되어 있습니다.
    (물론 Serialize 기술을 사용하면 Object도 주고 받을 순 있지요.)


    Binder를 통해서 넘기는 메세지 (data 와 object references) 는
    Parcel 클래스에 저장이 되어서 넘어가게 됩니다. Parcel은 메세지 컨테이너인 셈이죠.

    Parcel is not a general-purpose serialization mechanism. This class (and the corresponding Parcelable API for placing arbitrary objects into a Parcel) is designed as a high-performance IPC transport.

    Parcel 클래스는 고성능 IPC 전송을 가능하게 끔 설계가 되어 있기 때문에,

    모든 객체에 대해서 평범하게 serialization하지 않습니다.
    그래서 모든 Object들을 Binder를 통해 주고 받을 수는 없습니다.



    [Parcelable Interface]


    Primitive Type과 Primitive Array Type은 기본적으로 parcelable 합니다.

    뭐 그냥 일반 데이터인거죠.
    그리고 Parcelable Interface를 implements한 클래스가 (당연히) parcelable 합니다.


    오늘 우리가 할 일이 바로 이 Parcelable Interface를 사용하여
    parcelable Object를 만드는 일 인 것이죠.

    사실 parcelable한 type들이 많이 있습니다만, 나머지는 잘 모르겠군요... 어렵습니다.
    여튼 오늘은 parcelable Object를 만드는 것만 생각 합시다.



    [Parcelable Rect]


    Rect 클래스야 다들 쉽게 만드실 겁니다.
    left, top, right, bottom 4개의 필드만 있으면 되죠.


    integer든 float이든 상관 없습니다만....
    Android에는 Rect 클래스의 float 버전이 RectF 클래스 라고 따로 되어 있기 때문에,

    저는 integer로 만들겁니다.


    public class Rect {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }


    뭐 아주 간단 합니다. 그냥 C에서의 구조체 수준이네요.
    이것을 parcelable하게 바꾸어 봅시다.


    Step 1.


    public class Rect implements Parcelable {
        public int left;
        public int top;
        public int right;
        public int bottom;
       
        public int describeContents() {
            return 0;
        }
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(left);
            dest.writeInt(top);
            dest.writeInt(right);
            dest.writeInt(bottom);
        }
    }


    Parcelable을 implements하게 되면 꼭 추가해야 한다는 메소드 두 개도 같이 넣었습니다.
    내용도 채워 봤어요.


    describeContents() 메소드는 일단 건너뛰고...
    writeToParcel() 메소드는 Parcel에 데이터를 쓰는 부분 입니다.
    그냥 무작정 쓰면 됩니다. 전 아무 생각 없이 순서대로 그냥 썼습니다.


    그럼 이제 에러도 없으니 parcelable한 Rect 클래스가 되었느냐... 라고 한다면 아직 아닙니다.
    쓰는건 있는데 읽는건 없네요...


    Parcel로 부터 값을 읽어 오기 위해서는 Parcelable.Creator Interface 가 필요합니다.



    [Parcelable.Creator Interface]


    Parcel 에서 Parcelable 클래스의 인스턴스를 만들 때
    CREATOR라는 static field를 찾아서 실행 합니다.
    CREATOR는 Parcelable.Creator<T> type 으로 만들어져야 하는데
    이건 선언과 동시에 반드시 initialize 되어야 합니다.


    클래스 따로 만들어서 initialize 하기도 쉽지 않습니다.
    그냥 익명 클래스로 만들어 버립시다.


    Step 2.


    public class Rect implements Parcelable {
        public int left;
        public int top;
        public int right;
        public int bottom;
       
        public int describeContents() {
            return 0;
        }
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(left);
            dest.writeInt(top);
            dest.writeInt(right);
            dest.writeInt(bottom);
        }
       
        public static final Parcelable.Creator<Rect> CREATOR = new Creator<Rect>() {
            public Rect createFromParcel(Parcel source) {
                Rect r   = new Rect();
                r.left   = source.readInt();
                r.top    = source.readInt();
                r.right  = source.readInt();
                r.bottom = source.readInt();
                return r;
            }
            public Rect[] newArray(int size) {
                return new Rect[size];
            }
        };
    }


    Parcelable.Creator<T> 클래스는 createFromParcel() 과 newArray() 메소스가 필요 합니다.


    newArray() 메소드도 일단 넘어가고...
    createFromParcel() 메소드를 보면 리턴이 Rect입니다.
    그래서 Parcel에서 값을 읽어 새로운 Rect 인스턴스를 리턴 하는 구조로 만들면 끝입니다.


    readInt()를 했는데, 이것은 writeToParcel() 메소드에서 썼던 순서대로 읽어 오는 것입니다.
    쓸 때는 무작정 썼지만 읽을 때는 조심스럽게 읽어야하죠.


    이제 비로소 parcelable Rect 클래스를 만들었습니다.
    그냥 별거 없군요...



    [Appendix]


    위에서 describeContents() 메소드와 newArray() 메소드는 그냥 넘어 갔었습니다.
    네... 뭐 사실 잘 모르기도 합니다만,
    그냥 보통 parcelable Object를 만드는데에 크게 중요한 부분은 아니지요. 아하하...


    describeContents() 에서는 어떤 특별한 객체를 포함하고 있는지에 대한 설명을
    리턴값으로 표현 하는 것이라고 보면 됩니다.
    bit mask 형식의 integer를 리턴 하며,
    값을 체크 할 때 bit mask 체크를 해서 어떤 것들이 들어 있는지 알 수 있습니다.


    현재 사용하는 플래그는 Parcelable.CONTENTS_FILE_DESCRIPTOR (0x00000001)
    하나 밖에 정의 되어 있지 않습니다.
    소스를 다 뒤져 보지 않아서 또 어떤 값들이 쓰이는지는 확인 해보지 못했네요...


    newArray() 메소드는 그냥 배열로 만들어서 리턴 하는 겁니다.
    Parcel.createTypedArray() 메소드에서 newArray() 메소드를 호출 하는 걸 확인 했습니다.
    나중에 createTypedArray() 메소드를 사용 할 일이 있다면 newArray()가 호출이 되는 것이지요.

    'old > Basic' 카테고리의 다른 글

    The Froyo Code Drop  (0) 2010.07.06
    Parcelable Object 2  (0) 2010.07.06
    안드로이드 기본 개념  (0) 2010.07.06
    JNI (Java Native Interface)  (0) 2010.07.06
    http://mc500.tistory.com/entry/Goole-Android-Build-해-보  (0) 2010.02.09
    Posted by jazzlife
    ,

    안드로이드에서 사용하는 언어는 자바이지만 SUN의 자바와 API와 버추얼머신이 다르다.
    Dalvik 이라는 회사의 버추얼머신이 있었는데 구글에서 회사를 통째로 사들였다고 한다. 이때문에 안드로이드는 SUN의 라이센스로부터 자유롭다.



    안드로이드 플랫폼은 크게 네 부분으로 나뉜다. 커널부분, 하드웨어추상레이어, 라이브러리, 응용프로그램 프레임워크. 라이브러리들은 C/C++로 구현이 되어 있다. 기존의 여러 프로젝트 들을 통합하였기 때문에 C로 구현된 것들이 대부분이고 C++도 있다. 응용프로그램 프레임워크는 자바로 구현되어 있다. 프레임워크와 라이브러리 사이에는 자바와 C 사이의 서로다른 호출 규약등을 맵핑하는 JNI라는 마셜러가 존재한다. 하드웨어 드라이버를 C로 만들면 JNI를 구현해야 한다. 이를 위한 구글 가이드가 있고 샘플도 공개되어 있다. 그리고 SDK를 생성하여 응용프로그램 개발자에게 전달하면 된다. 하드웨어 부분은 구글에서 표준으로 정해두었기 때문에 따로 만질 필요가 없다. 추상레이어도 스펙이 다 되어있어 응용프로그램을 개발할 때는 API로 보이게 된다.
     기존 임베디드리눅스와 안드로이드의 차이점중 한가지는 장치드라이버들( 그래픽, 오디오, 카메라 등 )이 커널영역이 아닌 유저모드에서 동작한다. 유저모드로 동작 시키면 자신에게 할당되어 있는 번지에만 접근할 수 있기 때문에 조금 더 안전하지만 하드에어 제어시에는 MMU등을 참조하는 등의 추가적인 작업이 필요하기 때문에 조금 불편할 수 있다. 이렇게 드라이버들을 유저영역에 넣은 이유는 라이센스문제, 안정성, 문서화의 편리성( 커널버전이 업데이트 될 때마다 드라이버에 대한 문서를 갱신할 필요가 없다 ), 플러그인 방식으로 드라이버 배포가 가능한점이 있다. 안드로이드에서는 기존 리눅스의 라이브러리로 만든 것들은 동작하지 않는다고 보면 된다. 같은 이름의 라이브러리라 할지라도 안드로이드의 라이브러리들은 스마트폰에 탑재하는 것을 기준으로 불필요한 코드들을 제거하고 최적화를 해두었기 때문이다. POSIX Thread 관련 라이브러리를 예로 들면 create, join 등의 필수 함수를 제외한 나머지함수는 모두 제거되어있다.

    Android Kernel

    왜 안드로이드는 리눅스 커널 기반일까. 생각하기 나름이겠지만 우선 오픈소스이고 안전하게 입증된 드라이버 모델(well abstracted )이 적용되어 있고, 메모리 프로세스 관리 모델을 제공하고, 하드웨어와 입출력에 대한 보안모델을 제공하고, 공유라이브러리를 지원하는 등 비용과 시간이 적게 드는 이유가 한몫 한것 같다.

    현재 커널 안정화버전은 2.6.27 이다. 구글에서 순수 리눅스 커널을 가져다가 안드로이드 용으로 패치한 것이다. 기존의 리눅스와는 다르게 GLIBC를 지원하지 않고 EABI( Embedded Arm Binary Interface?)와 Binder를 사용한다. 그리고 커널 기능 향상을 위해 Alarm, Ashamem(공유메모리), Low memory-killer, debugger, logger 컴포넌트가 추가되었다.

    유저모드는 허가된 자원에만 접근이 가능하고 프로그램의 안정성을 높일 수 있지만 하드웨어 효율성은 떨어질 수 있다. 반대로 커널모드는 하드웨어 효율성은 높지만 안정성은 떨어질 수 있다. 안드로이드는 기본 드라이버들을 제외한 나머지 드라이버들은 유저스페이스에서 동작한다. 일반 PC는 사람의 생명을 빼았지는 않지만, 심장박동 컨트롤러, 핸드폰만 가지고 조난 당했을 경우와 같이 스마트폰을 모델로 하면 생명과 연결될 수 있기 때문에 안정성이 속도나 효율보다 더 중요하다.
     
    이전에는 일반적으로 일반 gcc를 썼지만 최근의 리눅스 커널을 빌드할 경우에는 ARM사에서 표준으로 정해둔 ABI( arm binary interface ) 규격의 컴파일러를 사용한다. 현재 안드로이드는 EABI( Extended??..) 기능을 지원한다. 일반 컴파일러에서 빌드한 것을 EABI에서 실행할 수 없기 때문에 EABI 컴파일러로 다시 빌드해야 한다. 최근의 임베디드 컴파일러는 대부분 EABI컴파일러 이다. 실수 연산방식에 있어서 하드플로팅포인트와 소프트웨어 플로팅포인트를 혼합하여 빌드할 수 있다. 기존의 ABI는 빌드옵션에서 VFP(Vector Floating Point)를 선택하거나 해제할 수 있었는데 이는 환경이 달라지면 다시 빌드 해야하는 단점이 있었다. EABI는 혼용이 되기 때문에 빌드의 부담이 줄어든다. 또한 구조체 팩킹이 편해졌고 호환성이 증가하였다. 그리고 syscall 규약에 변경사항이 있다. 이전에는 전달인자 4개까지는 레지스터를 쓰고 5개부터는 스택을 사용했는데 레지스터를 r0, r1, r2, r4 처럼 연속된 순서로 했었다.  EABI는 레지스터 간격이 달라지는등 호출 규약이 달라졌다.

    Kernel Enhancement

    기존의 임베디드 리눅스에 없던 안드로이드만의 향상된 기능에는 알람, 로우메모리킬러, 공유메모리드라이버, 커널디버거, 바인더, 파워매니지먼트, 로거가 있다. 결국 포팅 이슈는 이것들과 관련될 것이다. 스마트폰으로 만들다 보니까 알람이 기본적으로 제공되어야 한다. 포팅 시 이 기능을 사용하고 싶지 않다면 다른기능과 맞물려 있기 때문에 확인해보고 작업을 진행해야 한다. 공유메모리드라이버는 안드로이드에서 커스터마이징을 했고 메모리 할당이 필요하면 전부 ashmem에 있는 라이브러리를 이용한다.
    리눅스에서는 각 프로세스마다 우선순위를 주고 메모리가 부족하면 우선순위가 낮은 것을 없앤다. 안드로이드는 프로세스마다 우선순위를 주지 않고 그룹마다 우선순위를 준다. 그 이유는 바인더 때문이다.  메모리 부족 시 우선순위가 낮은 그룹을 제거한다. 안드로이드에는 프로그램 종료 기능이 없다. 화면은 내릴 수 있지만 알아서 프로그램이 제거되기 때문이다. 자바로 응용프로그램을 만들기 때문에 가비지컬렉터가 있다. 종료버튼이 있다 하더라도 UI가 없어질 뿐 그 자체가 바로 종료되는 것은 아니다.
    이중 가장 중요한 것은 바인더라고 할 수 있다. 바인더에 의해 안드로이드와 기존 리눅스의 차이가 생긴다. 커널버전 2.6.30이상부터는 커널과 안드로이드가 통합된다는 말이 있는데 2.6.29이전의 커널은 바인더폴트( 메모리 회수에 문제가 있는 버그)가 있었다. 그럼 기존에 나왔던 안드로이드폰들도 이 버그가 있었을까? 아마도 개발인력이 많기 때문에 자체적으로 해결했을 것 같다. 바인더 서비스를 넣으면 기존의 전통적인 커널구조가 객체지향 구조로 바뀐다. 컴포넌트 중심으로 양쪽간 메시지를 주고받는 구조로 바뀐다는 말이다. 이것의 장점은 한쪽이 잘못 되어도 반대쪽까지 잘못되지는 않는다는 점. 응답을 못받거나 결과만 이상하게 나올 뿐이다.
    전원관리기능은 기존의 리눅스가 가지고 있는 3단계에 2개를 더해 5단계로 이루어진다. 안드로이드 커널을 설정할때는 자신의 하드웨어에 전원관리 모듈이 없어도 반드시 포함시켜야 한다. 포팅을 하게 되면 시간이 지날수록 이것들에 집중해야 할 것이다.

    Binder


    바인더는 프로그램과 데이터를 공유하기 위한 것이다. 기존의 IPC는 보안이슈와 오버헤드이슈가 있다. 리눅스에서도 오픈바인더를 쓸 수 있지만 안드로이드에서 더 활성화를 시켜 두었다. 2.6.29에 기존 버그들이 완벽하게 패치되었다. 이전의 방식은 커널을 통해서 데이터를 공유하였다.(커널의 공유메모리 영역에 데이터를 올려두고 시그널등의 이벤트를 받아 메시지 큐 등을 써서 - 세마포어, 뮤텍스, 스레드 등 ). 바인더는 커널쪽의 메인호스팅이 없고 바인드라는 서비스가 있는 것이다. 바인드를 호출하면 응용프로그램과 디바이스쪽에 바인드서비스가 붙어 서로 통신하게 된다. 바인드서비스는 커널이 아니다. 서로다른 서비스간, 응용프로그램간 데이터를 주고받을 때 동작하게 된다. 여기서의 단점은, 기존의 방식은 하나의 매개체가 있고 공유해서 썼지만 바인드는 각각의 서비스마다 바인더가 있어야 하기 때문에 메모리 낭비가 되는 측면이 있다. 그럼에도 불구하고 쓰는 이유는 바인더는 각각 별개로 동작하기 때문에 주고 받는 통신을 가로챔 당할 가능성이 더 낮아지므로 보안성이 더 오르기 때문이다. 메모리 낭비를 줄이기 위해 바인더를 230kb정도의 크기로 최소화 시켰다. 바인더는 원래 PalmOS에 있던 것이기 때문에 라이센스가 있다. 약 3000라인정도 된다.
    바인더는 스레드 풀을 유지한다. 드라이버, 매니저 서비스마다 바인더가 양쪽에 붙게 되는데 풀로 관리한다는 것은 요구가 있기 전에 미리 자료구조화 시켜 자원을 확보해 두고 바로 맵핑해서 쓸 수 있도록, 바인드 하는데 시간이 걸리지 않도록 되어 있다.  응용 A가 B 디바이스드라이버와 통신을 하게 되면 채널이 생성되는데 만약 채널안에 연결되는 프로그램이 A 이외에 C도 존재 한다면 B가 종료되었다고 해서 바인더가 종료되면 안된다. 이를 위해 참조계수가 있어서 참조계수가 0이 되면 바인드서비스를 해제한다.

    Low Memory Killer
    리소스가 부족할 때 자동으로 실행된다. 안드로이드는 그룹 당 우선순위를 주어 해당되는 그룹을 한번에 해제한다. 그룹은 참조계수랑 연관이 있다.  ( 아마도 참조계수가 0 인 것들이 먼저 해제 될듯. )
    아래 그림은 Low Memory Killer 소스의 일부분이다. 프로세스가 실행되면 프로세스테이블에 등록되어 관리가 된다. 메모리 부족현상이 생기면 shirink 함수가 실행되어 링크드리스트로 되어있는 프로세스컨트롤 블록들을 끝까지 탐색한다. 제거되어야 할 프로세스가 있으면 SIGKILL을 전송한다.


    Power Management
    기존 리눅스드라이버의 파워매니저에 계층을 하나 더 올렸다. 보통 핸드폰은 5단계를 사용한다. 키보드-액정-CPU 순으로 전원이 차단되면서 제일 마지막은 D램에만 전원을 공급하는 상태가 된다. 전원은 CPU가 아닌 PMIC라는 전원공급칩이 제어를 한다. D램에만 전원이 공급되는 상태라도 터치스크린은 잠기지 않는다. 이 상태에서 발생하는 인터럽트도 PMIC가 관리한다.

    Native Libraries

    기존의 라이브러리를 그대로 사용하지 않고 EABI로 빌드된 라이브러리이다. 기존의 리눅스는 PC가 모태이기 때문에, 스마트 디바이스환경을 위해 만들어진 것이 아니기 때문에 arm또는 스마트디바이스에 최적화 되어 있지 않다. 바인더가 있고 자바의 JNI를 통해 C코드와 연결되기 때문에 필수적으로 사용되는 C라이브러리 사이즈를 줄이고 효율을 더 좋게 하였다. 안드로이드의 네이티브 라이브러리는 Bionic Lib이라고 부른다. 임베디드 리눅스쪽 라이브러리를 안드로이드로 가져오면 동작하지 않는다. 대부분이 BSD 라이센스기 때문에 코드공개의 의무가 없다. 그리고 프로세스마다 로드 되므로 작은 사이즈( libc.so 사이즈 231Kb )로 되어 있고 경량 pthread가 구현되어 있다. 모든 Native 코드는 bionic과 함께 컴파일 되어야 한다. 웹킷은 오픈소스 브라우저 기반이다. 애플의 사파리, 노키아의 심비안에 이미 적용되어 성능은 검증되어 있다. 브라우저 속도가 아주 빠른것이 특징이다. HTML 표준을 따르고 Active X는 지원하지 않는다. 미디어프레임워크는 PocketVideo OpenCORE 플랫폼 기반이다. 동영상 디코딩을 하며 표준 Video, Audio, Still-frame 포맷을 지원한다. 이를 이용해 상용제품을 양산할 경우 코덱 라이센스에 대한 비용이 발생할 수 있다. SQLite 는 기본 데이터 베이스이다. Mysql과 거의 유사하다. 위치기반 서비스등을 할 때 유용하게 쓰일 수 있다.
    Surface Manager는 모든 응용프로그램의 surface rendering을 프레임버퍼로 전달한다. 프레임버퍼는 LCD와 CPU속도에 차이가 있기 때문에  DRAM 또는 SRAM에 똑같은 구조를 만들어 두고 메모리 블록 전체를 복사해서 한번에 LCD에 출력한다. 이 때 그 메모리 공간을 프레임버퍼라고 한다. 기존의 임베디드리눅스는 2D, 3D를 따로 처리했지만 이 경우에는 동시에 2D, 3D를 처리한다. 화면 합성이나 결합, 반투명 효과등을 한번에 처리할 수 있다. 2D는 단일버퍼로 충분하지만 3D는 데이터량이 많아 단일버퍼로는 병목현상이 생길 수 있기 때문에 프레임버퍼를 더블프레임을 쓴다.  기존의 버퍼 사이즈를 두배로 늘려주면 된다. 더블버퍼링을 2D에도 적용하면 전경그림과 배경그림을 별도로 관리할 수 있어 오버헤드가 줄어든다.

    Audio Manager는 오디오 처리를 한다. 오디로 출력 라우팅 기능이 구현되어 있다. 이전에는 OSS를 사용했는데 안드로이드에서 제대로 동작하지 않기 때문에 ALSA를 써야한다. 기본적으로 ALSA는 디폴트 볼륨이 0 으로 설정되어 있기 때문에 테스트를 하기 위해서는 init 부분에서 볼륨설정을 먼저 해줘야 한다.

    Android Runtime
     

    SUN의 자바는 명령들이 전부 8비트 길이를 가지지만 안드로이드는 4바이트이다. 기존의 SUN은 명령어가 스택에 들어가기 때문에 PUSH,POP명령어를 쓰고 Dalvik은 뱅크드레지스터를 이용한 복사명령을 이용하고 레지스터에서 바로 실행시키기 때문에 속도가 더 빠르다. 4바이트가 레지스터에 전부 들어가기 때문에 낮은사양에서도 느려지지 않는 효과도 있다. 프로그램동작은 자바코드이고 드라이버들은 대부분 C/C++이지만 그 사이에 JNI가 있기때문에 동작이 가능하다. JNI는 자바코드에서 C나 C++  라이브러리를 호출할 수 있도록 만들어진 규약이다. 안드로이드에서 응용프로그램은 C/C++로도 만들 수 있다. 대신 UI를 가지기는 힘들다. 백그라운드 서비스를 제작할 경우 굳이 자바로 할 필요는 없다.

    HAL ( Hardware Abstraction Layer )

    예전에는 하드웨어 드라이버를 하드웨어 제작자가 만들었지만 요즘은 추상계층을 두어 상위 드라이버나 하위 네이티브 드라이버를 서로 독립적으로 개발할 수 있고 응용프로그램도 독립적으로 동작할 수 있다. 이는 일관된 함수 이름과 형식이 있기때문에 가능하다. 개발자가 구현하기 쉽게 표준화된 API들이 존재하며 모든 제조사가 자신의 컴포넌트를 안드로이드 플랫폼에 넣을 수 있도록 구성되었다. HAL은 라이센스문제를 피하고 안정성을 위해 유저스페이스에 존재한다.

    Application Framework
    액티비티 매니저는 응용프로그램의 생명주기를 담당한다. 패키지 매니저는 시스템에서 동작중인 응용프로그램들의 정보를 담당한다. 윈도우 매니저는 모든 응용프로그램과 관련된 화면을 담당한다. 뷰 시스템은 표준 위젯을 담당한다. 처음 바탕화면이 위젯이다. 윈도우는 dll 파일이 많지만 안드로이드는 하나의 패키지 파일로 되어있어 프로그램 배포가 쉽다.

    Bootup Sequence

    리눅스는 기본적으로 init이 가장먼저 실행된다. init.rc 라는 이름의 파일에는 init이 해야할 작업들이 기록되어 있다. 파일시스템 마운팅, 폴더 권한설정, 캐시폴더 삭제, 시작프로그램 동작 등이 기록되어 있다. 우선 데몬을 올린다. 데몬은 init에 의해 리눅스와 같이 시작되었다가 리눅스가 종료될 때 없어지는 프로그램으로서 데몬을 작성하는 규격에 따라 만들어져야 한다. Zygote가 Dalvik을 초기화 한다. C 밑에 있는 기본라이브러리들은 런타임을 통해 실행되고 상위 서비스들은 Dalvik을 통해 실행된다. 이러한 과정들을 위한 설정은 해당하는 config 파일을 수정하면 된다. 어떤 동작들을 바꾸고 싶으면 기본적으로 init.rc를 바꾸면 되고 Zygote를 바꾸고 싶으면 그 설정파일을 바꾸면 된다. 그리고 시스템서버, 서페이스매니저, 오디오매니저들이 올라간다. 그 다음에는 시스템 서비스들이 활성화 된다. 이들은 서비스이므로 서비스매니저에 등록된다.

    Bootup Sequence - Zygote
    Zygote가 실행되면 시스템 서비스가 활성화 된다. 응용프로그램에서는 android.process.* 을 가지고 접근할 수있다. Zygote와 시스템서버간에는 IPC 소켓으로( 127.0.0.x ) 통신을 한다.

    < 부팅 완료 후 각 프로세스들의 상태 >
    Android Internals Reference
    http://code.google.com/intl/ko/android/
    http://groups.google.com/group/android-internals
    http://www.android-internals.org/
    http://groups.google.com/groups/profile?enc_user=_EKOshMAAADzFnauhYxa0ga8JtF8CI5fWMj6vob75xS36mXc24h6ww
    http://groups.google.com/groups/profile?enc_user=lYDbNxEAAAD8uJiqPP7Wd-bc9b1O3waCkdEasx1kiYTQavV7mdW13Q

    'old > Basic' 카테고리의 다른 글

    Parcelable Object 2  (0) 2010.07.06
    Parcelable Object 1  (0) 2010.07.06
    JNI (Java Native Interface)  (0) 2010.07.06
    http://mc500.tistory.com/entry/Goole-Android-Build-해-보  (0) 2010.02.09
    how to make sqplite3 table  (0) 2010.02.09
    Posted by jazzlife
    ,

    JNI(Java Native Interface)

    플랫폼 독립성을 버리고, 기능을 취한다.

    자바 프로그램을 작성하다보면, 부득이하게 시스템 의존적 코딩을 필요로 하는 때가 있다. 하지만, 자바로서는 그러한 욕구를 감당할 수는 없다. 따라서, 자바의 클래스 파일이 C/C++의 함수를 호출하여, 그 결과만 되돌려받게 하는 것도 한 방법이다. 그렇지만 이 자체로서 이미 플랫폼 독립적이라는 특성은 사라졌다고 볼 수밖에 없다.

    프로그램 작성

    첫째 단계, 자바 프로그램 작성

    우선, Native접근이 필요한 메쏘드만 native 키워드를 사용해 구현하지 않은 채 그대로 두고, 나머지는 모두 구현한 후, 컴파일한다.

      public class Hello {
          public native void getHello();

          static {
              System.loadLibrary("hello");
          }
         
          public static void main(String[] args) {
              new Hello().getHello();
          }
      }

      javac Hello.java

    public native void getHello() : getHello 함수가 native method라는 것을 의미한다.

    static {
            System.loadLibrary("hello");
    }

    이것은 공유 라이브러리를 실행시 로드하는 코드로 반드시 static 초기화에 포함되어야 한다. 이것은 플랫폼에 따라 다르게 행동하는데, 만일 Win32이면, hello.dll을, 솔라리스이면, libhello.so라는 이름의 라이브러리를 찾는다.

    둘째 단계, C프로그램을 위한 헤더 파일 생성

    javah와 -jni옵션을 이용하여, 헤더파일을 생성해 보자.

      javah -jni Hello

    생성된 헤더 파일 Hello.h는 다음과 같다.

      /* DO NOT EDIT THIS FILE - it is machine generated */
      #include <jni.h>

      /* Header for class Hello */

      #ifndef _Included_Hello
      #define _Included_Hello
      #ifdef __cplusplus
      extern "C" {
      #endif


      /*
       * Class:     Hello
       * Method:    getHello
       * Signature: ()V
       */


      JNIEXPORT void JNICALL Java_Hello_getHello
        (JNIEnv *, jobject);

      #ifdef __cplusplus
      }
      #endif
      #endif

    여기서 생성된 함수 이름에 주목해 보자. '_'를 구분자로 크게 몇 부분으로 나뉘어져 있다.

      • Java : 항상 앞에 붙는다.
      • Hello : 클래스의 이름
      • getHello : 메쏘드의 이름

    셋째 단계, 함수를 구현한다.

    HelloImpl.c라는 이름으로 함수를 구현해 보자.

      #include <jni.h>
      #include "Hello.h"
      #include <stdio.h>

      JNIEXPORT void JNICALL Java_Hello_getHello(JNIEnv *env, jobject obj)
      {
          printf("OOPSLA\n");
          return;
      }

    넷째 단계, C파일을 컴파일하여, 공유 라이브러리를 생성한다.

    이때, 비주얼 C++을 이용할 경우 다음과 같은 옵션을 준다.

    cl /I본인의 자바 디렉토리)\include /I(본인의 자바 디렉토리)\include\win32 -LD HelloImp.c -Fehello.dll

    솔라리스에서 컴파일할 경우 다음과 같은 옵션을 준다.

    cc -G -I(자바 홈 디렉토리)/include -I(자바 홈 디렉토리)/include/solaris HelloImp.c -o libhello.so

    마지막 단계, 프로그램을 실행한다.

      java Hello

    실행 결과:

      OOPSLA

    자바의 타입과 C++/C의 타입 매핑 문제

    네이티브 메쏘드를 호출하는 것까지는 좋다. 하지만, 그 결과값이 있을 경우에는 타입의 괴리(Impedence)가 존재할 수밖에 없다. 따라서, jni.h에는 자바의 타입을 흉내낼 수 있는 여러 가지 타입을 정의해 놓았다.

    Primitive Types

    네이티브 메쏘드에서는 보통 자바의 타입 이름 앞에 j가 붙은 이름을 가진 타입을 사용함으로써, 마치 자바 프로그램을 작성하는 듯이 프로그램할 수 있다. 예를 들어, boolean은 jni.h에 jboolean이라는 이름의 타입과 매핑된다.

    • jboolean  : 자바의 boolean(8비트) - typedef unsigned char   jboolean
    • jbyte : 자바의 byte(8) - typedef signed char jbyte(WIN32의 경우이다. - System Dependent)
    • jchar  : 자바의 char(16) - typedef unsigned short  jchar
    • jshort : 자바의 short(16) - typedef short  jshort
    • jint : 자바의 int(32) - typedef long jint(WIN32의 경우이다. - System Dependent)
    • jlong : 자바의 long(64) - typedef __int64 jlong(WIN32의 경우이다. - System Dependent)
    • jfloat : 자바의 float(32) - typedef float jfloat
    • jdouble : 자바의 double(64) - typedef double  jdouble;
    • void : 자바의 void

    위의 리스트에서, 오른쪽은 실제 jni.h가 이 타입들을 어떻게 정의하고 있는지를 보여 준다. 여기서, jbyte, jint, jlong은 시스템에 따라 적용할 타입이 변경될 소지가 있으므로, WIN32의 경우, 자바디렉토리\include\win32\jni_md.h에 정의되어 있다. 솔라리스인 경우에는 참고로 다음과 같다.

      #ifdef _LP64 /* 64-bit Solaris */
      typedef int jint;
      #else
      typedef long jint;
      #endif
      typedef long long jlong;
      typedef signed char jbyte;

    객체 타입

    이 경우에도 비슷하다. 일반 객체는 jobject타입으로 통용하고, 나머지 특수한 클래스에 대해 매핑되는 타입을 두기로 한다.

    • jobject : 모든 자바 객체로 뒤에 나열된 모든 타입의 슈퍼 타입이다.
    • jstring : 자바의 java.lang.String클래스이다.
    • jclass : 자바의 java.lang.Class클래스이다.
    • jarray : 자바의 배열이다.
    • jthrowable ; 자바의 java.lang.Throwable 예외 사항이다.

    배열

    기타, 기본 타입의 배열을 위한 배열 타입도, 하나의 클래스로 정의되어 있다.

      jbooleanArray;
      jbyteArray;
      jcharArray;
      jshortArray;
      jintArray;
      jlongArray;
      jfloatArray;
      jdoubleArray;
      jobjectArray;


    함수와 메쏘드간의 인자 전달

    문제는 jstring을 비롯한 타입들은 실제 printf문 같은 곳에서 직접 쓰일 수 없다는 것이다. 이것들은 다시, C/C++의 타입으로 변환되어 사용되어야 한다. 이것에 관한 여러 함수들이 JNIEnv라는 구조체에 정의되어 있다.

    예를 들어, String객체를 C메쏘드에 넘기도록 앞의 Hello.java를 수정해 보자. 자바 소스는 단순히 선언을 바꾸는 것으로 끝난다.

      public native void getHello(String name);

    재컴파일과, javah를 이용해 만든 새로운 헤더 파일을 이용해 HelloImpl.c를 새로 작성해 보면,

      JNIEXPORT void JNICALL Java_Hello_getHello(JNIEnv *env, jobject obj,jstring name)
      {
            char *str_name = (*env)->GetStringUTFChars(env, name, 0);
            printf("Hello %s\n",str_name);
            (*env)->ReleaseStringUTFChars(env, name, str_name);
            return;
      }

    이와 같이, JNIEnv의 멤버 함수로부터 도움을 받아야 한다.

    이제 각 타입에 대해 인자 전달과 결과값 반환 방법을 알아 보자.

    Primitive Types

    기본 타입의 경우에는 다음과 같이 기존의 C/C++타입중 호환되는 타입의 재명명이므로, 그다지 인자 전달에 문제가 없다.

      typedef unsigned char   jboolean;
      typedef unsigned short  jchar;
      typedef short           jshort;
      typedef float           jfloat;
      typedef double          jdouble;

      WIN32:
      typedef long jint;
      typedef __int64 jlong;
      typedef signed char jbyte;

    다만, boolean의 경우에는 true/false값이 다음과 같이 전달된다.

      #define JNI_FALSE 0
      #define JNI_TRUE 1

    예를 들어, boolean타입의 인자를 주고, 반환받는 경우,

      Hello.java:
      Hello h = new Hello();
      System.out.println("h.getBoolean(true) = "+h.getBoolean(true));    // 결과 : true

      HelloImpl.c:
      JNIEXPORT jboolean JNICALL Java_Hello_getBoolean(JNIEnv * env, jobject jobj, jboolean jbool)
      {
             ......
              return JNI_TRUE;
      }

    배열 타입

    각 기본 타입에 따라 그 배열을 다룰 수 있는 배열 클래스가 제공된다.

      jarray
      jbooleanArray
      jbyteArray
      jcharArray
      jshortArray
      jintArray
      jlongArray
      jfloatArray
      jdoubleArray

    모든 배열 클래스들은 jarray 클래스의 하위 클래스이다.

    배열의 길이 파악

    모든 배열의 길이를 알아낼 수 있는 함수는 다음과 같다.

      jsize GetArrayLength(JNIEnv *, jarray array)

    (jsize는 jint와 동일하다)

    자바의 배열을 C의 배열로 변환하는 함수

    각 타입마다 자바 타입의 배열을 C타입으로 변환시켜 주는 함수가 있다.

    • jboolean * GetBooleanArrayElements(JNIEnv *env,jbooleanArray array, jboolean *isCopy)
    • jbyte * GetByteArrayElements(JNIEnv *env,jbyteArray array, jboolean *isCopy)
    • jchar * GetCharArrayElements(JNIEnv *env,jcharArray array, jboolean *isCopy)
    • jshort * GetShortArrayElements(JNIEnv *env,jshortArray array, jboolean *isCopy)
    • jint * GetIntArrayElements(JNIEnv *env,jintArray array, jboolean *isCopy)
    • jlong * GetLongArrayElements(JNIEnv *env,jlongArray array, jboolean *isCopy)
    • jfloat * GetFloatArrayElements(JNIEnv *env,jfloatArray array, jboolean *isCopy)
    • jdouble * GetDoubleArrayElements(JNIEnv *env,jdoubleArray array, jboolean *isCopy)

    정수의 경우를 예로 들어 보자.

           jsize iSize = (*env)->GetArrayLength(env,jiarray);
           jint *iArray = (*env)->GetIntArrayElements(env,jiarray,0);

           for(i=0; i < iSize; i++) {
                    printf("iArray[%d] = %d\n",i,iArray[i]);
                    iArray[i]++;
           }
           (*env)->ReleaseIntArrayElements(env,jiarray,iArray, 0);

     자바의 배열을 JNI에서 사용할 경우, 이것은 현재의 실행 루틴을 수행하는 쓰레드가 이 배열 객체에 대해 PIN 연산을 수행하거나, 아니면 전체의 로컬 복사본을 사용하게 되는 것이다. 이로 인해, 이 연산의 반대 급부로 반드시 UNPIN연산에 해당하는 ReleaseIntArrayElements가 불려지는 것이 좋다.

    • void ReleaseBooleanArrayElements(JNIEnv *env,jbooleanArray array,  jboolean *elems,  jint mode)
    • void ReleaseByteArrayElements(JNIEnv *env,jbyteArray array, jbyte *elems, jint mode)
    • void ReleaseCharArrayElements(JNIEnv *env,jcharArray array, jchar *elems,  jint mode)
    • void ReleaseShortArrayElements(JNIEnv *env,jshortArray array, jshort *elems,  jint mode)
    • void ReleaseIntArrayElements(JNIEnv *env,jintArray array, jint *elems, jint mode)
    • void ReleaseLongArrayElements(JNIEnv *env,jlongArray array, jlong *elems, jint mode)
    • void ReleaseFloatArrayElements(JNIEnv *env,jfloatArray array, jfloat *elems, jint mode)
    • void ReleaseDoubleArrayElements(JNIEnv *env,jdoubleArray array, jdouble *elems, jint mode)

    이 외에도 다음과 같은 함수들이 있다.

    • jdoubleArray NewDoubleArray(jsize len)를 비롯해 각 기본 타입마다 새로운 자바 배열을 생성할 수 있다.
    • void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, jboolean *buf)를 비롯해 각 기본 타입마다 Sub배열을 획득할 수 있는 함수가 제공된다.

    자바 객체의 사용

    네이티브 코드 내에서 자바 객체를 사용할 수도 있다. 즉 콜백과 비슷한 형식을 지원한다. private영역까지 접근할 수 있다.

    자바 객체의 클래스 정보 얻기

    일단, 인자로 넘겨지는 자바 객체에 대한 클래스 정보를 획득하는 함수가 제공된다.

      jclass cls = (*env)->GetObjectClass(env, jobj);

    여기서 주의할 것은 반환값으로 돌아오는 cls의 레퍼런스값은 오직 이 네이티브 메쏘드가 수행이 끝날 동안에만 유효하다는 것이다.

    자바 객체 및 클래스 타입의 래퍼런스는 네이티브 메쏘드 실행시마다 결정되므로, 한 번 실행해 얻은 레퍼런스를 다음번 메쏘드에서 다시 사용하려는 것은 잘못된 것이다.

    따라서, 원칙적으로 객체 레퍼런스 변수를 전역 변수로 할당하는 것은 위험한 일이다. 하지만, 꼭 허용되지 않는 것은 아니다. 전역 레퍼런스로 지정해 줄 수 있는 함수가 있다.

       jobject NewGlobalRef(JNI_Env* env, jobject lobj)



    네이티브 코드를 호출한 객체의 메쏘드에 대한 ID얻기

    객체의 메쏘드를 호출하기 위해 그 메쏘드의 ID를 획득해야 한다. 이것은 메쏘드의 이름과 시그너쳐를 이용하는데, 이름은 별 무리가 없겠으나, 시그너쳐는 까다롭다.

    시그너쳐는 메쏘드의 인자 리스트와 반환 타입 정보를 스트링화한 것으로, 다음과 같은 규칙이 있다.

    • 시그너쳐 : "(인자 리스트)반환값"으로 이루어진 스트링이다.
    • 인자리스트의 인자들은 아래의 타입 기호에 따라 표기되며, 서로 ';'으로 구분된다.
    • 반환값 역시 타입 기호에 따라 표시된다.

    타입 기호는 다음과 같다. 이것은 자바 가상 머신 스펙에서 클래스 파일 포맷 부분에 등장하는 기호와 동일하다.

      Z :  boolean  
      B :  byte
      C :  char
      S :  short
      I  :  int
      J :  long
      F :  float
      D : double

    • 이들의 배열은 이들의 타입 기호 앞에 '['를 붙인다. 즉 int 배열은 [I가 된다.
    • 클래스 타입의 경우, L뒤에 클래스명을 패키지명까지 포함해 나열하면 된다. 즉, String클래스인 경우, Ljava.lang.String이다.

    이것은 수작업으로 할 경우, 실수가 많을 수 있으나, javap를 이용해, 쉽게 기호화된 시그너쳐를 확인할 수 있다.

      javap -s -p Hello

      Compiled from Hello.java
      class Hello extends java.lang.Object {
          static {};
              /*   ()V   */
          Hello();
              /*   ()V   */
          public static void coo(java.lang.String, java.lang.String);
              /*   (Ljava/lang/String;Ljava/lang/String;)V   */
          private void foo(java.lang.String, java.lang.String);
              /*   (Ljava/lang/String;Ljava/lang/String;)V   */
          public native boolean getBoolean(boolean);
              /*   (Z)Z   */
          public native byte getByte(byte);
              /*   (B)B   */
          public native char getChar(char);
              /*   (C)C   */
          public native double getDouble(double);
              /*   (D)D   */
          public native float getFloat(float);
              /*   (F)F   */
          public native java.lang.String getHello(java.lang.String);
              /*   (Ljava/lang/String;)Ljava/lang/String;   */
          public native int getInt(int);
              /*   (I)I   */
          public native int getIntArray(int[])[];
              /*   ([I)[I   */
          public native long getLong(long);
              /*   (J)J   */
          public native short getShort(short);
              /*   (S)S   */
          public static void main(java.lang.String[]);
              /*   ([Ljava/lang/String;)V   */
      }

    이것은 다음과 같이 Hello클래스에 두 메쏘드를 추가한 후의 javap결과이다.

         private void foo(String str,String str1) {
              System.out.println("Hello.foo() = "+str+str1);
          }

          public static void coo(String str,String str1) {
              System.out.println("Hello.coo() = "+str+str1);
          }

    이제 foo메쏘드의 ID를 획득해 보자.

        // 클래스 정보 획득
        jclass cls = (*env)->GetObjectClass(env, obj);

        // 메쏘드 ID
        jmethodID fooM = (*env)->GetMethodID(env, cls, "foo", "(Ljava/lang/String;Ljava/lang/String;)V");

        // 그런 메쏘드가 없으면, 0반환
        if(fooM == 0) {
            printf("Method foo isn't found\n");
        }
        else {
                   ......
        } 


    인자 생성 및 메쏘드 호출

    이제 메쏘드를 호출하는 일이 다음 순서가 되겠다. 물론 여기에 해당되는 함수들이 제공되는데, 이 함수들을 통해, 클래스 내의 메쏘드를 호출하므로, 메쏘드 ID와 더불어 메쏘드의 인자들을 리스트화해서 보내야 한다. 두 가지 방법이 있다.

    • C의 va_list구조체를 이용한다.
    • jvalue 유니온를 이용한다.

    여기서는 jvalue 유니온을 이용해 보자. 그 구조는 다음과 같다.

      typedef union jvalue {
          jboolean z;
          jbyte    b;
          jchar    c;
          jshort   s;
          jint     i;
          jlong    j;
          jfloat   f;
          jdouble  d;
          jobject  l;
      } jvalue;

    그러면, 스트링 두 개를 인자로 받는  foo메쏘드를 호출해 보자. String은 객체이므로, l에 스트링 객체의 레퍼런스를 할당하면 된다.

      jvalue* args;
      .......

      args = (jvalue*)malloc(sizeof(jvalue)*2);
      args[0].l = (*env)->NewStringUTF(env,"oops");
      args[1].l = (*env)->NewStringUTF(env,"la");
      (*env)->CallVoidMethodA(env, obj, fooM,args );   // JNIEnv, jobject, Method ID, jvalue

    여기에서는 foo가 void 타입이므로,  CallVoidMethodA가 쓰였지만, 이 외에에도 반환값에 따라 여러 함수가 제공된다. 이들의 인자는 위와 동일하다.

    • jboolean CallBooleanMethodA
    • jbyte CallByteMethodA
    • jchar CallCharMethodA
    •  jshort CallShortMethodA
    •  jint CallIntMethodA
    •  jfloat CallFloatMethodA
    •  jdouble CallDoubleMethodA

    참고로, va_list를 이용하여 인자를 전달하는 함수들은 A로 끝나지 않고, V로 끝나는데, 역시 마지막 인자만 va_list타입으로 수정해 주면 된다.


    정적 메쏘드 호출

    정적 메쏘드 또한 호출할 수 있는 함수가 따로 있다.

    메쏘드 ID : GetStaticMethodID함수 이용

      jMethodID cooM = (*env)->GetStaticMethodID(env, cls, "coo", "(Ljava/lang/String;Ljava/lang/String;)V");
      if(cooM == 0) {
              printf("Method foo isn't found\n");
          }
      else {  .....
      }

    인자 형성 방법은 동일하다.

    메쏘드 호출 : CallStaticVoidMethodA 이용

      cooM = (*env)->GetStaticMethodID(env, cls, "coo", "(Ljava/lang/String;Ljava/lang/String;)V");
      if(cooM == 0) {
              printf("Method foo isn't found\n");
      }
      else {
              args = (jvalue*)malloc(sizeof(jvalue)*2);
              args[0].l = (*env)->NewStringUTF(env,"papa");
              args[1].l = (*env)->NewStringUTF(env,"ya");
              (*env)->CallStaticVoidMethodA(env, cls, cooM,args );
      }

     여기서 주의할 것은 CallStaticVoidMethodA의 두 번째 인자는 jobject타입이 아닌, jclass타입이란 것이다. 클래스 메쏘드이니 당연하기도 하다.


    상위 클래스의 메쏘드 호출

    한 걸음 더 나아가 상위 클래스의 메쏘드도 호출할 수 있다. 여기에 해당되는 메쏘드는 모두 NonVirtual이라는 이름을 포함하고 있는데, c++을 배운 이라면, NonVirtual의 의미를 알고 있을 것이다.

    상위 클래스 타입의 jclass 객체 획득

      jclass sClass = (*env)->GetSuperclass(env,cls);

    메쏘드 ID : GetMethodID함수 이용

      superFooM = (*env)->GetMethodID(env, sClass, "foo", "(Ljava/lang/String;Ljava/lang/String;)V");
      if(superFooM == 0) {
            printf("Method foo isn't found\n");
      }
      else {
             .....
      }

     메쏘드를 호출: CallNonvirtualVoidMethodA

      args = (jvalue*)malloc(sizeof(jvalue)*2);
      args[0].l = (*env)->NewStringUTF(env,"can");
      args[1].l = (*env)->NewStringUTF(env,"dy");
       (*env)->CallNonvirtualVoidMethodA(env,obj,sClass ,superFooM,args );

    멤버 필드 접근

     필드를 접근하는 방법 또한 메쏘드를 접근하는 방법과 크게 다르지 않다. 필드가 Private일지라도 접근할 수 있다. 이 또한 몇 가지 함수에 의존하고 있는데, 그 함수의 형식이 메쏘드를 호출하는 함수와 거의 유사하기 때문이다. 일단 다음의 기호를 잊지 말자.

      Z :  boolean  
      B :  byte
      C :  char
      S :  short
      I  :  int
      J :  long
      F :  float
      D : double

    • 이들의 배열은 이들의 타입 기호 앞에 '['를 붙인다. 즉 int 배열은 [I가 된다.
    • 클래스 타입의 경우, L뒤에 클래스명을 패키지명까지 포함해 나열하면 된다. 즉, String클래스인 경우, Ljava.lang.String이다.

     이제 Hello.java에 몇 가지 필드를 추가해 보자.

        private int intVal;
        private static int iStaticIntVal;
        private String strVal = "Hello";

    이를 재컴파일 후, javah를 실행하고, 새로이 생긴 함수의 선언부를 HelloImpl.c로 복사해 이것에 대해 추가적으로 구현해 보기로 하자.

    필드를 접근하는 것은 두 단계로 이루어진다.

    • 클래스와 필드의 이름과 타입을 이용해 필드의 ID를 얻어낸다.
    • 필드의 ID와 객체를 이용해 실제 필드의 값을 얻어낸다.

    인스턴스 필드 접근

    먼저 필드의 ID를 얻어내기 위한 함수는 다음과 같다.

       jfieldID GetFieldID(JNI_Env* env,jclass clazz, const char *name, const char *sig)

    그리고, 인스턴스 필드를 접근하는 함수들은 다음과 같다.

    1) Read 함수
    • jobject GetObjectField(JNI_Env* env,jobject obj, jfieldID fieldID)
    • jboolean GetBooleanField(JNI_Env* env,jobject obj, jfieldID fieldID)
    • jbyte GetByteField(JNI_Env* env,jobject obj, jfieldID fieldID)
    • jchar GetCharField(JNI_Env* env,jobject obj, jfieldID fieldID)
    • jshort GetShortField(JNI_Env* env,jobject obj, jfieldID fieldID)
    • jint GetIntField(JNI_Env* env,jobject obj, jfieldID fieldID)
    • jlong GetLongField(JNI_Env* env,jobject obj, jfieldID fieldID)
    • jfloat GetFloatField(JNI_Env* env,jobject obj, jfieldID fieldID)
    • jdouble GetDoubleField(JNI_Env* env,jobject obj, jfieldID fieldID)
    2) Write 함수
    • void SetObjectField(JNI_Env* env,jobject obj, jfieldID fieldID, jobject val)
    • void SetBooleanField(JNI_Env* env,jobject obj, jfieldID fieldID, jboolean val)
    • void SetByteField(JNI_Env* env,jobject obj, jfieldID fieldID,jbyte val)
    • void SetCharField(JNI_Env* env,jobject obj, jfieldID fieldID, jchar val)  
    • void SetShortField(JNI_Env* env,jobject obj, jfieldID fieldID,jshort val)
    • void SetIntField(JNI_Env* env,jobject obj, jfieldID fieldID,jint val)
    • void SetLongField(JNI_Env* env,jobject obj, jfieldID fieldID, jlong val)
    • void SetFloatField(JNI_Env* env,jobject obj, jfieldID fieldID, jfloat val)
    • void SetDoubleField(JNI_Env* env,jobject obj, jfieldID fieldID, jdouble val)

    클래스 필드 접근

    Static 필드를 접근하기 위해 먼저 ID를 얻어내야 한다.

      jfieldID GetStaticFieldID(JNI_Env* env,jclass clazz, const char *name,  const char *sig)

    그리고, 필드를 접근하는 함수들은 다음과 같다.

    Gettor 함수
    • jobject GetStaticObjectField(JNI_Env* env,jclass clazz, jfieldID fieldID)
    • jboolean GetStaticBooleanField(JNI_Env* env,jclass clazz, jfieldID fieldID)
    • jbyte GetStaticByteField(JNI_Env* env,jclass clazz, jfieldID fieldID)
    • jchar GetStaticCharField(JNI_Env* env,jclass clazz, jfieldID fieldID)
    • jshort GetStaticShortField(JNI_Env* env,jclass clazz, jfieldID fieldID)
    • jint GetStaticIntField(JNI_Env* env,jclass clazz, jfieldID fieldID)
    • jlong GetStaticLongField(JNI_Env* env,jclass clazz, jfieldID fieldID)
    • jfloat GetStaticFloatField(JNI_Env* env,jclass clazz, jfieldID fieldID)
    • jdouble GetStaticDoubleField(JNI_Env* env,jclass clazz, jfieldID fieldID)
    Settor 함수
    • void SetStaticObjectField(JNI_Env* env,jclass clazz, jfieldID fieldID, jobject value)
    • void SetStaticBooleanField(JNI_Env* env,jclass clazz, jfieldID fieldID, jint value)
    • void SetStaticByteField(JNI_Env* env,jclass clazz, jfieldID fieldID, jbyte value)
    • void SetStaticCharField(JNI_Env* env,jclass clazz, jfieldID fieldID, jchar value)
    • void SetStaticShortField(JNI_Env* env,jclass clazz, jfieldID fieldID, jshort value)
    • void SetStaticIntField(JNI_Env* env,jclass clazz, jfieldID fieldID, jint value)
    • void SetStaticLongField(JNI_Env* env,jclass clazz, jfieldID fieldID, jlong value)
    • void SetStaticFloatField(JNI_Env* env,jclass clazz, jfieldID fieldID, jfloat value)
    • void SetStaticDoubleField(JNI_Env* env,jclass clazz, jfieldID fieldID, jdouble value)

    예제

    이제 이들을 사용해 Hello 클래스의 필드들을 접근해 보기로 하자. Hello.java에 native메쏘드인 다음을 추가하고 컴파일 후, HelloImpl.c에서, 다음을 중심으로 코딩해 보자.

      public native void testFieldAccess()

    정적 변수 접근

    정적 필드인 iStaticIntVal 필드를 접근하여, 값을 읽어들인 후, 다시 2를 더해 값을 써 보자.

      jfieldID jFieldId;
      jint iStaticIntVal;
      jclass cls = (*env)->GetObjectClass(env, jobj);

      jFieldId = (*env)->GetStaticFieldID(env,cls,"iStaticIntVal","I");
      if(jFieldId == 0) {
                      printf("Field iStaticIntVal not Found in Hello class\n");
                      return;
      }

      //  값을 읽어들인 후,
      iStaticIntVal = (*env)->GetStaticIntField(env,cls,jFieldId);

      // 2를 더해, 다시 그 값을 쓴다.
      (*env)->SetStaticIntField(env,cls,jFieldId,(iStaticIntVal+2));  

    인스턴스 변수 접근

      jint iVal;
      jfieldID jFieldId;
      jclass cls = (*env)->GetObjectClass(env, jobj);
      jFieldId = (*env)->GetFieldID(env,cls,"intVal","I");
      if(jFieldId == 0) {
                      printf("Field intVal not Found in Hello class\n");
                      return;
      }
      iVal = (*env)->GetIntField(env,jobj,jFieldId);
      printf("iVal in C = %d\n",iVal);
      (*env)->SetIntField(env,jobj,jFieldId,iVal);

    그런데, 실제로 int를 가지고 실험해 본 결과, C에서 값을 잘 넘겨받는 것까지는 좋은데, 다시 자바로 값을 써 줄 때, 인자가 잘 전달되지 않는 현상을 보였다. 좀더 생각해 볼 문제이다.

    스트링 접근

    스트링은 객체이므로 객체를 접근하는 방식을 통해 접근해야 한다. 우선 GetFieldID 함수의 인자로 "Ljava/lang/String;"을 주목하면, 객체의 접근 방식과 동일함을 알 수 있다. 여기에서 세미콜론(;)을 빠뜨리면, 인식하지 못하므로 주의하기 바란다.

      jstring jstr;
      jfieldID jFieldId;
      jclass cls = (*env)->GetObjectClass(env, jobj);

      jFieldId = (*env)->GetFieldID(env,cls,"strVal","Ljava/lang/String;");
      if(jFieldId == 0) {
                      printf("Field strVal not Found in Hello class\n");
                      return;
      }

      jstr = (*env)->GetObjectField(env,jobj,jFieldId);
      (*env)->SetStaticIntField(env,cls,jFieldId,(iStaticIntVal+2));  
      str = (*env)->GetStringUTFChars(env, jstr, 0);
      printf("Get String : %s\n",str);

      새로운 스트링을 생성하여, 이것의 레퍼런스값을 str 필드에 할당하려면, 다음과 같이 한다.
      jstr = (*env)->NewStringUTF(env, "123");
      (*env)->SetObjectField(env, jobj, jFieldId, jstr);

    기타 객체를 호출하는 방법도 스트링과 거의 동일하다. (*env)->GetObjectField(env,jobj,jFieldId);를 통해, jobject타입의 객체를 얻어낸 후, 필요에 따라 이 객체의 메쏘드를 호출하면 된다.


    예외 사항

    예외 사항 던지기

    이제 예외 사항을 던지는 방법을 알아보자.  예외 사항들은 각각 클래스로 되어 있으므로, 이들의 클래스 정보를 얻어낸 후, 다음의 함수를 이용하면 된다.

       jint ThrowNew(JNI_Env* env, jclass clazz, const char *msg)

    Hello.java에서 testFieldAccess가 java.io.IOException을 던지도록 수정해 보자.

      public native void testFieldAccess() throws java.io.IOException;

      javac Hello.java
      javah -jni Hello

     이제 새로이 HelloImpl.c에서, testFieldAccess의 끝에 다음을 추가해 보자.

           jclass eCls;

           eCls = (*env)->FindClass(env, "java/io/IOException");
            if(eCls != 0) {
                    (*env)->ThrowNew(env, eCls, "IOException from C");
            }

    예외 사항 감지하기

    JNI의 C코드에서, 자바의 메쏘드를 호출했을 때.예외 사항이 돌아올 수 있다. 이 때, 감지하는 데 사용되는 함수는 다음과 같다.

      jthrowable ExceptionOccurred(JNIEnv* env)

    이것은 jthrowable타입의 예외 사항 객체를 반환해 준다. 그 예외 사항에 정보를 출력하는 데 사용되는 함수도 제공된다.

      void ExceptionDescribe(JNI_Env* env)

    그리고, 예외 사항이 처리된 후, 다시 예외 사항이 없는 것처럼 하려면, 다음과 같은 함수를 수행한다.

      void ExceptionClear(JNI_Env* env);

    이제 Hello.java에 새로운 메쏘드를 추가해 보자.

      public void returnException() throws java.io.IOException {
              throw new java.io.IOException("IOException from Java");
      }

    이 메쏘드를 C에서 호출하면, 예외 사항이 그쪽으로 반환될 것이다. 이것을 처리하는 코드는 다음과 같다.

             jmethodID jmId;
             jthrowable jexcp;

             jmId = (*env)->GetMethodID(env,cls,"returnException","()V");
              if(jmId == 0) {
                      printf("Method returnException not found\n");
              }
              else {
                      (*env)->CallVoidMethod(env,jobj,jmId);
                      jexcp = (*env)->ExceptionOccurred(env);  
                      if(jexcp) {
                              (*env)->ExceptionDescribe(env);   // 예외 사항 정보 출력
                              (*env)->ExceptionClear(env);
                      }
              }

    가비지 콜렉션

    강제 레퍼런스 해제

    원래 네이티브 메쏘드 내의 로컬 변수들이나 전역 변수들이 각각 지니고 있는 자바 객체에 대한 레퍼런스는 네이티브 메쏘드가 끝난 후에야 비로소 없어진다. 따라서, 네이티브 메쏘드가 오랜 시간동안 수행될 경우, 초반에만 실질적으로 사용될 뿐, 후반부에서는 쓰이지 않는 객체의 레퍼런스조차 메쏘드 전체가 수행이 끝날 때까지 남아있게 되는 셈이다. 이때 강제로 레퍼런스를 무효화시킬 수 있는 방법이 있다. 다음의 함수를 사용하면 된다.

    • void DeleteLocalRef(JNI_Env* env,jobject obj)   // 지역 변수
    • void DeleteGlobalRef(JNI_Env* env,jobject gref)  // 전역 변수

    동기화

    synchronized블록과 같은 동기화 블록을 C 코드내에서 지정해야 할 때가 있다. 바로 쓰레드에 의해 네이티브 메쏘드가 호출되었을 때, 이 메쏘드가 공유 변수들을 접근할 수 있기 때문이다. 그러한 용도로 사용되는 함수는 다음과 같다.

    • (*env)->MonitorEnter(JNI_Env* env,jobject obj);
    • (*env)->MonitorExit(JNI_Env* env, jobject obj);

     이 두 메쏘드 사이에 있는 코드들은 쓰레드들간에 동기화된다.

    'old > Basic' 카테고리의 다른 글

    Parcelable Object 1  (0) 2010.07.06
    안드로이드 기본 개념  (0) 2010.07.06
    http://mc500.tistory.com/entry/Goole-Android-Build-해-보  (0) 2010.02.09
    how to make sqplite3 table  (0) 2010.02.09
    What is Android?  (0) 2010.02.04
    Posted by jazzlife
    ,

    abstract와 interface

    old/JAVA 2010. 7. 1. 18:42
    abstract class : 추상화 정도가 낮고 몸통을 가진 methods를 포함할 수 있다. extends로 단일상속
    interface : 추상화 정도가 높고 몸통을 가질 수 없다. implements로 interface class에서 extends 다중상속
    Posted by jazzlife
    ,
    1. Menus (res/menu/*)

     ; android 네임스페이스를 가지고 정의되어야 한다
      (xmlns:android="http://schemas.android.com/apk/res/android")
     
     <menu>
      ; <item>과 <group> 노드들을 포함한다.
     
     <group>
      ; <item> element 포함
      id - 고유정수 integer ID
      menuCategory - 메뉴 우선순위(container,secondary,alternative)
      orderInCategory - category 내에서 item default 순서를 정의하는 정수
      title - 메뉴 타이틀의 문자열
      titleCondensed - 간결한 문자열 타이틀
      icon - drawable icon에 대한 리소스 식별자
      alphabeticShortcut - 알파벳 단축키
      numericShortcut - 숫자 단축키
      checkable - item check 가능 여부 (true, false)
      checked - item default check 가능 여부(true, false)
      visible - item default로 보여지는지 여부(true, false)
      enabled - item default로 활성화 하는지 여부(true, false)
     
    (ex)
     <menu xmlns:android="http://schemas.android.com/apk/res/android">
       <item  
         android:id="@+id/ex_item"
         android:title="Example Item"
         android:icon="@drawable/ex_item_icon" />
       <group android:id="@+id/ex_group">
         <item  
           android:id="@+id/ex_item2"
           android:title="Example Item2"
           android:icon="@drawable/ex_item2_icon" />
       </group>
       <item  
         android:id="@+id/ex_subMenu"
         android:title="Example subMenu" >
          <menu>
            <item
              android:id="@+id/ex_subMenu"_item"
             android:title="Example subMenu item" />
         </menu>
       </item>
     </menu>



    2. Searchable

        ; 검색기능을 제어하게끔 한다.

    <searchable xmlns:android="http://schemas.android.com/apk/res/android"
        android:label="@string/search_label"
        android:hint="@string/search_hint"
        ....
        <actionkey android:keycode="KEYCODE_CALL"...>
    />

    'old > UI Design' 카테고리의 다른 글

    adb shell에서 특정 activity실행하기  (0) 2010.07.06
    PowerManager에서 Screen ON/OFF 통지  (0) 2010.07.06
    Resources - Dimen, Color, 9-patch, Animation  (0) 2010.07.01
    String & StyledText  (0) 2010.06.30
    Style & Theme  (0) 2010.06.30
    Posted by jazzlife
    ,

    1. Dimension Values

    px : 스크린 상의 실제 픽셀
    in : 스크린의 물리적 크기에 기반한 인치
    mm: 스크린의 물리적 크기에 기반한 밀리미터
    pt: 스크린의 물리적 크기에 기반한 인치의 1/72
    dp(dip): Density-independent Pixels
    sp: Scale-independent Pixels, dp와 폰트크기에 의해 결정됨

    (resources구문) - <dimen name="name">1px</dimen>
    (xml구문) - android:textSize="@dimen/name"
    (java코드) - float dimen = Resources.getDimen(R.dimen.name)




    2. Color Drawables - colors.xml

    (resources구문) - <drawable name="solid_red>#f00</drawable>
    (java코드)
     Drawable redDrawable = Resources.getDrawable(R.drawable.solid_red);
     TextView tv = (Textview) findViewById(R.id.text);
     tv.setBackground(redDrawable);



    3. Nine-Patch Stretchable Image
        ; 확대 가능한 이미지 PNG 타입.
          확장자 *.9.png

    4. Animation

    1) Tweened Animation
        ; rotate, fading, moving, stretching을 포함한 간단한 애니메이션

    <set>
        ; 자기 자신 또는 다른 애니메이션을 재귀적으로 보유할 수 있는 컨테이너. 많은 child 앨리먼트 포함가능.
     
      shareInterpolator - 모든 인접한 child 앨리먼트 사이에  같은 interpolator를 공유하는지에 대한 여부.

    <alpha>
        ; fading, AlphaAnimation을 표현

        fromAlpha - 0.0~1.0 (0.0은 투명)
        toAlpha - 0.0~1.0 (0.0은 투명)

    <scale>
        ; 크기를 변경하는 애니메이션. ScaleAnimation을 표현

        fromXScale - 시작하는 X의 크기(1.0은 변화없음)
        toXScale - 끝나는 X의 크기(1.0은 변화없음)
        fromYScale - 시작하는 Y의 크기(1.0은 변화없음)
        toYScale - 끝나는 Y의 크기(1.0은 변화없음)
        pivotX - 핀이 꽂힌 중심의 X좌표
        pivotY - 핀이 꽂힌 중심의 Y좌표

    <translate>
        ; 수직/수평 움직임 애니메이션. TranslateAnimation을 표현.
        '%'로 끝나는 -100~100 값은 자신에 상대적인 백분율.
        '%p'로 끝나는 -100~100 값은 parent에 상대적인 백분율.
         접미어가 없는 부동소수점은 절대 값을 가리킨다.

        fromXDelta - 시작하는 X 위치.
        toXDelta - 끝나는 X 위치.
        fromYDelta - 시작하는 Y 위치.
        toYDelta - 끝나는 Y 위치.

    <rotate>
        ; 회전 애니메이션. RotateAnimation을 표현

        fromDegrees - 회전 시작 각도
        toDegrees - 회전 끝 각도
        pivotX - 회전 중심에 대한 픽셀 X 좌표. (0,0)은 왼쪽 상단 모서리
        pivotY - 회전 중심에 대한 픽셀 Y 좌표. (0,0)은 왼쪽 상단 모서리

    <interpolator tag>
        ; R.styleable 에서 정의된 임의의 interpolator 서브클래스 엘리먼트를 사용할 수 있다.
          타임라인 상에서 시각적 액션이 얼마나 빠르게 일어나는가를 설명하는 속도 곡선을 정의

    : 공통 엘리먼트

    duration - 효과 시간(1/1000s)
    startOffset - 효과에 대한 오프셋 시작 시간(1/1000s)
    fillBefore - true로 설정할 때, 애니메이션 시작 전에 적용
    fillAfter - true로 설정할 때, 애니메이션 시작 후에 적용
    repeatCount - 애니메이션 반복 횟수
    repeatMode - repeatCount가 0보다 클 때. 끝나는 순간의 동작을 정의 (옵션: 다시시작, 역진행)
    zAdjustment - 애니메이션 실행 시 z-축 지시모드 (normal, top, bottom)
    interpolator
       EaseInInterpolator - 점점 빠르게
       EaseOutInInterpolator - 점점 느리게
     리스트는 R.anim에서 제공되고 지정하기 위해서는 @android:anim/interpolatorName 구문을 이용

    (예)

    <set android:shareInterpolator=boolean>

     <alpha android:fromAlpha="0.5"
         android:toAlpha="0.5">  |
     
     <scale android:fromXScale="0.5"
         android:toXScale="0.5"
         android:fromYScale="0.5"
         android:toYScale="0.5"
         android:pivotX="(0.5,0.5)"
         android:pivotY="(0.5,0.5)"> |

     <translate android:fromXDelta="%50"
           android:toYDelta="%50"
           android:fromYDelta="%p50"
           android:toYDelta="%p50"> |

     <rotate android:fromDegrees="1.0"
         android:toDegrees="1.0"
         android:pivotX="(1.0,1.0)"
         android:pivotY="(1.0.1.0)" |
         
     <interpolator tag>

    </set>

    'old > UI Design' 카테고리의 다른 글

    PowerManager에서 Screen ON/OFF 통지  (0) 2010.07.06
    Resources - Menus, Searchable  (0) 2010.07.01
    String & StyledText  (0) 2010.06.30
    Style & Theme  (0) 2010.06.30
    Notification  (0) 2010.06.17
    Posted by jazzlife
    ,

    String & StyledText

    old/UI Design 2010. 6. 30. 20:09
    1. html tag 사용

    [strings.xml]
        <string name="ex1">"This'll work"</string>
        <string name="ex2">This\'ll work</string>
        <string name="ex3">This <b><i>work</i></b></string>

    2. html encoding 사용

    [styledStrings.java]
           String escapedTitle = TextUtils.htmlEncode("as");
            String resultsTextFormat = this.getResources().getString(R.string.ex4);
            String resultsText = String.format(resultsTextFormat,1,escapedTitle);
            CharSequence styledResults = Html.fromHtml(resultsText);
           
            LayoutInflater inflater = LayoutInflater.from(this);
           
            View layout = inflater.inflate(R.layout.main, (ViewGroup) findViewById(R.id.root));
            TextView text = (TextView) layout.findViewById(R.id.htmlStr);
            text.setText(styledResults);
           
            setContentView(layout);

    [strings.xml]
        <string name="ex4">%1d results for &lt;b>%2$s&lt;/b></string>


    'old > UI Design' 카테고리의 다른 글

    Resources - Menus, Searchable  (0) 2010.07.01
    Resources - Dimen, Color, 9-patch, Animation  (0) 2010.07.01
    Style & Theme  (0) 2010.06.30
    Notification  (0) 2010.06.17
    UI 이벤트 처리  (0) 2010.06.15
    Posted by jazzlife
    ,