/* * The MIT License (MIT) * * Copyright (c) 2015 DVDAndroid * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.dvd.android.xposed.enableambientdisplay.hook; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Build; import android.os.IBinder; import android.os.PowerManager; import android.os.SystemClock; import android.widget.ImageView; import com.dvd.android.xposed.enableambientdisplay.utils.Utils; import java.lang.reflect.Method; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XSharedPreferences; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_InitPackageResources; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.ACTION_HOT_REBOOT; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.ACTION_PREFS_CHANGED; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.ACTION_SLEEP; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.DOZE_ALPHA; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.DOZE_IN; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.DOZE_OUT; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.DOZE_PICK_UP; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.DOZE_PULSE_SCHEDULE; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.DOZE_RESETS; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.DOZE_SUPP; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.DOZE_VISIBILITY; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.EXTRA_KEY; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.EXTRA_VALUE; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.logD; import static com.dvd.android.xposed.enableambientdisplay.utils.Utils.logE; import static de.robv.android.xposed.XposedBridge.hookMethod; import static de.robv.android.xposed.XposedHelpers.callMethod; import static de.robv.android.xposed.XposedHelpers.callStaticMethod; import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; import static de.robv.android.xposed.XposedHelpers.findClass; import static de.robv.android.xposed.XposedHelpers.findMethodExactIfExists; import static de.robv.android.xposed.XposedHelpers.setObjectField; public class SystemUiHook { private static final String TAG = "SystemUiHook"; private static final String CLASS_DOZE_PARAMETERS_PATH = "com.android.systemui.statusbar.phone.DozeParameters"; private static final String CLASS_KEYGUARD = "com.android.systemui.keyguard.KeyguardViewMediator"; private static final String CLASS_NOTIFICATION_VIEW = "com.android.systemui.statusbar.NotificationTemplateViewWrapper"; private static Context sContext; private static int VALUE_DOZE_IN = 1000; private static int VALUE_DOZE_OUT = 1000; private static int VALUE_DOZE_VISIBILITY = 3000; private static int VALUE_DOZE_RESETS = 1; private static int VALUE_DOZE_ALPHA = 222; private static String VALUE_DOZE_PULSE_SCHEDULE = "10s,30s,60s"; private static BroadcastReceiver sPrefsChange = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch (intent.getAction()) { case ACTION_PREFS_CHANGED: logD(TAG, "Preference change broadcast signal received"); String key = intent.getStringExtra(EXTRA_KEY); if (key == null) break; logD(TAG, "Changed preference: " + key); switch (key) { case DOZE_IN: VALUE_DOZE_IN = intent.getIntExtra(EXTRA_VALUE, 1000); break; case DOZE_OUT: VALUE_DOZE_OUT = intent.getIntExtra(EXTRA_VALUE, 1000); break; case DOZE_VISIBILITY: VALUE_DOZE_VISIBILITY = intent.getIntExtra(EXTRA_VALUE, 3000); break; case DOZE_RESETS: VALUE_DOZE_RESETS = intent.getIntExtra(EXTRA_VALUE, 1); break; case DOZE_ALPHA: VALUE_DOZE_ALPHA = intent.getIntExtra(EXTRA_VALUE, 222); case DOZE_PULSE_SCHEDULE: VALUE_DOZE_PULSE_SCHEDULE = intent.getStringExtra(EXTRA_VALUE); break; } break; case ACTION_SLEEP: logD(TAG, "Sleep broadcast signal received"); try { PowerManager powerManager = (PowerManager) sContext.getSystemService(Context.POWER_SERVICE); callMethod(powerManager, "goToSleep", SystemClock.uptimeMillis()); } catch (Throwable t) { logE(TAG, t.getMessage(), t); } break; case ACTION_HOT_REBOOT: logD(TAG, "Hot reboot broadcast signal received"); //from gravitybox try { Class<?> classSm = findClass("android.os.ServiceManager", null); Class<?> classIpm = findClass("android.os.IPowerManager.Stub", null); IBinder b = (IBinder) callStaticMethod(classSm, "getService", Context.POWER_SERVICE); Object ipm = callStaticMethod(classIpm, "asInterface", b); callMethod(ipm, "crash", "Hot reboot"); } catch (Throwable t) { try { SystemProp.set("ctl.restart", "surfaceflinger"); SystemProp.set("ctl.restart", "zygote"); } catch (Throwable t2) { logE(TAG, "Hot reboot error: ", t); logE(TAG, "Hot reboot error: ", t2); } } } } }; public static void hook(final ClassLoader classLoader, XSharedPreferences prefs) { try { Class<?> keyguardClass = XposedHelpers.findClass(CLASS_KEYGUARD, classLoader); String setupMethodName = Build.VERSION.SDK_INT >= 22 ? "setupLocked" : "setup"; findAndHookMethod(keyguardClass, setupMethodName, new XC_MethodHook() { @Override protected void afterHookedMethod(final MethodHookParam param) throws Throwable { sContext = (Context) XposedHelpers.getObjectField(param.thisObject, "mContext"); registerReceiver(sContext); } }); final Class<?> hookClass = findClass(CLASS_DOZE_PARAMETERS_PATH, classLoader); initPrefs(prefs); Method pulseInApi21 = findMethodExactIfExists(hookClass, "getPulseInDuration"); Method pulseInAOSP = findMethodExactIfExists(hookClass, "getPulseInDuration", boolean.class); Method pulseInCM = findMethodExactIfExists(hookClass, "getPulseInDuration", int.class); if (pulseInApi21 != null) hookPulseInMethod(pulseInApi21); if (pulseInAOSP != null) hookPulseInMethod(pulseInAOSP); if (pulseInCM != null) hookPulseInMethod(pulseInCM); findAndHookMethod(hookClass, "getPulseVisibleDuration", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { param.setResult(VALUE_DOZE_VISIBILITY); } }); findAndHookMethod(hookClass, "getPulseOutDuration", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { param.setResult(VALUE_DOZE_OUT); } }); findAndHookMethod(hookClass, "getPulseScheduleResets", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { param.setResult(VALUE_DOZE_RESETS); } }); final Class<?> pulseSchedule = findClass(CLASS_DOZE_PARAMETERS_PATH + "$PulseSchedule", classLoader); findAndHookMethod(hookClass, "getPulseSchedule", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); param.setResult(callMethod(pulseSchedule.newInstance(), "parse", VALUE_DOZE_PULSE_SCHEDULE)); } }); if (Build.VERSION.SDK_INT >= 22) { Class<?> notificationView = findClass(CLASS_NOTIFICATION_VIEW, classLoader); findAndHookMethod(notificationView, "fadeIconAlpha", ImageView.class, boolean.class, long.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { setObjectField(param.thisObject, "mIconDarkAlpha", VALUE_DOZE_ALPHA); } }); findAndHookMethod(notificationView, "updateIconAlpha", ImageView.class, boolean.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { setObjectField(param.thisObject, "mIconDarkAlpha", VALUE_DOZE_ALPHA); } }); } } catch (Throwable t) { logE(TAG, t.getMessage(), t); } } private static void hookPulseInMethod(Method method) { hookMethod(method, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { param.setResult(VALUE_DOZE_IN); } }); } public static void hookRes(XC_InitPackageResources.InitPackageResourcesParam resParam, XSharedPreferences prefs) { resParam.res.setReplacement(Utils.PACKAGE_SYSTEMUI, "bool", DOZE_SUPP, true); resParam.res.setReplacement(Utils.PACKAGE_SYSTEMUI, "bool", DOZE_PICK_UP, true); if (Build.VERSION.SDK_INT < 22) { initPrefs(prefs); resParam.res.setReplacement(Utils.PACKAGE_SYSTEMUI, "integer", DOZE_ALPHA, VALUE_DOZE_ALPHA); } } private static void initPrefs(XSharedPreferences prefs) { prefs.reload(); VALUE_DOZE_IN = prefs.getInt(DOZE_IN, 1000); VALUE_DOZE_OUT = prefs.getInt(DOZE_OUT, 1000); VALUE_DOZE_VISIBILITY = prefs.getInt(DOZE_VISIBILITY, 3000); VALUE_DOZE_RESETS = prefs.getInt(DOZE_RESETS, 1); VALUE_DOZE_ALPHA = prefs.getInt(DOZE_ALPHA, 222); VALUE_DOZE_PULSE_SCHEDULE = prefs.getString(DOZE_PULSE_SCHEDULE, "10s,30s,60s"); } private static void registerReceiver(final Context context) { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ACTION_SLEEP); intentFilter.addAction(ACTION_PREFS_CHANGED); intentFilter.addAction(ACTION_HOT_REBOOT); context.registerReceiver(sPrefsChange, intentFilter); } //from gravitybox static class SystemProp { // Set the value for the given key public static void set(String key, String val) { try { Class<?> classSystemProperties = findClass("android.os.SystemProperties", null); callStaticMethod(classSystemProperties, "set", key, val); } catch (Throwable t) { logE(TAG, "SystemProp.set failed: ", t); } } } }