chromium/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeWebApkHost.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.chrome.browser.webapps;

import static org.chromium.chrome.browser.flags.ChromeSwitches.SKIP_WEBAPK_VERIFICATION;

import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.CommandLine;
import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskTraits;
import org.chromium.components.webapk.lib.client.ChromeWebApkHostSignature;
import org.chromium.components.webapk.lib.client.WebApkValidator;
import org.chromium.webapk.lib.client.WebApkIdentityServiceClient;

/** Contains functionality needed for Chrome to host WebAPKs. */
public class ChromeWebApkHost {
    /** Time in milliseconds to wait for {@link WebApkServiceClient} to finish. */
    private static final long WAIT_FOR_WORK_DISCONNECT_SERVICE_DELAY_MS = 1000;

    private static ApplicationStatus.ApplicationStateListener sListener;

    public static void init() {
        WebApkValidator.init(
                ChromeWebApkHostSignature.EXPECTED_SIGNATURE, ChromeWebApkHostSignature.PUBLIC_KEY);
        if (CommandLine.getInstance().hasSwitch(SKIP_WEBAPK_VERIFICATION)) {
            // Tell the WebApkValidator to work for all WebAPKs.
            WebApkValidator.setDisableValidation(true);
        }
    }

    /**
     * Checks whether Chrome is the runtime host of the WebAPK asynchronously. Accesses the
     * ApplicationStateListener needs to be called on UI thread.
     */
    public static void checkChromeBacksWebApkAsync(
            String webApkPackageName,
            WebApkIdentityServiceClient.CheckBrowserBacksWebApkCallback callback) {
        ThreadUtils.assertOnUiThread();

        if (sListener == null) {
            // Registers an application listener to disconnect all connections to WebAPKs
            // when Chrome is stopped.
            sListener =
                    new ApplicationStatus.ApplicationStateListener() {
                        @Override
                        public void onApplicationStateChange(int newState) {
                            if (newState == ApplicationState.HAS_STOPPED_ACTIVITIES
                                    || newState == ApplicationState.HAS_DESTROYED_ACTIVITIES) {
                                disconnectFromAllServices(/* waitForPendingWork= */ false);

                                ApplicationStatus.unregisterApplicationStateListener(sListener);
                                sListener = null;
                            }
                        }
                    };
            ApplicationStatus.registerApplicationStateListener(sListener);
        }

        WebApkIdentityServiceClient.getInstance(TaskTraits.UI_DEFAULT)
                .checkBrowserBacksWebApkAsync(
                        ContextUtils.getApplicationContext(), webApkPackageName, callback);
    }

    /** Disconnect from all of the services of all WebAPKs. */
    public static void disconnectFromAllServices(boolean waitForPendingWork) {
        if (waitForPendingWork && WebApkServiceClient.hasPendingWork()) {
            PostTask.postDelayedTask(
                    TaskTraits.UI_DEFAULT,
                    ChromeWebApkHost::disconnectFromAllServicesImpl,
                    WAIT_FOR_WORK_DISCONNECT_SERVICE_DELAY_MS);
        } else {
            disconnectFromAllServicesImpl();
        }
    }

    private static void disconnectFromAllServicesImpl() {
        WebApkIdentityServiceClient.disconnectAll(ContextUtils.getApplicationContext());
        WebApkServiceClient.disconnectAll();
    }
}