Android Platform 은 사용자에게 좀 더 안전한 환경을 제공하고자 단문 통신인 http 를 지양(!)하고자했다.

 

Android 6.0(M) 에서 AndroidManifest.xml 파일에 android:useClearTextTraffic 이란 attribute 를 제공하기 시작했으며,

이 attribute 를 false 로 선택할 경우, App 내부에서 http 통신을 하고자 할 경우, Platform 에서 트래픽을 막았다.

물론, 이 속성은 Api level = 28 (Android 8.0(P)) 까지 기본적으로 false 이다.

<application
	android:usesCleartextTraffic="true"/>

 

이 값으로는 약간 부족하다고 생각했었는지, Android 7.0(N, 24) 에서는 android:networkSecurityConfig 라는 attribute 를 추가했다.

이 attribute 는 cleartextTrafficPermitted 를 기술한 xml 파일(아래 코드 참조)을 값으로 추가할 수 있다.

<application
	android:networkSecurityConfig="@xml/network_security_config"/>
<?xml version="1.0" encoding="utf-8"?> 
<network-security-config> 
	<base-config cleartextTrafficPermitted="false"/> 
	<domain-config cleartextTrafficPermitted="true">         
		<domain includeSubdomains="true">chanyhan.tistory.com</domain>        
	</domain-config> 
</network-security-config>

 

 

여기서 주의할 점은 <base-config> 를 설정하지 않을 경우 기본 값을 따르며, android:useClearTextTraffic 보다 android:networkSecurityConfig 를 더 우선한다는 점이다.

다르게 이야기 하면, android:useClearTextTraffic=false 로 지정하더라도 android:networkSecurityConfig xml 값이 지정되어 있다면, xml 에 기술된 값을 우선시 한다는 것이다.

 

아래 표는 AndroidManifest.xml 에서 android:useClearTextTraffic=[값없음,true,false] 일 때, android:networkSecurityConfig 파일에서 cleartextTrafficPermitted=[값없음,true,false] 값에 대해 각각 NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted() 값을 보여주고 있다.

 

networkSecurityConfig :

xml 파일 지정하지 않음

networkSecurityConfig :

xml 파일 지정함

   

cleartextTrafficPermitted = 값없음

cleartextTrafficPermitted = true

cleartextTrafficPermitted = false

useClearTextTraffic = 값없음

true (Default)

true (Default)

true

false

useClearTextTraffic = true

true

true (Default)

true

false

useClearTextTraffic = false

false

true (Default)

true

false

물론, 이 값은 AndroidManifest.xml 파일에서 targetSdkVersion < 28 로 설정한 경우이다.

targetSdkVersion >= 28 이라면, true (Default) 가 false (Default) 로 바뀐다.

 

* 코드 상에서 이 값을 확인하는 방법은 크게 두 가지이다.

A)

context.packageManager.getApplicationInfo("com.example").flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC

B)

NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted()

 

A) 안은 일반적으로 AndroidManifest 에 기술된 값을 얻을 때 사용하는 방법이며, B) 안은 Api Level 23 에서 정의된 클래스를 이용한 것이다.

B) 는 A) 를 포함하고 있고, 코드도 간결하기 때문에, B) 를 사용하는 것이 좋다.

추가로 A) 안은 API Level 23 이하에서도 사용가능한 방법이기 때문에, Build.VERSION.SDK_INT 로 제한하지 않을 경우, 오동작할 수 있다.

물론, 위의 내용은 일반적인 HTTP 통신인 경우이다.

 

WebView 에서의 동작은 위와 조금 다르다.

https://developer.android.com/reference/android/security/NetworkSecurityPolicy 을 참고하면, 아래와 같은 노트를 확인할 수 있다.

isCleartextTrafficPermitted
...
NOTE: WebView honors this flag for applications targeting API level 26 and up.

말인 즉슨,  WebView 에서는  targetSdkVersion이 26 보다 같거나 큰 경우에만, 동작한다는 것이다.

위에서 언급한 useClearTextTraffic 이나 networkSecurityConfig 값을 설정하더라도 targetSdkVersion 이 26 보다 낮을 경우, WebView 에서는 HTTP 통신이 가능하다는 것이다.

 

엄밀히 말하면, 이는 Android 7.0, 24 에서 기본 적용된 Chrome WebKit 53.0 이상 버전에서 적용되는 사항이다.

Android 6.0, 23 에서 적용된 Chrome WebKit 44.0 버전에서는 적용되지 않는다.

