| Index: content/public/android/java/src/org/chromium/content/browser/androidoverlay/DialogOverlayImpl.java | 
| diff --git a/content/public/android/java/src/org/chromium/content/browser/androidoverlay/DialogOverlayImpl.java b/content/public/android/java/src/org/chromium/content/browser/androidoverlay/DialogOverlayImpl.java | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..52d90d2fbc8d8e92921123ca4cba2b57f6771731 | 
| --- /dev/null | 
| +++ b/content/public/android/java/src/org/chromium/content/browser/androidoverlay/DialogOverlayImpl.java | 
| @@ -0,0 +1,275 @@ | 
| +// Copyright 2017 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +package org.chromium.content.browser.androidoverlay; | 
| + | 
| +import android.content.Context; | 
| +import android.os.Handler; | 
| +import android.os.IBinder; | 
| +import android.view.Surface; | 
| + | 
| +import org.chromium.base.ContextUtils; | 
| +import org.chromium.base.ThreadUtils; | 
| +import org.chromium.base.annotations.CalledByNative; | 
| +import org.chromium.base.annotations.JNINamespace; | 
| +import org.chromium.gfx.mojom.Rect; | 
| +import org.chromium.media.mojom.AndroidOverlay; | 
| +import org.chromium.media.mojom.AndroidOverlayClient; | 
| +import org.chromium.media.mojom.AndroidOverlayConfig; | 
| +import org.chromium.mojo.system.MojoException; | 
| + | 
| +/** | 
| + * Default AndroidOverlay impl.  Uses a separate (shared) overlay thread to own a Dialog instance, | 
| + * probably via a separate object that operates only on that thread.  We will post messages to / | 
| + * from that thread from the UI thread. | 
| + */ | 
| +@JNINamespace("content") | 
| +public class DialogOverlayImpl implements AndroidOverlay, DialogOverlayCore.Host { | 
| +    private static final String TAG = "DialogOverlayImpl"; | 
| + | 
| +    private AndroidOverlayClient mClient; | 
| +    private Handler mOverlayHandler; | 
| +    // Runnable that we'll run when the overlay notifies us that it's been released. | 
| +    private Runnable mReleasedRunnable; | 
| + | 
| +    private final ThreadHoppingHost mHoppingHost; | 
| + | 
| +    private DialogOverlayCore mDialogCore; | 
| + | 
| +    private long mNativeHandle; | 
| + | 
| +    // If nonzero, then we have registered a surface with this ID. | 
| +    private int mSurfaceId; | 
| + | 
| +    /** | 
| +     * @param client Mojo client interface. | 
| +     * @param config initial overlay configuration. | 
| +     * @param handler handler that posts to the overlay thread.  This is the android UI thread that | 
| +     * the dialog uses, not the browser UI thread. | 
| +     * @param provider the overlay provider that owns us. | 
| +     */ | 
| +    public DialogOverlayImpl(AndroidOverlayClient client, final AndroidOverlayConfig config, | 
| +            Handler overlayHandler, Runnable releasedRunnable) { | 
| +        ThreadUtils.assertOnUiThread(); | 
| + | 
| +        mClient = client; | 
| +        mReleasedRunnable = releasedRunnable; | 
| +        mOverlayHandler = overlayHandler; | 
| + | 
| +        mDialogCore = new DialogOverlayCore(); | 
| +        mHoppingHost = new ThreadHoppingHost(this); | 
| + | 
| +        // Post init to the overlay thread. | 
| +        final DialogOverlayCore dialogCore = mDialogCore; | 
| +        final Context context = ContextUtils.getApplicationContext(); | 
| +        mOverlayHandler.post(new Runnable() { | 
| +            @Override | 
| +            public void run() { | 
| +                dialogCore.initialize(context, config, mHoppingHost); | 
| +            } | 
| +        }); | 
| + | 
| +        // Register to get token updates. | 
| +        mNativeHandle = nativeInit(config.routingToken.high, config.routingToken.low); | 
| +        assert mNativeHandle != 0; | 
| +    } | 
| + | 
| +    // AndroidOverlay impl. | 
| +    // Client is done with this overlay. | 
| +    @Override | 
| +    public void close() { | 
| +        ThreadUtils.assertOnUiThread(); | 
| + | 
| +        // TODO(liberato): verify that this actually works, else add an explicit shutdown and hope | 
| +        // that the client calls it. | 
| + | 
| +        // Allow surfaceDestroyed to proceed, if it's waiting. | 
| +        mHoppingHost.onCleanup(); | 
| + | 
| +        // Notify |mDialogCore| that it has been released.  This might not be called if it notifies | 
| +        // us that it's been destroyed.  We still might send it in that case if the client closes | 
| +        // the connection before we find out that it's been destroyed on the overlay thread. | 
| +        if (mDialogCore != null) { | 
| +            final DialogOverlayCore dialogCore = mDialogCore; | 
| +            mOverlayHandler.post(new Runnable() { | 
| +                @Override | 
| +                public void run() { | 
| +                    dialogCore.release(); | 
| +                } | 
| +            }); | 
| + | 
| +            // Note that we might get messagaes from |mDialogCore| after this, since they might be | 
| +            // dispatched before |r| arrives.  Clearing |mDialogCore| causes us to ignore them. | 
| +            cleanup(); | 
| +        } | 
| + | 
| +        // Notify the provider that we've been released by the client.  Note that the surface might | 
| +        // not have been destroyed yet, but that's okay.  We could wait for a callback from the | 
| +        // dialog core before proceeding, but this makes it easier for the client to destroy and | 
| +        // re-create an overlay without worrying about an intermittent failure due to having too | 
| +        // many overlays open at once. | 
| +        mReleasedRunnable.run(); | 
| +    } | 
| + | 
| +    // AndroidOverlay impl. | 
| +    @Override | 
| +    public void onConnectionError(MojoException e) { | 
| +        ThreadUtils.assertOnUiThread(); | 
| + | 
| +        close(); | 
| +    } | 
| + | 
| +    // AndroidOverlay impl. | 
| +    @Override | 
| +    public void scheduleLayout(final Rect rect) { | 
| +        ThreadUtils.assertOnUiThread(); | 
| + | 
| +        if (mDialogCore == null) return; | 
| + | 
| +        final DialogOverlayCore dialogCore = mDialogCore; | 
| +        mOverlayHandler.post(new Runnable() { | 
| +            @Override | 
| +            public void run() { | 
| +                dialogCore.layoutSurface(rect); | 
| +            } | 
| +        }); | 
| +    } | 
| + | 
| +    // DialogOverlayCore.Host impl. | 
| +    // |surface| is now ready.  Register it with the surface tracker, and notify the client. | 
| +    @Override | 
| +    public void onSurfaceReady(Surface surface) { | 
| +        ThreadUtils.assertOnUiThread(); | 
| + | 
| +        if (mDialogCore == null || mClient == null) return; | 
| + | 
| +        mSurfaceId = nativeRegisterSurface(surface); | 
| +        mClient.onSurfaceReady(mSurfaceId); | 
| +    } | 
| + | 
| +    // DialogOverlayCore.Host impl. | 
| +    @Override | 
| +    public void onOverlayDestroyed() { | 
| +        ThreadUtils.assertOnUiThread(); | 
| + | 
| +        if (mDialogCore == null) return; | 
| + | 
| +        // Notify the client that the overlay is gone. | 
| +        if (mClient != null) mClient.onDestroyed(); | 
| + | 
| +        // Also clear out |mDialogCore| to prevent us from sending useless messages to it.  Note | 
| +        // that we might have already sent useless messages to it, and it should be robust against | 
| +        // that sort of thing. | 
| +        cleanup(); | 
| + | 
| +        // Note that we don't notify |mReleasedRunnable| yet, though we could.  We wait for the | 
| +        // client to close their connection first. | 
| +    } | 
| + | 
| +    // DialogOverlayCore.Host impl. | 
| +    // Due to threading issues, |mHoppingHost| doesn't forward this. | 
| +    @Override | 
| +    public void waitForCleanup() { | 
| +        assert false : "Not reached"; | 
| +    } | 
| + | 
| +    /** | 
| +     * Send |token| to the |mDialogCore| on the overlay thread. | 
| +     */ | 
| +    private void sendWindowTokenToCore(final IBinder token) { | 
| +        ThreadUtils.assertOnUiThread(); | 
| + | 
| +        final DialogOverlayCore dialogCore = mDialogCore; | 
| +        mOverlayHandler.post(new Runnable() { | 
| +            @Override | 
| +            public void run() { | 
| +                dialogCore.onWindowToken(token); | 
| +            } | 
| +        }); | 
| +    } | 
| + | 
| +    /** | 
| +     * Callback from native that the window token has changed. | 
| +     */ | 
| +    @CalledByNative | 
| +    public void onWindowToken(final IBinder token) { | 
| +        ThreadUtils.assertOnUiThread(); | 
| + | 
| +        if (mDialogCore == null) return; | 
| + | 
| +        // Forward this change. | 
| +        // Note that if we don't have a window token, then we could wait until we do, simply by | 
| +        // skipping sending null if we haven't sent any non-null token yet.  If we're transitioning | 
| +        // between windows, that might make the client's job easier. It wouldn't have to guess when | 
| +        // a new token is available. | 
| +        sendWindowTokenToCore(token); | 
| +    } | 
| + | 
| +    /** | 
| +     * Callback from native that we will be getting no additional tokens. | 
| +     */ | 
| +    @CalledByNative | 
| +    public void onDismissed() { | 
| +        ThreadUtils.assertOnUiThread(); | 
| + | 
| +        // Notify the client that the overlay is going away. | 
| +        if (mClient != null) mClient.onDestroyed(); | 
| + | 
| +        // Notify |mDialogCore| that it lost the token, if it had one. | 
| +        sendWindowTokenToCore(null); | 
| + | 
| +        cleanup(); | 
| +    } | 
| + | 
| +    /** | 
| +     * Unregister for callbacks, unregister any surface that we have, and forget about | 
| +     * |mDialogCore|.  Multiple calls are okay. | 
| +     */ | 
| +    private void cleanup() { | 
| +        ThreadUtils.assertOnUiThread(); | 
| + | 
| +        if (mSurfaceId != 0) { | 
| +            nativeUnregisterSurface(mSurfaceId); | 
| +            mSurfaceId = 0; | 
| +        } | 
| + | 
| +        // Note that we might not be registered for a token. | 
| +        if (mNativeHandle != 0) { | 
| +            nativeDestroy(mNativeHandle); | 
| +            mNativeHandle = 0; | 
| +        } | 
| + | 
| +        // Also clear out |mDialogCore| to prevent us from sending useless messages to it.  Note | 
| +        // that we might have already sent useless messages to it, and it should be robust against | 
| +        // that sort of thing. | 
| +        mDialogCore = null; | 
| + | 
| +        // If we wanted to send any message to |mClient|, we should have done so already. | 
| +        mClient = null; | 
| +    } | 
| + | 
| +    /** | 
| +     * Initializes native side.  Will register for onWindowToken callbacks on |this|.  Returns a | 
| +     * handle that should be provided to nativeDestroy. | 
| +     */ | 
| +    private native long nativeInit(long high, long low); | 
| + | 
| +    /** | 
| +     * Stops native side and deallocates |handle|. | 
| +     */ | 
| +    private native void nativeDestroy(long nativeDialogOverlayImpl); | 
| + | 
| +    /** | 
| +     * Register a surface and return the surface id for it. | 
| +     * @param surface Surface that we should register. | 
| +     * @return surface id that we associated with |surface|. | 
| +     */ | 
| +    private static native int nativeRegisterSurface(Surface surface); | 
| + | 
| +    /** | 
| +     * Unregister a surface. | 
| +     * @param surfaceId Id that was returned by registerSurface. | 
| +     */ | 
| +    private static native void nativeUnregisterSurface(int surfaceId); | 
| +} | 
|  |