分享

Tag : android ? Shooting's Blog

 昵称3554661 2017-06-08
           Posts match “ android ” tag:

Android DumpGraphicsInfo flow

What situation will call dump graphics info?

  • NE occurs
  • ANR occurs
  • Manual dump (adb shell dumpsys gfxinfo [command])

Other information

  • GraphicsBinder其實就是一個”透過Binder”提供遠端服務的service -> (Binder只是媒介)
  • AMS是在setSystemProcess()裡面向ServiceManager註冊 addService “gfxinfo”提供服務
  • HWUI 這邊則是調用getService來使用”gfxinfo” 服務

Files

  • dumpsys.cpp
    • frameworks/native/cmds/dumpsys/dumpsys.cpp
  • ActivityManagerService
    • frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
  • ActivityThread
    • frameworks/base/core/java/android/app/ActivityThread.java
  • android_view_DisplayListCanvas
    • frameworks/base/core/jni/android_view_DisplayListCanvas.cpp

dumpsys.cpp

dumpsys.cpp (adb shell dumpsys gfxinfo / called by AEE)
int main(int argc, char* const argv[]) {
    //skip
    
    for (size_t i=0; i<N; i++) {
        sp<IBinder> service = sm->checkService(services[i]);
        if (service != NULL) {
            if (N > 1) {
                aout << "------------------------------------------------------------"
                        "-------------------" << endl;
                aout << "DUMP OF SERVICE " << services[i] << ":" << endl;
            }
            int err = service->dump(STDOUT_FILENO, args);  // <-- call GraphicsBinder.dump

ActivityManagerService.java

ActivityManagerService
public void setSystemProcess() {
        try {
            ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
            ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
            ServiceManager.addService("meminfo", new MemBinder(this));
            ServiceManager.addService("gfxinfo", new GraphicsBinder(this));  // <-- 
            ServiceManager.addService("dbinfo", new DbBinder(this));            
            // skip
ActivityManagerService
static class GraphicsBinder extends Binder {
        ActivityManagerService mActivityManagerService;
        GraphicsBinder(ActivityManagerService activityManagerService) {
            mActivityManagerService = activityManagerService;
        }

        @Override
        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            if (mActivityManagerService.checkCallingPermission(android.Manifest.permission.DUMP)
                    != PackageManager.PERMISSION_GRANTED) {
                pw.println("Permission Denial: can't dump gfxinfo from from pid="
                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
                        + " without permission " + android.Manifest.permission.DUMP);
                return;
            }

            mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args);  // <--
        }
    }
ActivityManagerService
final void dumpGraphicsHardwareUsage(FileDescriptor fd,
            PrintWriter pw, String[] args) {
            
            // skip
                 try {
                    TransferPipe tp = new TransferPipe();  // <-- Use this to output 
                    try {
                        r.thread.dumpGfxInfo(tp.getWriteFd().getFileDescriptor(), args); // <--
                        tp.go(fd);
                    } finally {
                        tp.kill();
                    }
                } catch (IOException e) {
                    pw.println("Failure while dumping the app: " + r);
                    pw.flush();
                } catch (RemoteException e) {
                    pw.println("Got a RemoteException while dumping the app " + r);
                    pw.flush();
                }

ActivityThread

ActivityThread
    @Override
    public void dumpGfxInfo(FileDescriptor fd, String[] args) {
        dumpGraphicsInfo(fd);
        WindowManagerGlobal.getInstance().dumpGfxInfo(fd, args);
    }
    
    private native void dumpGraphicsInfo(FileDescriptor fd);

android_view_DisplayListCanvas

android_view_DisplayListCanvas
static JNINativeMethod gActivityThreadMethods[] = {
    { "dumpGraphicsInfo",        "(Ljava/io/FileDescriptor;)V",
                                     (void*) android_app_ActivityThread_dumpGraphics }
};

static void
234android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) {
    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
    android::uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
}

Watchdog

  • 透過拿不拿的到 Lock 的方式來看系統是否卡住
  • 會去每分鐘polling四個thread來看是否卡住
    • main thread
    • android.ui
    • android.display
    • android.fg
  • 1 mins block --> timeout (30 seconds before will have predump)

watchdog.java

166        public void run() {
167            final int size = mMonitors.size();
168            for (int i = 0 ; i < size ; i++) {
169                synchronized (Watchdog.this) {
170                    mCurrentMonitor = mMonitors.get(i);
171                }
172                mCurrentMonitor.monitor();
173            }
174
175            synchronized (Watchdog.this) {
176                mCompleted = true;
177                mCurrentMonitor = null;
178            }
179        }

Reference

dumpsys
gfxinfo

Android ANR / NE / JE / SWT

Terminology

  • ANR = Application Not Response
  • NE = Native Exception
  • JE = Jave Exception
  • SWT = Software Watchdog Timeout

What can see in mobilelog?

  • main_log
    • AEE/AED
    • Dumping
      • ex. Dumping EXP/ANR, Dumping EXP/SWT, ...
  • events_log
    • am_anr
    • am_crash
    • watchdog
  • crash_log

What can see in db?

  • SYS_ANDROID_EVENT_LOG
    • am_anr
    • am_crash
    • watchdog
  • SWT_JBT_TRACES
    • callstack
  • SYS_ANDROID_LOG
    • Part of main_log (not as detail as main_log)
  • SYS_LIBRANK
    • memory usage
  • SYS_MEMORY_LOG
    • memory usage
  • NOTICE!
    • Time recorded in EVENT_LOG may be different from time in exp_main.
    • Exception occurs -> AEE collect data and write into exp_main takes some time.

SWT

  • How does SWT work?
    • Each 30 seconds, it will send event to important threads. (ex. mainthread, android.ui, android.display, android.fg)
    • Go to sleep.
    • Time up (30s later), it will check whether it's able to get response from those threads. If not, it will do predump.
    • NOTICE! It may not be exactly 30s, because if system is busy, then it may not be able to work.
SYS_ANDROID_LOG
11-17 01:46:04.468   947  1509 W Watchdog: SWT Watchdog before wait current time:549298598
11-17 01:46:04.468   947  1509 W Watchdog: SWT Watchdog before wait start:549298598
11-17 01:46:04.468   947  1509 W Watchdog: SWT Watchdog before wait CHECK_INTERVAL:30000
11-17 01:46:34.468   947  1509 W Watchdog: SWT Watchdog after wait current time:549328599
  • How does SWT work? (part2)

    • 透過拿不拿的到 Lock 的方式來看系統是否卡住
    • 會去每分鐘polling四個thread來看是否卡住
      • main thread
      • android.ui
      • android.display
      • android.fg
    • 1 mins block --> timeout (30 seconds before will have predump)
  • Important logs

SYS_ANDROID_EVENT_LOG
11-17 01:47:12.088   947  1509 I watchdog: Blocked in handler on main thread (main)
SYS_ANDROID_LOG
# 30s block predump
11-17 01:46:04.468   947  1509 W Watchdog: SWT Watchdog before wait current time:549298598
11-17 01:46:04.468   947  1509 W Watchdog: SWT Watchdog before wait start:549298598
11-17 01:46:04.468   947  1509 W Watchdog: SWT Watchdog before wait CHECK_INTERVAL:30000
11-17 01:46:34.468   947  1509 W Watchdog: SWT Watchdog after wait current time:549328599
    
# signal by someone    
11-17 01:46:42.087   947  1509 W Watchdog: SWT Watchdog before wait current time:549336217
11-17 01:46:42.087   947  1509 W Watchdog: SWT Watchdog before wait start:549336217
11-17 01:46:42.087   947  1509 W Watchdog: SWT Watchdog before wait CHECK_INTERVAL:30000
11-17 01:47:12.087   947  1509 W Watchdog: SWT Watchdog after wait current time:549366218

# 30s block again SWT occur
11-17 01:47:12.088   947  1509 E Watchdog: **SWT happen **Blocked in handler on main thread (main)    
11-17 01:47:21.834   947  1509 I Watchdog_N: dumpKernelStacks
11-17 01:47:21.872   947  1509 V Watchdog: ** save all info before killnig system server **
11-17 01:47:21.897   947  1509 D AES     :       cause : system_server_watchdog
11-17 01:47:21.924 13965 13965 D AEE/AED :   Type:system_server_watchdog
11-17 01:47:21.924 13965 13965 I AEE/AED : system_server_watchdog
main_log
11-17 01:47:12.088914   947  1509 D AEE/RTT_LIB: aee_exception_running: send req
11-17 01:47:12.089258   947  1509 D AEE/RTT_LIB: Rtt waiting daemon finish the job...
11-17 01:47:12.089799 26846 26846 D AEE/AED : $===AEE===AEE===AEE===$
11-17 01:47:12.089884 26846 26846 D AEE/AED : p 0 poll events 1 revents 1
11-17 01:47:12.090030 26846 26846 D AEE/AED : requesting from: pid=947 cmd=9
11-17 01:47:12.090098 26846 26846 D AEE/AED : Go RTT_AEE_GET_EXCEPTION_RUNNING

11-17 01:47:21.913622 13965 13965 I AEE/AED : FATAL 'SWT' raised
11-17 01:47:21.913700 13965 13965 I AEE/AED : Dumping EXP/SWT

Android Memory allocation

Memory allocation dump

  • adb shell showmap -s [pid]

    virtual                                       shared   shared  private  private
    size      RSS      PSS    RSWAP    PSWAP    clean    dirty    clean    dirty    # object
    -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
     128       40        0        0        0        0       40        0        0    1 /dev/__properties__
    1016        4        4        0        0        0        0        4        0    1 /dev/binder
      16        0        0        0        0        0        0        0        0    4 /dev/mali0
       8        8        0        0        0        8        0        0        0    2 /proc/xlog/setfil
      68       52        8        0        0       44        0        0        8    3 /system/bin/linker
      32       32       32        0        0        0        0       24        8    3 /system/bin/program_binary_service
    21104     5816     1965        0        0     5224        0       60      532    5 /system/lib/egl/libGLES_mali.so
    -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
    virtual                                       shared   shared  private  private
    size      RSS      PSS    RSWAP    PSWAP    clean    dirty    clean    dirty    # object
    -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
    108636    14244     5648        0        0    10692       40       88     3424  298 TOTAL   
    
  • adb shell procmem [pid]

    Vss      Rss      Pss      Uss     ShCl     ShDi     PrCl     PrDi  Name
    -------  -------  -------  -------  -------  -------  -------  -------  
    4096K      32K      32K      32K       0K       0K      32K       0K  [anon:libc_malloc]
    4K       0K       0K       0K       0K       0K       0K       0K  
    1012K       4K       4K       4K       0K       0K       4K       0K  [stack:509]
    4K       0K       0K       0K       0K       0K       0K       0K  
    4K       0K       0K       0K       0K       0K       0K       0K  
    1012K       4K       4K       4K       0K       0K       4K       0K  [stack:499]
    19288K    5284K    1313K      16K    5268K       0K      16K       0K  /system/lib/egl/libGLES_mali.so
    4K       0K       0K       0K       0K       0K       0K       0K  
    352K     352K     352K     352K       0K       0K     352K       0K  /system/lib/egl/libGLES_mali.so
    1404K     140K     140K     140K       0K       0K     140K       0K  /system/lib/egl/libGLES_mali.so
    -------  -------  -------  -------  -------  -------  -------  -------  
    108628K   14244K    5558K    3468K   10736K      40K    3472K       0K  TOTAL  
    
  • adb shell cat /proc/[pid]/smaps

    7fa8dd9000-7fa9cf5000 r-xp 00000000 fd:00 1906                           /system/lib64/libLLVM.so

    Size: 15472 kB

    Rss: 2652 kB

    Pss: 656 kB

    7fa9d04000-7fa9d9f000 r--p 00f28000 fd:00 1906 /system/lib64/libLLVM.so

    Size: 620 kB

    Rss: 620 kB

    Pss: 620 kB

    7fa9d9f000-7fa9da3000 rw-p 00fc3000 fd:00 1906 /system/lib64/libLLVM.so

    Size: 16 kB

    Rss: 16 kB

    Pss: 16 kB


    • adb shell cat /proc/[pid]/maps

      7fa8dd9000-7fa9cf5000 r-xp 00000000 fd:00 1906                           /system/lib64/libLLVM.so
      Size:              15472 kB
      Rss:                2652 kB
      Pss:                 656 kB
      Shared_Clean:       2652 kB
      Shared_Dirty:          0 kB
      Private_Clean:         0 kB
      Private_Dirty:         0 kB
      Referenced:         2652 kB
      Anonymous:             0 kB
      AnonHugePages:         0 kB
      Swap:                  0 kB
      PSwap:                 0 kB
      KernelPageSize:        4 kB
      MMUPageSize:           4 kB
      Locked:                0 kB
      VmFlags: rd ex mr mw me 
      
    • adb shell dumpsys meminfo

      Applications Memory Usage (kB):
      Uptime: 225322 Realtime: 225322

      Total PSS by process:
      90186 kB: com.android.systemui (pid 1164)
      85850 kB: com.android.launcher3 (pid 1695 / activities)
      68059 kB: system (pid 953)
      54755 kB: surfaceflinger (pid 287)
      43791 kB: com.mediatek.voicecommand (pid 1295)
      39102 kB: zygote (pid 452)


      How to find who allocate those memory

      • Step1: Generate NE manually

        • adb shell kill -11 [pid]
      • Step2: Parse

        • dump_malloc_info summary.txt PROCESS_MAPS
          • 主要是在 gdb 裡面下 dump_malloc_info summary.txt PROCESS_MAPS
          • summary.txt 是 output filename
          • PROCESS_MAPS 是DB 裡的檔案 用來記錄每段memory是load了甚麼library
          • 執行完後會在當前目錄下產生 summary.txt 用來記錄各lib memory allocate size
          • 執行完後會在當前目錄下產生 malloc_info 用來記錄各lib memory allocate backtrace
      dump_malloc_info
      cd D:\tools\GAT\prebuilt\android-sdk\bin
      startGDB or startGDB64
      (gdb) cd d:\temp(gdb) file symbols/system/bin/app_process32 or app_process64
      (gdb) set solib-search-path symbols/system/lib or lib64
      (gdb) core PROCESS_COREDUMP
      (gdb) source dump_malloc_info.py
      (gdb) dump_malloc_info summary.txt PROCESS_MAPS
      
      • Step3: Analyze
        • Check summary.txt to see target library memory allocation size
        • Open backtrace of memory allocation refer to target library
        • Check the bt refer to which line and which file
          • Check bt is in which memory segment of loaded library or bin
          • The symbol of library or bin should be loaded, otherwise it's not able to indicate line.
      malloc_info/_system_bin_program_binary_service
      size per alloc: 4160, alloc num: 336
      total size: 4160 * 336 = 1MBytes, 1397760Bytes (about 1.33mb)
      bt: 0x7f80ddec88 
      bt: 0x7f80faa6bc 
      bt: 0x7f86f6b274 
      bt: 0x7f8704cf30 
      bt: 0x7f8704d3c4   <---
      
      PROCESS_MAPS: program_binary_service is loaded in this memory segment(0x7f8704d3c4 in here)
      7f87048000-7f87050000 r-xp 00000000 b3:1b 485      /system/bin/program_binary_service
      
      GDB: Need program_binary_service symbol to be loaded
      (gdb) list *(0x7f8704d3c4)
      xxx.cpp: #lineCnt
      ex.
      abc.cpp: 12
      

Android build (make)

Fully build

make -j24
make -j24 -k 2>&1 | tee build_full.log

Rebuild boot image

make -j36 bootimage 2>&1 | tee build_boot.log

Rebuild system image

make -j36 systemimage 2>&1 | tee build_system.log

Rebuild module

mmm ./frameworks/base/libs/hwui
mmm ./frameworks/base/libs/hwui -k
mmm ./frameworks/base/libs/hwui -k 2>&1 | tee build_hwui.log
mmm -B ./frameworks/base/libs/hwui  //-B: automatically get dependent module to build

How to check build error

  • Search " *** "
    make: *** [out/target/product/k2v1_64_op02/obj/ETC/service_contexts_intermediates/service_contexts] Error 5 
    

Android make file (Android.mk)

Content index

  1. Executable Template
  2. Shared Library Template
  3. Static Library Template
  4. Use other shared library
  5. Use other static library
  6. Include path list
  7. Use c/cpp/cxx/ld flags
  8. Call subdir's Android.mk

Executable Template

LOCAL_PATH:= $(call my-dir)   # call function my-dir will return the path of Android.mk
include $(CLEAR_VARS)         # clean all variables mainly started with LOCAL_

LOCAL_SRC_FILES:= foo.c       # Source file list
LOCAL_MODULE:= foo            # The name of executable binary

include $(BUILD_EXECUTABLE)   # Start to build executable binary

Shared Library Template

LOCAL_PATH:= $(call my-dir)     # call function my-dir will return the path of Android.mk
include $(CLEAR_VARS)           # clean all variables mainly started with LOCAL_

LOCAL_SRC_FILES:= foo.c bar.c   # Source file list
LOCAL_MODULE:= libfoo           # The name of shared library
LOCAL_PRELINK_MODULE := false   # Prevent from prelink error

include $(BUILD_SHARED_LIBRARY) # Start to build shared library

Static Library Template

LOCAL_PATH:= $(call my-dir)     # call function my-dir will return the path of Android.mk
include $(CLEAR_VARS)           # clean all variables mainly started with LOCAL_

LOCAL_SRC_FILES:= foo.c bar.c   # Source file list
LOCAL_MODULE:= libbar           # The name of static library
LOCAL_PRELINK_MODULE := false   # Prevent from prelink error

include $(BUILD_STATIC_LIBRARY) # Start to build static library

Use other shared library

LOCAL_SHARED_LIBRARIES := libfoo

Use other static library

  • LOCAL_STATIC_LIBRARIES

    • These are the static libraries that you want to include in your module.
    • Mostly, we use shared libraries, but there are a couple of places, like executables in sbin and host executables where we use static libraries instead.
      LOCAL_STATIC_LIBRARIES := libbar
      
  • LOCAL_WHOLE_STATIC_LIBRARIES

    • These are the static libraries that you want to include in your module without allowing the linker to remove dead code from them.
    • This is mostly useful if you want to add a static library to a shared library and have the static library's content exposed from the shared library.
      LOCAL_WHOLE_STATIC_LIBRARIES := libbar
      

Include path list

LOCAL_C_INCLUDES +=                     usr/include                               usr/local/include                         $(LOCAL_PATH)/include

Use c/cpp/cxx/ld flags

LOCAL_CFLAGS   += -DONLY_C_NEEDED
LOCAL_CXXFLAGS += -DONLY_CXX_NEEDED
LOCAL_CPPFLAGS += -DBOTH_C_CXX_NEEDED
LOCAL_LDFLAGS  += -Wl,--exclude-libs=libgcc_eh.a
LOCAL_LDLIBS   += -lpthread

Call subdir's Android.mk

  • Not recursively, just the directly sudir.
  • I guess should be at last line, otherwise will have error like following
    • my-dir must be called before including any other makefile.. Stop.
      include $(call all-subdir-makefiles)
      
include $(call all-makefiles-under,$(LOCAL_PATH))
  • Notice
    • Don't use this with `include $(BUILD_EXECUTABLE/BUILD_SHARED_LIBRARY/BUILD_STATIC_LIBRARY) at the same Android.mk, there are some bugs.

Build: Options to speficy 32bit only

  • LOCAL_32_BIT_ONLY == true
    • Build 32 bit binary only which is specified by LOCAL_MODULE in Android.mk

Build: Indicator to build 32 or 64 bit

Intentionally add error message in Android.mk

Android.mk
$(error "shooting: Android.mk test")
result
Android.mk:4: *** "shooting: Android.mk test".  Stop.

Add log in Android.mk

Android.mk
$(info "This is log.")

Android native thread

How to write a native thread and how to use it

AbcThread.h
#include <utils/Thread.h>

Class AbcThread: public Thread
{
public: 
    AbcThread();
    virtual ~AbcThread();

protected:
    virtual bool threadLoop(); // Explain later for the return value
}
AbcThread.cpp
#include <utils/Thread.h>
#include <utils/Log.h>

AbcThread::AbcThread : 
        Thread(false) // Thread inherit from RefBase.
{
    ALOGD("Consctruct");
} 

AbcThread::~AbcThread
{
    ALOGD("Desctruct");
} 

AbcThread::ThreadLoop
{
    // Put what you want to do when thread is running here...        
    return true or false; 
}
Main.cpp
#define LOG_TAG "Main"  
  
#include <utils/Log.h>  
#include <utils/threads.h>  
#include "AbcThread.h"  
   
int main()  
{  
    sp<AbcThread>  thread = new AbcThread; // Note: Must write like this!
    thread->run("AbcThread");  // thread->run() is fine too    
}  
Android.mk
LOCAL_PATH := $(call my-dir)  
include $(CLEAR_VARS)  
  
LOCAL_SRC_FILES := 
    AbcThread.cpp \  
    Main.cpp \  
      
LOCAL_SHARED_LIBRARIES :=libandroid_runtime \  
    libcutils \  
    libutils    
      
LOCAL_MODULE := android_abcthread  
  
LOCAL_MODULE_TAGS := eng  
      
LOCAL_PRELINK_MODULE := false  
  
#include $(BUILD_SHARED_LIBRARY)  
include $(BUILD_EXECUTABLE)  

ThreadLoop explanation

/system/core/include/utils/Thread.h
// Derived class must implement threadLoop(). The thread starts its life
// here. There are two ways of using the Thread object:
// 1) loop: if threadLoop() returns true, it will be called again if
//          requestExit() wasn't called.
// 2) once: if threadLoop() returns false, the thread will exit upon return.
virtual bool        threadLoop() = 0;

Reference

http:///5.0.0_r2/xref/system/core/include/utils/Thread.h

How to print thread create callstack

Android file descriptor related

Get fd path

Native
char s[256], name[256];  
snprintf(s, 255, "/proc/%d/fd/%d", getpid(), fd);  
readlink(s, name, 255);  
Native2
char fdpath[PATH_MAX]
sprintf(fdpath, PATH_MAX, "/proc/%d/fd/%d", getpid(), fd); 

char path[PATH_MAX]
readlink(fdpath, path, 255);  

ALOGI("fd: %d[%s]", fd, path);
Kernel (kernel中從fd得到path可以參考kernel/fs/file.c)
get_file_name_from_fd pathname

Parcel file descriptor

  • CloseGuard 只是保護機制,用來檢查fd有沒有正常被上層釋放, 如果上層沒有呼叫 close, 一旦 ParcelFileDescriptor 生命週期結束,VM 回收物件的時候會呼叫 finalize, 此時 finalize 裡面會檢查 close guard 的狀態, 發現該 fd 沒有正常 close, 就會印出這些訊息.

  • 從 VM 的實作 來說,finalize 時間點不固定,而且 VM 會希望object finalize 不要做太多事情, 原本只是希望做一個通知的動作.

Check map

adb shell cat /proc/[pid]/maps

Share file descriptor using Android binder

  • sharing the file descriptor across process will be handled by the binder driver.
  • Note : duplicate the received file descriptor before the parcel object is destroyed.
  • Reference
    Server
    data.writeFileDescriptor(fd);
    
Client
int fd = data.readFileDescriptor();
int dupFd = dup(fd);

Exec

Idea?

  • Let program1 trigger program2, while program1 and program2 are binary in /system/bin.
/system/bin/program1
ALOGI("Program1 is starting...");
// Trigger /system/bin/program2
ALOGI("Program1 is ended.");
/system/bin/program2
ALOGI("Program2 is starting...");
ALOGI("Program2 is ended.");

Method1

  • int system (const char* command);
    • Execute system command.
    • Invokes the command processor to execute a command.
    • create a blocking process and wait until this process finished.
/system/bin/program1
// Trigger /system/bin/program2
ALOGD("+ trigger program");
system("/system/bin/program2");
ALOGD("- trigger program");

result (program1 will wait until system is finished, then keep execution.
Program1 is starting...
+ trigger program
Program2 is starting...
Program2 is ended.
- trigger program
Program1 is ended.

Method2

  • exec

    • The exec() family of functions replaces the current process image with a new process image.
    • Oly return if an error has occurred.
      • return value is -1, and errno is set to indicate the error.
    • No return if no error.
  • execl(), execlp(), and execle()

    • The const char *arg and subsequent ellipses in these functions can be thought of as arg0, arg1, ..., argn.
  • execv(), execvp(), and execvpe()

    • These functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program.
  • Reference

  • exec family

exec family
#include <unistd.h>

extern char **environ;

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
  • Examples
    internet example
    // run /bin/ls -al /etc/passwd
    // Execute document indicated by path, pass argv[0], argv[1], ... last one is indicated with NULL.
    execl(“/bin/ls”,”ls”,”-al”,”/etc/passwd”,(char * )0);
    
    
    

    // run ls -al /etc/passwd, environment variable PATH's value /bin find /bin/ls
    // Execute document under environment variable PATH which matches parameter file
    execlp(“ls”,”ls”,”-al”,”/etc/passwd”,(char *)0);

    // run /bin/ls -al /etc/passwd
    // Similiar to execl, but pass with argv pointer
    char * argv[ ]={“ls”,”-al”,”/etc/passwd”,(char*) }};
    execv(“/bin/ls”,argv);

    // execve()用来执行参数filename字符串所代表的文件路径,第二个参数系利用数组指针来传递给执行文件,argv要传递给
    // 程序的完整参数列表,包括argv[0],它一般是执行程序的名字;最后一个参数则为传递给执行文件的新环境变量数组。
    char *envp[] = {"PATH=/tmp", "USER=lingdxuyan", "STATUS=testing", NULL};
    char *argv_execve[] = {"env", NULL};
    execve("/usr/bin/env", argv_execve, envp)


    My example
    int ret = execv("/bin/ls", NULL);
        // failed -> /system/bin/pro (NE), /bin/pro, /pro, pro, ../pro, ./pro
        //
    
    
    /system/bin/program1
    // Trigger /system/bin/program2
    ALOGD("+ trigger program");
    int ret = execl("/system/bin/program2", NULL); 
    // or int ret = execv("/system/bin/program_binary_builder", NULL);
    ALOGW("Execution result %d errno %d -> %s", ret, errno, strerror(errno)); // print error
    ALOGD("- trigger program");
    
    
    result
    //execl("/system/bin/program2", NULL); 
    NE happened
    
    //execl("/system/bin/program2", NULL); 
    //  other failed path: ./program2, bin/program2, program2, ../program2
    Execution result -1 errno 2 -> No such file or directory
    

SurfaceTexture

SurfaceTexture

  • SurfaceTexture is the combination of a Surface and a GLES texture.
    • i.e. SurfaceTexture interacts with an EGL context.
  • When you create a SurfaceTexture, you are creating a BufferQueue for which your app is the consumer.
    • When a new buffer is queued by the producer, your app is notified via callback (onFrameAvailable()).
    • Your app calls updateTexImage(), which releases the previously-held buffer, acquires the new buffer from the queue, and makes some EGL calls to make the buffer available to GLES as an "external" texture.
  • External textures (GL_TEXTURE_EXTERNAL_OES) are not quite the same as textures created by GLES (GL_TEXTURE_2D).
    • User have to configure your renderer a bit differently, and there are things you can't do with them.
  • When SurfaceTexture created the BufferQueue, it set the consumer's usage flags to GRALLOC_USAGE_HW_TEXTURE, ensuring that any buffer created by gralloc would be usable by GLES.
    • So the format of the data in the buffer is something GLES can recognize
  • Each buffer is accompanied by a timestamp and transformation parameters.
    • The transformation is provided for efficiency in case the source data might be in the "wrong" orientation for the consumer.
    • The timestamp is useful for certain buffer sources. ex. Trun captured frames into a video

SurfaceTexture and Surface

  • SurfaceTexture is called GLConsumer, which more accurately reflects its role as the owner and consumer of a BufferQueue.
    • Consuming buffers of graphics data and making them available as textures.
  • Surface represents the producer side of the SurfaceTexture's BufferQueue.
    • Surface is construct with a SurfaceTexture
    • When you create a Surface from a SurfaceTexture, what you're doing is creating an object that represents the producer side of the SurfaceTexture's BufferQueue.

Case study:Grafika's "Continuous Capture" Activity

  • The "Continuous capture" activity displays video from the camera as it's being recorded.
  • There are 3 BufferQueue involved [producer <-> consumer]
    • Camera <-> SurfaceTexture (The app uses a SurfaceTexture to receive frames from Camera,converting them to an external GLES texture.)
    • SurfaceView <-> SurfaceFlinger (The app declares a SurfaceView, which we use to display the frames.)
    • MediaCodec <-> mediaServer (We configure a MediaCodec encoder with an input Surface to create the video.)
    • All three of the BufferQueues are handled with a single EGL context in the app, and the GLES operations are performed on the UI thread.
  • Flow
    • SurfaceView's surfaceCreated() callback
      • EGLContext is created.
      • EGLSurfaces are created for the display and for the video encoder.
    • A new frame arrives
      • Tell SurfaceTexture to acquire it and make it available as a GLES texture, then render it with GLES commands on each EGLSurface.
    • The encoder thread pulls the encoded output from MediaCodec and stashes it in memory.

Android native service

/frameworks/native/include/binder/IInterface.h
DECLARE_META_INTERFACE
IMPLEMENT_META_INTERFACE

Register service to native service manager

/frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
sp<IServiceManager> sm(defaultServiceManager());
sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);
/system/core/rootdir/init.rc
service surfaceflinger /system/bin/surfaceflinger
    class core  // service starting sequence: core > main > default
    user system  
    group graphics drmrpc
    onrestart restart zygote  // execute the command when service restart

SurfaceFlinger

  • main_surface_flinger is an executable binary, init process 會執行它, 讓他註冊成為 service.
  • 寫在 init.rc (service surfaceflinger /system/bin/surfaceflinger) 就會呼叫到main_sufaceflinger
  • flinger->init 的時候就會 create gl context, 等 app 送 buffer 到 SF 就會 call gl command.
  • Create gl context 就會 init 3d driver.

Log information

  • ADB_SERVICES: create_subproc ret_fd=47 pid=3081 <-- means register succeeded

TextureView

TextureView

  • TextureView is a object combining a View with a SurfaceTexture.
  • TextureView wraps a SurfaceTexture, taking over the responsibility of responding to the callbacks and acquiring new buffers.
    • The arrival of new buffers causes TextureView to issue a View invalidate request.
    • When asked to draw, the TextureView uses the contents of the most recently received buffer as its data source, rendering wherever and however the View state indicates it should.
  • Render on a TextureView with GLES just as you would SurfaceView.
    • Just pass the SurfaceTexture to the EGL window creation call. However, doing so exposes a potential problem.
    • EGL swap buffer <-> BufferQueue <-> Consumer
      • It may stuck.
      • The solution is to have BufferQueue ensure there is always a buffer available to be dequeued.
        • One way to guarantee this is to have BufferQueue discard the contents of the previously-queued buffer when a new buffer is queued, and to place restrictions on minimum buffer counts and maximum acquired buffer counts.

SurfaceView or TextureView?

  • SurfaceView and TextureView fill similar roles, but have very different implementations.
  • SurfaceView
    • More efficiently, better performance
  • TextureView
    • Behave like any other view
    • Will be composited twice

Case Study: Grafika's Play Video (TextureView)

  • Grafika includes a pair of video players, one implemented with TextureView, the other with SurfaceView.
  • While SurfaceView requires a custom implementation of FrameLayout, resizing SurfaceTexture is a simple matter of configuring a transformation matrix with TextureView#setTransform()
    • SurfaceView: you're sending new window position and size values to SurfaceFlinger through WindowManager
    • TextureView: you're just rendering it differently
  • The rest behavior is almost similiar, only composition is different (by SurfaceFlinger or by TextureView)

Case Study: Grafika's Double Decode

  • The basic structure of this activity is a pair of TextureViews that show two different videos playing side-by-side.
  • Implement with 2 TextureView
    • It can keeps SurfaceTexture inside TextureView even the activity is shutdown or orientation changed.
    • Each video decoder is driven from a separate thread.
    • At first glance it might seem like we need EGL contexts local to each thread; but remember the buffers with decoded output are actually being sent from mediaserver to our BufferQueue consumers (the SurfaceTextures).
    • The TextureViews take care of the rendering for us, and they execute on the UI thread.
  • Implement with 2 SurfaceView
    • The Surfaces would be destroyed during an orientation change.
    • It need two layers, and limitations on the number of available overlays strongly motivate us to keep the number of layers to a minimum.

Python for Android logcat and dumpsys

Part of dump behavior (record main_log, produce and dump screen shot, ...)
class AsynchronousFileReader(threading.Thread):
    def __init__(self, mainlog):
        threading.Thread.__init__(self)                        
        self._mainlog = mainlog;
        
    def run(self):              
        subprocess.call('adb logcat -c', shell=True)
        self._process = subprocess.Popen('adb shell logcat -b main -b system -v threadtime', stdout=self._mainlog)

    def stop(self):        
        self._process.terminate()
        print 'wait for logcat stopped...'
        time.sleep(5)        

def runAsyncLogCat:
    # Launch asynchronous readers of the process' stdout
    mainlog = open('main_log', 'w+')
    
    stdout_reader = AsynchronousFileReader(mainlog)    
    stdout_lock = threading.Lock()
    stdout_reader.start()
    
    out = raw_input('Stop logging?')
    stdout_reader.stop()                      
    mainlog.close()

def shell(cmd):
    # cmd example: 'adb shell mkdir -p /data/my_dump'
    #              'adb shell /system/bin/screencap -p /data/my_sf_dump'
    #              'adb shell setprop debug.bq.dump ""'
    #              'adb shell "dumpsys SurfaceFlinger" > ' + 'screenshot/timestamps_xxx' + '/SF_dump/SF_bqdump_all.log'
    subprocess.call(cmd, shell=True)

def shellPIPE(cmd):
    # Run command and return result
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = p.communicate()
    return out

def dumpsys(cmd, filename):
    # Dump property and wrtie to file
    # cmd example: 'adb shell getprop debug.xxx.xxx'
    #              'adb shell dumpsys gfxinfo'
    temp = []
    try:
        out = shellPIPE(cmd)
        #time.sleep(2)
        myFile = open(filename, 'w+')
        myFile.write(out)        
        myFile.seek(0, 0)                
        for eachLine in myFile:
            temp.append(eachLine)        
    except:
        print 'error: ' + cmd + ', ', sys.exc_info()
        traceback.print_tb(sys.exc_info()[2])
    else:
        myFile.close()
    return temp  

def main(argv):
    try:
        # Get top activity name
        out = shellPIPE("adb shell dumpsys activity top")
        myFile = open('scripts/top.txt', 'w+')
        myFile.write(out)
        myFile.seek(0, 0);

        activity = ""
        result = True

        for eachLine in myFile:
          if "pid=" in eachLine:
              activity = eachLine.split(" ")[3].strip()
              pid = eachLine.split("pid=")[-1].strip()
              break
        myFile.close()

        if (activity == ""):
            print "error: device not fond"
            result = False
        else:           
            cmd_index_str = sys.argv[1]
            cmd_list = parse_cmd_str(cmd_index_str)
            result = execute_set_cmd(cmd_list, activity, pid)                        
    except:
        print 'error: unknown error', sys.exc_info()
        traceback.print_tb(sys.exc_info()[2])      

if __name__ == '__main__':
    main(sys.argv)     

Fork

Definition

  • System call fork() is used to create processes. It takes no arguments and returns a process ID. The purpose of fork() is to create a new process, which becomes the child process of the caller. After a new child process is created, both processes will execute the next instruction following the fork() system call. Therefore, we have to distinguish the parent from the child. This can be done by testing the returned value of fork().

    • fork() returns a negative value, the creation of a child process was unsuccessful.
    • fork() returns a zero to the newly created child process.
    • fork() returns a positive value, the process ID of the child process, to the parent. The returned process ID is of type pid_t defined in sys/types.h. Normally, the process ID is an integer. Moreover, a process can use function getpid() to retrieve the process ID assigned to this process.
  • 一個進程調用fork()函數後,系統先给新的進程分配資源,例如存儲數據和代碼的空間。然後把原來的進程的所有值都
    复制到新的新進程中,只有少數值與原來的進程的值不同。相當於克隆了一個自己。

    • fork出錯可能有兩種原因:
      • 1)當前的進程數已經達到了系統規定的上限,這時errno的值被設置为EAGAIN。
      • 2)系統內存不足,這時errno的值被設置为ENOMEM。