즉, Android 6.0, 23, 의 기본 Chrome WebKit 44.0 을 업데이트 하지 않은 상황이라면, 어떤 설정을 적용하더라도 WebView 에서 HTTP 통신이 가능하다.

 

종합하면 WebView 의 경우, targetSdkVersion>=26 으로 설정하고, Chrome WebKit 53.0 이상 버전을 사용한다면, (일반적으로 Android 7.0 24 이상의 단말), isCleartextTrafficPermitted 설정을 적용할 수 있다.

Locale("ar") 을 사용하는 폰에서는 "20030613" 이 "٢٠٠٣٠٦١٣ " 으로 출력된다.

일반적인 UI String 으로 사용할 때에는 문제가 없지만,

이 값을 로그를 남기거나 로그의 식별자로 사용할 때는 오류가 발생할 수 있으므로,

SimpleDateFormat("yyyyMMdd", Locale.ENGLISH) 처럼 Locale 을 고정시켜 주는 것이 좋다.

이 글은 안드로이드 레퍼런스 폰의 팩토리 이미지를 빌드하는 방법에 관하여 간단히 설명해 놓은것입니다.


솔직히 설명할 필요도 없이 아래 세 개의 링크만 클릭하면 모든 설명들이 자세하게 되어 있지만, 약간의 예외 및 기타 잡다한 참견들을 추가하도록 하겠습니다. ^^;


https://source.android.com/setup/build/initializing

https://source.android.com/setup/build/downloading

https://source.android.com/setup/build/building



참고로 위 세 개의 링크만 잘 읽어보면, 쉽게 빌드할 수 있지만, 알고보면, 그렇게 쉽지 않은 이유는 조그마한 환경변수나 버전이 하나만 틀어져버려도 에러가 발생할 수 있기 때문입니다.

그래서, 필자도 처음에는 Mac OS 에서 빌드를 시작해서 결국, Mac OS 위 VmWare Fusion 30일용 + Ubuntu 14.04  에서 몇 번 실패한 끝에 겨우 빌드에 성공했습니다.


일단, Ubuntu 14.04 를 설치한 황을 가정하고 설명을 시작하겠습니다. 이유는 제가 빌드에 성공한 버전이기도 하지만, 그나마 예외적인 사항들이 적은 편이기 때문입니다.


https://source.android.com/setup/build/requirements 를 보면 먼저 간단한 요구사항들이 있습니다.


JDK

See Installing the JDK for the prebuilt path and installation instructions for older versions.



그 중 가장 중요한 것은 어떤 버전을 빌드할 것인가 입니다. 이를 테면, Lollipop 을 빌드한다고 하면,  Java 1.7 이 필요합니다. 최신의 Nougat 이상을 빌드하고 싶다면, 현재 보통 사용하고 있는 1.8 을 그냥 사용 하면 되겠지만, 그 이전 버전일 경우에는 위 표를 반드시 알고 미리 준비해두어야 합니다. 만약, JAVA 버전이 다를 경우, 빌드시 에러가 발생합니다.



이후에는 https://source.android.com/setup/build/initializing 에 따라 14.04 에 해당하는 명령을 실행해주면 됩니다. 위 그림에서 '클릭하여 복사' 하여 실행하셔도 됩니다. 



이젠 소스를 다운로드 받아야 합니다. https://source.android.com/setup/build/downloading 에 따라 소스를 받으면 되는데, repo 설정과 그외 부분은 위 그림에서와 같이 '클릭하여 복사'를 잘 이용하시면 편리합니다. 가장 중요한 것은 어떤 브랜치를 가져올것인가 입니다. 

아래 그림에서 'repo init - u https://android.googesource.com/platform/manifest -b android-4.0.1_r1' 에서 android-4.0.1_r1 에 해당하는 부분인데요.

푸른 색 글씨의 Source Code Tags and Builds 링크를 따라가서 무엇을 받을지를 결정하셔야 합니다.




저는 Nexus6P 를 빌드해야 했기 때문에 Nexus6P 를 검색해서 실제 Factory Image 가 올라와있는 버전(MDB08K)을 선택했습니다.




https://developers.google.com/android/images 에서 미리 자신이 가지고 있는 단말의 Factory Image 를 미리 받아서 언제든 원복 시킬수 있거나 소스코드를 수정했을 때, 본래 이미지와 비교할 수 있는 버전을 받아 놓는게 좋습니다. 팩토리 이미지를 설치하는 방법은 다음 글에서 설명해드릴게요.



