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

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

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

Powered by Google App Engine
This is Rietveld 408576698