BundleCompat.java revision 0f4ca634bbc43ddff900c35f7d2a43b55d8c830d
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        private BundleCompatBaseImpl() {
45        }
46
47        public static IBinder getBinder(Bundle bundle, String key) {
48            if (!sGetIBinderMethodFetched) {
49                try {
50                    sGetIBinderMethod = Bundle.class.getMethod("getIBinder", String.class);
51                    sGetIBinderMethod.setAccessible(true);
52                } catch (NoSuchMethodException e) {
53                    Log.i(TAG, "Failed to retrieve getIBinder method", e);
54                }
55                sGetIBinderMethodFetched = true;
56            }
57
58            if (sGetIBinderMethod != null) {
59                try {
60                    return (IBinder) sGetIBinderMethod.invoke(bundle, key);
61                } catch (InvocationTargetException | IllegalAccessException
62                        | IllegalArgumentException e) {
63                    Log.i(TAG, "Failed to invoke getIBinder via reflection", e);
64                    sGetIBinderMethod = null;
65                }
66            }
67            return null;
68        }
69
70        public static void putBinder(Bundle bundle, String key, IBinder binder) {
71            if (!sPutIBinderMethodFetched) {
72                try {
73                    sPutIBinderMethod =
74                            Bundle.class.getMethod("putIBinder", String.class, IBinder.class);
75                    sPutIBinderMethod.setAccessible(true);
76                } catch (NoSuchMethodException e) {
77                    Log.i(TAG, "Failed to retrieve putIBinder method", e);
78                }
79                sPutIBinderMethodFetched = true;
80            }
81
82            if (sPutIBinderMethod != null) {
83                try {
84                    sPutIBinderMethod.invoke(bundle, key, binder);
85                } catch (InvocationTargetException | IllegalAccessException
86                        | IllegalArgumentException e) {
87                    Log.i(TAG, "Failed to invoke putIBinder via reflection", e);
88                    sPutIBinderMethod = null;
89                }
90            }
91        }
92    }
93
94    private BundleCompat() {}
95
96    /**
97     * A convenience method to handle getting an {@link IBinder} inside a {@link Bundle} for all
98     * Android versions.
99     * @param bundle The bundle to get the {@link IBinder}.
100     * @param key    The key to use while getting the {@link IBinder}.
101     * @return       The {@link IBinder} that was obtained.
102     */
103    @Nullable
104    public static IBinder getBinder(@NonNull Bundle bundle, @Nullable String key) {
105        if (Build.VERSION.SDK_INT >= 18) {
106            return bundle.getBinder(key);
107        } else {
108            return BundleCompatBaseImpl.getBinder(bundle, key);
109        }
110    }
111
112    /**
113     * A convenience method to handle putting an {@link IBinder} inside a {@link Bundle} for all
114     * Android versions.
115     * @param bundle The bundle to insert the {@link IBinder}.
116     * @param key    The key to use while putting the {@link IBinder}.
117     * @param binder The {@link IBinder} to put.
118     */
119    public static void putBinder(@NonNull Bundle bundle, @Nullable String key,
120            @Nullable IBinder binder) {
121        if (Build.VERSION.SDK_INT >= 18) {
122            bundle.putBinder(key, binder);
123        } else {
124            BundleCompatBaseImpl.putBinder(bundle, key, binder);
125        }
126    }
127}
128