ext4에서 'readdir' system call을 이용해서 directory를 읽으면, directory가 가지는 file list를 읽을 수 있다.

이때, 읽히는 file의 순서는 어떻게 정해지는가?

경험적으로 대부분의 개발자라면, 이 순서가 일정하지 않다는 것을 알고 있을 것이다.

이 부분에 대해서 좀더 깊게 살펴보기도 하자.


linux kernel에서 ext4 관련 부분의 코드를 분석해 보면 아래의 사실들을 알아 낼 수 있다.

- ext4는 directory내에서 file search등을 빠르게 하기 위해서 htree(hash tree)를 이용한다.

- 이때 사용되는 hash값의 order가 곧 readdir 로 읽어들이는 file의 order이다.

- hash 값은 'HashFunc(<hash algorithm>, <hash seed>, <file name>)"을 통해서 구한다.

- hash algorithm과 hash seed의 경우, super block에 적힌 값을 사용하면, 없는 경우 default 값을 사용한다.

- directory내의 각 file의 hash값은 directory file의 'f_pos'값 (file position값)으로 사용된다.


즉, super block에 적힌 hash algorithm과 hash seed에 따라서, directory가 같은 이름들의 file을 가지고 있다고 하더라도, readdir이 읽어 오는 file의 순서가 달라진다는 말이다.


실제로 이를 확인하기 위해서는 'dumpe2fs' 와 'debugfs' tool을 사용할 수 있다.


Assumption : '/' 가 '/dev/sda1' 에 mount 되어 있음.


>$ sudo dumpe2fs -h /dev/sda1
...
Default directory hash:   half_md4     <== hash algorithm
Directory Hash Seed:      5841608b-14fe-405e-8d28-76236cc8c496  <== seed (UUID format)
...


이후 아래와 같은 방법으로 각 file name에 해당하는 hash 값을 알 수 있다.

>$ sudo debugfs /dev/sda1
dx_hash -h half_md4 -s c773a461-6150-4fe8-abe8-96acc6086d7e vmlinuz
Hash of /vmlinuz is 0xf097024e (minor 0x0)
추가적으로 'ls -f' 를 사용하면, sorting되지 않은 순서로 file list를 읽을 수 있는데, 이 순서가 바로, hash 값에 따른 순서 즉 readdir로 읽어 들이는 순서이다.
이를 확인하기 위해서는
>$ rm dxhashs; for e in $(ls -f /); do echo "dx_hash -h half_md4 -s 5841608b-14fe-405e-8d28-76236cc8c496 $e" >> dxhashs; done; sudo debugfs -f dxhashs /dev/sda1 | grep "Hash of" | cut -d' ' -f5

와 같이 하면, 정확하게 ascending order로 출력되는 hash 값을 볼 수 있다.
즉 'ls -f' 를 통해서 보여지는 file name의 순서가 hash값이 ascending order라는 뜻이고, hash 값이 ascending order에 따라 읽어 들이는 file name의 순서가 'readdir'로 읽어 들이는 순서와 일치한다.

Hash Seed를 바꾸는 방법은 아래와 같다.(간단한데, 나중에 분명히 잊어 먹을테니...)
/dev/sda2 가 /opt에 mount되어 있고, ext4 file system인 경우.

># unmount /opt
># debugfs -w /dev/sda2
>debugfs: ssv hash_seed ceeba115-4091-4b79-aac9-36ccee4a5349  <= UUID of hash seed to change


/libcore/luni/src/main/java/java/util/jar/JarVerifier.java

여기 source를 확인해 보면,

+ '.RSA', '.DSA', '.EC' 로 끝나는 file을 찾고, 같은 이름의 '.SF' 파일을 찾는다.(Android의 경우는, 'CERT.RSA' 와 'CERT.SF' 파일)

    : '.RSA', '.DSA', '.EC' : Certification File.

    : '.SF' : Signature File

    : Signature file을 certification file을 통해서 먼저 verification한다.

+ .SF 파일의 각 file entry에 대해서 '-Digest' 값을 읽어 들인다.


<< PackageParser >>

'Signing Verifier'에서 읽어들인 "file entry:<HASH>" 값 mapping table을 이용해서, zip file안에 있는 file들에 대해 hash를 검사한다.

    : System APK에 대해서는 Manifest의 hash만 검사 (trusted app.) 그렇지 않은 경우 모든 file - 'META-INF/' directory제외 - 에 대해서 검사.

    



일반 적으로...

exit code = 128 + (signal number)


