chromium/content/public/android/java/src/org/chromium/content/browser/NfcHost.java

// Copyright 2016 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;

import android.app.Activity;
import android.util.SparseArray;

import org.jni_zero.CalledByNative;

import org.chromium.base.Callback;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.WindowAndroid;

/** Tracks the Activiy for a given WebContents on behalf of a NFC instance that cannot talk
 * directly to WebContents.
 */
class NfcHost implements WindowEventObserver {
    private static final SparseArray<NfcHost> sContextHostsMap = new SparseArray<NfcHost>();

    // The WebContents with which this host is associated.
    private final WebContents mWebContents;

    // The context ID with which this host is associated.
    private final int mContextId;

    // The callback that the NFC instance has registered for being notified when the Activity
    // changes.
    private Callback<Activity> mCallback;

    /** Provides access to NfcHost via context ID. */
    public static NfcHost fromContextId(int contextId) {
        return sContextHostsMap.get(contextId);
    }

    @CalledByNative
    private static void create(WebContents webContents, int contextId) {
        // The ctor will put the instance into sContextHostsMap.
        new NfcHost(webContents, contextId);
    }

    NfcHost(WebContents webContents, int contextId) {
        mWebContents = webContents;

        mContextId = contextId;
        sContextHostsMap.put(mContextId, this);
    }

    /** Called by the NFC implementation (via ContentNfcDelegate) to allow that implementation to
     * track changes to the Activity associated with its context ID (i.e., the activity associated
     * with |mWebContents|).
     */
    public void trackActivityChanges(Callback<Activity> callback) {
        // Only the main frame is allowed to access NFC
        // (https://w3c.github.io/web-nfc/#security-policies). The renderer enforces this by
        // dropping connection requests from nested frames.  Therefore, this class should never see
        // a request to track activity changes while there is already such a request.
        assert mCallback == null : "Unexpected request to track activity changes";
        mCallback = callback;

        // This may be null in tests.
        WindowEventObserverManager manager = WindowEventObserverManager.from(mWebContents);
        if (manager != null) {
            manager.addObserver(this);
        }

        WindowAndroid window = mWebContents.getTopLevelNativeWindow();
        mCallback.onResult(window != null ? window.getActivity().get() : null);
    }

    /**
     * @see org.chromium.device.nfc.NfcDelegate#stopTrackingActivityForHost(int)
     */
    public void stopTrackingActivityChanges() {
        mCallback = null;

        // This may be null in tests.
        WindowEventObserverManager manager = WindowEventObserverManager.from(mWebContents);
        if (manager != null) {
            manager.removeObserver(this);
        }

        sContextHostsMap.remove(mContextId);
    }

    /** Updates the Activity associated with this instance. */
    @Override
    public void onWindowAndroidChanged(WindowAndroid newWindowAndroid) {
        Activity activity = null;
        if (newWindowAndroid != null) {
            activity = newWindowAndroid.getActivity().get();
        }
        assert mCallback != null : "should have callback";
        mCallback.onResult(activity);
    }
}