Example

pid_t pid;
if((pid = fork()) < 0) {
    ALOGI("an error occurred while forking\n");
} else if(pid == 0) {
    /* Child process */
    ALOGI("the child's pid is: %d\n", getpid());
    char* cmd = "/system/bin/program2";
    char *args[1] = {cmd};
    int ret = execv(cmd, args);
    // Normally it won't reach here unless error occurs
    ALOGW("Execute child process failed: result %d errno %d -> %s", ret, errno, strerror(errno));
} else {
    /* Parent process */
    ALOGI("parent continues execution: child pid is %d\n", pid);
    
}

Reference

Zombie process?

  • 在fork()/execve()過程中,假設子程序結束時父程序仍存在,而父程序fork()之前既沒設置SIGCHLD信號處理函數調用waitpid()等待子進程結束,又沒有設置忽略該信號,則子程序成為僵屍程序,無法正常結束,即使是root身份kill -9也不能殺死僵屍程序。且此zombie process會占用process table, 除非父程序結束或有wait child process才可以。

  • zombies are the living dead. Zombie processes have died (either from a signal or because they exited), and the parent process has not yet executed a wait() for the process. The zombie is dead, but still occupies a process table slot and will continue to do so until its parent waits for it, or the parent exits. If the parent exits, the child will be inherited by the init process (usually PID 1), and one of the main purposes of that process (if not the only one) is to wait for children to die.

  • Solution

    • 1. 在父程序設置SIGCHLD信號的處理函式, 使其父程序自動忽略子程序的狀態變更,但長期常駐程式, 可能不適用, 難保不會出現系統資源耗用的現象.
    • 2. 在父程序設置SIGCHLD信號的處理函式, 並呼叫waitpid(), 等待捕獲子程序的返回狀態.
    • 3. 呼叫2次fork(), 父程序呼叫fork(第一次)產生子程序, 子程序再呼叫fork(第二次)產生孫程序, 隨即子程序終結死亡, 此時孫程序變為"孤兒程序",init程序會接管孫程序, 變成它的父程序, 而init程序會自行負責處理SIGCHLD信號.