ex.

exit code가 139(128  + 11)라면, 11번 signal이 뭔지 알아야 하고 "kill -l"로 확인할 수 있다.

보통의 경우 11번은 SIGSEGV  이다. 즉, segmentation fault로 죽었다는 말...



이미 많이 알려진 topic이지만.. 정리차원에서...


* 표준적인 방법은 'startForeground()' API를 통하는 방법이다.

그렇지만, 'startForeground()' 를 이용할 경우에는 반드시 'Notification()'을 띄워야 한다.


* 만약 Notification없이 죽지 않는 Service를 만들때, 그 service를 platform key로 signing할 수 있다면(즉 platform app인 경우)

<android:persistent='true' />를 이용할 수 있다.

이는 '<application', '<service', '<activity' 에 각각 적용 가능하다.

다만, Persistent App의 경우 항상 memory에 상주한다. (Kill 로 죽이더라도 곧바로 재시작한다.)


/libcore/luni/src/main/java/java/util/jar/JarVerifier.java


Here is steps for verifying package with it's signature at META-INF in Android system.

- Find files ends with '.RSA', '.DSA' or '.EC'(Certification file) and then find '.SF' file(Signature File) that has same basename.

  (In case of Android, 'CERT.RSA' and 'CERT.SF' file)

- Verifying Signature File by using Certification file => Signature File is verified.

- Read '-Digest' values from CERT.SF file. => Valid hash value for files are read.

- Then, when parsing package(Apk), all file entries except for files in 'META-INF' directory, are scanned and compared with corresponding hash value in CERT.SF.


Interesting point is, in Android, verification is processed based on file entries in APK.

That is, removing some entries from APK doesn't make any problem in terms of signature verification. :)

* When 'variable expansion' at Command Section, is processed at GNUMake --------------------------------------------------------------------- In GNUMake source code, command line section of each Target seems to be expanded at 'new_job()' function. In detail, 'for-loop part' under '/* Expand the command lines and store the results in LINES. */' comment. And then, first command line is fetched at 'job_next_command()' function. Then, when 'job slot(in case of using mutlple job)' is available, this command line that expansion is done, is executed on newly created child process. new_job - expand command line variables (line-by-line) - waiting until job slot is available. - job is executed.. Note that all variables in command section, are expanded before processing first command line. Note that, variables in pre-requisites and dependencies are expanded before expanding command line section. So, following gnumake code is dangerous. out/target-file: <...> ./generate-target-file.sh $@ ./postprocess.sh $(shell readlink -e $@) [ Issue ] "$(shell ...) function" part is expanded before "./generate-target-file.sh" is executed. So, af the first build, 'out/target-file' doesn't exist, "readlink -e $@" return empty string. And this may lead to unexpected result.

--- should be modified like below out/target-file: <...> ./generate-target-file.sh $@ ./postprocess.sh $$(readlink -e $@)


 Abi관련 변수들


Build.CPU_ABI

Build.SUPPORTED_ABIS
ro.product.cpu.abilist64
ro.product.cpu.abilist32


=========================

64bit, 32bit library의 경우


BaseDexClassLoader 에서 참조하는 native library는 null인데, 이는 default library path를 사용하는 것이고...

코드를 쫓아가다보면...

'java.library.path"

에서 library directory를 가져오는데(/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java) 이 값은 default로 LD_LIBRARY_PATH 환경변수값을 사용한다.(java_lang_System.cpp)

android_get_LD_LIBRARY_PATH() 함수를 찾아서 가보면, kDefaultLdPaths 에서 값을 읽어오는데, 아래와 같이 정의되어 있다.


91static const char* const kDefaultLdPaths[] = {
92#if defined(__LP64__)
93  "/vendor/lib64",
94  "/system/lib64",
95#else
96  "/vendor/lib",
97  "/system/lib",
98#endif
99  NULL
100};
101

그리고


system library의 경우

system/lib/

system/lib64/

vendor/lib/

vendor/lib64


========================

system library의 경우는 위와 같고... APK에 포함된 library의 경우는... aapt를 확인해야 하는데, 

/frameworks/base/tools/aapt/Command.cpp

를 보면, 

