BundleCompat.java revision 9dede51868bbbe16aadcd65e04860bea8ea50e05
1/*
2 * Copyright (C) 2015 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
17package androidx.core.app;
18
19import android.os.Build;
20import android.os.Bundle;
21import android.os.IBinder;
22import android.util.Log;
23
24import androidx.annotation.NonNull;
25import androidx.annotation.Nullable;
26
27import java.lang.reflect.InvocationTargetException;
28import java.lang.reflect.Method;
29
30/**
31 * Helper for accessing features in {@link Bundle}.
32 */
33public final class BundleCompat {
34
35    static class BundleCompatBaseImpl {
36        private static final String TAG = "BundleCompatBaseImpl";
37
38        private static Method sGetIBinderMethod;
39        private static boolean sGetIBinderMethodFetched;
40
41        private static Method sPutIBinderMethod;
42        private static boolean sPutIBinderMethodFetched;
43
44        public static IBinder getBinder(Bundle bundle, String key) {
45            if (!sGetIBinderMethodFetched) {
46                try {
47                    sGetIBinderMethod = Bundle.class.getMethod("getIBinder", String.class);
48                    sGetIBinderMethod.setAccessible(true);
49                } catch (NoSuchMethodException e) {
50                    Log.i(TAG, "Failed to retrieve getIBinder method", e);
51                }
52                sGetIBinderMethodFetched = true;
53            }
54
55            if (sGetIBinderMethod != null) {
56                try {
57                    return (IBinder) sGetIBinderMethod.invoke(bundle, key);
58                } catch (InvocationTargetException | IllegalAccessException
59                        | IllegalArgumentException e) {
60                    Log.i(TAG, "Failed to invoke getIBinder via reflection", e);
61                    sGetIBinderMethod = null;
62                }
63            }
64            return null;
65        }
66
67        public static void putBinder(Bundle bundle, String key, IBinder binder) {
68            if (!sPutIBinderMethodFetched) {
69                try {
70                    sPutIBinderMethod =
71                            Bundle.class.getMethod("putIBinder", String.class, IBinder.class);
72                    sPutIBinderMethod.setAccessible(true);
73                } catch (NoSuchMethodException e) {
74                    Log.i(TAG, "Failed to retrieve putIBinder method", e);
75                }
76                sPutIBinderMethodFetched = true;
77            }
78
79            if (sPutIBinderMethod != null) {
80                try {
81                    sPutIBinderMethod.invoke(bundle, key, binder);
82                } catch (InvocationTargetException | IllegalAccessException
83                        | IllegalArgumentException e) {
84                    Log.i(TAG, "Failed to invoke putIBinder via reflection", e);
85                    sPutIBinderMethod = null;
86                }
87            }
88        }
89    }
90
91    private BundleCompat() {}
92
93    /**
94     * A convenience method to handle getting an {@link IBinder} inside a {@link Bundle} for all
95     * Android versions.
96     * @param bundle The bundle to get the {@link IBinder}.
97     * @param key    The key to use while getting the {@link IBinder}.
98     * @return       The {@link IBinder} that was obtained.
99     */
100    @Nullable
101    public static IBinder getBinder(@NonNull Bundle bundle, @Nullable String key) {
102        if (Build.VERSION.SDK_INT >= 18) {
103            return bundle.getBinder(key);
104        } else {
105            return BundleCompatBaseImpl.getBinder(bundle, key);
106        }
107    }
108
109    /**
110     * A convenience method to handle putting an {@link IBinder} inside a {@link Bundle} for all
111     * Android versions.
112     * @param bundle The bundle to insert the {@link IBinder}.
113     * @param key    The key to use while putting the {@link IBinder}.
114     * @param binder The {@link IBinder} to put.
115     */
116    public static void putBinder(@NonNull Bundle bundle, @Nullable String key,
117            @Nullable IBinder binder) {
118        if (Build.VERSION.SDK_INT >= 18) {
119            bundle.putBinder(key, binder);
120        } else {
121            BundleCompatBaseImpl.putBinder(bundle, key, binder);
122        }
123    }
124}
125