그래서 저는 'repo init - u https://android.googesource.com/platform/manifest -b android-6.0.0_r24' 로 다운로드를 받았습니다.

그리고, 페이지의 설명대로 'repo sync' 를 시작하면 다운로드를 시작합니다.

주의하실 점은 소스 자체의 크기는 그렇게 크다고 볼 수 없지만, 요즘은 git 정보가 너무 많아서 다운로드에만 50G 이상이 사용됩니다. 빌드할 때 여유공간까지 생각하면 150G 정도의 충분한 여유공간을 만들어 놓는 것이 좋습니다. 

물론, 정보를 적게 가져오는 방법이 있기는 하지만, 여기서는 일단 스킵하고 최대한 많은 여유공간을 확보해두시는 걸 권장해드리고 싶습니다.

그리고, 다운로드 받는데도 꽤 오랜 시간이 걸립니다. 족히 한시간은 걸렸던 걸로 기억이되네요.


다운로드를 받고나서 빌드를 하는 과정은 오히려 어렵지 않습니다. https://source.android.com/setup/build/building 에 나온 대로 'make clobber' -> 'source build/envsetup.sh' -> 'lunch' -> 'make -j4' 순서로 실행해 주시면 됩니다.



사이트에서는 'lunch aosp_arm-eng' 를 예로 들었는데, 'lunch' 만 실행해 보시면 아래와 같이 옵션이 많이 있다는걸 알게됩니다.



저는 Nexus6P 의 코드네임 angler 를 따라 17번  aosp_angler-userdebug 를 선택해서 빌드했습니다.



BUILD_ID=MDB08K 를 보내 뭔가 제대로 된 느낌입니다. ^^; 

'make -j4' 로 빌드를 시작하면 됩니다. 빌드 환경에 따라 -j2 혹은 -j8 로 시작하셔도 됩니다. 숫자가 클수록 빨리 끝나기는 합니다. 그래도 최소 한시간정도는 기다려야 합니다.

빌드가 성공적으로 끝나면 /WORKING_DIRECTORY/out/target/product/[target_code_name] 에 이미지가 생성됩니다.

저는 Nexus6P 이기 때문에 angler 디렉토리에 생성되어 있습니다.



이렇게 빌드된 이미지 파일들을 단말에 설치하면 됩니다. 설치 방법은 다음글에서 안내해 드릴게요.

Compile 은 안드로이드 상위 버전(예: sdkVersion=19)으로 한 뒤, 하위 버전(예: sdkVersion=8)에서 실행할 때 아래와 같은 에러를 만나게 되는 경우에는 Library Project 혹은 Library 의 문제가 아니라, 프로젝트에서 사용하고 있는 
"?android:attr/" 의 문제이다. 
상위 버전의 attr 을 resource id 를 사용하는 것이기 때문에 하위버전에서는 엉뚱한 리소스를 찾다가 실패하는 것이다. 

 Caused by: android.content.res.Resources$NotFoundException: Resource is not a Drawable (color or path): TypedValue{t=0x2/d=0x10102fd a=-1} 
혹은 
 java.io.FileNotFoundException: res/drawable-hdpi/divider_horizontal_bright_opaque.9.png

와 같은 에러를 만나게 된다.


프로젝트 xml 파일에서 "?android:attr/" 로 검색하여 최신 속성을 사용하고 있는 것인지 확인해야 한다.


p.s. 본디, 이런 포스팅은 하지 않는데,.. 너무 어이없이 삽질을 하다보니 일단 기록을 남겨둔다.

  1. Denial 2014.08.09 07:01 신고

    저두 정말 어처구니 없는 삽질을 했었네요 ㅠ 눈치챌 즈음에 여기를 발견했습니다. ㅎㅎ

시작하기에 앞서 아래 사이트를 참조하면 좋다.

http://android-developers.blogspot.kr/2013/10/getting-your-sms-apps-ready-for-kitkat.html (영문) 

http://android-developers-kr.blogspot.kr/2013/10/blog-post_15.html (국문 번역)


위 사이트를 참조하면 기본 SMS 앱으로 설정되려면, 아래와 같이 두 개의 BroadcastReciever(SMS_DELIVER 와 WAP_PUSH_DELIVER action 을 받을 수 있는)와 sms, smsto, mms, mmsto scheme 을 받아줄 수 있는 Activity, 그리고, RESPOND_VIA_MESSAGE action 을 받을 수 있는 Service 가 구현되어 있어야 한다.


