Let's Talk About the Study Methods of Framework

Let’s Talk About the Study Methods of Framework #

Hello everyone, I am Lu Xiaoming and I am currently working as an Android system development engineer at an internet mobile company. I am delighted to share my 9 years of experience in the mobile industry and my methods for learning Android in the Geek Time Android Development Master Course column.

Today I want to share with you the methods for learning and debugging Framework.

First, Android is an open-source software stack based on Linux, created for a wide range of devices and models. The following diagram shows the main components of the Android platform.

From the diagram, you can see the following major components:

  • Linux Kernel
  • Android Runtime
  • Native C/C++ Libraries
  • Java API Framework (which I will refer to as the Framework layer later)
  • System Applications

What we see in various app markets are mostly third-party applications, which are installed in the data partition, can be uninstalled, and have certain restrictions on permissions. For example, they cannot directly set the date and time and need to call the system application settings to perform the operation.

The four major components we use in application development are implemented in the Framework layer. The application configures them in AndroidManifest.xml based on agreed-upon rules, and then overrides the corresponding base classes. During the startup process, the system parses the AndroidManifest.xml, stores the application’s information, and then starts the corresponding application based on user actions or system broadcasts.

So, let’s first take a look at what constitutes the Framework layer.

The Framework layer is the internal implementation of the system methods called during application development. For example, the TextView and Button controls we use are implemented here. Here are a few more examples: we use the getRunningAppProcesses method of ActivityManager to view the list of currently running processes, and we use the notify method of NotificationManager to send a system notification.

Let’s take a look at the code paths related to the Framework.

How can we quickly learn and organize the knowledge system of the Framework? There are several common learning methods:

  • Reading books (convenient for organizing knowledge systems, but can only provide direction for solving problems).
  • Directly reading the source code (inefficient and challenging).
  • Logging and printing stack traces (improves efficiency, but requires repeatedly compiling and adding log and stack trace code).
  • Direct debugging, convenient and real-time (requires a debugging version).

First, you can learn through purchasing relevant books, which mainly cover the knowledge system of the Linux operating system, such as processes, threads, inter-process communication, virtual memory, and establish your own software architecture. On this basis, learn about the Android startup process, the creation of the SystemServer service process, the creation process of various service threads (AMS/PMS, etc.), and the startup process of the Launcher. Once you are familiar with these, you also need to understand the main working principles of the ART virtual machine and the init and Zygote processes. Later, as you work and gain practical experience, you will find that the Framework mainly revolves around application startup, display, broadcast messages, key events, and service addition. The implementation of these codes mainly uses two languages: Java and C++.

After studying for a while through books or online resources, you will find that many problems do not have ready-made solutions. At this time, you need to dig into and learn from the source code. But besides reading official documentation, don’t forget that debugging the Framework is also a sharp tool that allows you to easily locate and analyze the source code.

Next, let’s take a look at the debugging of the Java part of the Framework. For the C++ part, you need to use GDB for debugging, and you can practice it on your own. The debugging process can refer to the series of articles “In-depth Android Source Code (Part 1)” published on WeChat.

Here, we will use Android Studio for debugging. Before debugging, we need to master some knowledge. The debugging of Java code mainly depends on two factors: the process you want to debug and the package path corresponding to the class you want to debug. At the same time, you need to ensure that the mobile environment you run and the code you want to debug match. As long as these two pieces of information match, you can still debug even if the code does not compile.

The system services we want to debug are in the SystemServer process, and we can verify this using the following commands (here I am demonstrating in an environment with Android image corresponding to the same version installed on Genymotion).

ps -A |grep system_server  Check the PID of the system service process
cat /proc/pid/maps |grep services Use cat to see the memory mapping of this process and check if "services" is mapped into memory.

Here we see the information: /system/framework/oat/x86/services.odex.

odex is a further optimization by the Android system for DEX, which is aimed at improving execution efficiency. From this information, we can confirm that our services.jar is indeed located here, that is, the code associated with our system services. We can trace this code by debugging the SystemServer process.

Now let’s establish the debugging environment.

  • Open Genymotion and select the downloaded Android 9.0 image file, then start the emulator.

  • Find the corresponding ActivityManagerService.java file. I downloaded the code corresponding to Android 9.0 from http://androidxref.com/.

  • Open Android Studio, go to File -> New -> New Project and click Next until the project is created.

  • Create a new package name, find it in the ActivityManagerService.java file, which is com.android.server.am here, and put the ActivityManagerService.java file in it.

  • Set a breakpoint above the startActivity method in ActivityManagerService.java, and then go to Run -> Attach debugger to Android process, check Show all processes, select the SystemServer process, and confirm.