Sol1
int main (int argc, char *argv[])
{
    signal (SIGCHLD,SIG_IGN); //設置SIGCHLD
    char *prog_list = {"prog1","prog2", "prog3"};

    for (int i=0; i<3; i++) {
        char *arg_list = {prog_list[i], NULL};
        forkExecProc (prog_list[i], arg_list);
    }

    while (true) {
        sleep (2);
    }
    return 0;
}
int forkExecProc (char *prog, char **arg_list)
{
    ...
}
Sol1: easy sample
signal(SIGCHLD, SIG_IGN); // <-- ignore child fate, don't let it become zombie
pid=fork();
if (pid==0) {
    exit(0); // <--- zombie should NOT be created here
} else {
     // some parent code ...
}
Sol2
void sig_fork(int signo)
{
    pid_t pid;
    int stat;
    // 呼叫waitpid(),等待子程序返回, 若無子程序返回, 也不一直等待 
    pid=waitpid(0,&stat,WNOHANG);
    
    return;
}


int main (int argc, char *argv[])
{
    signal (SIGCHLD, sig_fork); // 設置SIGCHLD, 並呼叫waitpid(), 捕獲子程序的返回狀態
    
    char *prog_list = {"prog1","prog2", "prog3"};

    ...
    
    return 0;
}


