chromium/content/public/android/java/src/org/chromium/content/browser/androidoverlay/AndroidOverlayProviderImpl.java

// Copyright 2017 The Chromium Authors
// 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 org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;

import org.chromium.base.ThreadUtils;
import org.chromium.media.mojom.AndroidOverlay;
import org.chromium.media.mojom.AndroidOverlayClient;
import org.chromium.media.mojom.AndroidOverlayConfig;
import org.chromium.media.mojom.AndroidOverlayProvider;
import org.chromium.mojo.bindings.InterfaceRequest;
import org.chromium.mojo.system.MojoException;
import org.chromium.services.service_manager.InterfaceFactory;

/**
 * Default impl of AndroidOverlayProvider.  Creates AndroidOverlayImpls.  We're a singleton, in the
 * sense that all provider clients talk to the same instance in the browser.
 */
@JNINamespace("content")
public class AndroidOverlayProviderImpl implements AndroidOverlayProvider {
    private static final String TAG = "AndroidOverlayProvider";

    // Maximum number of concurrent overlays that we allow.
    // Note: DialogOverlayImpl::CompleteInit() calls WebContentsDelegate::SetOverlayMode() directly,
    // because there can only be one overlay alive at a time. If we were to support multiple
    // concurrent overlays, we need to revisit this logic.
    private static final int MAX_OVERLAYS = 1;

    // Number of AndroidOverlays that have been created but not released.
    private int mNumOverlays;

    // Runnable that notifies us that a client has been released.
    private Runnable mNotifyReleasedRunnable =
            new Runnable() {
                @Override
                public void run() {
                    notifyReleased();
                }
            };

    /**
     * Create an overlay matching |config| for |client|, and bind it to |request|.  Remember that
     * potentially many providers are created.
     */
    @Override
    public void createOverlay(
            InterfaceRequest<AndroidOverlay> request,
            AndroidOverlayClient client,
            AndroidOverlayConfig config) {
        ThreadUtils.assertOnUiThread();

        // If this is no longer true, we need to update DialogOverlayImpl::CompleteInit().
        assert MAX_OVERLAYS == 1;

        // Limit the number of concurrent surfaces.
        if (mNumOverlays >= MAX_OVERLAYS) {
            client.onDestroyed();
            client.close();
            return;
        }

        mNumOverlays++;

        DialogOverlayImpl impl =
                new DialogOverlayImpl(
                        client, config, mNotifyReleasedRunnable, /* asPanel= */ false);
        DialogOverlayImpl.MANAGER.bind(impl, request);
    }

    /**
     * Called by AndroidOverlays when they no longer need the thread via |mNotifyReleasedRunnable|.
     */
    private void notifyReleased() {
        ThreadUtils.assertOnUiThread();
        assert mNumOverlays > 0;
        mNumOverlays--;

        // We don't stop the looper thread here, else android can get mad when it tries to send
        // a message from the dialog on this thread.  AndroidOverlay might have to notify us
        // separately to tell us when it's done with the thread, if we don't want to wait until
        // then to start creating a new SV.
        // Instead, we just avoid shutting down the thread at all for now.
    }

    // Remember that we can't tell which client disconnected.
    @Override
    public void close() {}

    // Remember that we can't tell which client disconnected.
    @Override
    public void onConnectionError(MojoException e) {}

    // Are overlays supported by the embedder?
    @CalledByNative
    private static boolean areOverlaysSupported() {
        return true;
    }

    /** Mojo factory. */
    public static class Factory implements InterfaceFactory<AndroidOverlayProvider> {
        private static AndroidOverlayProviderImpl sImpl;

        public Factory() {}

        @Override
        public AndroidOverlayProvider createImpl() {
            if (sImpl == null) sImpl = new AndroidOverlayProviderImpl();
            return sImpl;
        }
    }
}