1981            AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
1982            if (dir != NULL) {
1983                if (dir->getFileCount() > 0) {
1984                    SortedVector<String8> architectures;
1985                    for (size_t i=0; i<dir->getFileCount(); i++) {
1986                        architectures.add(ResTable::normalizeForOutput(
1987                                dir->getFileName(i).string()));
1988                    }


코드가 보이는데... 즉 "lib/<archtecture-name>"의 형태로 directory가 구성되었다는 것을 알 수 있다.


====================================

APP이 뜰때, 64bit인지 32bit인지 확인해서 zygote64를 base로 뜰건지 zygote32를 base로 뜰건지 알아야 하는데,

Process.java

에서

openZygoteSocketIfNeeded(String abi)

처럼 abi가 넘어가게 된다.

이 abi값은 proces start에 argument로 넘어오는데,

/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

3192            Process.ProcessStartResult startResult = Process.start(entryPoint,
3193                    app.processName, uid, uid, gids, debugFlags, mountExternal,
3194                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
3195                    app.info.dataDir, entryPointArgs);

3177            String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
3178            if (requiredAbi == null) {
3179                requiredAbi = Build.SUPPORTED_ABIS[0];
3180            }
3181
3182            String instructionSet = null;
3183            if (app.info.primaryCpuAbi != null) {
3184                instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
3185            }


에서 requiredAbi argument가 있고... default abi를 사용하긴 하나, 결국 package가 지원하는 abi를 알아야 한다.



/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

5726                    final int copyRet;
5727                    if (isAsec) {
5728                        copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
5729                    } else {
5730                        copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
5731                                nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
5732                    }
5733
5734                    if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
5735                        throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
5736                                "Error unpackaging native libs for app, errorCode=" + copyRet);
5737                    }
5738
5739                    if (copyRet >= 0) {
5740                        pkg.applicationInfo.primaryCpuAbi = abiList[copyRet];
5741                    } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) {
5742                        pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
5743                    } else if (needsRenderScriptOverride) {
5744                        pkg.applicationInfo.primaryCpuAbi = abiList[0];
5745                    }


findSupportedAbi() 

com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, jclass clazz,

=> 413static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supportedAbisArray) {


결국 "/lib/<abi-name>"이란 규칙을 이용해서 apk가 지원하는 ABI를 찾는다.

그런데, PackageManagerService를 보면, package가 '.apk' file인 경우 - Monolithic apk - 와 directory이 경우 - Cluster apk - 두 가지로 나뉘어 지는 것을 알 수 있다.

두 경우 모두 cpu Abi를 결정하기 위해서 위의 방법을 사용하는데, 두 경우 모두


<package file/dir>/lib/<cpu abi>


형태의 구성을 가정하고 있다.

따라서, apk build 자체가 위의 구성으로 나오는 package build와 실제 so는 system에 두고 link만들 거는 system apk 모두를 cover하고 있다.


ex.

Monolithic


Gallery.apk

|-AndroidManifest.xml

|-...

|-lib/armeabi-v7a/xxx.so

|...




Cluster (PDK build시 "/system/app/<package name>" directory)


Gallery/ ('/system/app/Gallery')

|-Gallery.apk

|-x86_64/Gallery.odex

|-lib/x86_64/xxx.so



두 경우 모두 "<Gallery apk/dir>/lib/<cpu abi>"형태로 구성되고 이를 보고 판단한다.


아래 코드는 추가적으로 참고하자..

5686                    if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
5687                        if (isAsec) {
5688                            abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
5689                        } else {
5690                            abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
5691                                    nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
5692                                    useIsaSpecificSubdirs);
5693                        }
5694                    }
5695
5696                    maybeThrowExceptionForMultiArchCopy(
5697                            "Error unpackaging 64 bit native libs for multiarch app.", abi64);
5698
5699                    if (abi64 >= 0) {
5700                       


뭐 결국은, apk의 "/lib/<abi-name>"이 platform이 지원하는 64bit abi에 포함되면, 64bit apk로 판단하는 방식이다...

즉, APK가 어떤 abi를 지원하는지는 manifest file 등등 이런 곳에 따로 기록되어 있는것이 아니고, native library의 "/lib/<abi-name>"규칙에 의존해서 찾는다.


Platform build시 jni library들은 default로 모두 'system/lib'쪽에 들어가게 되어 있는데 - APK에 lib이 포함되지 않는다. - 그럼, 해당 APK의 ABI어떻게 알 수 있는가?

(최초 booting시 해당 APK를 AOT(Ahead Of Time) compile을 위해서는 어떤 target ABI로 compile할지가 정해져야 한다.)


일단 생각해보면, 어차피 VM(DEX)에만 의존하는 APK라면 특별히 ABI에 영향을 받지 않으므로 그냥 platform default ABI로 인식하고, AOT compile을 수행하면 된다. 그렇지만, 사용하는 native library들이 있고, 이 library를 load해야하는 APK라면, ABI에 따라서 어떤 zygote(32bit 혹은 64bit)에서 fork될 것인가를 결정해야 하므로, ABI를 system이 알아야할 필요가 있다.


이 경우는, 'LOCAL_MULTILIB := 32' 같은 방식으로 second abi를 사용하도록 정의하고 빌드하면, 'system/app/<package>'쪽에 보면


*** x86_64 용 TARGET ABI로 빌드하고, 'LOCAL_MULTILIB := 32'를 사용하는 Gallery2를 예로 들면...

Gallery2

- Gallery2.apk

- lib/x86/<xxx>.so -> /system/lib/<xxx>.so (symbolic link)

- x86/Gallery2.odex



즉, 제대로 32bit 용으로 빌드되어 있다. (그렇지만, Gallery2.apk내부에는 'lib/<abi>' 형태의 directory가 없다.

그렇지만, 만약, 32bit용으로 DEX가 pre-optimization되어 있지 않다면, 어떻게 Gallery2가 32bit용 package인지 system이 알고 32bit용으로 dex를 optimization - ART의 경우 AOT compile - 시킬수 있을까?



/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

6485        } else {
6486            // Cluster install
6487            info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
6488            info.nativeLibraryRootRequiresIsa = true;
6489
6490            info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
6491                    getPrimaryInstructionSet(info)).getAbsolutePath();
6492
6493            if (info.secondaryCpuAbi != null) {
6494                info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir,
6495                        VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath();
6496            }
6497        }
6498    }




