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