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