termux-app
282 строки · 10.7 Кб
1package com.termux.shared.reflection;
2
3import android.os.Build;
4
5import androidx.annotation.NonNull;
6import androidx.annotation.Nullable;
7
8import com.termux.shared.logger.Logger;
9
10import org.lsposed.hiddenapibypass.HiddenApiBypass;
11
12import java.lang.reflect.Constructor;
13import java.lang.reflect.Field;
14import java.lang.reflect.Method;
15import java.util.Arrays;
16
17public class ReflectionUtils {
18
19private static boolean HIDDEN_API_REFLECTION_RESTRICTIONS_BYPASSED = Build.VERSION.SDK_INT < Build.VERSION_CODES.P;
20
21private static final String LOG_TAG = "ReflectionUtils";
22
23/**
24* Bypass android hidden API reflection restrictions.
25* https://github.com/LSPosed/AndroidHiddenApiBypass
26* https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces
27*/
28public static void bypassHiddenAPIReflectionRestrictions() {
29if (!HIDDEN_API_REFLECTION_RESTRICTIONS_BYPASSED && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
30Logger.logDebug(LOG_TAG, "Bypassing android hidden api reflection restrictions");
31try {
32HiddenApiBypass.addHiddenApiExemptions("");
33} catch (Throwable t) {
34Logger.logStackTraceWithMessage(LOG_TAG, "Failed to bypass hidden API reflection restrictions", t);
35}
36
37HIDDEN_API_REFLECTION_RESTRICTIONS_BYPASSED = true;
38}
39}
40
41/** Check if android hidden API reflection restrictions are bypassed. */
42public static boolean areHiddenAPIReflectionRestrictionsBypassed() {
43return HIDDEN_API_REFLECTION_RESTRICTIONS_BYPASSED;
44}
45
46
47
48
49
50/**
51* Get a {@link Field} for the specified class.
52*
53* @param clazz The {@link Class} for which to return the field.
54* @param fieldName The name of the {@link Field}.
55* @return Returns the {@link Field} if getting the it was successful, otherwise {@code null}.
56*/
57@Nullable
58public static Field getDeclaredField(@NonNull Class<?> clazz, @NonNull String fieldName) {
59try {
60Field field = clazz.getDeclaredField(fieldName);
61field.setAccessible(true);
62return field;
63} catch (Exception e) {
64Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get \"" + fieldName + "\" field for \"" + clazz.getName() + "\" class", e);
65return null;
66}
67}
68
69
70
71/** Class that represents result of invoking a field. */
72public static class FieldInvokeResult {
73public boolean success;
74public Object value;
75
76FieldInvokeResult(boolean success, Object value) {
77this.value = success;
78this.value = value;
79}
80}
81
82/**
83* Get a value for a {@link Field} of an object for the specified class.
84*
85* Trying to access {@code null} fields will result in {@link NoSuchFieldException}.
86*
87* @param clazz The {@link Class} to which the object belongs to.
88* @param fieldName The name of the {@link Field}.
89* @param object The {@link Object} instance from which to get the field value.
90* @return Returns the {@link FieldInvokeResult} of invoking the field. The
91* {@link FieldInvokeResult#success} will be {@code true} if invoking the field was successful,
92* otherwise {@code false}. The {@link FieldInvokeResult#value} will contain the field
93* {@link Object} value.
94*/
95@NonNull
96public static <T> FieldInvokeResult invokeField(@NonNull Class<? extends T> clazz, @NonNull String fieldName, T object) {
97try {
98Field field = getDeclaredField(clazz, fieldName);
99if (field == null) return new FieldInvokeResult(false, null);
100return new FieldInvokeResult(true, field.get(object));
101} catch (Exception e) {
102Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get \"" + fieldName + "\" field value for \"" + clazz.getName() + "\" class", e);
103return new FieldInvokeResult(false, null);
104}
105}
106
107
108
109
110
111/**
112* Wrapper for {@link #getDeclaredMethod(Class, String, Class[])} without parameters.
113*/
114@Nullable
115public static Method getDeclaredMethod(@NonNull Class<?> clazz, @NonNull String methodName) {
116return getDeclaredMethod(clazz, methodName, new Class<?>[0]);
117}
118
119/**
120* Get a {@link Method} for the specified class with the specified parameters.
121*
122* @param clazz The {@link Class} for which to return the method.
123* @param methodName The name of the {@link Method}.
124* @param parameterTypes The parameter types of the method.
125* @return Returns the {@link Method} if getting the it was successful, otherwise {@code null}.
126*/
127@Nullable
128public static Method getDeclaredMethod(@NonNull Class<?> clazz, @NonNull String methodName, Class<?>... parameterTypes) {
129try {
130Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
131method.setAccessible(true);
132return method;
133} catch (Exception e) {
134Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get \"" + methodName + "\" method for \"" + clazz.getName() + "\" class with parameter types: " + Arrays.toString(parameterTypes), e);
135return null;
136}
137}
138
139
140
141/**
142* Wrapper for {@link #invokeVoidMethod(Method, Object, Object...)} without arguments.
143*/
144public static boolean invokeVoidMethod(@NonNull Method method, Object obj) {
145return invokeVoidMethod(method, obj, new Object[0]);
146}
147
148/**
149* Invoke a {@link Method} on the specified object with the specified arguments that returns
150* {@code void}.
151*
152* @param method The {@link Method} to invoke.
153* @param obj The {@link Object} the method should be invoked from.
154* @param args The arguments to pass to the method.
155* @return Returns {@code true} if invoking the method was successful, otherwise {@code false}.
156*/
157public static boolean invokeVoidMethod(@NonNull Method method, Object obj, Object... args) {
158try {
159method.setAccessible(true);
160method.invoke(obj, args);
161return true;
162} catch (Exception e) {
163Logger.logStackTraceWithMessage(LOG_TAG, "Failed to invoke \"" + method.getName() + "\" method with object \"" + obj + "\" and args: " + Arrays.toString(args), e);
164return false;
165}
166}
167
168
169
170/** Class that represents result of invoking a method that has a non-void return type. */
171public static class MethodInvokeResult {
172public boolean success;
173public Object value;
174
175MethodInvokeResult(boolean success, Object value) {
176this.value = success;
177this.value = value;
178}
179}
180
181/**
182* Wrapper for {@link #invokeMethod(Method, Object, Object...)} without arguments.
183*/
184@NonNull
185public static MethodInvokeResult invokeMethod(@NonNull Method method, Object obj) {
186return invokeMethod(method, obj, new Object[0]);
187}
188
189/**
190* Invoke a {@link Method} on the specified object with the specified arguments.
191*
192* @param method The {@link Method} to invoke.
193* @param obj The {@link Object} the method should be invoked from.
194* @param args The arguments to pass to the method.
195* @return Returns the {@link MethodInvokeResult} of invoking the method. The
196* {@link MethodInvokeResult#success} will be {@code true} if invoking the method was successful,
197* otherwise {@code false}. The {@link MethodInvokeResult#value} will contain the {@link Object}
198* returned by the method.
199*/
200@NonNull
201public static MethodInvokeResult invokeMethod(@NonNull Method method, Object obj, Object... args) {
202try {
203method.setAccessible(true);
204return new MethodInvokeResult(true, method.invoke(obj, args));
205} catch (Exception e) {
206Logger.logStackTraceWithMessage(LOG_TAG, "Failed to invoke \"" + method.getName() + "\" method with object \"" + obj + "\" and args: " + Arrays.toString(args), e);
207return new MethodInvokeResult(false, null);
208}
209}
210
211
212
213/**
214* Wrapper for {@link #getConstructor(String, Class[])} without parameters.
215*/
216@Nullable
217public static Constructor<?> getConstructor(@NonNull String className) {
218return getConstructor(className, new Class<?>[0]);
219}
220
221/**
222* Wrapper for {@link #getConstructor(Class, Class[])} to get a {@link Constructor} for the
223* {@code className}.
224*/
225@Nullable
226public static Constructor<?> getConstructor(@NonNull String className, Class<?>... parameterTypes) {
227try {
228return getConstructor(Class.forName(className), parameterTypes);
229} catch (Exception e) {
230Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get constructor for \"" + className + "\" class with parameter types: " + Arrays.toString(parameterTypes), e);
231return null;
232}
233}
234
235/**
236* Get a {@link Constructor} for the specified class with the specified parameters.
237*
238* @param clazz The {@link Class} for which to return the constructor.
239* @param parameterTypes The parameter types of the constructor.
240* @return Returns the {@link Constructor} if getting the it was successful, otherwise {@code null}.
241*/
242@Nullable
243public static Constructor<?> getConstructor(@NonNull Class<?> clazz, Class<?>... parameterTypes) {
244try {
245Constructor<?> constructor = clazz.getConstructor(parameterTypes);
246constructor.setAccessible(true);
247return constructor;
248} catch (Exception e) {
249Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get constructor for \"" + clazz.getName() + "\" class with parameter types: " + Arrays.toString(parameterTypes), e);
250return null;
251}
252}
253
254
255
256/**
257* Wrapper for {@link #invokeConstructor(Constructor, Object...)} without arguments.
258*/
259@Nullable
260public static Object invokeConstructor(@NonNull Constructor<?> constructor) {
261return invokeConstructor(constructor, new Object[0]);
262}
263
264/**
265* Invoke a {@link Constructor} with the specified arguments.
266*
267* @param constructor The {@link Constructor} to invoke.
268* @param args The arguments to pass to the constructor.
269* @return Returns the new instance if invoking the constructor was successful, otherwise {@code null}.
270*/
271@Nullable
272public static Object invokeConstructor(@NonNull Constructor<?> constructor, Object... args) {
273try {
274constructor.setAccessible(true);
275return constructor.newInstance(args);
276} catch (Exception e) {
277Logger.logStackTraceWithMessage(LOG_TAG, "Failed to invoke \"" + constructor.getName() + "\" constructor with args: " + Arrays.toString(args), e);
278return null;
279}
280}
281
282}
283