int forkExecProc (char *prog, char **arg_list)
{
    pid_t child;

    /* check if fork fail that first child is not created */
    if ((child = fork ())< 0) {
        fprintf (stderr, "fork error");
    } else if (child == 0) {        /* run into first child */
        fprintf (stdout, "fork to execute : [%s]\n", arg_list);
        /* replaces the current process image with a new process image */
        execvp(prog, arg_list);

        /* if execvp() is return, mean that on error */
        fprintf(stderr, "execvp error");
        exit(0);
    }

    /* no block to wait for first child chang state */
    /* must be use signal (SIGCHLD, xxx) to fetch child change state */
    waitpid (-1, NULL, WNOHANG); // 父程序呼叫waitpid(),不阻塞等待子程序的返回狀態, 待引發SIGCHLD

    return child;
}
Sol2: easy sample
pid=fork();
if (pid==0) {
    exit(0);    
} else {
    waitpid(pid);  // <--- this call reaps zombie
    // some parent code ...
}
Sol3
int main (int argc, char *argv[])
{
    char *prog_list = {"prog1","prog2", "prog3"};

    for (int i=0; i<3; i++) {
        char *arg_list = {prog_list[i], NULL};
        forkExecProc (prog_list[i], arg_list);
    }

    while (true) {
        sleep (2);
    }
    
    return 0;
}

