1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.nearby.common.bluetooth.fastpair; 18 19 import java.lang.reflect.InvocationTargetException; 20 import java.lang.reflect.Method; 21 22 /** 23 * Utilities for calling methods using reflection. The main benefit of using this helper is to avoid 24 * complications around exception handling when calling methods reflectively. It's not safe to use 25 * Java 8's multicatch on such exceptions, because the java compiler converts multicatch into 26 * ReflectiveOperationException in some instances, which doesn't work on older sdk versions. 27 * Instead, use these utilities and catch ReflectionException. 28 * 29 * <p>Example usage: 30 * 31 * <pre>{@code 32 * try { 33 * Reflect.on(btAdapter) 34 * .withMethod("setScanMode", int.class) 35 * .invoke(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) 36 * } catch (ReflectionException e) { } 37 * }</pre> 38 */ 39 // TODO(b/202549655): remove existing Reflect usage. New usage is not allowed! No exception! 40 public final class Reflect { 41 private final Object mTargetObject; 42 Reflect(Object targetObject)43 private Reflect(Object targetObject) { 44 this.mTargetObject = targetObject; 45 } 46 47 /** Creates an instance of this helper to invoke methods on the given target object. */ on(Object targetObject)48 public static Reflect on(Object targetObject) { 49 return new Reflect(targetObject); 50 } 51 52 /** Finds a method with the given name and parameter types. */ withMethod(String methodName, Class<?>... paramTypes)53 public ReflectionMethod withMethod(String methodName, Class<?>... paramTypes) 54 throws ReflectionException { 55 try { 56 return new ReflectionMethod(mTargetObject.getClass().getMethod(methodName, paramTypes)); 57 } catch (NoSuchMethodException e) { 58 throw new ReflectionException(e); 59 } 60 } 61 62 /** Represents an invokable method found reflectively. */ 63 public final class ReflectionMethod { 64 private final Method mMethod; 65 ReflectionMethod(Method method)66 private ReflectionMethod(Method method) { 67 this.mMethod = method; 68 } 69 70 /** 71 * Invokes this instance method with the given parameters. The called method does not return 72 * a value. 73 */ invoke(Object... parameters)74 public void invoke(Object... parameters) throws ReflectionException { 75 try { 76 mMethod.invoke(mTargetObject, parameters); 77 } catch (IllegalAccessException e) { 78 throw new ReflectionException(e); 79 } catch (InvocationTargetException e) { 80 throw new ReflectionException(e); 81 } 82 } 83 84 /** 85 * Invokes this instance method with the given parameters. The called method returns a non 86 * null value. 87 */ get(Object... parameters)88 public Object get(Object... parameters) throws ReflectionException { 89 Object value; 90 try { 91 value = mMethod.invoke(mTargetObject, parameters); 92 } catch (IllegalAccessException e) { 93 throw new ReflectionException(e); 94 } catch (InvocationTargetException e) { 95 throw new ReflectionException(e); 96 } 97 if (value == null) { 98 throw new ReflectionException(new NullPointerException()); 99 } 100 return value; 101 } 102 } 103 } 104