scanPackageDirtyLI() -> 여기서 installer를 이용해서 app data directory / lib directory 등을 만든다. nativeFindSupportedAbi => copyNativeBinariesForSupportedAbi : 여기서 abi를 가지고 instrunction set directory로 copy한다. 이때 nativeLibraryRootDir 로 복사하는데, 이 nativeLibraryRootDir 로 될 수 있는게 <bundled app> 은 final boolean bundledApp = isSystemApp(info) && !isUpdatedSystemApp(info); [[ Monolithic ]] Monolithic && bundled "/system/lib[64]/<apkname>" Monolithic 이고 Non-bundled "/data/app-lib/" 이다 [[ Cluster ]] info.nativeLibraryDir = new File(info.nativeLibraryRootDir, getPrimaryInstructionSet(info)).getAbsolutePath(); if (info.secondaryCpuAbi != null) { info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir, VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath(); } 즉 Cluster 의 경우 NativeLibraryDir은 abi가 아니라 instruction set 이름으로 찾아들어간다. VMRuntime.java 38 static { 39 ABI_TO_INSTRUCTION_SET_MAP.put("armeabi", "arm"); 40 ABI_TO_INSTRUCTION_SET_MAP.put("armeabi-v7a", "arm"); 41 ABI_TO_INSTRUCTION_SET_MAP.put("mips", "mips"); 42 ABI_TO_INSTRUCTION_SET_MAP.put("mips64", "mips64"); 43 ABI_TO_INSTRUCTION_SET_MAP.put("x86", "x86"); 44 ABI_TO_INSTRUCTION_SET_MAP.put("x86_64", "x86_64"); 45 ABI_TO_INSTRUCTION_SET_MAP.put("arm64-v8a", "arm64"); 46 }


VMRuntime.getInstructionSet(info.secondaryCpuAbi)) 에서 arch이름을 찾는 규칙이 위와 같다.

즉 cpuabi 는 instruction set 이름을 찾기 위해서 사용되지 그 자체는 package manager service에서 사용되지 않는다.

재미있는 것은 build system에서는

"cpu abi" -> "arch"

오 가타은 이름을 사용하고 있고, package manager service에서는

"cpu abi" -> "instruction set"

을 사용한다.

즉, 같은 이름을 build system과 package manager service에서, 'arch', 'instruction set'이라는 이름으로 서로 다르게 사용하고 있다.




실제 emulator를 실행시킨 후 위의 - 첫번째 booting에서 apk가 install된다. - package의 data directory를 가 보면.


$ ls -l /data/data/<package-name>

lib -> /data/app-lib/<package-name>


ex.

root@generic_x86_64:/data/data/com.android.gallery # ls -l lrwxrwxrwx install install 2014-11-10 14:13 lib -> /data/app-lib/com.android.gallery