int TaskHandler::forkExecProc (char *prog, char **arg_list)
{
    pid_t child;

    /* check if fork fail that first child is not created */
    if ((child = fork ())< 0) { // 產生子程序
        fprintf (stderr, "fork error");
    } else if (child == 0) {        /* run into first child */

        /* check if fork fail that second child is not created */
        if ((child = fork ())< 0) { // 產生孫程序
            fprintf (stderr, "fork error");
        }
        else if (child > 0) {       /* run into parent of second child whick is first child */
            /* terminate the first child, in order that second child's parent becomes init */
            exit(0); // 子程序自行終結, 此時孫程序被init接管為它的父程序
        }
        else {                      /* run into second child */
            // 孫程序繼續執行下列步驟
            fprintf (stdout, "fork to execute : [%s]\n", arg_list);
            /* replaces the current process image with a new process image */
            execvp(prog, arg_list);

            /* if execvp() is return, mean that on error */
            fprintf(stderr, "execvp error");
            exit(0);
        }
    }

    /* wait for first child chang status */
    waitpid (child, NULL, 0); // 父程序呼叫waitpid(), 等待子程序終結,並捕獲返回狀態

    return child;
}
Sol3: easy sample
pid=fork();
if (pid==0) {
    // child
    if (fork()==0) {
        // grandchild
        sleep(1); // sleep a bit to let child die first
        exit(0);  // grandchild exits, no zombie (adopted by init)
    }
    exit(0);      // child dies first
} else {
     waitpid(pid);  // still need to wait on child to avoid it zombified
     // some parent code ...
}

