Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(399)

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java

Issue 2546613002: Revert of Fix leaks in InputConnectionHandlerThread (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.content.browser.input; 5 package org.chromium.content.browser.input;
6 6
7 import android.os.Handler; 7 import android.os.Handler;
8 import android.os.HandlerThread; 8 import android.os.HandlerThread;
9 import android.view.View; 9 import android.view.View;
10 import android.view.inputmethod.EditorInfo; 10 import android.view.inputmethod.EditorInfo;
11 11
12 import org.chromium.base.Log; 12 import org.chromium.base.Log;
13 import org.chromium.base.ThreadUtils;
14 import org.chromium.base.VisibleForTesting; 13 import org.chromium.base.VisibleForTesting;
15 14
16 /** 15 /**
17 * A factory class for {@link ThreadedInputConnection}. The class also includes triggering 16 * A factory class for {@link ThreadedInputConnection}. The class also includes triggering
18 * mechanism (hack) to run our InputConnection on non-UI thread. 17 * mechanism (hack) to run our InputConnection on non-UI thread.
19 */ 18 */
20 // TODO(changwan): add unit tests once Robolectric supports Android API level >= 21. 19 // TODO(changwan): add unit tests once Robolectric supports Android API level >= 21.
21 // See crbug.com/588547 for details. 20 // See crbug.com/588547 for details.
22 public class ThreadedInputConnectionFactory implements ChromiumBaseInputConnecti on.Factory { 21 public class ThreadedInputConnectionFactory implements ChromiumBaseInputConnecti on.Factory {
23 private static final String TAG = "cr_Ime"; 22 private static final String TAG = "cr_Ime";
24 private static final boolean DEBUG_LOGS = false; 23 private static final boolean DEBUG_LOGS = false;
25 24
26 // Most of the time we do not need to retry. But if we have lost window focu s while triggering 25 // Most of the time we do not need to retry. But if we have lost window focu s while triggering
27 // delayed creation, then there is a chance that detection may fail in the f ollowing scenario: 26 // delayed creation, then there is a chance that detection may fail in the f ollowing scenario:
28 // InputMethodManagerService checks the window focus by directly calling 27 // InputMethodManagerService checks the window focus by directly calling
29 // WindowManagerService#inputMethodClientHasFocus(). But the window focus ch ange is 28 // WindowManagerService#inputMethodClientHasFocus(). But the window focus ch ange is
30 // propagated to the view via ViewRootImpl's message queue. Therefore, it ma y take another 29 // propagated to the view via ViewRootImpl's message queue. Therefore, it ma y take another
31 // UI message loop until View#hasWindowFocus() is aligned with what IMMS see s. 30 // UI message loop until View#hasWindowFocus() is aligned with what IMMS see s.
32 private static final int CHECK_REGISTER_RETRY = 1; 31 private static final int CHECK_REGISTER_RETRY = 1;
33 32
34 private Handler mHandler; 33 private final Handler mHandler;
35 private final InputMethodManagerWrapper mInputMethodManagerWrapper; 34 private final InputMethodManagerWrapper mInputMethodManagerWrapper;
36 private final InputMethodUma mInputMethodUma; 35 private final InputMethodUma mInputMethodUma;
37 private ThreadedInputConnectionProxyView mProxyView; 36 private ThreadedInputConnectionProxyView mProxyView;
38 private ThreadedInputConnection mThreadedInputConnection; 37 private ThreadedInputConnection mThreadedInputConnection;
39 private CheckInvalidator mCheckInvalidator; 38 private CheckInvalidator mCheckInvalidator;
40 private boolean mReentrantTriggering; 39 private boolean mReentrantTriggering;
41 40
42 // A small class that can be updated to invalidate the check when there is a n external event 41 // A small class that can be updated to invalidate the check when there is a n external event
43 // such as window focus loss or view focus loss. 42 // such as window focus loss or view focus loss.
44 private static class CheckInvalidator { 43 private static class CheckInvalidator {
45 private boolean mInvalid; 44 private boolean mInvalid;
46 45
47 public void invalidate() { 46 public void invalidate() {
48 ImeUtils.checkOnUiThread(); 47 ImeUtils.checkOnUiThread();
49 mInvalid = true; 48 mInvalid = true;
50 } 49 }
51 50
52 public boolean isInvalid() { 51 public boolean isInvalid() {
53 ImeUtils.checkOnUiThread(); 52 ImeUtils.checkOnUiThread();
54 return mInvalid; 53 return mInvalid;
55 } 54 }
56 } 55 }
57 56
58 ThreadedInputConnectionFactory(InputMethodManagerWrapper inputMethodManagerW rapper) { 57 ThreadedInputConnectionFactory(InputMethodManagerWrapper inputMethodManagerW rapper) {
59 mInputMethodManagerWrapper = inputMethodManagerWrapper; 58 mInputMethodManagerWrapper = inputMethodManagerWrapper;
59 mHandler = createHandler();
60 mInputMethodUma = createInputMethodUma(); 60 mInputMethodUma = createInputMethodUma();
61 } 61 }
62 62
63 @VisibleForTesting 63 @VisibleForTesting
64 protected Handler createHandler() { 64 protected Handler createHandler() {
65 HandlerThread thread = 65 HandlerThread thread =
66 new HandlerThread("InputConnectionHandlerThread", HandlerThread. NORM_PRIORITY); 66 new HandlerThread("InputConnectionHandlerThread", HandlerThread. NORM_PRIORITY);
67 thread.start(); 67 thread.start();
68 return new Handler(thread.getLooper()); 68 return new Handler(thread.getLooper());
69 } 69 }
70 70
71 private void destroyHandler() {
72 if (mHandler == null) return;
73 final Handler handler = mHandler;
74 handler.post(new Runnable() {
75 @Override
76 public void run() {
77 ThreadUtils.postOnUiThread(new Runnable() {
78 @Override
79 public void run() {
80 if (handler != null) {
81 handler.getLooper().quit();
82 }
83 }
84 });
85 }
86 });
87 mHandler = null;
88 }
89
90 @VisibleForTesting 71 @VisibleForTesting
91 protected ThreadedInputConnectionProxyView createProxyView( 72 protected ThreadedInputConnectionProxyView createProxyView(
92 Handler handler, View containerView) { 73 Handler handler, View containerView) {
93 return new ThreadedInputConnectionProxyView( 74 return new ThreadedInputConnectionProxyView(
94 containerView.getContext(), handler, containerView); 75 containerView.getContext(), handler, containerView);
95 } 76 }
96 77
97 @VisibleForTesting 78 @VisibleForTesting
98 protected InputMethodUma createInputMethodUma() { 79 protected InputMethodUma createInputMethodUma() {
99 return new InputMethodUma(); 80 return new InputMethodUma();
(...skipping 29 matching lines...) Expand all
129 ImeUtils.computeEditorInfo( 110 ImeUtils.computeEditorInfo(
130 inputType, inputFlags, inputMode, selectionStart, selectionEnd, outAttrs); 111 inputType, inputFlags, inputMode, selectionStart, selectionEnd, outAttrs);
131 if (DEBUG_LOGS) { 112 if (DEBUG_LOGS) {
132 Log.w(TAG, "initializeAndGet. outAttr: " + ImeUtils.getEditorInfoDeb ugString(outAttrs)); 113 Log.w(TAG, "initializeAndGet. outAttr: " + ImeUtils.getEditorInfoDeb ugString(outAttrs));
133 } 114 }
134 115
135 // IMM can internally ignore subsequent activation requests, e.g., by ch ecking 116 // IMM can internally ignore subsequent activation requests, e.g., by ch ecking
136 // mServedConnecting. 117 // mServedConnecting.
137 if (mCheckInvalidator != null) mCheckInvalidator.invalidate(); 118 if (mCheckInvalidator != null) mCheckInvalidator.invalidate();
138 119
139 if (mHandler == null) mHandler = createHandler();
140
141 if (shouldTriggerDelayedOnCreateInputConnection()) { 120 if (shouldTriggerDelayedOnCreateInputConnection()) {
142 triggerDelayedOnCreateInputConnection(view); 121 triggerDelayedOnCreateInputConnection(view);
143 return null; 122 return null;
144 } 123 }
145 if (DEBUG_LOGS) Log.w(TAG, "initializeAndGet: called from proxy view"); 124 if (DEBUG_LOGS) Log.w(TAG, "initializeAndGet: called from proxy view");
146 125
147 if (mThreadedInputConnection == null) { 126 if (mThreadedInputConnection == null) {
148 if (DEBUG_LOGS) Log.w(TAG, "Creating ThreadedInputConnection..."); 127 if (DEBUG_LOGS) Log.w(TAG, "Creating ThreadedInputConnection...");
149 mThreadedInputConnection = new ThreadedInputConnection(view, imeAdap ter, mHandler); 128 mThreadedInputConnection = new ThreadedInputConnection(view, imeAdap ter, mHandler);
150 } else { 129 } else {
151 mThreadedInputConnection.resetOnUiThread(); 130 mThreadedInputConnection.resetOnUiThread();
152 } 131 }
153 return mThreadedInputConnection; 132 return mThreadedInputConnection;
154 } 133 }
155 134
156 private void triggerDelayedOnCreateInputConnection(final View view) { 135 private void triggerDelayedOnCreateInputConnection(final View view) {
157 if (DEBUG_LOGS) Log.w(TAG, "triggerDelayedOnCreateInputConnection"); 136 if (DEBUG_LOGS) Log.w(TAG, "triggerDelayedOnCreateInputConnection");
158 // Prevent infinite loop when View methods trigger onCreateInputConnecti on 137 // Prevent infinite loop when View methods trigger onCreateInputConnecti on
159 // on some OEM phones. (crbug.com/636197) 138 // on some OEM phones. (crbug.com/636197)
160 if (mReentrantTriggering) return; 139 if (mReentrantTriggering) return;
161 140
162 // We need to check this before creating invalidator. 141 // We need to check this before creating invalidator.
163 if (!view.hasFocus()) return; 142 if (!view.hasFocus()) return;
164 143
165 mCheckInvalidator = new CheckInvalidator(); 144 mCheckInvalidator = new CheckInvalidator();
166 145
167 if (!view.hasWindowFocus()) mCheckInvalidator.invalidate(); 146 if (!view.hasWindowFocus()) mCheckInvalidator.invalidate();
168 147
169 if (mHandler == null) return;
170 // We cannot reuse the existing proxy view, if any, due to crbug.com/664 402. 148 // We cannot reuse the existing proxy view, if any, due to crbug.com/664 402.
171 mProxyView = createProxyView(mHandler, view); 149 mProxyView = createProxyView(mHandler, view);
172 150
173 mReentrantTriggering = true; 151 mReentrantTriggering = true;
174 // This does not affect view focus of the real views. 152 // This does not affect view focus of the real views.
175 mProxyView.requestFocus(); 153 mProxyView.requestFocus();
176 mReentrantTriggering = false; 154 mReentrantTriggering = false;
177 155
178 view.getHandler().post(new Runnable() { 156 view.getHandler().post(new Runnable() {
179 @Override 157 @Override
180 public void run() { 158 public void run() {
181 // This is a hack to make InputMethodManager believe that the pr oxy view 159 // This is a hack to make InputMethodManager believe that the pr oxy view
182 // now has a focus. As a result, InputMethodManager will think t hat mProxyView 160 // now has a focus. As a result, InputMethodManager will think t hat mProxyView
183 // is focused, and will call getHandler() of the view when creat ing input 161 // is focused, and will call getHandler() of the view when creat ing input
184 // connection. 162 // connection.
185 163
186 // Step 1: Set mProxyView as InputMethodManager#mNextServedView. 164 // Step 1: Set mProxyView as InputMethodManager#mNextServedView.
187 // This does not affect the real window focus. 165 // This does not affect the real window focus.
188 mProxyView.onWindowFocusChanged(true); 166 mProxyView.onWindowFocusChanged(true);
189 167
190 // Step 2: Have InputMethodManager focus in on mNextServedView. 168 // Step 2: Have InputMethodManager focus in on mNextServedView.
191 // As a result, IMM will call onCreateInputConnection() on mProx yView on the same 169 // As a result, IMM will call onCreateInputConnection() on mProx yView on the same
192 // thread as mProxyView.getHandler(). It will also call subseque nt InputConnection 170 // thread as mProxyView.getHandler(). It will also call subseque nt InputConnection
193 // methods on this IME thread. 171 // methods on this IME thread.
194 mInputMethodManagerWrapper.isActive(view); 172 mInputMethodManagerWrapper.isActive(view);
195 173
196 // Step 3: Check that the above hack worked. 174 // Step 3: Check that the above hack worked.
197 // Do not check until activation finishes inside InputMethodMana ger (on IME thread). 175 // Do not check until activation finishes inside InputMethodMana ger (on IME thread).
198 if (mHandler == null) return;
199 mHandler.post(new Runnable() { 176 mHandler.post(new Runnable() {
200 @Override 177 @Override
201 public void run() { 178 public void run() {
202 postCheckRegisterResultOnUiThread(view, mCheckInvalidato r, 179 postCheckRegisterResultOnUiThread(view, mCheckInvalidato r,
203 CHECK_REGISTER_RETRY); 180 CHECK_REGISTER_RETRY);
204 } 181 }
205 }); 182 });
206 } 183 }
207 }); 184 });
208 } 185 }
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
267 public void onViewFocusChanged(boolean gainFocus) { 244 public void onViewFocusChanged(boolean gainFocus) {
268 if (DEBUG_LOGS) Log.d(TAG, "onViewFocusChanged: " + gainFocus); 245 if (DEBUG_LOGS) Log.d(TAG, "onViewFocusChanged: " + gainFocus);
269 if (!gainFocus && mCheckInvalidator != null) mCheckInvalidator.invalidat e(); 246 if (!gainFocus && mCheckInvalidator != null) mCheckInvalidator.invalidat e();
270 if (mProxyView != null) mProxyView.onOriginalViewFocusChanged(gainFocus) ; 247 if (mProxyView != null) mProxyView.onOriginalViewFocusChanged(gainFocus) ;
271 } 248 }
272 249
273 @Override 250 @Override
274 public void onViewAttachedToWindow() { 251 public void onViewAttachedToWindow() {
275 if (DEBUG_LOGS) Log.d(TAG, "onViewAttachedToWindow"); 252 if (DEBUG_LOGS) Log.d(TAG, "onViewAttachedToWindow");
276 if (mProxyView != null) mProxyView.onOriginalViewAttachedToWindow(); 253 if (mProxyView != null) mProxyView.onOriginalViewAttachedToWindow();
277 if (mHandler == null) mHandler = createHandler();
278 } 254 }
279 255
280 @Override 256 @Override
281 public void onViewDetachedFromWindow() { 257 public void onViewDetachedFromWindow() {
282 if (DEBUG_LOGS) Log.d(TAG, "onViewDetachedFromWindow"); 258 if (DEBUG_LOGS) Log.d(TAG, "onViewDetachedFromWindow");
283 if (mCheckInvalidator != null) mCheckInvalidator.invalidate(); 259 if (mCheckInvalidator != null) mCheckInvalidator.invalidate();
284 if (mProxyView != null) mProxyView.onOriginalViewDetachedFromWindow(); 260 if (mProxyView != null) mProxyView.onOriginalViewDetachedFromWindow();
285 destroyHandler();
286 }
287
288 @Override
289 public void destroy() {
290 if (mCheckInvalidator != null) mCheckInvalidator.invalidate();
291 destroyHandler();
292 } 261 }
293 } 262 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698