Exploration and Solution of the Reasons for Frequent Exit of MIUI Background Programs

Links to the original text: http://www.cnblogs.com/sadgoblin/p/3696471.html

 

First of all, the mobile phone with a meter under the Tucao is of good quality. At the time of last year, it used to be used in addition to the camera, which has been repaired free of charge.

The disadvantage is that the MIUI v5 on board can not fully release the potential of APQ8064T 2G memory. The newly opened programs are often "terminated" soon after switching back to the background. If you want to switch back, you can only wait for the system to reload the application.

I initially suspected that the system occupied too much resources, but every time I looked at memory, there were still 700 MB available (where the MIUI task manager and Android's own application manager gave inconsistent results, MIUI generally gave a smaller value, but also 700 MB). And the Moto Atrix, which has only 1G of old slag memory, will not automatically shut down the program you opened before, even if you put it in overnight, which negates the conjecture of insufficient memory.

Then I turned to the suspicion that MIUI's process management automatically turned off idle daemons. So I locked all the processes in the task manager (i.e. dragging down the App icon after pressing HOME for a long time), and then automatically shut down, although it would be better, it could not be completely eradicated. Even programs that occupy very little memory, such as settings, will still be kill ed after a period of idle time.

Since we still can't find the behind-the-scenes killer, we have to check Logcat to see if the killer left any traces.

After a search, Logcat gave me these:

04-28 14:47:37.844: I/ActivityManager(597): No longer want com.cleanmaster.miui_module (pid 918): hidden #25
04-28 14:47:37.925: I/ActivityManager(597): No longer want com.miui.guardprovider (pid 1342): hidden #25
04-28 14:47:43.020: W/ExtraActivityManagerService(597): No longer want com.miui.networkassistant (pid 1823) for more free memory
04-28 14:47:43.020: I/ActivityManager(597): No longer want com.android.fileexplorer (pid 1805): hidden #25

You can see that two guys, Activity Manager Service and ExtraActivity Manager Service, are constantly killing my background program.

Google's attempt to find an existing solution has resulted in only a few related articles failing to explain the root cause of the problem, just a general discussion such as "why the program I developed was shut down in the background" and so on.

After a fruitless search, I decided to look directly at the Android source code, and Google searched "No longer want" site:android.googlesource.com./

I found this. https://android.googlesource.com/platform/frameworks/base/+/ee7621c0f5de6eca2cfb9fb2b6117fb61e13cc41%5E!/

In the commit description, empty process and hidden process are treated separately; previously they were treated in a unified way, sharing a common upper limit (mProcess Limit). Now empty and hidden have separate upper limits, but it is not known whether the commit version is consistent with the Android version of MIUI. Regardless of this, look directly at the Android version of 2s MIUI v5, which is 4.1.1:

https://android.googlesource.com/platform/frameworks/base/+/android-4.1.1_r6.1/services/java/com/android/server/am/ActivityManagerService.java

This is one of the source codes of am (Activity Manager), Activity Manager Service. java. Search for "No longer want" and get:

if (numHidden > mProcessLimit) {
    Slog.i(TAG, "No longer want " + app.processName
            + " (pid " + app.pid + "): hidden #" + numHidden);
    EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
            app.processName, app.setAdj, "too many background");
    app.killedBackground = true;
    Process.killProcessQuiet(app.pid);
}

It seems that the system shuts down the redundant hidden process due to the excessive number of background processes. And the mechanism of empty process in commit mentioned above does not exist here, hidden process and empty process are processed as background process together, and the sum of them can not exceed a threshold. This threshold mProcess Limit is initialized as:

int mProcessLimit = ProcessList.MAX_HIDDEN_APPS;

In this way, we can basically determine the solution to the problem: simply increase the mProcess Limit of the Activity Manager Service instance, or modify the ProcessList.MAX_HIDDEN_APPS. In ProcessList.java, MAX_HIDDEN_APPS is static final, so it cannot be modified unless it compiles the ROM itself. Interestingly, the value in AOSP is 15, and MIUI seems to have increased it to 24 to accommodate its more bulky system, but it still seems insufficient.

Fortunately, the Activity Service Manager provides such a method:

public void setProcessLimit(int max)

It can be called directly to modify mProcess Limit. So how do I call a method in a system class? Activity Manager Service does not exist in android.jar of Android SDK, so it is impossible to call it directly in third party App. Maybe you can compile an android.jar with hidden classes to make the call, but this will cause many compatibility problems with the change of system version, so it is negated.