wait() and waitpid()

  • Need to include <sys/wait.h>
  • pid_t wait (int *status);

    • wait() functino會先暫停目前所在的process,並且等待他所擁有的任何一個child process離開。該child process離開時的回傳值(不論是exit所傳的或是return所傳的回傳值)都會被儲存在status這個變數裡面。如果執行此function時,child process已經結束,wait() function會立即返回。
    • Return Value:
      • -1:發生錯誤,錯誤訊息會被設定到errno裡面
      • pid_t:若執行成功,則會回傳所等到的child process的process ID。
  • pid_t waitpid (pid_t pid, int *status, int options);

    • waitpid() function會先暫停目前所在的process,並且等待pid所指定的child process離開。
      • pid = -1:waitpid()會等待任何一個child process。其特性會變成跟wait()一樣。
      • pid = 0:waitpid()會等待任何一個group process ID跟parent process相同的child process離開。
      • pid > 0: waitpid()會等待與process ID與pid相同的child process離開。
      • pid < -1:waitpid()會等待任何一個group process ID跟pid的絕對值相同的child process離開。
    • Reference with options
    • options

Android API

Define ANDORID_API to let other class can see the method

/**
 * exports marked symbols
 *
 * if used on a C++ class declaration, this macro must be inserted
 * after the "class" keyword. For instance:
 *
 * template <typename TYPE>
 * class ANDROID_API Singleton { }
 */

