Posts match “ android ” tag:
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 (adb shell dumpsys gfxinfo / called by AEE)
int main(int argc, char* const argv[]) {
//skip
for (size_t i=0; i
sp 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); //
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(); //
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
@Override
public void dumpGfxInfo(FileDescriptor fd, String[] args) {
dumpGraphicsInfo(fd);
WindowManagerGlobal.getInstance().dumpGfxInfo(fd, args);
}
private native void dumpGraphicsInfo(FileDescriptor fd);
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)
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
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
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]/smaps7fa8dd9000-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 meminfoApplications 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
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
Content index
Executable Template
Shared Library Template
Static Library Template
Use other shared library
Use other static library
Include path list
Use c/cpp/cxx/ld flags
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
LOCAL_MULTILIB
32: Build 32 bit library only
both: Build both 64bit and 32bit libraries
This is set by TARGET_SUPPORTS_64_BIT_APPS, TARGET_SUPPORT_32_BIT_APPS and TARGET_IS_64BIT
Example
Android.mk
LOCAL_MULTILIB:=32 # Build 32 bit library
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.")
How to write a native thread and how to use it
AbcThread.h
#include
Class AbcThread: public Thread
{
public:
AbcThread();
virtual ~AbcThread();
protected:
virtual bool threadLoop(); // Explain later for the return value
}
AbcThread.cpp
#include
#include
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
#include
#include "AbcThread.h"
int main()
{
sp 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
How to print thread create callstack
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);
January 15, 2016
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.
exec family
exec family
#include
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 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.
/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 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
June 19, 2014
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.
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)
January 22, 2016
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); //
pid=fork();
if (pid==0) {
exit(0); //
} 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); //
// 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
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離開。
January 23, 2015
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
* 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()'
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
December 2, 2016
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.
February 21, 2014
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);
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
Add callstack to native code
#include
CallStack stack(LOG_TAG)
January 24, 2015
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 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);
}
January 7, 2015
Get native parent process id
Native
#include
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