<manifest>
    ...
    <application>
        <!-- BroadcastReceiver that listens for incoming SMS messages -->
        <receiver android:name=".SmsReceiver"
                android:permission="android.permission.BROADCAST_SMS">
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_DELIVER" />
            </intent-filter>
        </receiver>

        <!-- BroadcastReceiver that listens for incoming MMS messages -->
        <receiver android:name=".MmsReceiver"
            android:permission="android.permission.BROADCAST_WAP_PUSH">
            <intent-filter>
                <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
                <data android:mimeType="application/vnd.wap.mms-message" />
            </intent-filter>
        </receiver>

        <!-- Activity that allows the user to send new SMS/MMS messages -->
        <activity android:name=".ComposeSmsActivity" >
            <intent-filter>
                <action android:name="android.intent.action.SEND" />                
                <action android:name="android.intent.action.SENDTO" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="sms" />
                <data android:scheme="smsto" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
        </activity>

        <!-- Service that delivers messages from the phone "quick response" -->
        <service android:name=".HeadlessSmsSendService"
                 android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
                 android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="sms" />
                <data android:scheme="smsto" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
        </service>
    </application>
</manifest>


Uri.Builder 사용하시는 분들이 별로 없으신 것 같아서 간단히 설명드립니다.


Uri = scheme://authority/path?query=value 


와 같은 형태입니다.


Uri.Builder 를 사용하시면 Uri 를 쉽게 만들어서 사용하실 수 있습니다.


예를 들어, 


https://gdata.youtube.com/feeds/api/users/abc/playlists?v=2&alt=jsonc


와 같은 URL 이 있다면 아래 코드와 같이 코딩하신 뒤, toString() 을 사용하시면 됩니다.


https://gdata.youtube.com/feeds/api/users/abc/playlists?v=2&alt=jsonc 


Uri.Builder ub=new Uri.Builder();

ub.scheme("https")

.authority("gdata.youtube.com")

.appendPath("feeds").appendPath("api").appendPath("users")

.appendPath("abc").appendPath("playlists")

.appendQueryParameter("v", "2").appendQueryParameter("alt""jsonc");


String url=ub.build().toString();


특히, QueryParameter 의 경우에는 자주 바뀌거나 추가될 수 있기 때문에 코드의 재사용성이 꽤 괜찮을 것 같습니다. 


Google Support Library v7 appcompat 사용 방법

1. Eclipse> File> Import> Existing Android Code Into Workspace


2. [Android SDK Directory]/extras/android/support/ 선택 

- 이렇게 선택할 경우 support 되는 프로젝트가 모두 표시된다. 이 중 하나를 선택한다.




3. 이렇게 추가된 프로젝트는 라이브러리 프로젝트로 설정되어 있다.

- Project> Properties> isLibrary 가 체크되어 있다.


4. 그러므로, v4 와 같이 jar 파일만 lib 디렉토리에 추가해주는 것이 아니라, Project> Properties> Library > Add> 를 통해 라이브러리 프로젝트를 추가해 주어야 한다.


이렇게 jar 파일로만 해결이 되지 않는 이유는 리소스를 포함하고 있기 때문이다.

startActivityForResult() 로 호출한 결과를 어디에서 처리해야 할까? 개인적인 생각으로는 Fragment 에서 시작한 Activity 라면 Fragment 에서 처리하는 것이 맞다고 생각한다.

어쨋건 Fragment 가 생긴 뒤, Activity 에서 시작한 startActivityForResult 의 결과물에 대하여, 시스템에서 Fragment 에서 생성한 경우, 16bit - 2byte shift 해서 처리하고 있다.

즉, 안드로이드 시스템에서 Activity 와 Fragment 에서 시작한 Activity 를 구분하고 있다고 보면 된다. 그래서, 만약, Fragment 에서 생성한 ActivityResult 를 상위 Activity 에서 처리하고 싶다고 한다면, >>16 을 사용해야 한다.


FragmentA.java

----------------------------------- 

.... 

requestCode=1; 

startActivityForResult(intent, requestCode) 

.... 

----------------------------------- 


 ActivityA.java 

----------------------------------- 

.... 

@Override 