그리고 아래의 code를 보자.


/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

6477            } else {
6478                final String apkName = deriveCodePathName(codePath);
6479                info.nativeLibraryRootDir = new File(mAppLib32InstallDir, apkName)
6480                        .getAbsolutePath();
6481            }


위의 코드에서 'mAppLib32InstallDir'이 바로 '/data/app-lib' directory이다.

이렇게 자신의 library를 찾아가게 된다.







예를 들어, 'gitweb', 'bugzilla' 등을 service하는 server를 configuration한다고 가정하면,

apache에서, 이것들을 한 virtual host '80 port'에 설정하고, uri 로 구분하는 방법 - (*A) - 과, 아예 virtual host를 다르게 설정하는 방법 - (*B) - 두가지가 있을 것이다.

(*A)의 경우, "<domain name>/gitweb" "<domain name>/bugzilla" 이런 식으로 나누어서 한 virtual host에서 service하면 port의 낭비도 막고, 쓸데 없이 virtual host를 하나 더 사용하는 낭비도 줄일 수 있다.

그렇지만, 경우, client의 browser의 cache 정책이나 설정등에 따라, 의도치 않은 동작들이 발생하는 경우를 많이 겪었다.

따라서, 경험적으로, 이런 경우, 불편하고, 또 약간 낭비적인 요소가 있더라도 그냥, 여러개의 virtual host로 설정하는 편이 여러가지 지뢰(?)를 피할 수 있는 가장 편한 방법이다.


Rough Block Diagram

[Display Device] <>--- [Logical Display] <>--- [WindowManager]

DisplayInfo

has followings (Initial values are set as information from display device).

:logicalHeight

:logicalWidth

:appWidth

:appHeight


DisplayDevice

has followings

:mCurrentLayerStackRect

:mCurrentDisplayRect


shown on the display.

|

v

[LayerStackRect] ---(project to)---> [DisplayRect]

^

|

This value is outbound of WindowManager.


Above information is very rough and... NOT strictly verified.


Here is useful tool supported by Google to handle displaying area and density - wm.

You can find it at '/system/bin/wm'


root@generic:/system/bin # ./wm reset usage: wm [subcommand] [options] wm size [reset|WxH] wm density [reset|DENSITY] wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]

Very useful isn't it!



Android VM은 일반적인 Java VM과 마찬가지로 boot class path와 class path를 지원한다.

그렇지만, Android App(APK)에서 위의 내용들이 어떻게 사용되고 있는가에 대한 점은 상세히 분석해 볼 필요가 있다.

왜냐하면, Android에서는 App(APK)가 처음부터 loading되는 것이 아니고 zygote를 통해서 fork되는 방식이기 때문에  Java의 경우와는 약간 다르기 때문이다.


Class Loader 의 종류와 내용.


VMClassLoader / BootClassLoader

BOOTCLASSPATH만을 load하는 기본적인 class loader


System Class Loader

BootClassLoader + "CLASSPATH" 를 가지는 Loader.

VM이 최초 "static main"을 invoke시킬때 사용하는 class loader이다.

근거:

startVm() -> DvmStartup() -> dvmPrepMainThread()

: SystemClassLoader를 Thread의 ContextClassLoader로 setting함.

AndroidRuntime::start()pEnv->FindClass() - Function pointer to 'dalvik/vm/Jni.cpp:FindClass' - 를 이용해서 "static main"을 invoke하는데, 이때 SystemClassLoader가 사용됨.


Context Class Loader

SystemClassLoader + "Package private configuration"을 가지는 loader.

VM이 뜬 이후, Android context instance - ex. Activity - 를 시작할때 - ex. performLaunchActivity() - 사용되는 class loader로 기본 System Class Loader에 package private configuration(LoadedApk.java) - ex. shared java library - 을 포함한다.

근거:

LoadedApk.getClassLoader() 함수를 자세히 살펴볼 필요가 있는데, package정보의 java shared library나 native shared library정보 등을 이용해서 PathClassLoader를 생성하고 이를 이용하는데, 이때 base class loader가 default로, SystemClassLoader이기 때문이다.


여기서 또 한가지 중요한 내용은, Java의 경우, class가 최초 reference될때, referer가 load될때 사용된 classLoader를 이용해서, reference되는 class도 load된다는 것이다. 이것은 Android VM의 경우도 마찬가지인데 예로, dalvik의 OP_NEW_INSTANCE op code처리 부분을 보면, dvmResolveClass() 를 call하고, 함수 내부의 구현을 보면, referer의 class loader 를 이용하는 것을 볼 수 있다.


