Service
-------
init process 는 다음을 통해서 각종 service를 control함.

SIGCHLD signal handler를 정의해서, service의 상태를 파악함
service가 죽는 경우 SIGCHLD signal이 날아오고, 이 signal로 어떤 service가 죽었는지/시작했는지 파악하고, 각 service의 flag에 따라서 다음 행동 (해당 service를 다시 시작하거나, system을 restart하거나 (critical service))을 결정함.
[property_service.c] '/dev/socket/property_service' socket을 열고 message를 기다림 (PROP_SERVICE_NAME).
'property_set(...)' interface를 통해서, property_service socket에 message가 써지고, init process의 property service부분에서 이 message를 handling함.
'ctl.start/stop/restart' 는 source code에 hard-coded된 특수한 message로, 해당 이름의 service를 control한다.

잊어먹지 않기 위해서 일단 기록해 둠....

⚫ ueventd
    ⚬ ueventd creates and 'poll' netlink socket.
        ueventd_main [init/ueventd.c]

    ⚬ read kernel event message -> parse -> handle
        handle_device_event [init/devices.c]
            -> device_changed [init/init.c]
                -> queue_device_triggers [init/init_parser.c]
                            |
            +---------------+
            |
            v
        : search action list to know what to do for this event.
          searching action which name is device-added/removed-[device node].
          [device node] is from 'DEVPATH' variable of uevent message.

    ⚬ action list : list declared at [init_parser.c]
        • builtin action (added by 'queue_builtin_action [init/init_parser.c]')
            ex. property_init, wait_for_coldboot_done etc.
        • from script (added by 'parse_action [init/init_parser.c]')
            section "on device-added-[device path] / on device-removed-[device path]"
            ex.
                [init.rc]
                on device-added-/dev/block/mmcblk0p18
                    exec /system/bin/sh /system/bin/hello_mmc.sh

⚫ other daemons (ex usbd)
    ⚬ create and 'poll' netlink socket.

This article is to describe summary and some important points to establish gnu system root on android devices based on my experience.

Notation
    [] : version used.

===============
Introduction
===============
    Now a days, performance of Android mobile device is much like PC.
    So, why don't you use Android device just like linux desktop?
    As a first step, here is summary to construct gnu system root on Android device.

