use java creat a class called dog_Tag : android ? Shooting's Blog

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值