1 /* 2 * Copyright (C) 2020 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.internal.os; 18 19 import android.os.Binder; 20 import android.text.TextUtils; 21 22 import com.android.internal.annotations.VisibleForTesting; 23 24 import java.lang.reflect.InvocationTargetException; 25 import java.lang.reflect.Method; 26 import java.lang.reflect.Modifier; 27 import java.util.HashMap; 28 29 /** 30 * Maps a binder class and transaction code to the default transaction name. Since this 31 * resolution is class-based as opposed to instance-based, any custom implementation of 32 * {@link Binder#getTransactionName} will be ignored. 33 * 34 * The class is NOT thread safe 35 * 36 * @hide 37 */ 38 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 39 public class BinderTransactionNameResolver { 40 private static final Method NO_GET_DEFAULT_TRANSACTION_NAME_METHOD; 41 private static final boolean USE_TRANSACTION_CODES_FOR_UNKNOWN_METHODS = 42 Flags.useTransactionCodesForUnknownMethods(); 43 44 /** 45 * Generates the default transaction method name, which is just the transaction code. 46 * Used when the binder does not define a static "getDefaultTransactionName" method. 47 * 48 * @hide 49 */ noDefaultTransactionName(int transactionCode)50 public static String noDefaultTransactionName(int transactionCode) { 51 return String.valueOf(transactionCode); 52 } 53 54 static { 55 try { 56 NO_GET_DEFAULT_TRANSACTION_NAME_METHOD = BinderTransactionNameResolver.class.getMethod( 57 "noDefaultTransactionName", int.class); 58 } catch (NoSuchMethodException e) { 59 throw new RuntimeException(e); 60 } 61 } 62 63 private final HashMap<Class<? extends Binder>, Method> 64 mGetDefaultTransactionNameMethods = new HashMap<>(); 65 66 /** 67 * Given a binder class name and transaction code, returns the corresponding method name. 68 * 69 * @hide 70 */ getMethodName(Class<? extends Binder> binderClass, int transactionCode)71 public String getMethodName(Class<? extends Binder> binderClass, int transactionCode) { 72 Method method = mGetDefaultTransactionNameMethods.get(binderClass); 73 if (method == null) { 74 try { 75 method = binderClass.getMethod("getDefaultTransactionName", int.class); 76 } catch (NoSuchMethodException e) { 77 method = NO_GET_DEFAULT_TRANSACTION_NAME_METHOD; 78 } 79 if (method.getReturnType() != String.class 80 || !Modifier.isStatic(method.getModifiers())) { 81 method = NO_GET_DEFAULT_TRANSACTION_NAME_METHOD; 82 } 83 mGetDefaultTransactionNameMethods.put(binderClass, method); 84 } 85 86 try { 87 String methodName = (String) method.invoke(null, transactionCode); 88 if (USE_TRANSACTION_CODES_FOR_UNKNOWN_METHODS) { 89 return TextUtils.isEmpty(methodName) 90 ? String.valueOf(transactionCode) 91 : methodName; 92 } else { 93 return methodName; 94 } 95 } catch (IllegalAccessException | InvocationTargetException e) { 96 throw new RuntimeException(e); 97 } 98 } 99 } 100