So we have to sacrifice our big killer: the Xposed framework! Xposed Framework is a tool for modifying system resources and code injection.

http://repo.xposed.info/module/de.robv.android.xposed.installer

The process is outlined as follows: Through the Xposed framework, after the startRunning() method of ActivityManagerService, the code is injected and setProcessLimit(40) is executed.

 

The code is as follows:

package com.barius.morebackground;

import java.lang.reflect.Method;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

public class XposedModule implements IXposedHookLoadPackage {

	private static boolean LOG_ON = true;
	private static void LOG(String content) {
		if (LOG_ON) {
			XposedBridge.log(content);
		}
	}

	private static final String[] TARGET_PACKAGE_NAMES = {
		"android", 
		"com.barius.morebackground"
	};

	private static final int NEW_PACKAGE_LIMIT = 40;

	public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
		// only want certain target packages
		boolean targetFound = false;
		int targetIdx = -1;
		for (int i = 0; i < TARGET_PACKAGE_NAMES.length; i++) {
			if (lpparam.packageName.equals(TARGET_PACKAGE_NAMES[i])) {
				targetFound = true;
				targetIdx = i;
			}
		}
		if (!targetFound) {
			return;
		}

		LOG("=== MoreBackground Loaded app: " + lpparam.packageName);

		switch (targetIdx) {
		case 0:
			hackActivityManagerService(lpparam);
			break;
		case 1:
			//changeProcessLimit(lpparam);
			//checkProcessLimit(lpparam);
			break;
		}

		LOG("=== Job done.");
	}

	private boolean hackActivityManagerService(final LoadPackageParam lpparam) {
		return changeProcessLimit(lpparam);
	}
	
	
	// !!! MASSIVE DESTRUCTION !!! USE WITH CAUTION !!!
	private void hookEveryMethod(LoadPackageParam lpparam) {
		String targetClassName = "com.android.server.am.ActivityManagerService";
		
		final Class<?> clazz = XposedHelpers.findClass(targetClassName, lpparam.classLoader);
		Method[] methods = clazz.getMethods();
		for(int i = 0; i < methods.length; i++) {
			Method m = methods[i];
			XposedBridge.hookMethod(m, new XC_MethodHook() {
				@Override
				protected void afterHookedMethod(MethodHookParam param) throws Throwable {
					LOG("--- Called: " + param.method.getName());
				}
			});
		}
	}
	
	private boolean changeProcessLimit(LoadPackageParam lpparam) {
		final String targetClassName = "com.android.server.am.ActivityManagerService";
		final String targetMethodName = "startRunning";

		final Class<?> clazz = XposedHelpers.findClass(targetClassName, lpparam.classLoader);
		final Method startRunning = XposedHelpers.findMethodExact(clazz, targetMethodName,
				String.class, String.class, String.class, String.class);
		XposedBridge.hookMethod(startRunning, new XC_MethodHook() {
			@Override
			protected void afterHookedMethod(MethodHookParam param) throws Throwable {
				LOG("After " + targetMethodName + "()");

				Object _this = param.thisObject;
				LOG(_this.getClass().getName());


				LOG("--- Using XposedHelper to invoke method");
				XposedHelpers.callMethod(_this, "setProcessLimit", NEW_PACKAGE_LIMIT);
				LOG("--- ... done");
			}
		});
		
		return true;
	}

	private void checkProcessLimit(LoadPackageParam lpparam) {

	}
}

 

ExtraActivity Manager Service still kills the process, but it seems to be MIUI process manager, and it won't kill the process unlocked, let alone kill it. Logcat shows:

04-28 15:17:53.242: I/ActivityManager(597): No longer want com.miui.notes (pid 1786): hidden #41

# 41 indicates that the modification was successful and the background process limit was changed to 40. MIUI reports that the remaining memory floats around 500 MB. It's not clear how much impact this will have on the power consumption of the system. Let's try it first.

 

OK, this is down-to-earth, and there is no need to brush unstable third-party systems for process problems.

posted on 2014-04-28 15:31 Sad Goblin Read (...) Comments (...) edit Collection

Reprinted at: https://www.cnblogs.com/sadgoblin/p/3696471.html

Keywords: Android MIUI Java Google

Added by FMB on Fri, 09 Aug 2019 07:16:23 +0300