이제 정리할 시간이다.

Android의 경우, 최초 vm이 뜰때는 System Class Loader가 사용되나, context가 수행될 때는 Context Class Loader가 사용된다. 따라서, Package(APK)에 추가적으로 사용할 library를 기록해 두면, Context내부에서는 정상적으로 사용이 가능하다.


그러면, Package 에서 사용할 library들은 어떤식으로 정의할 수 있을까?


먼저 사용할 library가 permission file에 정의되어 있고, platform build시 /system/etc/permissions 에 위치해야 한다.

(당연히, 해당 library도 정해진 위치에 있어야 한다.)


com.my.android.test.xml

<permissions>

<library name="com.my.android.test"

         file="/system/framework/com.my.android.test.jar" />

</permissions>


이제 위의 library는 package manager에 의해 shared library로 인식된다.


Package에서 사용하기 위해서는, package의 AndroidManifest.xml에 application element의 sub element로 아래 'uses-library' element를 추가해야 한다.


AndroidManifest.xml

<uses-library android:name="com.my.android.test" />


이렇게 정의되면, package manager service가 Context Class Loader에 해당 library를 추가해 준다.


또 한가지 짚고 넘어가야 할 것은, CLASSPATH 에 대한 부분이다.

CLASSPATH의 경우, VM이 시작할때, setting되는 java.class.path property를 읽는데, 이 값은 Dvm의 classPathStr 값이다.


[[ TO DO ]]

그런데, 재미있는 사실은, system의 CLASSPATH 환경변수를 설정하더라도, Zygote에서 fork되는 App에서는 이를 바로 사용하지 못하는데 (실험적. 확인 필요)이 부분은 좀더 분석해 봐야 한다.



====== Rough Reference - Starting new Activity ======


ActivityStackSupervisor
startActivityLocked
startActivityUncheckedLocked

ActivityStack (target stack)
startActivityLocked

ActivityManagerService

attachApplicationLocked

ActivityStackSupervisor

resumeTopActivityLocked

ActivityManagerService.java
startProcessLocked() -> Process.start("android.app.ActivityThread", app.processName, uid, uid, gids ....)

Process.java
Process.start()
startViaZygote()
zygoteSendArgsAndGetResult() === send argument list via socket ===> zygote

<<< Socket connection >>>

ZygoteInit
registerZygoteSocket()
acceptCommandPeer()

ZygoteConnection
readArgumentList()

Zygote.java
forkAndSpecialize

--- child process ---

'WrapperInit.execApplication' or 'execStandalone'
# executing execv -> "/system/bin/app_process /system/bin --application --nice-name=xxx com.android.internal.os.WrapperInit <pipe fd> <targetSdkVersion> <shell args>
<shell args> : Args from Process.zygoteSendArgsAndGetResult <= which comes from startProcessLocked ("android.app.ActivityThread" ...)
# executing execv -> "/system/bin/dalvikvm -classpath '...' ...

--- execv ---

AppRuntime : AndroidRuntime -> "com.android.internal.os.RuntimeInit"

AndroidRuntime.start()
: startVm()
...
# starts Java startClass or starts "main" method.

RuntimeInit.main()

AndroidRuntime.cpp:: com_android_internal_os_RuntimeInit_nativeFinishInit()

app_main::AppRuntime.onStarted

AndroidRuntime.callMain()

*** <class>.main() *** => WrapperInit.main (see app_process argument above....).

RuntimeInit
wrapperInit()
applicationInit()
invokeStaticMain()

static ActivityThread.main : <= See WrapperInit.execApplication.

ActivityThread
attach()
ActivityManager.attachApplication() ==> ActivityManagerService.attachApplication()
# enter message loop!

ActivityManagerService
attachApplication()
attachApplicationLocked()
:--- attaching applications...

ActivityStackSupervisor.attachApplication()

ActivityStackSupervisor
realStartActivityLocked()

ActivityThread.scheduleLaunchActivity()
-> queue message : ActivityThread.LAUNCH_ACTIVITY

'Domain > Android' 카테고리의 다른 글

Multiple ABI and 64bit on Lolipop  (2) 2014.11.08
[Android] WindowManager & Display Area  (0) 2014.08.08
Create and use java library from packages.  (0) 2014.02.03
[NDK] 불편한 진실... 00  (0) 2013.11.25
Android resource compiler의 한계...  (0) 2013.11.09

+ Recent posts