Practice Sample to Run a Peers' Practice Notebook 2nd Issue

Practice Sample to Run A Peers’ Practice Notebook 2nd Issue #

Hello, I am Wei Lu.

Continuing from the previous exercise notes, today we will cover the content of lessons 6-8, 12, 17, and 19 (mainly focusing on the lessons with sample exercises), which are much easier compared to lessons 1-5.

Chapter06

This project demonstrates the use of PLT Hook technology to obtain Atrace logs and provides insight into some underlying mechanisms of systrace.

There are no problems and the project can be run directly. After running the project, click on “开启Atrace日志” (Enable Atrace logs), and then you can see the captured logs in the Logcat log, as follows:

11:40:07.031 8537-8552/com.dodola.atrace I/HOOOOOOOOK: ========= install systrace hoook =========
11:40:07.034 8537-8537/com.dodola.atrace I/HOOOOOOOOK: ========= B|8537|Record View#draw()
11:40:07.034 8537-8552/com.dodola.atrace I/HOOOOOOOOK: ========= B|8537|DrawFrame
11:40:07.035 8537-8552/com.dodola.atrace I/HOOOOOOOOK: ========= B|8537|syncFrameState
    ========= B|8537|prepareTree
    ========= E
    ========= E
    ========= B|8537|eglBeginFrame
    ========= E
    ========= B|8537|computeOrdering
    ========= E
    ========= B|8537|flush drawing commands
    ========= E
11:40:07.036 8537-8552/com.dodola.atrace I/HOOOOOOOOK: ========= B|8537|eglSwapBuffersWithDamageKHR
    ========= B|8537|setSurfaceDamage
    ========= E
11:40:07.042 8537-8552/com.dodola.atrace I/HOOOOOOOOK: ========= B|8537|queueBuffer
    ========= E
11:40:07.043 8537-8552/com.dodola.atrace I/HOOOOOOOOK: ========= B|8537|dequeueBuffer
    ========= E
    ========= E
    ========= E

By comparing the B| events and E| events, we can calculate the time used by each event in the application. So from the above log, the draw() method of View took 9ms.

The implementation method here uses the PLT Hook of Profilo to hook the write and __write_chk methods of libc.so. libc is the basic C library function, and to understand why these methods are hooked, we need to supplement C and Linux-related knowledge.

Similarly, Chapter06-plus demonstrates how to use PLT Hook technology to obtain the stack trace of thread creation. The README provides detailed implementation steps, so I won’t go into details here.

Chapter07

This sample is about learning how to add Trace Tags to code. You can apply this code to your own project and use systrace to view the results. This is called systrace + function instrumentation.

Operating steps:

  • Open the Chapter07 project in Android Studio.
  • Run the Gradle Task :systrace-gradle-plugin:buildAndPublishToLocalMaven to compile the plugin.
  • Open the systrace-sample-android project in Android Studio.
  • Build and run the app (the instrumented class files can be found in the Chapter07/systrace-sample-android/app/build/systrace_output/classes directory).

Let’s compare the effects before and after instrumentation. Before instrumentation:

After instrumentation:

You can see that TraceTags are inserted before and after method execution. This way, the code between the beginSection and endSection methods will be traced.

