1 /*
2  * Copyright (C) 2018 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.inputmethod;
18 
19 import android.annotation.AnyThread;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.os.IBinder;
23 
24 import com.android.internal.annotations.GuardedBy;
25 
26 import java.lang.ref.WeakReference;
27 import java.util.WeakHashMap;
28 
29 /**
30  * A weak-reference-based mapper from IME token to {@link InputMethodPrivilegedOperations} that is
31  * used only to support deprecated IME APIs in {@link android.view.inputmethod.InputMethodManager}.
32  *
33  * <p>This class is designed to be used as a per-process global registry.</p>
34  */
35 public final class InputMethodPrivilegedOperationsRegistry {
InputMethodPrivilegedOperationsRegistry()36     private InputMethodPrivilegedOperationsRegistry() {
37         // Not intended to be instantiated.
38     }
39 
40     private static final Object sLock = new Object();
41 
42     @Nullable
43     @GuardedBy("sLock")
44     private static WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>> sRegistry;
45 
46     @Nullable
47     private static InputMethodPrivilegedOperations sNop;
48 
49     @NonNull
50     @AnyThread
getNopOps()51     private static InputMethodPrivilegedOperations getNopOps() {
52         // Strict thread-safety is not necessary because temporarily creating multiple nop instance
53         // is basically harmless
54         if (sNop == null) {
55             sNop = new InputMethodPrivilegedOperations();
56         }
57         return sNop;
58     }
59 
60     /**
61      * Put a new entry to the registry.
62      *
63      * <p>Note: {@link InputMethodPrivilegedOperationsRegistry} does not hold strong reference to
64      * {@code token} and {@code ops}.  The caller must be responsible for holding strong references
65      * to those objects, that is until {@link android.inputmethodservice.InputMethodService} is
66      * destroyed.</p>
67      *
68      * @param token IME token
69      * @param ops {@link InputMethodPrivilegedOperations} to be associated with the given IME token
70      */
71     @AnyThread
put(IBinder token, InputMethodPrivilegedOperations ops)72     public static void put(IBinder token, InputMethodPrivilegedOperations ops) {
73         synchronized (sLock) {
74             if (sRegistry == null) {
75                 sRegistry = new WeakHashMap<>();
76             }
77             sRegistry.put(token, new WeakReference<>(ops));
78         }
79     }
80 
81     /**
82      * Get a {@link InputMethodPrivilegedOperations} from the given IME token.  If it is not
83      * available, return a fake instance that does nothing except for showing warning messages.
84      *
85      * @param token IME token
86      * @return real {@link InputMethodPrivilegedOperations} object if {@code token} is still valid.
87      *         Otherwise a fake instance of {@link InputMethodPrivilegedOperations} hat does nothing
88      *         except for showing warning messages
89      */
90     @NonNull
91     @AnyThread
get(IBinder token)92     public static InputMethodPrivilegedOperations get(IBinder token) {
93         synchronized (sLock) {
94             if (sRegistry == null) {
95                 return getNopOps();
96             }
97             final WeakReference<InputMethodPrivilegedOperations> wrapperRef = sRegistry.get(token);
98             if (wrapperRef == null) {
99                 return getNopOps();
100             }
101             final InputMethodPrivilegedOperations wrapper = wrapperRef.get();
102             if (wrapper == null) {
103                 return getNopOps();
104             }
105             return wrapper;
106         }
107     }
108 
109     /**
110      * Explicitly removes the specified entry.
111      *
112      * <p>Note: Calling this method is optional. In general, {@link WeakHashMap} and
113      * {@link WeakReference} guarantee that the entry will be removed after specified binder proxies
114      * are garbage collected.</p>
115      *
116      * @param token IME token to be removed.
117      */
118     @AnyThread
remove(IBinder token)119     public static void remove(IBinder token) {
120         synchronized (sLock) {
121             if (sRegistry == null) {
122                 return;
123             }
124             sRegistry.remove(token);
125             if (sRegistry.isEmpty()) {
126                 sRegistry = null;
127             }
128         }
129     }
130 }
131