protected void onActivityResult ( int requestCode, int resultCode, Intent data) { 

int fragmentRequestCode = requestCode >> 16 ; 

// fragmentRequestCode=1 } 

.... 

----------------------------------- 


여기서 알수 있는 것은 Fragment 에서 생성한 requestCode 는 0xffff 보다 작아야 한다는 것이다. 그래야 본래의 값을 잃어버리지 않을 수 있다.


삼성 개발자 데이는 삼성이 제공하는 여러 개발 툴들에 대한 설명을 하는 자리였습니다.


1. Chord SDK

안드로이드 기반의 Group Play 를 지원하는 Library 정도로 보시면 될 것 같은데요. 삼성 단말에서만 지원된다라고 하면 좀 아쉬울 것 같습니다.

Framework 수준에서 처리되는 것이 성능상 좋을 듯 하지만, 이와 유사한 기능을 제공하는 오픈 소스 라이브러리가 나오면 그냥 Game Over 되는게 아닐까 싶다는 생각이 드네요.

이런 라이브러리가 존재한다면 모르지만, 그 전까지는 이런 SDK 를 가지고 놀아보는 것도 재미있을 것 같다는 생각이 듭니다.

멀티 플레이 게임을 시연했었는데요. 


2. S-Pen SDK

S-Pen 은 다 좋은데, 시장 자체가 그리 크지 않습니다. 갤럭시 노트 시리즈에서만 동작하기 때문입니다. 갤럭시 노트만으로 시장이 얼마나 될런지 의문입니다.

만약, 관련 앱을 개발한다고 하면, 무료 앱보다는 고 퀄리티의 유료앱으로 승부를 보는 것이 좀 더 유리할 것 같다는 생각이 듭니다.


3. Samsung In-App Purchase, Samsung Apps

Samsung Apps 의 장점은 검증절차입니다. T-Store 와 마찬가지로 안드로이드 계열의 최대 약점 파편화에 대응하기 위한 수단입니다. 좀 쉽게 말하자면, 그 수 많은 단말들을 다 테스트 해 볼 수 없으니, 이들을 이용하는 것입니다.

단점이라고 할 수 있는 건,... 스스로 얘기한 6000만 가입자. 뿌려진 단말에 비하면, 상대적으로 초라한 수치. 즉, 생각보다 사람들이 잘 안쓴다는 것 입니다.

In-App Purchase 의 장점은 각 나라별로 익숙한 결재수단을 제공해 준다는 점. 이를 테면, 어느 나라는 신용카드 어느나라는 통신사 결재, 등등... 

함정은 안드로이드 계열의 유료 결재는 그리 크지 않다는 점. 그리고,... 안타깝지만,... 보통의 개발자들은 다른 나라 시장은 그닷 잘 생각하지 않는다는 점 입니다.


4. All-Share Framework

참 매력적인 기능인데... 여러모로 접근이 어렵다는 점이 가장 큰 장벽 입니다.

이 기능은 몇 년 전부터 구현되어 여러 기기에 탑재되어 구동되고 있지만,..  실제로 이 기능을 알고 사용하는 사람은 많지 않은 것으로 생각됩니다.

아무리 TV와 폰 둘 다 에뮬레이터로 테스트 해볼 수 있다고 하지만, 실제 사용자의 UX 와는 좀 거리가 먼 것 같습니다.

그래도, 그나마 Mobile Device + TV 모두 시장에 가장 많이 풀려 있기 때문에 LG에서 추진하는 것보다는 좀 낫다는 생각입니다.

개인적인 생각으로는 LG와 합작으로 회사하나 차려서 다 몰아놓고 같이 개발하는게 좋을 것 같습니다... (S-LCD 처럼..)



예전에 MS 가 하던 짓과 비슷한 짓을 하려하는 듯한 분위기가 약간 느껴졌습니다. 우리가 최고고, 우리가 만든게, 곧 표준이다. 의 느낌... 

그리고, 여전히 이걸로 돈을 벌려는 거야? 아니면, 단말을 잘 팔려고 하는 거야? 의 애매한 포지션에서 정리가 안 된듯한 모습이 보였습니다. 아마도 계속 애매하게 나갈 것 같긴 하지만 말입니다. 





  1. 재마니 2013.05.09 20:40

    Chord sdk보단 퀄컴의 alljoyn sdk를 가지고 놀아보세요.


1. 아침에 95.9 손석희의 시선집중을 들으며 출근했다. 왜 국판은 DMB 를 버리고 FM Radio 를 넣은 모델을 출시를 안하는건가...
2. Page Buddy> Earphone pages 기능은 꽤 유용한 것 같다.

P.S. 근데, 스크린 캡쳐 기능은 없는건가?




+ Recent posts