public class TraceTag {

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    public static void i(String name) {
        Trace.beginSection(name);
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    public static void o() {
        Trace.endSection();
    }

In fact, there is a similar class called TraceCompat in the Support-Compat library, which can be directly used in the project.

Then run the project and open systrace:

python $ANDROID_HOME/platform-tools/systrace/systrace.py gfx view wm am pm ss dalvik app sched -b 90960 -a com.sample.systrace  -o test.log.html

Finally, open the generated test.log.html file to view the systrace record:

Of course, this step can also be done using the Monitor in the SDK, with the same effect.

With systrace + function instrumentation, we can easily observe the execution time of each method, and then optimize the time-consuming methods, especially for application startup optimization.

Chapter08

This project demonstrates the impact of disabling class verification on performance.

During the class loading process, there is a step of verifying the class, which involves verifying each instruction of the class’s methods. This is a time-consuming operation. This example removes the verification step by using Hook. The effect of this example is more pronounced under Dalvik, and less so under ART.

Remove the verification code (you can refer to Alibaba’s Atlas):

AndroidRuntime runtime = AndroidRuntime.getInstance();
runtime.init(this.getApplicationContext(), true);
runtime.setVerificationEnabled(false);

I won’t show the specific running effect here; you can simply run it to experience it.

Chapter12

By overriding the getSharedPreferences method of Application to replace the implementation of the system’s SharedPreferences, the main optimization is in modifying the implementation of Apply, which merges multiple Apply methods in memory instead of committing them multiple times.

Modify the Apply part of SharedPreferencesImpl as follows:

public void apply() {
    // Call commitToMemory() first
    final MemoryCommitResult mcr = commitToMemory();

    boolean hasDiskWritesInFlight = false;
    synchronized (SharedPreferencesImpl.this) {
        // mDiskWritesInFlight > 0 means commitToMemory() has been called before
        hasDiskWritesInFlight = mDiskWritesInFlight > 0;
    }
    // The original code does not have this judgment and commits directly.
    if (!hasDiskWritesInFlight) {
        final Runnable awaitCommit = new Runnable() {
            public void run() {
                try {
                    mcr.writtenToDiskLatch.await();
                } catch (InterruptedException ignored) {
                }
            }
        };

        QueuedWork.add(awaitCommit);

        Runnable postWriteRunnable = new Runnable() {
            public void run() {
                awaitCommit.run();
QueuedWork.remove(awaitCommit);
};
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
}

// Okay to notify the listeners before it's hit disk
// because the listeners should always get the same
// SharedPreferences instance back, which has the
// changes reflected in memory.
notifyListeners(mcr);

Chapter14

This is a comprehensive analysis of SQLite. If you are interested, you can download and take a look.

Chapter17

This project shows how to use PLT Hook technology to obtain network request related information.

By PLT Hook, proxy the several important functions of Socket:

/**

  • Hook all so files in memory directly, but need to exclude the libc defined by the socket-related methods itself (otherwise, there will be a loop)
  • plt hook */ void hookLoadedLibs() { ALOG(“hook_plt_method”); hook_plt_method_all_lib(“libc.so”, “send”, (hook_func) &socket_send_hook); hook_plt_method_all_lib(“libc.so”, “recv”, (hook_func) &socket_recv_hook); hook_plt_method_all_lib(“libc.so”, “sendto”, (hook_func) &socket_sendto_hook); hook_plt_method_all_lib(“libc.so”, “recvfrom”, (hook_func) &socket_recvfrom_hook); hook_plt_method_all_lib(“libc.so”, “connect”, (hook_func) &socket_connect_hook); }

int hook_plt_method_all_lib(const char* exclueLibname, const char* name, hook_func hook) { if (refresh_shared_libs()) { // Could not properly refresh the cache of shared library data return -1; }

int failures = 0;

for (auto const& lib : allSharedLibs()) {
    if (strcmp(lib.first.c_str(), exclueLibname) != 0) {
        failures += hook_plt_method(lib.first.c_str(), name, hook);
    }
}

return failures;

}

Run the project and access the domain name of Baidu https://www.baidu.com, the output is as follows:

17:08:28.347 12145-12163/com.dodola.socket E/HOOOOOOOOK: socket_connect_hook sa_family: 10 17:08:28.349 12145-12163/com.dodola.socket E/HOOOOOOOOK: stack:com.dodola.socket.SocketHook.getStack(SocketHook.java:13) java.net.PlainSocketImpl.socketConnect(Native Method) java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:334) java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:196) java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:178) java.net.SocksSocketImpl.connect(SocksSocketImpl.java:356) java.net.Socket.connect(Socket.java:586) com.android.okhttp.internal.Platform.connectSocket(Platform.java:113) com.android.okhttp.Connection.connectSocket(Connection.java:196) com.android.okhttp.Connection.connect(Connection.java:172) com.android.okhttp.Connection.connectAndSetOwner(Connection.java:367) com.android.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:130) com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:329) com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:246) com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnection AF_INET6 ipv6 IP===>183.232.231.173:443 socket_connect_hook sa_family: 1 Ignore local socket connect 02-07 17:08:28.637 12145-12163/com.dodola.socket E/HOOOOOOOOK: respond: 百度一下,你就知道

We can see that we have obtained the relevant information of the network request.

Finally, we can use the hook of the Connect function to achieve many requirements, such as:

  • Disabling application network access
  • Filtering advertising IP
  • Disabling positioning function

Chapter19

Use Java Hook to implement power consumption monitoring of Alarm, WakeLock, and GPS.

Implementation principle

According to the hints provided by the instructor, dynamically proxy the mService of the corresponding PowerManager, AlarmManager, and LocationManager. The methods to intercept are in PowerManagerService, AlarmManagerService, and LocationManagerService.

Core implementation code:

Object oldObj = mHostContext.getSystemService(Context.XXX_SERVICE);
Class<?> clazz = oldObj.getClass();
Field field = clazz.getDeclaredField("mService");
field.setAccessible(true);

final Object mService = field.get(oldObj);
setProxyObj(mService);

Object newObj = Proxy.newProxyInstance(this.getClass().getClassLoader(), mService.getClass().getInterfaces(), this);
field.set(oldObj, newObj)

A few calling methods are written to trigger, and the stack information is output by judging the corresponding method name.

The output stack information is as follows:

Of course, the 3.2 version of Studio also has a powerful power consumption analyzer, which can also detect this information, as shown in the following figure (the version of Studio I used is 3.3):

Shortcomings in implementation:

  • Compatibility may not be particularly perfect (looking forward to the instructor’s standard answer).
  • Did not follow the rules of power consumption monitoring to do some business processing.

Experience:

  • The implementation itself is not complicated. It just takes some time to find the hook point. I spent some time reviewing the source code of the corresponding Services and have a deeper understanding of their workflow.
  • I rarely use dynamic proxy in ordinary cases. This time I made up for it and had a great time using it.

This homework took me a day. Some classmates provided PRs for previous assignments, so it was relatively easy. But this time there was no reference, and I took a detour, but the harvest was also huge. I won’t go into details. If you’re interested, you can refer to my implementation. The complete code can be found on GitHub for reference only.

References