==============
Setup Sequence
==============
    Installing cross compiling tools
    ----------------------------------------------------------------------
        *** NOTE ****************************************************************
        *    Followings are not tested because I just used built Sourcery g++.  *
        *    [ Codesourcery 2007q3-53 ]                                         *
        *************************************************************************
        * Reference : http://frank.harvard.edu/~coldwell/toolchain/
        * Build GNU binutils
            - Using '--disable-nls' option is recommended (to reduce compile time and dependency.
        * Install Linux Kernel Headers
            - Latest Sourcery g++(2010.09-50) supports only at-least-2.6.16-target-kernel.
        * Build GLIBC headers
        * Build GCC for headers:
            - Using '--disable-nls --without-headers' is recommended.
            - example : configure option of GCC in Codesourcery 2007q3-53
                /vobs/linuxjava/cs_toolchain/mycstc/cs_build_dir/src/gcc-4.2/configure --build=i686-pc-linux-gnu
                  --host=i686-pc-linux-gnu --target=arm-none-linux-gnueabi --enable-threads --disable-libmudflap
                  --disable-libssp --disable-libgomp --disable-libstdcxx-pch --with-gnu-as --with-gnu-ld
                  --enable-languages=c,c++ --enable-shared --enable-symvers=gnu --enable-__cxa_atexit
                  --with-pkgversion=CodeSourcery Sourcery G++ Lite 2007q3-53
                  --with-bugurl=https://support.codesourcery.com/GNUToolchain/
                  --disable-nls --prefix=/opt/codesourcery --with-sysroot=/opt/codesourcery/arm-none-linux-gnueabi/libc
                  --with-build-sysroot=/vobs/linuxjava/cs_toolchain/mycstc/cs_build_dir/install/arm-none-linux-gnueabi/libc
                  --enable-poison-system-directories
                  --with-build-time-tools=/vobs/linuxjava/cs_toolchain/mycstc/cs_build_dir/install/arm-none-linux-gnueabi/bin
                  --with-build-time-tools=/vobs/linuxjava/cs_toolchain/mycstc/cs_build_dir/install/arm-none-linux-gnueabi/binff
        * Build GLIBC libraries
        * Rebuild GCC using newly-built-GLIBC.

    Construct gnu system root
    -------------------------
        * Environments
            # this directory will be new gnu system root on Android target.
            PREFIX=/data/arm-gnu-sysroot/
            # gnu root directory on target device
            TGNUROOT=/data/gnuroot/
            TARGET=arm-none-linux-gnueabi
            export ARCH=arm
            export CROSS_COMPILE=${TARGET}-
            # set PATH
            PATH=<cross-tools>:$PATH
        * Install headers and GLIBC
            - copy cross-compiled glibc, headers etc to $TROOT except for i686 executables and related files
        * Build GNU binutils [binutils-2.18]
            - ./configure --prefix=${PREFIX} --host=${TARGET} --target=${TARGET} --disable-nls
        * Build GCC [gcc-4.2.1]
            - ./configure --prefix=${PREFIX} --build=i686-pc-linux-gnu --target=${TARGET} --host=${TARGET}
                --disable-nls --enable-languages=c
        * Build bash [bash-4.0]
            - ./configure --prefix=${PREFIX} -host=${TARGET} --without-bash-malloc
            - install to $PREFIX/bin. And link $PREFIX/bin/sh to $PREFIX/bin/bash
        * Build gnu make [make-3.82]
            - ./configure --prefix=${PREFIX} -host={TARGET}
        * Build busybox
            - Set cross compiler prefix to 'arm-none-linux-gnueabi-' and make.
            - install to $PREFIX/bin
              (usually commands in busybox are located at /bin)
            - create link to busybox for each command.
        * copy to target device
            - adb push $PREFIX $TGNUROOT
        * link rootfs to gnu system root.
            - ln -s $TGNUROOT/lib /lib
            - ln -s $TGNUROOT/bin /bin
              ...

===============
Confirm & Check
===============
    Build Emacs-23.2
    ----------------
        * push Emacs-23.2 source code to target.
        * ./configure --without-xpm --without-jpeg --without-tiff --without-gif --without-png --without-rsvg
             --without-xft --without-libotf --without-m17n-flt --without-toolkit-scroll-bars --without-xaw3d
             --without-xim --without-gpm
        * 'make' should be done without error.

    Running Emacs
    -------------
        * copy termcap.src from "http://www.opensource.apple.com/source/emacs/emacs-39/emacs/etc/termcap.src".
          And put this /usr/etc/termcap.src.
        * And set following environment variable
            TERM=linux
            TERMCAP=/usr/etc/termcap.src
        * Emacs should work without error in 'adb shell' terminal.

===============
NOTE
===============
    Points
    ------
        * headers and GLIBC built for cross-compiling should be used as they are at target system root.
          (To use cross-compiled binaries on target device without any potential issues.)
        * Some open source packages check only /lib and /usr/lib (/usr/local/lib isn't considered).
          So, fundamental libraries would be better to be installed at /usr/lib,
         even if it's default location is /usr/local/lib

Setting up gnu system root is the first step to construct gnu software stack on the system.
And, now it's done.
So, next step is installing packages to set up development-friendly environment on the Android device.
Below description is summary of issues that I faced against during installing gnu packages.

* Executing /usr/bin/su doesn't run ~/.bashrc
    ~/.bashrc is not executed if user shell is set up as /bin/sh, even if /bin/sh is just symbolic link of /bin/bash.
    Let's assume that /bin/sh -> /bin/bash.
    In this case, executing /bin/sh means "run bash as just 'shell' - sh".
    So, to make ~/.bashrc be run automatically, default shell of user should be set as /bin/bash not /bin/sh

* default .bashrc of normal(ex. ubuntu) desktop PC can be used as a template in Android device.

* terminal editor programs - vi, nano, emacs etc - don't work or work abnormally.
    Environment related with 'Terminal' should be set correctly.
        - Environment variables
            export TERM=xterm
            export TERMINFO=/etc/terminfo
            export TERMCAP=/etc/termcap
    Related files at /etc
        terminfo
            this has setting values for various terminal types - linux, vt100, xterm and so on.
            /etc/terminfo of desktop linux PC can be used as it is.
        termcap
            this is required by emacs.
            termcap.src(included at termcap source release) can be used as /etc/termcap.

* mode of newly created file becomes 0666 or 0777.
    umask should be set at .bashrc .
        umask 022

* window of editor programs like vi, emacs is default screen size (80x24)
 even if real screen size of terminal emulator is bigger.
    resize command should be executed.
    putting it at .bashrc is recommended.
    this command sets environment variables - COLUMNS and LINES - according to current terminal window size
   and export it.

* various colors are not used at the output of ls command.
    check that dircolors command is installed and this command is set correctly at .bashrc

* sudo command doesn't give any chance to enter password. It only says "Sorry, try again.".
    pam is not correctly binded with sudo.
    install /etc/pam.d/sudo file.
    (sample.pam included at sudo source code release can be a good reference.
     - especially, second option - use pam_unix - is recommended)

* icmp protocol is not recognized by ping - included at inetutils - command.
    check /etc/protocols file is correctly installed.
    this file can be used by copying desktop's one.

* localhost is not recognized even though ip address - 127.0.0.1  - works well.
    check followings at /etc/hosts
        127.0.0.1    localhost
    check below line at /etc/nsswitch.conf (related with nameserver)
        hosts: files dns

Anything to do?

* common

+ '-rdynamic' is not supported.
  android dynamic linker allows 'undefined symbol' only for weak symbol.(See (*1))
+ 'tmpfile()' function always returns NULL. "No such file or directory!"
+ 'strtod()' doesn't support '0xXXXX' format.

* NDK 8

'regex.h' exists in 'include'. But function bodies are not in library.

* NDK 9

'pthread_rwlock_xxxx' is declared in 'pthread.h'. But body is missing in library.
'pthread_exit' is declared without 'noreturn' attribute.
'regex' functions doesn't work as it should be. (bug? or not-implemented yet?? I have no idea.)

to be continued...

===== Details =====

(*1) : '-rdynamic' and 'weak' symbol (Updated at 2011-Aug-29)

Here is sample code to for testing 'weak' symbol at Android.

[ main.c ]
#include <dlfcn.h>
#include <stdio.h>

int g_d;

void
main() {
    void* handle;
    void  (*prf)(void);

    g_d = 369;

    handle = dlopen("/data/libgtst.so", RTLD_LAZY);
    if (!handle) {
        printf("Fail to load library\n");
        return;
    }

    prf = dlsym(handle, "print_g");
    if (NULL != dlerror()) {
        printf("Fail to get symbol print_g\n");
        return;
    }

    (*prf)();
    printf("=== DONE! ===\n");
}

[ lib.c ]
#include <stdio.h>

extern int g_d __attribute__((weak));

void
print_g(void) {
    printf("==> %d\n", g_d);
}

[ Android.mk ]
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := libgtst
LOCAL_SRC_FILES := lib.c

LOCAL_CFLAGS :=
LOCAL_C_INCLUDES += $(NDK_PROJECT_PATH)
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_SRC_FILES := main.c
LOCAL_CFLAGS :=
LOCAL_LDFLAGS :=
LOCAL_C_INCLUDES += $(NDK_PROJECT_PATH)
include $(BUILD_EXECUTABLE)

----- Test Steps -----
* build above codes at Android NDK
adb push libgtst.so /data/libgtst.so
adb push test /data/test
adb shell /data/test
==> 369
=== DONE! ===

to be continued...

Terms Definition :

Reboot : Real reboot. System restarts from 'boot logo'.
Soft reboot : System restarts from 'Power on animation'. (NOT from 'Boot logo'.)

In user mode

On Dalvik

ANR (Application Not Responding)

* Event dispatching timeout : 5 sec.
=> Windows can't dispatch events over 5 sec.
* Broadcast timeout : 10 sec.
=> Broadcast receiver can't complete things to do in 10 sec.
* Service timeout : 20 sec.
=> Service can't finish jobs in 20 sec..

Hang up

* There is no UI response, even if window dispatched events.
* Framework misses window that events should be passed to. (It doesn't never happens.)
=> Note! : We should wait over 5 sec. to tell it's hang-up or ANR. (See 'Event dispatching timeout')

FC (Force Close)

* Unexpected stop. Ex. null object access,  exceptions.

return from 'main' function.

* It's normal end of process if it is intentional. But in some cases, we may come across unexpected return from 'main'. System's point of view, it's just normal exit of process. So, no error message is left.

At native

Exception (ex. data abort etc)

* Kernel sends appropriate Signal (ex. SIGSEGV) to user process. And user process may be killed. In this case, process is killed due to signal from kernel.  So, we cannot expect UI feedback, but low level logs - ex. tombstone.

In privilege mode

Exception : Reboot. (Kernel 'oops' is executed before reboot. So, we may  be able to find corresponding kernel log/panic data.

Other important and notable cases.

system_process :

* Unexpected stop of  this process may cause system-soft-reboot. (This should be distinguished from reboot caused by kernel exception)

home / launcher

* Unexpected stop causes one-time-flickering of home screen to restart home. Let's imagine following scenario.
-> Home enters unexpected busy state(cannot dispatch user input during this busy time). After 3~4 sec., there is exception and home is stopped -> restarted.
In this case, it may look like just 'Smooth Reset'!

Error message : Android requires .class compatibility set to 5.0. Please fix project properties.

Solution
- go to "project -> property -> Java Compiler ->"
- Set "Compiler compliance level" to 1.5. And push "Apply" button.

Now it is resolved.

Interesting thing : Once this issue is resolved by changing "Compiler compliance", even if it we restore this value back to 1.6, this error isn't reproduced. I don't have any idea about the reason.

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

[Android] NDK issues (found)  (0) 2011.02.15
[Android] Classify unexpected cases  (0) 2010.10.29
[Android-Eclair] Miscellaneous tips.  (0) 2010.04.09
[Android-Eclair] Using exact size of Views  (0) 2009.12.14
[Android-Ecliar][Tips] build Android..  (0) 2009.12.03

* We can use 'View.draw(Canvas cavans)' to draw the view on selected canvas manually. But, as documented, the view must do a full layout. See following example.

--- It doesn't work ---
TextView tv = new TextView(this);
tv.setLayoutParams(new LayoutParams(400, 400));
tv.setText("Test");
tv.draw(canvas);

--- It works ---
TextView tv = new TextView(this);
tv.layout(0, 0, 400, 400);
tv.setText("Test");
tv.draw(canvas);

'setLayoutParams' is not doing layout, but just setting parameters for layout. So, we should manually do layout - ex, calling 'layout' function. And then we can draw the view.

* In general, view's coordinate domain is user content area - excluding status bar area. But, in case of Popup, it includes status bar. For example, base origin of left and top value of function 'popup.showAtLocation(parent, NO_GRAVITY, left, top)' is top-left of LCD screen - Not user content area. So, popup shown by 'popup.showAtLocation(parent, NO_GRAVITY, 0, 0)' is partially covered by status bar.

* 'android:layout_weight' is only valid in LinearyLayout - It is natural.

* When array is passed from Java to native code, there are two opions - copy or not. If array size is small, this doesn't matter. But, in case of large array, this can be important. In Android-Eclair, array is never copied. So, developer don't worry about passing large-size-array to native. You can check this from the macro code "dalvik/vm/jni.c: GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname)".

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

On Android, usually developer just set parameter of layout (LayoutParams.FILL_PARENT ... etc) for compatibility.

Then, when the exact size of each view is determined? The size of each View is decided when "onLayout()" is called.

The problem is, sometimes we need to know exact size of some Views and do something with this.
In this case, in my opinion, "onWindowFocusChanged()" is quite good place to do this. (in Activity).
At the moment when "onWindowFocusChanged()" is firstly called, all exact size of Views are decided. That is, we can get each View's exact size by calling "getWidth()/getHeigh()". - yes, I know. We need to handle quite many exceptional cases... :-(
But, until now, I cannot find any better place to do this.

Note!
Views added at "onLayout" of ViewGroup, are not drawn in Canvas before layout is re-updated!.

pseudo code)

    class View myView {
        onDraw(...) { // -- (*1)
            ...
        }
    }

    class LinearLayout layout {
        onLayout(...) {
            addView(myView)... // ---(*2)
        }
    }

At (*2), we can know exact size of 'layout'. So we can create myView's instance with layout's exact size, and add it to 'layout'.
In this case, even though (*1) is called, (process stops at (*1) when I set breakpoint.), it is not shown in the LCD screen.
Why? Because, newly added view - henceforth new View - is already excluded in the process of calculating View's exact size. So, layout of this new View is just empty rectangle! So, this cannot be drawn!.
So, all layout(by parameter) in View Tree should be decided, before starting framework's calculating-layout process (recursive calls of each View's 'onLayout()') to determine exact size of each View in View Tree.

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]

* We can refer *.mk in /build/target/product/ to know which Apps. are installed in which product!.

* External WebKit build.
Whole STL is not 100% compatible with ARM, Android uses some STL subset functions of HP - swap, min, max. (see external/webkit/WebKit/android/stl/algorithm). The problem is, if there is standard STL, this conflicts with above function (std::swap). So, if we should use standard STL, we need to modify something about this...

[[ blog 이사 과정에서 정확한 posting날짜가 분실됨. 년도와 분기 정도는 맞지 않을까? ]]
 

* Here is test result about calling onXXX functions.

Starting -> Showing

+ onWindowAttributeChanged x N
+ onContentChanged
+ onCreate
+ onStart
+ onPostCreate
+ onTitleChanged
+ onResume
+ onPostResume
+ onAttachToWindow
+ onWindowFocusChanged

Portrait Landscape (without handling configuration changes directly)

+ onSaveInstanceState
+ onPause
+ onStop
+ onRetainNonConfigurationInstance
+ onDestroy
+ onWindowAttributeChanged x N
+ onContentChanged
+ onCreate
+ onStart
+ onRestoreInstanceState <---
+ onPostCreate
+ onTitleChanged
+ onResume
+ onPostResume
+ onAttachToWindow
+ onWindowFocusChanged

Incomming Call

+ onSaveInstanceState
+ onPause
+ onWindowFocusChanged
+ onStop

Call ended

+ onRestart
+ onStart
+ onResume
+ onPostResume
+ onWindowFocusChanged

* To handle configuration change directly.
- Add android:configChanges="keyboardHidden|orientation" at the Activity in AndroidManifest.xml
- Override onConfigurationChanged(Configuration newConfig) ..

* Using onSaveInstanceSteate/onRestoreInstanceState
onSaveInstanceSteate/onRestoreInstanceState is called very often. So, we would better to avoid writing additional code except for saving/restoring state. Especially, allocating memory in "onRestoreInstanceState" should be avoided, even if this is quite good place to initialize newly restarted or resumed activity. Putting these code in it, may raise "OutOfMemory Exception" due to GC issue. (We cannot control GC. But, this function is called very frequently and memory is allocated here. So, at some moment, if memory is not collected for a while, memory shortage can be occurred.)

* Using ViewTreeObserver.
We can know the moment when something is changed on screen view by using listeners of ViewTreeObserver - ex. orientation is changed, input method is shown and so on. Especially, OnGlobalLayoutListener is very useful. How about try using this?

+ Recent posts