package biz.bokhorst.xprivacy;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
import android.annotation.SuppressLint;
import android.os.Build;
import android.util.Log;
@SuppressLint("InlinedApi")
public class XActivityManagerService extends XHook {
private Methods mMethod;
private static Semaphore mOndemandSemaphore;
private static boolean mFinishedBooting = false;
private static boolean mLockScreen = false;
private static boolean mSleeping = false;
private static boolean mShutdown = false;
private XActivityManagerService(Methods method) {
super(null, method.name(), null);
mMethod = method;
}
@Override
public boolean isVisible() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
if (mMethod == Methods.goingToSleep || mMethod == Methods.wakingUp)
return false;
return (mMethod != Methods.appNotResponding && mMethod != Methods.finishBooting && mMethod != Methods.updateSleepIfNeededLocked);
}
public String getClassName() {
return "com.android.server.am.ActivityManagerService";
}
// @formatter:off
// 4.2+ public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason)
// 4.3+ public boolean inputDispatchingTimedOut(final ProcessRecord proc, final ActivityRecord activity, final ActivityRecord parent, final boolean aboveSystem, String reason)
// 4.0.3+ final void appNotResponding(ProcessRecord app, ActivityRecord activity, ActivityRecord parent, boolean aboveSystem, final String annotation)
// 4.0.3+ public void systemReady(final Runnable goingCallback)
// 4.0.3+ final void finishBooting()
// 4.1+ public void setLockScreenShown(boolean shown)
// 4.0.3-5.0.x public void goingToSleep()
// 4.0.3-5.0.x public void wakingUp()
// 4.0.3+ public boolean shutdown(int timeout)
// 4.2+ public final void activityResumed(IBinder token)
// public final void activityPaused(IBinder token)
// http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.2_r1/com/android/server/am/ActivityManagerService.java/
// http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.2_r1/com/android/server/am/ActivityRecord.java/
// @formatter:on
// @formatter:off
private enum Methods {
inputDispatchingTimedOut, appNotResponding,
systemReady, finishBooting, setLockScreenShown, goingToSleep, wakingUp, shutdown,
updateSleepIfNeededLocked
};
// @formatter:on
public static List<XHook> getInstances() {
List<XHook> listHook = new ArrayList<XHook>();
listHook.add(new XActivityManagerService(Methods.inputDispatchingTimedOut));
listHook.add(new XActivityManagerService(Methods.appNotResponding));
listHook.add(new XActivityManagerService(Methods.systemReady));
listHook.add(new XActivityManagerService(Methods.finishBooting));
// setLockScreenShown appears to be not present in some 4.2.2 ROMs
listHook.add(new XActivityManagerService(Methods.setLockScreenShown));
listHook.add(new XActivityManagerService(Methods.goingToSleep));
listHook.add(new XActivityManagerService(Methods.wakingUp));
listHook.add(new XActivityManagerService(Methods.shutdown));
listHook.add(new XActivityManagerService(Methods.updateSleepIfNeededLocked));
return listHook;
}
public static void setSemaphore(Semaphore semaphore) {
mOndemandSemaphore = semaphore;
}
public static boolean canOnDemand() {
return (mFinishedBooting && !mLockScreen && !mSleeping);
}
public static boolean canWriteUsageData() {
return !mShutdown;
}
@Override
protected void before(XParam param) throws Throwable {
switch (mMethod) {
case inputDispatchingTimedOut:
// Delay foreground ANRs while on demand dialog open
if (mOndemandSemaphore != null && mOndemandSemaphore.availablePermits() < 1) {
Util.log(this, Log.WARN, "Foreground ANR uid=" + getUidANR(param));
param.setResult(5 * 1000); // 5 seconds
}
break;
case appNotResponding:
// Ignore background ANRs while on demand dialog open
if (mOndemandSemaphore != null && mOndemandSemaphore.availablePermits() < 1) {
Util.log(this, Log.WARN, "Background ANR uid=" + getUidANR(param));
param.setResult(null);
}
break;
case systemReady:
// Do nothing
break;
case finishBooting:
// Do nothing
break;
case setLockScreenShown:
if (param.args.length > 0 && param.args[0] instanceof Boolean)
try {
if ((Boolean) param.args[0]) {
mLockScreen = true;
Util.log(this, Log.WARN, "Lockscreen=" + mLockScreen);
}
} catch (Throwable ex) {
Util.bug(this, ex);
}
break;
case goingToSleep:
mSleeping = true;
Util.log(this, Log.WARN, "Sleeping=" + mSleeping);
break;
case wakingUp:
// Do nothing
break;
case shutdown:
mShutdown = true;
Util.log(this, Log.WARN, "Shutdown");
break;
case updateSleepIfNeededLocked:
// Do nothing;
break;
}
}
@Override
protected void after(XParam param) throws Throwable {
switch (mMethod) {
case inputDispatchingTimedOut:
case appNotResponding:
break;
case systemReady:
// Do nothing
Util.log(this, Log.WARN, "System ready");
break;
case finishBooting:
mFinishedBooting = true;
Util.log(this, Log.WARN, "Finished booting");
break;
case setLockScreenShown:
if (param.args.length > 0 && param.args[0] instanceof Boolean)
if (!(Boolean) param.args[0]) {
mLockScreen = false;
Util.log(this, Log.WARN, "Lockscreen=" + mLockScreen);
}
break;
case goingToSleep:
// Do nothing
break;
case wakingUp:
mSleeping = false;
Util.log(this, Log.WARN, "Sleeping=" + mSleeping);
break;
case shutdown:
// Do nothing
break;
case updateSleepIfNeededLocked:
if (param.thisObject != null) {
Field methodSleeping = param.thisObject.getClass().getDeclaredField("mSleeping");
methodSleeping.setAccessible(true);
mSleeping = (Boolean) methodSleeping.get(param.thisObject);
Util.log(this, Log.WARN, "Sleeping=" + mSleeping);
}
break;
}
}
// Helper methods
private int getUidANR(XParam param) throws IllegalAccessException {
int uid = -1;
try {
Class<?> pr = Class.forName("com.android.server.am.ProcessRecord");
if (param.args.length > 0 && param.args[0] != null && param.args[0].getClass().equals(pr)) {
Field fUid = pr.getDeclaredField("uid");
fUid.setAccessible(true);
uid = (Integer) fUid.get(param.args[0]);
}
} catch (ClassNotFoundException ignored) {
} catch (NoSuchFieldException ignored) {
} catch (Throwable ex) {
Util.bug(this, ex);
}
return uid;
}
}