#define ANDROID_API __attribute__((visibility("default")))
Example: frameworks/base/libs/hwui/ProgramCache.h
ProgramCache();
~ProgramCache();
// === After revise ===
ANDROID_API ProgramCache();
ANDROID_API ~ProgramCache();
xxx.cpp
uirenderer::ProgramCache cache;
  • If not use Android_API, when other file access will has link error as following
    • xxx.cpp: undefined reference to `android::uirenderer::ProgramCache::ProgramCache()'

Android draw animation

Scale animation

  • Draw rectangle from small size to big size in continuous frames.
    • Handler + Draw + Invalidate
      • Adopted.
    • Handler + Revise layout
      • Will do full frame updtae due to request layout (and the layout is full-screen)
    • Scale animation

Background

  • How to draw rectangle
Paint myPaint = new Paint();
myPaint.setColor(Color.rgb(0, 0, 0));
myPaint.setStrokeWidth(10);
c.drawRect(100, 100, 200, 200, myPaint);
  • How to use handler

    public class MainActivity extends Activity {
    Handler mHandler;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        mHandler = new Handler();
        mHandler.post(runnable);
        // Post but with delay time
        //mHandler.postDelayed(runnable, 1000); // delayed 1 second
    }
    
    final Runnable runnable = new Runnable() {
        public void run() {
            // TODO Auto-generated method stub
            // things need to do in background
        }
    };
    }
    

Handler + Draw + Invalidate

DrawView.java
public class DrawView extends View {
    Paint paint = new Paint();
    int mWidth = 0;
    int mHeight = 0;
    int mColor = Color.WHITE;

    public DrawView(Context context) {
        super(context);
    }

    public void setSizeColor(int width, int height, int color) {
        mWidth = width;
        mHeight = height;
        mColor = color;
    }

    @Override
    public void onDraw(Canvas canvas) {
        paint.setStrokeWidth(0);
        paint.setColor(mColor);
        canvas.drawRect(0, 0, mWidth, mHeight, paint );
    }
}
MainActivity.java
public class MainActivity extends Activity {

    LinearLayout layoutFull;
    Handler mHandler;
    DrawView drawView;    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        layoutFull = (LinearLayout) findViewById(R.id.layoutFull);        

        drawView = new DrawView(this);
        drawView.setSizeColor(10, 10, Color.CYAN);
        layoutFull.addView(drawView);
        mHandler = new Handler();
        int duration = 1000;
        int interval = 100;
        int width = 10;
        int height = 10;

        int colorArray[] = {Color.MAGENTA, Color.BLUE, Color.GREEN, Color.YELLOW, Color.GRAY,
                           Color.CYAN, Color.GRAY, Color.RED};
        // Up-scale
        for (int x=0; x<25; x++) {
            width += 25;
            height += 25;
            Runnable tmp = createRunnableRectObj(width, height, colorArray[x%8]);
            duration += interval;
            mHandler.postDelayed(tmp, duration);
        }
        
        // Down-scale
        for (int x=0; x<25; x++) {
            width -= 25;
            height -= 25;
            Runnable tmp = createRunnableRectObj(width, height, colorArray[x%8]);
            duration += interval;
            mHandler.postDelayed(tmp, duration);
        }


    }
    
 Runnable createRunnableRectObj(final int width, final int height, final int color) {
        final Runnable runnable = new Runnable() {
            public void run() {
                drawView.setSizeColor(width, height, color);
                drawView.invalidate();
            }
        };
        return runnable;
    }    

Handler + Revise layout

public class MainActivity extends Activity {
    LinearLayout layout1;
 layout1 = new LinearLayout(this);
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        LinearLayout.LayoutParams layoutParams = 
                new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 50);
        layout1.setBackgroundColor(Color.BLUE);
        
        mHandler = new Handler();
        int duration = 1000;
        int interval = 500;
        mHandler.postDelayed(runnable, duration);
        duration += interval;
        mHandler.postDelayed(runnable2, duration);
}

final Runnable runnable = new Runnable() {
        public void run() {
            // TODO Auto-generated method stub
            LinearLayout.LayoutParams layoutParams = 
                    new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100);
            layout1.setLayoutParams(layoutParams);
            layout1.requestLayout();
        }
    };
    
final Runnable runnable2 = new Runnable() {
        public void run() {
            // TODO Auto-generated method stub
            LinearLayout.LayoutParams layoutParams = 
                    new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 200);
            layout1.setLayoutParams(layoutParams);
            layout1.setBackgroundColor(Color.GREEN);
            layout1.requestLayout();
        }
    };    

Scale animation

SurfaceFlinger

Relationship between Fd, Fence, Buffer

process和process之間,用binder,會用到fd,
buffer queue 的 deqeue 和 acquire,就是用 binder,所以它會用到fd,
然後buffer自己也會化成一個fd來傳, 所以光是dequeue,就可能要兩個fd,

fence本身也會化成一個fd,
buffer通常會帶兩個fence,所以多耗用兩個fd,
所以光一塊buffer身上就用了3個fd了,
上面這些釐清了fd是從哪邊來的...

btw, fence的話其實是確保buffer的讀寫.

然後會process在用binder傳遞資料時,
會去檢查兩邊process有沒有足夠的fd可以用,
如果A process fd用光了,binder就會傳一個error過去給 B process, B process自己決定要如何處理這error.
eg. A process是 SF & B process是system_server, SF fd用光了,所以 B 收到 error, 對這error的處理就是直接abort

所以一個dequebuffer 傳送端需要3個fd for buffer, 2個fd for deque and binder send,
A把fd傳過去 B就要用fd接, 所以B也會用到, B就需要共5個fd.

HWUI

Enable or disable HWUI in PMS

/frameworks/base/core/java/android/content/pm/PackageParser.java
Line 1973 : control whether to enable HWUI
boolean hardwareAccelerated = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_hardwareAccelerated,
owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH);

http:///4.4.2_r1/xref/frameworks/base/core/java/android/content/pm/PackageParser.java

Configuration for HWUI

AOSP -> alps\device\mediatek[proj]\system.prop (USE_OPENGL_RENDERER)
To turn off HWUI support, USE_OPENGL_RENDERER = false

ProjectConfig will inheritance, flavor project will inherit base project's setting.
If the load is builded, inheritence result will be put at "alps\out\target\product\$(proj)\obj\CUSTGEN\config\ProjectConfig.mk"

How to check whether HWUI is enabled or not

  • adb shell dumpsys SurfaceFlinger
    • mConnectedAPI=1 means the window is rendered by OpenGL, ie. Hwui
    • mConnectedAPI=2 means the window is rendered by CPU, ie. skia

Force to enable Hwui

adb shell setprop persist.sys.ui.hw true
adb shell stop
adb shell start

Build HwAccelerationTest

mmm -B frameworks/base/tests/HwAccelerationTest 2>&1 | tee hwui_test.log

Output path:
alps\out\target\product[product]\data\app\HwAccelerationTest.apk

Android coding technique

Add callstack to native code


#include <utils/CallStack.h>
CallStack stack(LOG_TAG)

Android AMS

When application goes from foreground to back ground, it will trigger "updateOomAdjLocked" in ActivityManagerService.

  • ActivityManagerService.java (frameworks\base\services\core\java\com\android\server\am)
final void updateOomAdjLocked() {
     // skip
    app.thread.scheduleTrimMemory(level)
     // skip
}
  • ActivityThread.java (frameworks\base\core\java\android\app)
public void scheduleTrimMemory(int level) {
    sendMessage(H.TRIM_MEMORY, null, level);
}
case TRIM_MEMORY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "trimMemory");
                    handleTrimMemory(msg.arg1);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
`
final void handleTrimMemory(int level) {
        if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Trimming memory to level: " + level);

        ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);

        final int N = callbacks.size();
        for (int i = 0; i < N; i++) {
            callbacks.get(i).onTrimMemory(level);
        }

        WindowManagerGlobal.getInstance().trimMemory(level);
    }

Android process

Get native parent process id

Native
#include <unistd.h>
 
ALOGD("ppid=%d", getppid());
Java
int ppid = android.os.Process.myPpid();

Check process is 32 bit or 64 bit

adb shell cat /proc/[pid]/maps
32bit (app_process32 & address is 32bit)
ab171000-ab174000 r-xp 00000000 b3:0f 253 /system/bin/app_process32
64bit (app_process64 & address is 64bit)
558a54f000-558a553000 r-xp 00000000 b3:0f 254 /system/bin/app_process64

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多