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 final WeakReference<InputMethodPrivilegedOperations> previousOps = 78 sRegistry.put(token, new WeakReference<>(ops)); 79 if (previousOps != null) { 80 throw new IllegalStateException(previousOps.get() + " is already registered for " 81 + " this token=" + token + " newOps=" + ops); 82 } 83 } 84 } 85 86 /** 87 * Get a {@link InputMethodPrivilegedOperations} from the given IME token. If it is not 88 * available, return a fake instance that does nothing except for showing warning messages. 89 * 90 * @param token IME token 91 * @return real {@link InputMethodPrivilegedOperations} object if {@code token} is still valid. 92 * Otherwise a fake instance of {@link InputMethodPrivilegedOperations} hat does nothing 93 * except for showing warning messages 94 */ 95 @NonNull 96 @AnyThread get(IBinder token)97 public static InputMethodPrivilegedOperations get(IBinder token) { 98 synchronized (sLock) { 99 if (sRegistry == null) { 100 return getNopOps(); 101 } 102 final WeakReference<InputMethodPrivilegedOperations> wrapperRef = sRegistry.get(token); 103 if (wrapperRef == null) { 104 return getNopOps(); 105 } 106 final InputMethodPrivilegedOperations wrapper = wrapperRef.get(); 107 if (wrapper == null) { 108 return getNopOps(); 109 } 110 return wrapper; 111 } 112 } 113 114 /** 115 * Explicitly removes the specified entry. 116 * 117 * <p>Note: Calling this method is optional. In general, {@link WeakHashMap} and 118 * {@link WeakReference} guarantee that the entry will be removed after specified binder proxies 119 * are garbage collected.</p> 120 * 121 * @param token IME token to be removed. 122 */ 123 @AnyThread remove(IBinder token)124 public static void remove(IBinder token) { 125 synchronized (sLock) { 126 if (sRegistry == null) { 127 return; 128 } 129 sRegistry.remove(token); 130 if (sRegistry.isEmpty()) { 131 sRegistry = null; 132 } 133 } 134 } 135 136 /** 137 * Check the given IME token registration status. 138 * 139 * @param token IME token 140 * @return {@code true} when the IME token has already registered 141 * {@link InputMethodPrivilegedOperations}, {@code false} otherwise. 142 */ 143 @AnyThread isRegistered(IBinder token)144 public static boolean isRegistered(IBinder token) { 145 synchronized (sLock) { 146 if (sRegistry == null) { 147 return false; 148 } 149 return sRegistry.containsKey(token); 150 } 151 } 152 } 153