Now when we click on an icon on the desktop of the Genymotion emulator to start a new screen.

You will find that our breakpoints have taken effect.

You can see the stack trace information and some variable values that have been broken down, and then we can debug step by step to trace the startup process.

For learning system service threads, debugging can quickly grasp the process. Combined with reading the source code, you can quickly learn and master the entire logic of the system framework, thus saving learning time and cost.

Above, we have verified the debugging of the system service AMS service code. The debugging methods for other services are the same. You can use the following command to view specific thread information.

ps -T 353
Here, 353 is the process number of SystemServer obtained by using ps -A | grep SystemServer.

In the above image, only the first line with PID = TID represents the main thread. Below are the logcat output messages we usually use for viewing.

03-10 09:33:01.804   240   240 I hostapd : type=1400 audit(0.0:1123): avc: de
03-10 09:33:37.320   353  1213 D WificondControl: Scan result ready event
03-10 09:34:00.045   404   491 D hwcomposer: hw_composer sent 6 syncs in 60s

I also boxed in a thread named ActivityManager in the above image. This is the name of the thread, and by looking at the TID (368) in this line, you know that the following log was output by this thread.

03-10 08:47:33.574   353   368 I ActivityManager: Force stopping com.android.providers

After learning the above knowledge, I believe you have learned how to debug system services. Through debugging analysis, we can learn the system service framework in depth and make it easier to understand the vast amount of complex code.

Now let’s go back and enter ps -A in the terminal again to see the following information.

In the first column here, you can see the current user, which includes system root and u0_axx, with different users having different permissions. What we are currently concerned with is the second and third columns. The second column represents the PID (process ID), and the third column represents the PPID (parent process ID).

You can see that I have circled the same parent process here, so let’s find out who this process 323 is.

root 323 1 1089040 127540 poll_schedule_timeout f16fcbc9 S zygote

This name is often mentioned when learning the Android system because it is the hatcher of our Android world. Every creation of an upper-layer application is created by Zygote calling fork to create a child process. Child processes can quickly inherit the resources already loaded by the parent process, especially the JAR files required by the application, such as /system/framework/framework.jar, where the basic controls we need for the app, like View, TextView, ImageView, are located.

Next, let’s discuss another debugging method, which is debugging TextView (the debugging method for other controls like Button is the same). As mentioned earlier, this code is compiled into /system/framework/framework.jar. Therefore, we need to find it in Zygote using the ps command and cat /proc/pid/maps command. At the same time, it can be found by every child process created by Zygote, such as the main interface TextView of Gallery that we want to debug.

Let’s verify this by using ps -A | grep gallery3d to find the PID of the Gallery process, and cat /proc/pid/maps | grep framework.jar to see the following information:

efcd5000-efcd6000 r--s 00000000 08:06 684                                /system/framework/framework.jar

This shows that the process for the application we want to debug indeed exists in the memory mapping. Now, we need to set a breakpoint in the gallery3d process.

Next, we’ll set up the debugging environment:

  • Open Genymotion, select the Android 9.0 image that has been downloaded, start the emulator, and then start the Gallery app on the desktop.

  • Find the corresponding TextView.java code for the emulator.

  • Open Android Studio, go to File -> New -> New Project and click Next until it is completed.

  • Create a new package name, find the package name from the TextView.java file (in this case, android.widget), and put TextView.java in it.

  • Set a breakpoint above the onDraw method in TextView.java, then find the menu option “Run -> Attach debugger to Android process”, check “Show all processes”, select the com.android.gallery3d process (we know this main interface has a TextView control) and confirm.

We then click on the menu button in the upper-left corner of this interface and select any option to click on. We find that the breakpoint is effective, as shown in the following figure.

Then, we can use the debugging button on the interface (or the shortcut key) to debug the code.

Today, I explained how to debug the AMS service thread of the system services in the Framework. The debugging methods for other services like PMS and WMS are the same. I also explained how to debug a TextView control in an application. The debugging methods for other controls like Button and ImageView are the same as for TextView.

Through today’s learning, I hope to provide you with the most convenient path for learning about the system framework. In solving system problems, you can conveniently use debugging and analysis to quickly locate and fix issues.