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

Side by Side Diff: media/base/android/java/src/org/chromium/media/DialogSurfaceController.java

Issue 1967553002: DO NOT COMMIT - DialogSurface initial implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: added check for gpu process for dialog surface manager. Created 4 years, 6 months 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
(Empty)
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
3 // found in the LICENSE file.
4
5 package org.chromium.media;
6
7 import android.app.Activity;
8 import android.app.Dialog;
9 import android.content.Context;
10 import android.os.Handler;
11 import android.os.IBinder;
12 import android.view.Gravity;
13 import android.view.Surface;
14 import android.view.SurfaceHolder;
15 import android.view.Window;
16 import android.view.WindowManager;
17
18 import org.chromium.base.ApplicationStatus;
19 import org.chromium.base.Log;
20
21 /**
22 * Provide access to Dialog-based Surfaces.
23 *
24 * There are two threads involved, which we call "primary" and "looper". The
25 * primary thread is the one that native uses to talk to us, such as the gpu
26 * main thread. All calls to us from native must be on this thread.
27 *
28 * Note that this class may run in the browser or gpu process, depending on
29 * whether we want the gpu process to have the activity window token or not.
30 *
31 * The wrapper class (DialogSurfaceControllerWrapper) always runs locally with
32 * the JNI code that uses it, probably in the gpu process.
33 *
34 * The looper thread is a separate thread which has the looper for the dialogs
35 * that we create. We may call into native on that thread. Note that the
36 * native side handles the resulting race conditions.
37 *
38 * TODO(liberato): DialogSurfaceControllerImpl?
39 */
40 class DialogSurfaceController extends IDialogSurfaceController.Stub {
41 private static final String TAG = "cr_media";
42
43 /**
44 * Call back into native with a message about our state. This can be called
45 * on any thread. It's okay if it refers to a native object that no longer
46 * exists; we can't really avoid it. The native side handles it.
47 *
48 * IMPORTANT: Do not call this with mLock held. Expect that the callback
49 * may call us.
50 */
51 private final IDialogSurfaceCallback mCallback;
52 private final Handler mHandler;
53 private final Context mContext;
54
55 // Callback operations.
56 // These must match gpu_surface_controller.h .
57 private static final int OP_CREATED = 0;
58 private static final int OP_DESTROYED = 1;
59
60 private DialogSurfaceManager mOwner;
61
62 ////// Either thread, protected by mLock
63 private final Object mLock = new Object();
64 private Dialog mDialog;
65 private boolean mReleased;
66 private Surface mSurface;
67
68 /**
69 * Called from primary thread.
70 * @param context Context that we use.
71 * @param owner Owning manager that we'll notify on release().
72 * @param handler handler for a thread with a looper.
73 * @param nativeSurfaceController handle provided by native to identify us.
74 * @param x initial x position in chrome compositor (not screen) coords.
75 * @param y initial y position in chrome compositor (not screen) coords.
76 * @param width initial width.
77 * @param height initial height.
78 */
79 public DialogSurfaceController(Context context, DialogSurfaceManager owner, Handler handler,
80 IDialogSurfaceCallback callback, int x, int y, int width, int height ) {
81 mContext = context;
82 mOwner = owner;
83 mHandler = handler;
84 mCallback = callback;
85
86 mReleased = false;
87
88 scheduleCreateDialog(x, y, width, height);
89 }
90
91 // Note that the native wrapper should call release() anyway on destruction.
92 protected void finalize() throws Throwable {
93 release();
watk 2016/06/10 21:46:38 Should we log or assert here?
liberato (no reviews please) 2016/06/10 22:50:05 log, done.
94 }
95
96 /**
97 * Release the underlying surface, and generally clean up.
98 */
99 @Override
100 public void release() {
101 synchronized (mLock) {
102 if (!mReleased) mOwner.notifyReleased(this);
103 mOwner = null;
104
105 // Note that we can't prevent callbacks; they must execute without
106 // the lock held so that the callback can do things that requires
107 // the lock (e.g., GetSurface). However, the native side handles
108 // races with deleted objects, not us.
109
110 // Prevent any in-flight create from succeeding, and hide any dialog
111 // that we currently have.
112 mReleased = true;
113 if (mDialog != null) {
114 Runnable r = new Runnable() {
115 @Override
116 public void run() {
117 synchronized (mLock) {
118 mDialog.dismiss();
119 mDialog = null;
120 }
121 }
122 };
123 mHandler.post(r);
124 }
125 }
126 }
127
128 @Override
129 public Surface getSurface() {
130 synchronized (mLock) {
131 return mSurface;
132 }
133 }
134
135 @Override
136 public void scheduleLayoutSurface(final int x, final int y, final int width, final int height) {
137 Dialog dialog_sync;
138 synchronized (mLock) {
139 dialog_sync = mDialog;
140 // Note that mDialog might be replaced, but that means that
141 // somebody called scheduleLayoutSurface while a create was pending
142 // before they got notification that the surface was ready.
143 }
144
145 final Dialog dialog = dialog_sync;
watk 2016/06/10 21:46:38 Do you need dialog_sync? Seems like you only need
liberato (no reviews please) 2016/06/10 22:50:05 true, good point. it has to be final for binding
146
147 if (dialog == null) return;
148
149 Runnable r = new Runnable() {
150 @Override
151 public void run() {
152 layoutDialog(dialog, x, y, width, height);
153 }
154 };
155
156 mHandler.post(r);
157 }
158
159 /**
160 * Callbacks for finding out about the Dialog's Surface.
161 * These happen on the looper thread.
162 */
163 private class Callbacks implements SurfaceHolder.Callback2 {
164 @Override
165 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
166
167 @Override
168 public void surfaceCreated(SurfaceHolder holder) {
169 synchronized (mLock) {
170 mSurface = holder.getSurface();
171 }
172
173 try {
174 mCallback.onCallback(OP_CREATED);
175 } catch (Exception e) {
176 Log.e(TAG, "SurfaceCreated: callback failed: " + e);
177 release();
178 }
179 }
180
181 @Override
182 public void surfaceDestroyed(SurfaceHolder holder) {
183 synchronized (mLock) {
184 mSurface = null;
185 }
186
187 try {
188 mCallback.onCallback(OP_DESTROYED);
189 } catch (Exception e) {
190 Log.e(TAG, "SurfaceDestroyed: callback failed: " + e);
191 release();
192 }
193 }
194
195 @Override
196 public void surfaceRedrawNeeded(SurfaceHolder holder) {}
197 }
198
199 /**
200 * Schedule creation of the dialog on our looper thread. Sets mThread
201 * asynchronously. If release() occurs before then, then the dialog will
202 * not be created.
203 */
204 private void scheduleCreateDialog(final int x, final int y, final int width, final int height) {
205 synchronized (mLock) {
206 mDialog = null;
207 }
208
209 Runnable r = new Runnable() {
210 @Override
211 public void run() {
212 Dialog dialog = new Dialog(mContext);
213 dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
214 dialog.setCancelable(false);
215 layoutDialog(dialog, x, y, width, height);
216 dialog.getWindow().takeSurface(new Callbacks());
217 synchronized (mLock) {
218 // If we've been released in the interim, then stop here.
219 if (mReleased) return;
220
221 dialog.show();
222
223 // TODO(liberato): It would be really nice not to need to
224 // do this here, but we'd need an empty android style.
225 // Once we add that to the Dialog construction, then this
226 // isn't needed.
227 layoutDialog(dialog, x, y, width, height);
228 mDialog = dialog;
229 }
230 }
231 }; // new Runnable
232
233 // Post it to our dedicated thread.
234 mHandler.post(r);
235 }
236
237 /**
238 * Layout the dialog on the current thread. This should be called from the
239 * looper thread.
240 * Call scheduleLayoutDialog from anywhere else.
241 */
242 private void layoutDialog(Dialog dialog, int x, int y, int width, int height ) {
243 // Rather than using getAttributes, we just create them from scratch.
244 // The default dialog attributes aren't what we want.
245 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams ();
246
247 // TODO(liberato): adjust for CompositorView screen location here.
248 layoutParams.x = x;
249 layoutParams.y = y;
250 layoutParams.width = width;
251 layoutParams.height = height;
252 layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
253
254 // Use a panel, which is over the compositor view, until we get the
255 // compositor to switch to a transparent output surface.
256 // layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA ;
257 layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
258
259 layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
260 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
261 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
262 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
263
264 // Don't set FLAG_SCALED. in addition to not being sure what it does
265 // (SV uses it), it also causes a crash in WindowManager when we hide
266 // (not dismiss), navigate, and/or exit the app without hide/dismiss.
267 // There's a missing null check in WindowManagerService.java@3170
268 // on M MR2. To repro, change dimiss() to hide(), bring up a SV, and
269 // navigate away or press home.
270
271 // Turn off the position animation, so that it doesn't animate from one
272 // position to the next.
273 try {
274 int currentFlags =
275 (Integer) layoutParams.getClass().getField("privateFlags").g et(layoutParams);
276 layoutParams.getClass()
277 .getField("privateFlags")
278 .set(layoutParams, currentFlags | 0x00000040);
279 } catch (Exception e) {
280 }
281
282 layoutParams.token = getWindowToken();
283
284 dialog.getWindow().setAttributes(layoutParams);
285 }
286
287 private IBinder getWindowToken() {
288 // TODO(liberato): is this the right activity?
289 final Activity act = ApplicationStatus.getLastTrackedFocusedActivity();
290 return act.getWindow().getDecorView().getRootView().getWindowToken();
291 }
292 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698