/* * Copyright (C) 2014 75py * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.nagopy.android.xposed.utilities; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import android.os.Build; import com.nagopy.android.xposed.util.XLog; import com.nagopy.android.xposed.utilities.util.Const; import de.robv.android.xposed.IXposedHookInitPackageResources; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.IXposedHookZygoteInit; import de.robv.android.xposed.XSharedPreferences; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam; import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; /** * 後悔はしていない */ public class XposedModules implements IXposedHookZygoteInit, IXposedHookLoadPackage, IXposedHookInitPackageResources { List<XModuleInfo> mXModuleInfoList = new ArrayList<XposedModules.XModuleInfo>(); private String modulePath; private static final String LOG_FORMAT_INVOKE = "invoke: %s(%s)"; private static final String LOG_FORMAT_SUCCESS = "success: %s(%s)"; @Override public void initZygote(StartupParam startupParam) throws Throwable { // モジュールパスを保存 modulePath = startupParam.modulePath; XSharedPreferences xSharedPreferences = new XSharedPreferences(Const.PACKAGE_NAME); xSharedPreferences.reload(); /** 適用するモジュールと、マスタ設定のキーのマップ */ Map<Class<?>, String> xposedModules = new HashMap<Class<?>, String>(); xposedModules.put(ModActionBar.class, "master_mod_action_bar_enable"); xposedModules.put(ModAppPicker.class, "master_mod_app_picker_enable"); xposedModules.put(ModBatteryIcon.class, "master_mod_battery_icon_enable"); xposedModules.put(ModBrightness.class, "master_mod_brightness_enable"); xposedModules.put(ModImmersiveFullScreenMode.class, "master_mod_immersive_full_screen_mode_enabled"); xposedModules.put(ModLockscreenClock.class, "master_mod_lockscreen_clock_enable"); xposedModules.put(ModLockscreenTorch.class, "master_mod_lockscreen_torch_enable"); xposedModules.put(ModNotification.class, "master_mod_notification_enable"); xposedModules.put(ModNotificationExpandedClock.class, "master_mod_notification_expanded_clock_enable"); xposedModules.put(ModOtherUtilities.class, "master_mod_other_utilities_enable"); xposedModules.put(ModStatusBarClock.class, "master_mod_status_bar_enable"); xposedModules.put(ModToast.class, "master_mod_toast_dao_enable"); for (Entry<Class<?>, String> entry : xposedModules.entrySet()) { Class<?> module = entry.getKey(); if (!module.isAnnotationPresent(XposedModule.class)) { // モジュールじゃない場合は次へ continue; } XposedModule xposedModuleAnnotation = module.getAnnotation(XposedModule.class); XModuleInfo info = new XModuleInfo(); info.clsName = module.getSimpleName(); // モジュール設定のインスタンスを生成 info.settings = xposedModuleAnnotation.setting().newInstance(); // クラスについてるXMinSdkVersionを取得 XMinSdkVersion classMinSdkVersion = module.getAnnotation(XMinSdkVersion.class); Method[] methods = module.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(InitZygote.class)) { XModuleMethod initZygoteMethod = new XModuleMethod(); // メソッドをセットしとく initZygoteMethod.method = method; // 対象SDKバージョンを取得 initZygoteMethod.minSdkVersion = getMinSdkVersion(method, classMinSdkVersion); // サマリー取得 InitZygote annotation = method.getAnnotation(InitZygote.class); initZygoteMethod.summary = annotation.summary(); info.initZygote.add(initZygoteMethod); } else if (method.isAnnotationPresent(HandleLoadPackage.class)) { XModuleMethod handleLoadPackage = new XModuleMethod(); HandleLoadPackage annotation = method.getAnnotation(HandleLoadPackage.class); // メソッドをセットしとく handleLoadPackage.method = method; // 対象パッケージを取得 handleLoadPackage.targetPackageName = Arrays.asList(annotation.targetPackage()); // 対象SDKバージョンを取得 handleLoadPackage.minSdkVersion = getMinSdkVersion(method, classMinSdkVersion); // サマリー取得 handleLoadPackage.summary = annotation.summary(); info.handleLoadPackage.add(handleLoadPackage); } else if (method.isAnnotationPresent(HandleInitPackageResources.class)) { XModuleMethod handleInitPackageResources = new XModuleMethod(); HandleInitPackageResources annotation = method .getAnnotation(HandleInitPackageResources.class); // メソッドをセットしとく handleInitPackageResources.method = method; // 対象パッケージを取得 handleInitPackageResources.targetPackageName = Arrays.asList(annotation .targetPackage()); // 対象SDKバージョンを取得 handleInitPackageResources.minSdkVersion = getMinSdkVersion(method, classMinSdkVersion); // サマリー取得 handleInitPackageResources.summary = annotation.summary(); info.handleInitPackageResources.add(handleInitPackageResources); } } // モジュールの有効・無効を判定 info.enabled = xSharedPreferences.getBoolean(entry.getValue(), false); // 追加 mXModuleInfoList.add(info); XLog.d(info); XposedBridge.log(info.toString()); } // 実行 for (XModuleInfo moduleInfo : mXModuleInfoList) { if (!moduleInfo.enabled) { // モジュールが無効の場合は次へ continue; } if (moduleInfo.initZygote.isEmpty()) { continue; } moduleInfo.d("initZygote START"); for (XModuleMethod methodInfo : moduleInfo.initZygote) { if (Build.VERSION.SDK_INT >= methodInfo.minSdkVersion) { try { // ログ出力 moduleInfo.d(String.format(LOG_FORMAT_INVOKE, methodInfo.method.getName(), methodInfo.summary)); // メソッド実行 methodInfo.method.invoke(null, startupParam, moduleInfo.settings); // 成功ログ出力 moduleInfo.d(String.format(LOG_FORMAT_SUCCESS, methodInfo.method.getName(), methodInfo.summary)); } catch (Throwable t) { // エラーログ出力 XLog.e(t); } } } moduleInfo.d("initZygote FINISH"); } } /** * MinSdkVersionを取得 * * @param method * @param classMinSdkVersion * @return */ private int getMinSdkVersion(Method method, XMinSdkVersion classMinSdkVersion) { if (classMinSdkVersion != null) { return classMinSdkVersion.value(); } else if (method.isAnnotationPresent(XMinSdkVersion.class)) { XMinSdkVersion minSdkVersion = method.getAnnotation(XMinSdkVersion.class); if (minSdkVersion != null) { return minSdkVersion.value(); } } return Build.VERSION_CODES.JELLY_BEAN; } @Override public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { // 実行 for (XModuleInfo moduleInfo : mXModuleInfoList) { if (!moduleInfo.enabled) { // モジュールが無効の場合は次へ continue; } if (moduleInfo.handleLoadPackage.isEmpty()) { continue; } for (XModuleMethod methodInfo : moduleInfo.handleLoadPackage) { if (!methodInfo.targetPackageName.contains(lpparam.packageName)) { // 対象じゃない場合はスキップ continue; } if (Build.VERSION.SDK_INT >= methodInfo.minSdkVersion) { try { // ログ出力 moduleInfo.d(String.format(LOG_FORMAT_INVOKE, methodInfo.method.getName(), methodInfo.summary)); // メソッド実行 methodInfo.method.invoke(null, modulePath, lpparam, moduleInfo.settings); // 成功ログ出力 moduleInfo.d(String.format(LOG_FORMAT_SUCCESS, methodInfo.method.getName(), methodInfo.summary)); } catch (Throwable t) { // エラーログ出力 XLog.e(t); } } } } } @Override public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable { // 実行 for (XModuleInfo moduleInfo : mXModuleInfoList) { if (!moduleInfo.enabled) { // モジュールが無効の場合は次へ continue; } if (moduleInfo.handleInitPackageResources.isEmpty()) { continue; } for (XModuleMethod methodInfo : moduleInfo.handleInitPackageResources) { if (!methodInfo.targetPackageName.contains(resparam.packageName)) { // 対象じゃない場合はスキップ continue; } if (Build.VERSION.SDK_INT >= methodInfo.minSdkVersion) { try { // ログ出力 moduleInfo.d(String.format(LOG_FORMAT_INVOKE, methodInfo.method.getName(), methodInfo.summary)); // メソッド実行 methodInfo.method.invoke(null, modulePath, resparam, moduleInfo.settings); // 成功ログ出力 moduleInfo.d(String.format(LOG_FORMAT_SUCCESS, methodInfo.method.getName(), methodInfo.summary)); } catch (Throwable t) { // エラーログ出力 XLog.e(t); } } } } } private static class XModuleInfo { @Override public String toString() { return "XModuleInfo [clsName=" + clsName + ", enabled=" + enabled + ", initZygote=" + initZygote + ", handleLoadPackage=" + handleLoadPackage + ", handleInitPackageResources=" + handleInitPackageResources + "]"; } String clsName; /** 設定のインスタンス */ Object settings; /** モジュールの有効判定 */ boolean enabled; // それぞれの実行メソッド List<XModuleMethod> initZygote = new ArrayList<XposedModules.XModuleMethod>(); List<XModuleMethod> handleLoadPackage = new ArrayList<XposedModules.XModuleMethod>(); List<XModuleMethod> handleInitPackageResources = new ArrayList<XposedModules.XModuleMethod>(); public void d(Object obj) { XLog.d(clsName, obj); } } private static class XModuleMethod { @Override public String toString() { return "XModuleMethod [method=" + method + ", minSdkVersion=" + minSdkVersion + ", targetPackageName=" + targetPackageName + "]"; } /** サマリー(ログ表示用) */ String summary; /** 実行メソッド */ Method method; /** 最低バージョン */ int minSdkVersion = Build.VERSION_CODES.JELLY_BEAN; /** 対象パッケージ名 */ List<String> targetPackageName = Collections.emptyList(); } @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface XModuleSettings { } @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface XMinSdkVersion { int value(); } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface InitZygote { String summary() default ""; } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface HandleLoadPackage { String[] targetPackage(); String summary() default ""; } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface HandleInitPackageResources { String[] targetPackage(); String summary() default ""; } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface XposedModule { Class<?> setting(); } }