chromium/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java

// Copyright 2015 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.customtabs;

import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_HEIGHT_DEFAULT;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_HEIGHT_FIXED;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_POSITION_DEFAULT;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_POSITION_END;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_DEFAULT;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_NONE;
import static androidx.browser.customtabs.CustomTabsIntent.ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_TOP;
import static androidx.browser.customtabs.CustomTabsIntent.CLOSE_BUTTON_POSITION_DEFAULT;
import static androidx.browser.customtabs.CustomTabsIntent.EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR;
import static androidx.browser.customtabs.CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_BREAKPOINT_DP;
import static androidx.browser.customtabs.CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE;
import static androidx.browser.customtabs.CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_ENABLE_MAXIMIZATION;
import static androidx.browser.customtabs.CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_POSITION;
import static androidx.browser.customtabs.CustomTabsIntent.EXTRA_ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION;
import static androidx.browser.customtabs.CustomTabsIntent.EXTRA_CLOSE_BUTTON_POSITION;
import static androidx.browser.customtabs.CustomTabsIntent.EXTRA_INITIAL_ACTIVITY_HEIGHT_PX;
import static androidx.browser.customtabs.CustomTabsIntent.EXTRA_INITIAL_ACTIVITY_WIDTH_PX;
import static androidx.browser.customtabs.CustomTabsIntent.EXTRA_TOOLBAR_CORNER_RADIUS_DP;

import android.app.Activity;
import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Network;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Pair;
import android.widget.RemoteViews;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.annotation.VisibleForTesting;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsIntent.ActivityHeightResizeBehavior;
import androidx.browser.customtabs.CustomTabsIntent.ActivitySideSheetDecorationType;
import androidx.browser.customtabs.CustomTabsIntent.ActivitySideSheetRoundedCornersPosition;
import androidx.browser.customtabs.CustomTabsIntent.CloseButtonPosition;
import androidx.browser.customtabs.CustomTabsSessionToken;
import androidx.browser.customtabs.TrustedWebUtils;
import androidx.browser.trusted.ScreenOrientation;
import androidx.browser.trusted.TrustedWebActivityDisplayMode;
import androidx.browser.trusted.TrustedWebActivityIntentBuilder;
import androidx.browser.trusted.sharing.ShareData;
import androidx.browser.trusted.sharing.ShareTarget;

import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.BuildInfo;
import org.chromium.base.IntentUtils;
import org.chromium.base.LocaleUtils;
import org.chromium.base.Log;
import org.chromium.base.ResettersForTesting;
import org.chromium.base.cached_flags.BooleanCachedFieldTrialParameter;
import org.chromium.base.cached_flags.StringCachedFieldTrialParameter;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.base.version_info.VersionInfo;
import org.chromium.build.BuildConfig;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
import org.chromium.chrome.browser.browserservices.intents.ColorProvider;
import org.chromium.chrome.browser.browserservices.intents.CustomButtonParams;
import org.chromium.chrome.browser.customtabs.CustomTabsFeatureUsage.CustomTabsFeature;
import org.chromium.chrome.browser.flags.ActivityType;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.share.ShareUtils;
import org.chromium.chrome.browser.ui.google_bottom_bar.GoogleBottomBarCoordinator;
import org.chromium.chrome.browser.ui.google_bottom_bar.proto.IntentParams.GoogleBottomBarIntentParams;
import org.chromium.components.browser_ui.widget.TintedDrawable;
import org.chromium.components.embedder_support.util.Origin;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.device.mojom.ScreenOrientationLockType;
import org.chromium.net.NetId;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/**
 * A model class that parses the incoming intent for Custom Tabs specific customization data.
 *
 * <p>Lifecycle: is activity-scoped, i.e. one instance per CustomTabActivity instance. Must be
 * re-created when color scheme changes, which happens automatically since color scheme change leads
 * to activity re-creation.
 */
public class CustomTabIntentDataProvider extends BrowserServicesIntentDataProvider {
    private static final String TAG = "CustomTabIntentData";

    @IntDef({LaunchSourceType.OTHER, LaunchSourceType.MEDIA_LAUNCHER_ACTIVITY})
    @Retention(RetentionPolicy.SOURCE)
    public @interface LaunchSourceType {
        int OTHER = -1;
        int MEDIA_LAUNCHER_ACTIVITY = 3;
    }

    @IntDef({
        BackgroundInteractBehavior.DEFAULT,
        BackgroundInteractBehavior.ON,
        BackgroundInteractBehavior.OFF
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface BackgroundInteractBehavior {
        int DEFAULT = 0;
        int ON = 1;
        int OFF = 2;

        // Must be the last one.
        int NUM_ENTRIES = 3;
    }

    /** Extra used to keep the caller alive. Its value is an Intent. */
    public static final String EXTRA_KEEP_ALIVE = "android.support.customtabs.extra.KEEP_ALIVE";

    public static final String ANIMATION_BUNDLE_PREFIX = "android:activity.";
    public static final String BUNDLE_PACKAGE_NAME = ANIMATION_BUNDLE_PREFIX + "packageName";
    public static final String BUNDLE_ENTER_ANIMATION_RESOURCE =
            ANIMATION_BUNDLE_PREFIX + "animEnterRes";
    public static final String BUNDLE_EXIT_ANIMATION_RESOURCE =
            ANIMATION_BUNDLE_PREFIX + "animExitRes";

    /** URL that should be loaded in place of the URL passed along in the data. */
    public static final String EXTRA_MEDIA_VIEWER_URL =
            "org.chromium.chrome.browser.customtabs.MEDIA_VIEWER_URL";

    /** Extra that enables embedded media experience. */
    public static final String EXTRA_ENABLE_EMBEDDED_MEDIA_EXPERIENCE =
            "org.chromium.chrome.browser.customtabs.EXTRA_ENABLE_EMBEDDED_MEDIA_EXPERIENCE";

    /** Indicates the type of UI Custom Tab should use. */
    public static final String EXTRA_UI_TYPE =
            "org.chromium.chrome.browser.customtabs.EXTRA_UI_TYPE";

    /** Extra that defines the initial background color (RGB color stored as an integer). */
    public static final String EXTRA_INITIAL_BACKGROUND_COLOR =
            "org.chromium.chrome.browser.customtabs.EXTRA_INITIAL_BACKGROUND_COLOR";

    /**
     * Indicates the source where the Custom Tab is launched. The value is defined as
     * {@link LaunchSourceType}.
     */
    public static final String EXTRA_BROWSER_LAUNCH_SOURCE =
            "org.chromium.chrome.browser.customtabs.EXTRA_BROWSER_LAUNCH_SOURCE";

    // Deprecated. Use CustomTabsIntent#EXTRA_TRANSLATE_LANGUAGE_TAG
    /**
     * Extra that, if set, specifies Translate UI should be triggered with
     * specified target language.
     */
    @Deprecated @VisibleForTesting
    static final String EXTRA_TRANSLATE_LANGUAGE =
            "androidx.browser.customtabs.extra.TRANSLATE_LANGUAGE";

    /**
     * Extra that, if set, specifies that the loaded page should be automatically translated once it
     * loads with the specified target language. This overrides EXTRA_TRANSLATE_LANGUAGE.
     */
    @VisibleForTesting
    static final String EXTRA_AUTO_TRANSLATE_LANGUAGE =
            "androidx.browser.customtabs.extra.AUTO_TRANSLATE_LANGUAGE";

    public static final String EXTRA_OPEN_IN_BROWSER_STATE =
            "androidx.browser.customtabs.extra.OPEN_IN_BROWSER_STATE";

    @IntDef({
        CustomTabsButtonState.BUTTON_STATE_OFF,
        CustomTabsButtonState.BUTTON_STATE_ON,
        CustomTabsButtonState.BUTTON_STATE_DEFAULT
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface CustomTabsButtonState {
        int BUTTON_STATE_OFF = CustomTabsIntent.SHARE_STATE_OFF;
        int BUTTON_STATE_ON = CustomTabsIntent.SHARE_STATE_ON;
        int BUTTON_STATE_DEFAULT = CustomTabsIntent.SHARE_STATE_DEFAULT;
    }

    /**
     * Parameter that, if true, indicates that the {@link EXTRA_AUTO_TRANSLATE_LANGUAGE} should be
     * automatically allowed from any first party package name.
     */
    public static final BooleanCachedFieldTrialParameter AUTO_TRANSLATE_ALLOW_ALL_FIRST_PARTIES =
            ChromeFeatureList.newBooleanCachedFieldTrialParameter(
                    ChromeFeatureList.CCT_AUTO_TRANSLATE, "allow_all_first_parties", false);

    /**
     * Parameter that lists a pipe ("|") separated list of package names from which the {@link
     * EXTRA_AUTO_TRANSLATE_LANGUAGE} should be allowed. This defaults to a single list item
     * consisting of the package name of the Android Google Search App.
     */
    public static final StringCachedFieldTrialParameter AUTO_TRANSLATE_PACKAGE_NAME_ALLOWLIST =
            ChromeFeatureList.newStringCachedFieldTrialParameter(
                    ChromeFeatureList.CCT_AUTO_TRANSLATE,
                    "package_names_allowlist",
                    "com.google.android.googlequicksearchbox");

    /** Pipe ("|") separated list of package names allowed to use the interactive Omnibox. */
    // TODO(b/40239922): remove when no longer relevant.
    private static final String DEFAULT_OMNIBOX_ALLOWED_PACKAGE_NAMES =
            BuildConfig.ENABLE_DEBUG_LOGS ? "org.chromium.customtabsclient" : null;

    public static final StringCachedFieldTrialParameter OMNIBOX_ALLOWED_PACKAGE_NAMES =
            ChromeFeatureList.newStringCachedFieldTrialParameter(
                    ChromeFeatureList.SEARCH_IN_CCT,
                    "omnibox_allowed_package_names",
                    DEFAULT_OMNIBOX_ALLOWED_PACKAGE_NAMES);

    private static final String EXTRA_TWA_DISCLOSURE_UI =
            "androidx.browser.trusted.extra.DISCLOSURE_VERSION";

    private static final int DEFAULT_BREAKPOINT_DP = 840;

    private static final int MAX_CUSTOM_MENU_ITEMS = 7;

    private static final int MAX_CUSTOM_TOOLBAR_ITEMS = 2;

    private static final String FIRST_PARTY_PITFALL_MSG =
            "The intent contains a non-default UI type, but it is not from a first-party app. "
                    + "To make locally-built Chrome a first-party app, sign with release-test "
                    + "signing keys and run on userdebug devices. See use_signing_keys GN arg.";

    // Extra whose value is an array of ints that is supplied to
    // SyntheticTrialRegistry::RegisterExternalExperiments().
    public static final String EXPERIMENT_IDS =
            "org.chromium.chrome.browser.customtabs.AGA_EXPERIMENT_IDS";

    // These Extra Intent parameters allow an Intent to enable or disable a set of Features.
    // The set of Features that may be enabled or disabled is restricted by the code,
    // and initially only two Features may be enabled together, or disabled together.
    public static final String EXPERIMENTS_ENABLE =
            "org.chromium.chrome.browser.customtabs.EXPERIMENTS_ENABLE";
    public static final String EXPERIMENTS_DISABLE =
            "org.chromium.chrome.browser.customtabs.EXPERIMENTS_DISABLE";

    /**
     * Extra that, if set, makes the Custom Tab Activity's height to be x pixels, the Custom Tab
     * will behave as a bottom sheet. x will be clamped between 50% and 100% of screen height.
     * TODO(jinsukkim): Deprecate this.
     */
    public static final String EXTRA_INITIAL_ACTIVITY_HEIGHT_IN_PIXEL_LEGACY =
            "androidx.browser.customtabs.extra.INITIAL_ACTIVITY_HEIGHT_IN_PIXEL";

    /**
     * Extra that, if set, allows you to interact with the background app when a PCCT is launched.
     * Note: Deprecated. Use {@link CustomTabsIntent#isBackgroundInteractionEnabled(Intent)}.
     */
    @Deprecated
    public static final String EXTRA_ENABLE_BACKGROUND_INTERACTION =
            "androidx.browser.customtabs.extra.ENABLE_BACKGROUND_INTERACTION";

    /**
     * Extra that, if set, makes the toolbar's top corner radii to be x pixels. This will only have
     * effect if the custom tab is behaving as a bottom sheet. Currently, this is capped at 16dp.
     * TODO(jinsukkim): Deprecate this.
     */
    public static final String EXTRA_TOOLBAR_CORNER_RADIUS_IN_PIXEL_LEGACY =
            "androidx.browser.customtabs.extra.TOOLBAR_CORNER_RADIUS_IN_PIXEL";

    private static final String DEFAULT_POLICY_PARAM_NAME = "default_policy";
    private static final String DEFAULT_POLICY_USE_DENYLIST = "use-denylist";
    private static final String DEFAULT_POLICY_USE_ALLOWLIST = "use-allowlist";
    private static final String ALLOWLIST_ENTRIES_PARAM_NAME = "allowlist_entries";
    private static final String DENYLIST_ENTRIES_PARAM_NAME = "denylist_entries";

    public static final StringCachedFieldTrialParameter THIRD_PARTIES_DEFAULT_POLICY =
            ChromeFeatureList.newStringCachedFieldTrialParameter(
                    ChromeFeatureList.CCT_RESIZABLE_FOR_THIRD_PARTIES,
                    DEFAULT_POLICY_PARAM_NAME,
                    DEFAULT_POLICY_USE_DENYLIST);
    public static final StringCachedFieldTrialParameter DENYLIST_ENTRIES =
            ChromeFeatureList.newStringCachedFieldTrialParameter(
                    ChromeFeatureList.CCT_RESIZABLE_FOR_THIRD_PARTIES,
                    DENYLIST_ENTRIES_PARAM_NAME,
                    "");
    public static final StringCachedFieldTrialParameter ALLOWLIST_ENTRIES =
            ChromeFeatureList.newStringCachedFieldTrialParameter(
                    ChromeFeatureList.CCT_RESIZABLE_FOR_THIRD_PARTIES,
                    ALLOWLIST_ENTRIES_PARAM_NAME,
                    "");

    /**
     * Extra that specifies the {@link PendingIntent} to be sent when the user swipes up from the
     * secondary (bottom) toolbar.
     *
     * <p>Use {@link CustomTabsIntent.Builder#setSecondaryToolbarSwipeUpGesture(PendingIntent)} or
     * {@link CustomTabsIntent#EXTRA_SECONDARY_TOOLBAR_SWIPE_UP_GESTURE} as this is deprecated.
     */
    @Deprecated
    public static final String EXTRA_SECONDARY_TOOLBAR_SWIPE_UP_ACTION =
            "androidx.browser.customtabs.extra.SECONDARY_TOOLBAR_SWIPE_UP_ACTION";

    /**
     * Allow user gestures on content area to be used not only for scrolling contents but also for
     * resizing CCT. Used for Partial Custom Tab Bottom Sheet only.
     */
    public static final String EXTRA_ACTIVITY_SCROLL_CONTENT_RESIZE =
            "androidx.browser.customtabs.extra.ACTIVITY_SCROLL_CONTENT_RESIZE";

    /**
     * Extra that specifies the {@link Network} to be bound when launching a custom tab or tabs that
     * have been pre-created.
     */
    public static final String EXTRA_NETWORK = "androidx.browser.customtabs.extra.NETWORK";

    private final Intent mIntent;
    private final CustomTabsSessionToken mSession;
    private final boolean mIsTrustedIntent;
    private final Intent mKeepAliveServiceIntent;
    private Bundle mAnimationBundle;

    private final int mUiType;
    private final int mTitleVisibilityState;
    private final String mMediaViewerUrl;
    private final boolean mEnableEmbeddedMediaExperience;
    private final boolean mIsFromMediaLauncherActivity;
    private final boolean mDisableStar;
    private final boolean mDisableDownload;
    private final @ActivityType int mActivityType;
    @Nullable private final List<String> mTrustedWebActivityAdditionalOrigins;
    @Nullable private Set<Origin> mAllTrustedWebActivityOrigins;
    @Nullable private final TrustedWebActivityDisplayMode mTrustedWebActivityDisplayMode;
    @Nullable private String mUrlToLoad;

    private boolean mEnableUrlBarHiding;
    private boolean mInteractWithBackground;
    private List<CustomButtonParams> mCustomButtonParams;
    private Drawable mCloseButtonIcon;
    private List<Pair<String, PendingIntent>> mMenuEntries = new ArrayList<>();
    private boolean mShowShareItemInMenu;
    private List<CustomButtonParams> mToolbarButtons = new ArrayList<>(1);
    private List<CustomButtonParams> mBottombarButtons = new ArrayList<>(2);
    private List<CustomButtonParams> mGoogleBottomBarButtons = new ArrayList<>();
    private RemoteViews mRemoteViews;
    @ActivitySideSheetDecorationType private int mSideSheetDecorationType;
    @ActivitySideSheetRoundedCornersPosition private int mSideSheetRoundedCornersPosition;
    private int[] mClickableViewIds;
    private PendingIntent mRemoteViewsPendingIntent;
    private PendingIntent mSecondaryToolbarSwipeUpPendingIntent;
    private PendingIntent.OnFinished mOnFinishedForTesting;

    /** Whether this CustomTabActivity was explicitly started by another Chrome Activity. */
    private final boolean mIsOpenedByChrome;

    /** ISO 639 language code */
    @Nullable private final String mTranslateLanguage;

    /** ISO 639 language code, overrides {@link mTranslateLanguage} if non-null. */
    @Nullable private final String mAutoTranslateLanguage;

    private final int mDefaultOrientation;

    @Nullable private final int[] mGsaExperimentIds;

    @NonNull private final ColorProvider mColorProvider;

    private final int mBreakPointDp;
    private final @Px int mInitialActivityHeight;
    private final @Px int mInitialActivityWidth;
    private final @Px int mPartialTabToolbarCornerRadius;

    private final boolean mIsPartialCustomTabFixedHeight;
    private final boolean mContentScrollMayResizeTab;

    /**
     * {@link Network} to be bound when launching a custom tab or tabs that have been pre-created.
     */
    @Nullable private final Network mNetwork;

    /** Add extras to customize menu items for opening Reader Mode UI custom tab from Chrome. */
    public static void addReaderModeUIExtras(Intent intent) {
        intent.putExtra(EXTRA_UI_TYPE, CustomTabsUiType.READER_MODE);
        IntentUtils.addTrustedIntentExtras(intent);
    }

    /**
     * Evaluates whether the passed Intent and/or CustomTabsSessionToken are
     * from a trusted source. Trusted in this case means from the app itself or
     * via a first-party application.
     *
     * @param intent The Intent used to start the custom tabs activity, or null.
     * @param session The connected session for the custom tabs activity, or null.
     * @return True if the intent or session are trusted.
     */
    public static boolean isTrustedCustomTab(Intent intent, CustomTabsSessionToken session) {
        if (IntentHandler.wasIntentSenderChrome(intent)) return true;
        String packageName = getClientPackageNameFromSessionOrCallingActivity(intent, session);
        return CustomTabsConnection.getInstance().isFirstParty(packageName);
    }

    static @Nullable String getClientPackageNameFromSessionOrCallingActivity(
            Intent intent, CustomTabsSessionToken session) {
        String packageNameFromSession =
                CustomTabsConnection.getInstance().getClientPackageNameForSession(session);
        if (!TextUtils.isEmpty(packageNameFromSession)) return packageNameFromSession;

        String packageNameFromIntent =
                IntentUtils.safeGetStringExtra(
                        intent, IntentHandler.EXTRA_CALLING_ACTIVITY_PACKAGE);
        if (!TextUtils.isEmpty(packageNameFromIntent)) return packageNameFromIntent;

        return null;
    }

    public static void configureIntentForResizableCustomTab(Context context, Intent intent) {
        CustomTabsSessionToken session = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
        boolean isTrustedCustomTab = isTrustedCustomTab(intent, session);
        String packageName = getClientPackageNameFromSessionOrCallingActivity(intent, session);
        @Px
        int initialActivityHeight =
                getInitialActivityHeight(
                        isTrustedCustomTab,
                        getInitialActivityHeightFromIntent(intent),
                        packageName);
        @Px
        int initialActivityWidth =
                getInitialActivityWidth(
                        isTrustedCustomTab, getInitialActivityWidthFromIntent(intent), packageName);
        if (initialActivityHeight <= 0 && initialActivityWidth <= 0) {
            // fallback to normal Custom Tab.
            return;
        }
        intent.setClassName(context, TranslucentCustomTabActivity.class.getName());
        // When scrolling up the web content, we don't want to hide the URL bar.
        intent.putExtra(CustomTabsIntent.EXTRA_ENABLE_URLBAR_HIDING, false);
    }

    private static @Px int getInitialActivityHeight(
            boolean isTrustedIntent, @Px int initialActivityHeight, String packageName) {
        boolean enabledDueToThirdParty =
                ChromeFeatureList.sCctResizableForThirdParties.isEnabled()
                        && isAllowedThirdParty(packageName);
        return (isTrustedIntent || enabledDueToThirdParty) ? initialActivityHeight : 0;
    }

    private static @Px int getInitialActivityWidth(
            boolean isTrustedIntent, @Px int initialActivityWidth, String packageName) {
        boolean enabledDueToThirdParty = isAllowedThirdParty(packageName);
        return (isTrustedIntent || enabledDueToThirdParty) ? initialActivityWidth : 0;
    }

    /** Returns the initial activity height in px. */
    private static int getInitialActivityHeightFromIntent(Intent intent) {
        int heightPx1 =
                IntentUtils.safeGetIntExtra(
                        intent,
                        CustomTabIntentDataProvider.EXTRA_INITIAL_ACTIVITY_HEIGHT_IN_PIXEL_LEGACY,
                        0);
        if (heightPx1 > 0) return heightPx1;
        int heightPx2 = IntentUtils.safeGetIntExtra(intent, EXTRA_INITIAL_ACTIVITY_HEIGHT_PX, 0);
        return heightPx2 > 0 ? heightPx2 : 0;
    }

    private static int getInitialActivityWidthFromIntent(Intent intent) {
        int widthPx = IntentUtils.safeGetIntExtra(intent, EXTRA_INITIAL_ACTIVITY_WIDTH_PX, 0);
        return widthPx > 0 ? widthPx : 0;
    }

    private static int getActivityBreakPointFromIntent(Intent intent) {
        int breakPointDp =
                IntentUtils.safeGetIntExtra(
                        intent, EXTRA_ACTIVITY_SIDE_SHEET_BREAKPOINT_DP, DEFAULT_BREAKPOINT_DP);
        return breakPointDp < 0 ? DEFAULT_BREAKPOINT_DP : breakPointDp;
    }

    private static int getActivitySideSheetDecorationTypeFromIntent(Intent intent) {
        int decorationType = CustomTabsIntent.getActivitySideSheetDecorationType(intent);
        return decorationType == ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DEFAULT
                        || decorationType < 0
                        || decorationType > ACTIVITY_SIDE_SHEET_DECORATION_TYPE_DIVIDER
                ? ACTIVITY_SIDE_SHEET_DECORATION_TYPE_SHADOW
                : decorationType;
    }

    private static int getActivitySideSheetRoundedCornersPositionFromIntent(Intent intent) {
        int roundedCornersPosition =
                CustomTabsIntent.getActivitySideSheetRoundedCornersPosition(intent);
        return roundedCornersPosition == ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_DEFAULT
                        || roundedCornersPosition < 0
                        || roundedCornersPosition > ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_TOP
                ? ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION_NONE
                : roundedCornersPosition;
    }

    /**
     * Extracts the name that identifies the embedding app from the referrer.
     *
     * @return Host name as an id if the referrer is of a well-formed URI with app intent scheme. If
     *     not, just the whole referrer string. TODO(crbug.com/40234088): Move this to
     *     IntentHandler.
     */
    static String getAppIdFromReferrer(Activity activity) {
        String referrer =
                CustomTabActivityLifecycleUmaTracker.getReferrerUriString(activity)
                        .toLowerCase(Locale.US);
        if (TextUtils.isEmpty(referrer)) return "";

        Uri uri = Uri.parse(referrer);
        boolean isUrl = TextUtils.equals(UrlConstants.APP_INTENT_SCHEME, uri.getScheme());
        if (isUrl) {
            String host = uri.getHost();
            if (!TextUtils.isEmpty(host)) return host;
        }
        return referrer;
    }

    /**
     * Constructs a {@link CustomTabIntentDataProvider}.
     *
     * @param intent The intent to launch the CCT.
     * @param colorScheme The colorScheme parameter specifies which color scheme the Custom Tab
     * should use.
     * It can currently be either {@link CustomTabsIntent#COLOR_SCHEME_LIGHT} or
     * {@link CustomTabsIntent#COLOR_SCHEME_DARK}.
     * If Custom Tab was launched with {@link CustomTabsIntent#COLOR_SCHEME_SYSTEM}, colorScheme
     * must reflect the current system setting. When the system setting changes, a new
     * CustomTabIntentDataProvider object must be created.
     */
    public CustomTabIntentDataProvider(Intent intent, Context context, int colorScheme) {
        if (intent == null) assert false;
        mIntent = intent;

        mSession = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
        mIsTrustedIntent = isTrustedCustomTab(intent, mSession);

        mAnimationBundle =
                IntentUtils.safeGetBundleExtra(
                        intent, CustomTabsIntent.EXTRA_EXIT_ANIMATION_BUNDLE);

        mKeepAliveServiceIntent = IntentUtils.safeGetParcelableExtra(intent, EXTRA_KEEP_ALIVE);

        mNetwork = IntentUtils.safeGetParcelableExtra(intent, EXTRA_NETWORK);

        mIsOpenedByChrome = IntentHandler.wasIntentSenderChrome(intent);

        final int requestedUiType =
                IntentUtils.safeGetIntExtra(intent, EXTRA_UI_TYPE, CustomTabsUiType.DEFAULT);
        mUiType = verifiedUiType(requestedUiType);

        mColorProvider = new CustomTabColorProviderImpl(intent, context, colorScheme);

        retrieveCustomButtons(intent, context);

        mEnableUrlBarHiding =
                IntentUtils.safeGetBooleanExtra(
                        intent, CustomTabsIntent.EXTRA_ENABLE_URLBAR_HIDING, true);
        mContentScrollMayResizeTab =
                IntentUtils.safeGetBooleanExtra(
                        intent, EXTRA_ACTIVITY_SCROLL_CONTENT_RESIZE, false);

        Bitmap bitmap =
                IntentUtils.safeGetParcelableExtra(
                        intent, CustomTabsIntent.EXTRA_CLOSE_BUTTON_ICON);
        if (bitmap != null && !checkCloseButtonSize(context, bitmap)) {
            IntentUtils.safeRemoveExtra(intent, CustomTabsIntent.EXTRA_CLOSE_BUTTON_ICON);
            bitmap.recycle();
            bitmap = null;
        }
        if (bitmap == null) {
            mCloseButtonIcon =
                    TintedDrawable.constructTintedDrawable(context, R.drawable.btn_close);
        } else {
            mCloseButtonIcon = new TintedDrawable(context, bitmap);
        }

        List<Bundle> menuItems =
                IntentUtils.getParcelableArrayListExtra(intent, CustomTabsIntent.EXTRA_MENU_ITEMS);

        addOpenInBrowserOption(intent, context);
        updateExtraMenuItems(menuItems);
        // Disable CCT share options for automotive. See b/300292495.
        if (ShareUtils.enableShareForAutomotive(true)) {
            addShareOption(intent, context);
        }

        boolean isTwa =
                mSession != null
                        && IntentUtils.safeGetBooleanExtra(
                                intent,
                                TrustedWebUtils.EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY,
                                false);

        mActivityType =
                isTwa
                        ? ActivityType.TRUSTED_WEB_ACTIVITY
                        : isAuthTab() ? ActivityType.AUTH_TAB : ActivityType.CUSTOM_TAB;
        mTrustedWebActivityAdditionalOrigins =
                IntentUtils.safeGetStringArrayListExtra(
                        intent, TrustedWebActivityIntentBuilder.EXTRA_ADDITIONAL_TRUSTED_ORIGINS);

        // Do not fill in `mAllTrustedWebActivityOrigins` yet, because we cannot `getUrlToLoad()`
        // until native is loaded.

        mTrustedWebActivityDisplayMode = resolveTwaDisplayMode();
        mTitleVisibilityState =
                IntentUtils.safeGetIntExtra(
                        intent,
                        CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE,
                        CustomTabsIntent.NO_TITLE);
        mRemoteViews =
                IntentUtils.safeGetParcelableExtra(intent, CustomTabsIntent.EXTRA_REMOTEVIEWS);
        mClickableViewIds =
                IntentUtils.safeGetIntArrayExtra(
                        intent, CustomTabsIntent.EXTRA_REMOTEVIEWS_VIEW_IDS);
        mRemoteViewsPendingIntent =
                IntentUtils.safeGetParcelableExtra(
                        intent, CustomTabsIntent.EXTRA_REMOTEVIEWS_PENDINGINTENT);
        mSecondaryToolbarSwipeUpPendingIntent = getSecondaryToolbarSwipeUpGesture(intent);
        mMediaViewerUrl =
                isMediaViewer()
                        ? IntentUtils.safeGetStringExtra(intent, EXTRA_MEDIA_VIEWER_URL)
                        : null;
        mEnableEmbeddedMediaExperience =
                isTrustedIntent()
                        && IntentUtils.safeGetBooleanExtra(
                                intent, EXTRA_ENABLE_EMBEDDED_MEDIA_EXPERIENCE, false);
        mIsFromMediaLauncherActivity =
                isTrustedIntent()
                        && (IntentUtils.safeGetIntExtra(
                                        intent, EXTRA_BROWSER_LAUNCH_SOURCE, LaunchSourceType.OTHER)
                                == LaunchSourceType.MEDIA_LAUNCHER_ACTIVITY);
        mDisableStar = !CustomTabsIntent.isBookmarksButtonEnabled(intent);
        mDisableDownload = !CustomTabsIntent.isDownloadButtonEnabled(intent);

        mTranslateLanguage = getTranslateLanguage(intent);

        mAutoTranslateLanguage =
                IntentUtils.safeGetStringExtra(intent, EXTRA_AUTO_TRANSLATE_LANGUAGE);

        // Import the {@link ScreenOrientation}.
        mDefaultOrientation =
                convertOrientationType(
                        IntentUtils.safeGetIntExtra(
                                intent,
                                TrustedWebActivityIntentBuilder.EXTRA_SCREEN_ORIENTATION,
                                ScreenOrientation.DEFAULT));

        mGsaExperimentIds = IntentUtils.safeGetIntArrayExtra(intent, EXPERIMENT_IDS);
        boolean usingDynamicFeatures =
                CustomTabsConnection.getInstance().setupDynamicFeatures(intent);

        mBreakPointDp = getActivityBreakPointFromIntent(intent);
        mInitialActivityHeight = getInitialActivityHeightFromIntent(intent);
        mInitialActivityWidth = getInitialActivityWidthFromIntent(intent);
        mPartialTabToolbarCornerRadius = getToolbarCornerRadiusFromIntent(context, intent);
        // The default behavior is that the PCCT's height is resizable.
        @ActivityHeightResizeBehavior
        int activityHeightResizeBehavior =
                IntentUtils.safeGetIntExtra(
                        intent, EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR, ACTIVITY_HEIGHT_DEFAULT);
        mIsPartialCustomTabFixedHeight = activityHeightResizeBehavior == ACTIVITY_HEIGHT_FIXED;

        mInteractWithBackground = CustomTabsIntent.isBackgroundInteractionEnabled(intent);
        if (IntentUtils.safeHasExtra(intent, EXTRA_ENABLE_BACKGROUND_INTERACTION)) {
            @BackgroundInteractBehavior
            int backgroundInteractBehavior =
                    IntentUtils.safeGetIntExtra(
                            intent,
                            EXTRA_ENABLE_BACKGROUND_INTERACTION,
                            BackgroundInteractBehavior.DEFAULT);
            mInteractWithBackground = backgroundInteractBehavior != BackgroundInteractBehavior.OFF;
        }
        mSideSheetDecorationType = getActivitySideSheetDecorationTypeFromIntent(intent);
        mSideSheetRoundedCornersPosition =
                getActivitySideSheetRoundedCornersPositionFromIntent(intent);

        logCustomTabFeatures(intent, colorScheme, usingDynamicFeatures);
        String packageName = getClientPackageNameFromSessionOrCallingActivity(mIntent, mSession);
        RecordHistogram.recordBooleanHistogram(
                "CustomTabs.HasNonSpoofablePackageName", !TextUtils.isEmpty(packageName));
    }

    /** Returns the toolbar corner radius in px. */
    private static int getToolbarCornerRadiusFromIntent(Context context, Intent intent) {
        int defaultRadius =
                context.getResources()
                        .getDimensionPixelSize(R.dimen.custom_tabs_default_corner_radius);
        int radiusPx =
                IntentUtils.safeGetIntExtra(intent, EXTRA_TOOLBAR_CORNER_RADIUS_IN_PIXEL_LEGACY, 0);
        if (radiusPx > 0) return radiusPx;

        int radiusDp = IntentUtils.safeGetIntExtra(intent, EXTRA_TOOLBAR_CORNER_RADIUS_DP, 0);
        if (radiusDp > 0) {
            return Math.round(radiusDp * context.getResources().getDisplayMetrics().density);
        }

        return defaultRadius;
    }

    private static PendingIntent getSecondaryToolbarSwipeUpGesture(Intent intent) {
        PendingIntent pendingIntent = CustomTabsIntent.getSecondaryToolbarSwipeUpGesture(intent);
        if (pendingIntent == null) {
            pendingIntent =
                    IntentUtils.safeGetParcelableExtra(
                            intent,
                            CustomTabIntentDataProvider.EXTRA_SECONDARY_TOOLBAR_SWIPE_UP_ACTION);
        }
        return pendingIntent;
    }

    private static String getTranslateLanguage(Intent intent) {
        String translateLanguage = null;
        Locale locale = CustomTabsIntent.getTranslateLocale(intent);
        if (locale != null) translateLanguage = LocaleUtils.toLanguageTag(locale);
        if (TextUtils.isEmpty(translateLanguage)) {
            translateLanguage = IntentUtils.safeGetStringExtra(intent, EXTRA_TRANSLATE_LANGUAGE);
        }
        return translateLanguage;
    }

    private void updateExtraMenuItems(List<Bundle> menuItems) {
        if (menuItems == null) return;
        for (int i = 0; i < Math.min(MAX_CUSTOM_MENU_ITEMS, menuItems.size()); i++) {
            Bundle bundle = menuItems.get(i);
            String title = IntentUtils.safeGetString(bundle, CustomTabsIntent.KEY_MENU_ITEM_TITLE);
            PendingIntent pendingIntent =
                    IntentUtils.safeGetParcelable(bundle, CustomTabsIntent.KEY_PENDING_INTENT);
            if (TextUtils.isEmpty(title) || pendingIntent == null) {
                continue;
            }
            mMenuEntries.add(new Pair<String, PendingIntent>(title, pendingIntent));
        }
    }

    /**
     * Triggers the client-defined action when the user clicks a custom menu item.
     *
     * @param activity The {@link Activity} to use for sending the {@link PendingIntent}.
     * @param menuIndex The index that the menu item is shown in the result of {@link
     *     #getMenuTitles()}.
     * @param url The URL to attach as additional data to the {@link PendingIntent}.
     * @param title The title to attach as additional data to the {@link PendingIntent}.
     */
    public void clickMenuItemWithUrlAndTitle(
            Activity activity, int menuIndex, String url, String title) {
        Intent addedIntent = new Intent();
        addedIntent.setData(Uri.parse(url));
        addedIntent.putExtra(Intent.EXTRA_SUBJECT, title);
        try {
            // Media viewers pass in PendingIntents that contain CHOOSER Intents.  Setting the data
            // in these cases prevents the Intent from firing correctly.
            String menuTitle = mMenuEntries.get(menuIndex).first;
            PendingIntent pendingIntent = mMenuEntries.get(menuIndex).second;
            ActivityOptions options = ActivityOptions.makeBasic();
            ApiCompatibilityUtils.setActivityOptionsBackgroundActivityStartMode(options);
            pendingIntent.send(
                    activity,
                    0,
                    isMediaViewer() ? null : addedIntent,
                    mOnFinishedForTesting,
                    null,
                    null,
                    options.toBundle());
            if (shouldEnableEmbeddedMediaExperience()
                    && TextUtils.equals(
                            menuTitle, activity.getString(R.string.download_manager_open_with))) {
                RecordUserAction.record("CustomTabsMenuCustomMenuItem.DownloadsUI.OpenWith");
            }
        } catch (CanceledException e) {
            Log.e(TAG, "Custom tab in Chrome failed to send pending intent.");
        }
    }

    private boolean checkCloseButtonSize(Context context, Bitmap bitmap) {
        int size = context.getResources().getDimensionPixelSize(R.dimen.toolbar_icon_height);
        if (bitmap.getHeight() == size && bitmap.getWidth() == size) return true;
        return false;
    }

    /**
     * Get the verified UI type, according to the intent extras, and whether the intent is trusted.
     * @param requestedUiType requested UI type in the intent, unqualified
     * @return verified UI type
     */
    private int verifiedUiType(int requestedUiType) {
        if (!isTrustedIntent()) {
            if (VersionInfo.isLocalBuild()) Log.w(TAG, FIRST_PARTY_PITFALL_MSG);
            return CustomTabsUiType.DEFAULT;
        }

        return requestedUiType;
    }

    /**
     * Gets custom buttons from the intent and updates {@link #mCustomButtonParams}, {@link
     * #mBottombarButtons} and {@link #mToolbarButtons}.
     */
    private void retrieveCustomButtons(Intent intent, Context context) {
        assert mCustomButtonParams == null;
        mCustomButtonParams = CustomButtonParamsImpl.fromIntent(context, intent);
        Set<Integer> googleBottomBarSupportedCustomButtonParamIds =
                getGoogleBottomBarSupportedCustomButtonParamIds();
        for (CustomButtonParams params : mCustomButtonParams) {
            if (googleBottomBarSupportedCustomButtonParamIds.contains(params.getId())) {
                mGoogleBottomBarButtons.add(params);
                params.updateShowOnToolbar(false);
            } else if (!params.showOnToolbar()) {
                mBottombarButtons.add(params);
            } else if (mToolbarButtons.size() < getMaxCustomToolbarItems()) {
                mToolbarButtons.add(params);
            } else {
                Log.w(TAG, "Only %d items are allowed in the toolbar", getMaxCustomToolbarItems());
            }
        }

        for (CustomButtonParams params :
                getAdditionalSupportedGoogleBottomBarCustomButtonParams(context)) {
            params.updateShowOnToolbar(false);
            mCustomButtonParams.add(params);
            mGoogleBottomBarButtons.add(params);
        }
    }

    /**
     * Determines which buttons should be displayed in the Google Bottom Bar.
     *
     * @return A set of integers representing the customButtonParamIds of the buttons that should be
     *     displayed in the Google Bottom Bar. If the Google Bottom Bar is not enabled, an empty set
     *     is returned.
     */
    private Set<Integer> getGoogleBottomBarSupportedCustomButtonParamIds() {
        if (!isGoogleBottomBarEnabled(this)) {
            return Set.of();
        }
        GoogleBottomBarIntentParams intentParams =
                CustomTabsConnection.getInstance().getGoogleBottomBarIntentParams(this);
        return GoogleBottomBarCoordinator.getSupportedCustomButtonParamIds(intentParams);
    }

    /**
     * Retrieves a list of additional CustomButtonParams that are only supported for the Google
     * Bottom Bar and should never be shown on the CCT toolbar.
     *
     * @param context Current context.
     * @return A list of {@link CustomButtonParams}. If the Google Bottom Bar is not enabled, or no
     *     supported buttons are found, an empty list is returned.
     */
    private List<CustomButtonParams> getAdditionalSupportedGoogleBottomBarCustomButtonParams(
            Context context) {
        List<CustomButtonParams> supportedGoogleBottomBarCustomButtonParams = new ArrayList<>();
        if (!isGoogleBottomBarEnabled(this)) {
            return supportedGoogleBottomBarCustomButtonParams;
        }
        List<Bundle> googleBottomBarButtonBundles =
                CustomTabsConnection.getInstance().getGoogleBottomBarButtons(this);
        List<CustomButtonParams> googleBottomBarButtons =
                CustomButtonParamsImpl.fromBundleList(
                        context, googleBottomBarButtonBundles, /* tinted= */ false);
        for (CustomButtonParams params : googleBottomBarButtons) {
            if (GoogleBottomBarCoordinator.isSupported(params.getId())) {
                supportedGoogleBottomBarCustomButtonParams.add(params);
            } else {
                Log.w(TAG, "Unused GoogleBottomBarButton id: %s", params.getId());
            }
        }
        return supportedGoogleBottomBarCustomButtonParams;
    }

    private static boolean isGoogleBottomBarEnabled(BrowserServicesIntentDataProvider provider) {
        return GoogleBottomBarCoordinator.isFeatureEnabled()
                && CustomTabsConnection.getInstance()
                        .shouldEnableGoogleBottomBarForIntent(provider);
    }

    private int getMaxCustomToolbarItems() {
        return MAX_CUSTOM_TOOLBAR_ITEMS;
    }

    /**
     * Adds a share option to the custom tab according to the {@link
     * CustomTabsIntent#EXTRA_SHARE_STATE} stored in the intent.
     *
     * <p>Shows share options according to the following rules:
     *
     * <ul>
     *   <li>If {@link CustomTabsIntent#SHARE_STATE_ON} or {@link
     *       CustomTabsIntent#SHARE_STATE_DEFAULT}, add to the top toolbar if empty, otherwise add
     *       to the overflow menu if it is not customized.
     *   <li>If {@link CustomTabsIntent#SHARE_STATE_OFF}, add to the overflow menu depending on
     *       {@link CustomTabsIntent#EXTRA_DEFAULT_SHARE_MENU_ITEM}.
     * </ul>
     */
    private void addShareOption(Intent intent, Context context) {
        boolean usingInteractiveOmnibox =
                CustomTabsConnection.getInstance().shouldEnableOmniboxForIntent(this);
        int shareState =
                IntentUtils.safeGetIntExtra(
                        intent,
                        CustomTabsIntent.EXTRA_SHARE_STATE,
                        usingInteractiveOmnibox
                                ? CustomTabsIntent.SHARE_STATE_OFF
                                : CustomTabsIntent.SHARE_STATE_DEFAULT);
        if (shareState == CustomTabsIntent.SHARE_STATE_DEFAULT) {
            if (mToolbarButtons.isEmpty()) {
                mToolbarButtons.add(
                        CustomButtonParamsImpl.createShareButton(
                                context, getColorProvider().getToolbarColor()));
            } else if (mMenuEntries.isEmpty()) {
                mShowShareItemInMenu = true;
            }
        } else if (shareState == CustomTabsIntent.SHARE_STATE_ON) {
            if (mToolbarButtons.isEmpty()) {
                mToolbarButtons.add(
                        CustomButtonParamsImpl.createShareButton(
                                context, getColorProvider().getToolbarColor()));
            } else {
                mShowShareItemInMenu = true;
            }
        } else {
            mShowShareItemInMenu =
                    IntentUtils.safeGetBooleanExtra(
                            intent,
                            CustomTabsIntent.EXTRA_DEFAULT_SHARE_MENU_ITEM,
                            mIsOpenedByChrome && mUiType == CustomTabsUiType.DEFAULT);
        }
    }

    private void addOpenInBrowserOption(Intent intent, Context context) {
        boolean usingInteractiveOmnibox =
                CustomTabsConnection.getInstance().shouldEnableOmniboxForIntent(this);
        int openInBrowserState =
                IntentUtils.safeGetIntExtra(
                        intent,
                        EXTRA_OPEN_IN_BROWSER_STATE,
                        usingInteractiveOmnibox
                                ? CustomTabsButtonState.BUTTON_STATE_DEFAULT
                                : CustomTabsButtonState.BUTTON_STATE_OFF);

        if (openInBrowserState == CustomTabsButtonState.BUTTON_STATE_DEFAULT
                && isInteractiveOmniboxAllowed()) {
            openInBrowserState = CustomTabsButtonState.BUTTON_STATE_ON;
        }

        if (openInBrowserState == CustomTabsButtonState.BUTTON_STATE_ON) {
            if (mToolbarButtons.isEmpty()) {
                mToolbarButtons.add(
                        CustomButtonParamsImpl.createOpenInBrowserButton(
                                context, getColorProvider().getToolbarColor()));
            }
        }
    }

    private String resolveUrlToLoad(Intent intent) {
        String url = IntentHandler.getUrlFromIntent(intent);

        // Intents fired for media viewers have an additional file:// URI passed along so that the
        // tab can display the actual filename to the user when it is loaded.
        if (isMediaViewer()) {
            String mediaViewerUrl = getMediaViewerUrl();
            if (!TextUtils.isEmpty(mediaViewerUrl)) {
                Uri mediaViewerUri = Uri.parse(mediaViewerUrl);
                if (UrlConstants.FILE_SCHEME.equals(mediaViewerUri.getScheme())) {
                    url = mediaViewerUrl;
                }
            }
        }
        return url;
    }

    private @Nullable TrustedWebActivityDisplayMode resolveTwaDisplayMode() {
        Bundle bundle =
                IntentUtils.safeGetBundleExtra(
                        mIntent, TrustedWebActivityIntentBuilder.EXTRA_DISPLAY_MODE);
        if (bundle == null) {
            return null;
        }
        try {
            return TrustedWebActivityDisplayMode.fromBundle(bundle);
        } catch (Throwable e) {
            return null;
        }
    }

    /**
     * Returns the {@link ScreenOrientationLockType} which matches {@link ScreenOrientation}.
     * @param orientation {@link ScreenOrientation}
     * @return The matching ScreenOrientationLockType. {@link ScreenOrientationLockType#DEFAULT} if
     *         there is no match.
     */
    private static int convertOrientationType(@ScreenOrientation.LockType int orientation) {
        switch (orientation) {
            case ScreenOrientation.DEFAULT:
                return ScreenOrientationLockType.DEFAULT;
            case ScreenOrientation.PORTRAIT_PRIMARY:
                return ScreenOrientationLockType.PORTRAIT_PRIMARY;
            case ScreenOrientation.PORTRAIT_SECONDARY:
                return ScreenOrientationLockType.PORTRAIT_SECONDARY;
            case ScreenOrientation.LANDSCAPE_PRIMARY:
                return ScreenOrientationLockType.LANDSCAPE_PRIMARY;
            case ScreenOrientation.LANDSCAPE_SECONDARY:
                return ScreenOrientationLockType.LANDSCAPE_SECONDARY;
            case ScreenOrientation.ANY:
                return ScreenOrientationLockType.ANY;
            case ScreenOrientation.LANDSCAPE:
                return ScreenOrientationLockType.LANDSCAPE;
            case ScreenOrientation.PORTRAIT:
                return ScreenOrientationLockType.PORTRAIT;
            case ScreenOrientation.NATURAL:
                return ScreenOrientationLockType.NATURAL;
            default:
                Log.w(
                        TAG,
                        "The provided orientaton is not supported, orientation = %d",
                        orientation);
                return ScreenOrientationLockType.DEFAULT;
        }
    }

    /**
     * Logs the usage of intents of all CCT features to a large enum histogram in order to track
     * usage by apps.
     * @param intent The intent used to launch the CCT.
     * @param colorScheme The requested color scheme to use with the CCT.
     * @param isUsingDynamicFeatures Whether the intent specified Features to dynamically enable or
     *                               disable.
     */
    private void logCustomTabFeatures(
            Intent intent, int colorScheme, boolean isUsingDynamicFeatures) {
        if (!CustomTabsFeatureUsage.isEnabled()) return;
        CustomTabsFeatureUsage featureUsage = new CustomTabsFeatureUsage();

        // Ordering: Log all the features ordered by CustomTabsFeature enum, when they apply.
        if (mAnimationBundle != null) {
            featureUsage.log(CustomTabsFeature.EXTRA_EXIT_ANIMATION_BUNDLE);
        }
        if (IntentUtils.safeHasExtra(intent, CustomTabsIntent.EXTRA_TINT_ACTION_BUTTON)) {
            featureUsage.log(CustomTabsFeature.EXTRA_TINT_ACTION_BUTTON);
        }
        if (IntentUtils.safeHasExtra(intent, EXTRA_INITIAL_BACKGROUND_COLOR)) {
            featureUsage.log(CustomTabsFeature.EXTRA_INITIAL_BACKGROUND_COLOR);
        }
        if (mInteractWithBackground) {
            featureUsage.log(CustomTabsFeature.EXTRA_ENABLE_BACKGROUND_INTERACTION);
        }
        if (mCloseButtonIcon != null) featureUsage.log(CustomTabsFeature.EXTRA_CLOSE_BUTTON_ICON);
        if (getCloseButtonPosition() != CLOSE_BUTTON_POSITION_DEFAULT) {
            featureUsage.log(CustomTabsFeature.EXTRA_CLOSE_BUTTON_POSITION);
        }
        if (colorScheme == CustomTabsIntent.COLOR_SCHEME_DARK) {
            featureUsage.log(CustomTabsFeature.CTF_DARK);
        }
        if (colorScheme == CustomTabsIntent.COLOR_SCHEME_LIGHT) {
            featureUsage.log(CustomTabsFeature.CTF_LIGHT);
        }
        if (IntentUtils.safeHasExtra(intent, CustomTabsIntent.EXTRA_COLOR_SCHEME)) {
            featureUsage.log(CustomTabsFeature.EXTRA_COLOR_SCHEME);
        }
        if (colorScheme == CustomTabsIntent.COLOR_SCHEME_SYSTEM) {
            featureUsage.log(CustomTabsFeature.CTF_SYSTEM);
        }
        if (mDisableDownload) featureUsage.log(CustomTabsFeature.EXTRA_DISABLE_DOWNLOAD_BUTTON);
        if (mDisableStar) featureUsage.log(CustomTabsFeature.EXTRA_DISABLE_STAR_BUTTON);
        if (mGsaExperimentIds != null) featureUsage.log(CustomTabsFeature.EXPERIMENT_IDS);
        if (IntentUtils.safeHasExtra(
                        intent,
                        CustomTabIntentDataProvider.EXTRA_INITIAL_ACTIVITY_HEIGHT_IN_PIXEL_LEGACY)
                || IntentUtils.safeHasExtra(intent, EXTRA_INITIAL_ACTIVITY_HEIGHT_PX)) {
            featureUsage.log(CustomTabsFeature.EXTRA_INITIAL_ACTIVITY_HEIGHT_PX);
        }
        if (IntentUtils.safeHasExtra(intent, EXTRA_INITIAL_ACTIVITY_WIDTH_PX)) {
            featureUsage.log(CustomTabsFeature.EXTRA_INITIAL_ACTIVITY_WIDTH_PX);
        }
        if (IntentUtils.safeHasExtra(intent, EXTRA_ACTIVITY_SIDE_SHEET_BREAKPOINT_DP)) {
            featureUsage.log(CustomTabsFeature.EXTRA_ACTIVITY_SIDE_SHEET_BREAKPOINT_DP);
        }
        if (IntentUtils.safeHasExtra(intent, EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE)) {
            featureUsage.log(CustomTabsFeature.EXTRA_ACTIVITY_SIDE_SHEET_DECORATION_TYPE);
        }
        if (IntentUtils.safeHasExtra(intent, EXTRA_ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION)) {
            featureUsage.log(CustomTabsFeature.EXTRA_ACTIVITY_SIDE_SHEET_ROUNDED_CORNERS_POSITION);
        }
        if (mEnableEmbeddedMediaExperience) {
            featureUsage.log(CustomTabsFeature.EXTRA_ENABLE_EMBEDDED_MEDIA_EXPERIENCE);
        }
        if (mIsFromMediaLauncherActivity) {
            featureUsage.log(CustomTabsFeature.EXTRA_BROWSER_LAUNCH_SOURCE);
        }
        if (mMediaViewerUrl != null) featureUsage.log(CustomTabsFeature.EXTRA_MEDIA_VIEWER_URL);
        if (mMenuEntries != null) featureUsage.log(CustomTabsFeature.EXTRA_MENU_ITEMS);
        if (IntentUtils.safeHasExtra(intent, IntentHandler.EXTRA_CALLING_ACTIVITY_PACKAGE)) {
            featureUsage.log(CustomTabsFeature.EXTRA_CALLING_ACTIVITY_PACKAGE);
        }
        if (getClientPackageName() != null) featureUsage.log(CustomTabsFeature.CTF_PACKAGE_NAME);
        if (IntentUtils.safeHasExtra(intent, EXTRA_TOOLBAR_CORNER_RADIUS_IN_PIXEL_LEGACY)
                || IntentUtils.safeHasExtra(intent, EXTRA_TOOLBAR_CORNER_RADIUS_DP)) {
            featureUsage.log(CustomTabsFeature.EXTRA_TOOLBAR_CORNER_RADIUS_DP);
        }
        if (isPartialHeightCustomTab()) {
            featureUsage.log(CustomTabsFeature.CTF_PARTIAL);
        }
        if (isPartialWidthCustomTab()) {
            featureUsage.log(CustomTabsFeature.CTF_PARTIAL_SIDE_SHEET);
        }
        if (mRemoteViewsPendingIntent != null) {
            featureUsage.log(CustomTabsFeature.EXTRA_REMOTEVIEWS_PENDINGINTENT);
        }
        if (mClickableViewIds != null) {
            featureUsage.log(CustomTabsFeature.EXTRA_REMOTEVIEWS_VIEW_IDS);
        }
        if (mRemoteViews != null) featureUsage.log(CustomTabsFeature.EXTRA_REMOTEVIEWS);
        if (!mIsPartialCustomTabFixedHeight) {
            featureUsage.log(CustomTabsFeature.EXTRA_ACTIVITY_HEIGHT_RESIZE_BEHAVIOR);
        }
        if (mDefaultOrientation != ScreenOrientation.DEFAULT) {
            featureUsage.log(CustomTabsFeature.EXTRA_SCREEN_ORIENTATION);
        }
        if (mIsOpenedByChrome) featureUsage.log(CustomTabsFeature.CTF_SENT_BY_CHROME);
        if (mKeepAliveServiceIntent != null) featureUsage.log(CustomTabsFeature.EXTRA_KEEP_ALIVE);
        if (mShowShareItemInMenu) featureUsage.log(CustomTabsFeature.EXTRA_DEFAULT_SHARE_MENU_ITEM);
        if (IntentUtils.safeHasExtra(intent, CustomTabsIntent.EXTRA_SHARE_STATE)) {
            featureUsage.log(CustomTabsFeature.EXTRA_SHARE_STATE);
        }
        if (mTitleVisibilityState != CustomTabsIntent.NO_TITLE) {
            featureUsage.log(CustomTabsFeature.EXTRA_TITLE_VISIBILITY_STATE);
        }
        if (IntentUtils.safeHasExtra(intent, CustomTabsIntent.EXTRA_TOOLBAR_ITEMS)) {
            featureUsage.log(CustomTabsFeature.EXTRA_TOOLBAR_ITEMS);
        }
        if (mTranslateLanguage != null) {
            featureUsage.log(CustomTabsFeature.EXTRA_TRANSLATE_LANGUAGE);
        }
        if (mAutoTranslateLanguage != null) {
            featureUsage.log(CustomTabsFeature.EXTRA_AUTO_TRANSLATE_LANGUAGE);
        }
        if (IntentUtils.safeHasExtra(intent, TrustedWebActivityIntentBuilder.EXTRA_DISPLAY_MODE)) {
            featureUsage.log(CustomTabsFeature.EXTRA_DISPLAY_MODE);
        }
        if (mActivityType == ActivityType.TRUSTED_WEB_ACTIVITY) {
            featureUsage.log(CustomTabsFeature.EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY);
        }
        if (mTrustedWebActivityAdditionalOrigins != null) {
            featureUsage.log(CustomTabsFeature.EXTRA_ADDITIONAL_TRUSTED_ORIGINS);
        }
        if (mEnableUrlBarHiding) featureUsage.log(CustomTabsFeature.EXTRA_ENABLE_URLBAR_HIDING);
        if (isUsingDynamicFeatures) {
            featureUsage.log(CustomTabsFeature.EXTRA_INTENT_FEATURE_OVERRIDES);
        }
        if (showSideSheetMaximizeButton()) {
            featureUsage.log(CustomTabsFeature.EXTRA_ACTIVITY_SIDE_SHEET_ENABLE_MAXIMIZATION);
        }
        if (mSecondaryToolbarSwipeUpPendingIntent != null) {
            featureUsage.log(CustomTabsFeature.EXTRA_SECONDARY_TOOLBAR_SWIPE_UP_ACTION);
        }
        if (IntentUtils.safeHasExtra(intent, EXTRA_ACTIVITY_SIDE_SHEET_POSITION)) {
            featureUsage.log(CustomTabsFeature.EXTRA_ACTIVITY_SIDE_SHEET_POSITION);
        }
        if (CustomTabsConnection.getInstance().shouldEnableGoogleBottomBarForIntent(this)) {
            featureUsage.log(CustomTabsFeature.EXTRA_ENABLE_GOOGLE_BOTTOM_BAR);
        }
        if (CustomTabsConnection.getInstance().hasExtraGoogleBottomBarButtons(this)) {
            featureUsage.log(CustomTabsFeature.EXTRA_GOOGLE_BOTTOM_BAR_BUTTONS);
        }
        if (mNetwork != null) featureUsage.log(CustomTabsFeature.EXTRA_NETWORK);
    }

    @Override
    public int getDefaultOrientation() {
        return mDefaultOrientation;
    }

    @Override
    public @ActivityType int getActivityType() {
        return mActivityType;
    }

    @Override
    public Intent getIntent() {
        return mIntent;
    }

    @Override
    public @Nullable CustomTabsSessionToken getSession() {
        return mSession;
    }

    @Override
    public @Nullable Intent getKeepAliveServiceIntent() {
        return mKeepAliveServiceIntent;
    }

    @Override
    public boolean isPartialHeightCustomTab() {
        if (BuildInfo.getInstance().isAutomotive) return false;
        return getInitialActivityHeight() > 0;
    }

    @Override
    public boolean isPartialWidthCustomTab() {
        if (BuildInfo.getInstance().isAutomotive) return false;
        return getInitialActivityWidth() > 0;
    }

    @Override
    public boolean isPartialCustomTab() {
        return isPartialHeightCustomTab() || isPartialWidthCustomTab();
    }

    @Override
    public boolean shouldAnimateOnFinish() {
        return getInsecureClientPackageNameForOnFinishAnimation() != null;
    }

    /** Returns client package name for finishing animation. */
    public String getInsecureClientPackageNameForOnFinishAnimation() {
        // The package name may come from the insecure info contained in the animation
        // bundle which won't do any harm in the operation.
        if (mAnimationBundle == null) return null;
        return mAnimationBundle.getString(BUNDLE_PACKAGE_NAME);
    }

    @Override
    public @Nullable String getClientPackageName() {
        return getClientPackageNameFromSessionOrCallingActivity(mIntent, mSession);
    }

    @Override
    public @Nullable String getClientPackageNameIdentitySharing() {
        return IntentUtils.safeGetStringExtra(mIntent, IntentHandler.EXTRA_LAUNCHED_FROM_PACKAGE);
    }

    @Override
    public int getAnimationEnterRes() {
        return shouldAnimateOnFinish()
                ? mAnimationBundle.getInt(BUNDLE_ENTER_ANIMATION_RESOURCE)
                : 0;
    }

    @Override
    public int getAnimationExitRes() {
        return shouldAnimateOnFinish() && !isPartialCustomTab()
                ? mAnimationBundle.getInt(BUNDLE_EXIT_ANIMATION_RESOURCE)
                : 0;
    }

    @Override
    public boolean isTrustedIntent() {
        return mIsTrustedIntent;
    }

    @Override
    public @Nullable String getUrlToLoad() {
        if (mUrlToLoad == null) {
            mUrlToLoad = resolveUrlToLoad(getIntent());
        }
        return mUrlToLoad;
    }

    @Override
    public boolean shouldEnableUrlBarHiding() {
        return mEnableUrlBarHiding;
    }

    @Override
    public boolean contentScrollMayResizeTab() {
        return mContentScrollMayResizeTab;
    }

    @Override
    public ColorProvider getColorProvider() {
        return mColorProvider;
    }

    @Override
    public @Nullable Drawable getCloseButtonDrawable() {
        return mCloseButtonIcon;
    }

    @Override
    public int getTitleVisibilityState() {
        return mTitleVisibilityState;
    }

    @Override
    public boolean shouldShowShareMenuItem() {
        return mShowShareItemInMenu;
    }

    @Override
    public List<CustomButtonParams> getCustomButtonsOnToolbar() {
        return mToolbarButtons;
    }

    @Override
    public List<CustomButtonParams> getCustomButtonsOnBottombar() {
        return mBottombarButtons;
    }

    @Override
    public List<CustomButtonParams> getCustomButtonsOnGoogleBottomBar() {
        return mGoogleBottomBarButtons;
    }

    @Override
    public @Nullable RemoteViews getBottomBarRemoteViews() {
        return mRemoteViews;
    }

    @Override
    @Nullable
    public int[] getClickableViewIDs() {
        if (mClickableViewIds == null) return null;
        return mClickableViewIds.clone();
    }

    @Override
    public @Nullable PendingIntent getRemoteViewsPendingIntent() {
        return mRemoteViewsPendingIntent;
    }

    @Nullable
    @Override
    public PendingIntent getSecondaryToolbarSwipeUpPendingIntent() {
        return mSecondaryToolbarSwipeUpPendingIntent;
    }

    @Override
    public List<CustomButtonParams> getAllCustomButtons() {
        return mCustomButtonParams;
    }

    @Override
    public List<String> getMenuTitles() {
        ArrayList<String> list = new ArrayList<>();
        for (Pair<String, PendingIntent> pair : mMenuEntries) {
            list.add(pair.first);
        }
        return list;
    }

    /**
     * Set the callback object for {@link PendingIntent}s that are sent in this class. For testing
     * purpose only.
     */
    void setPendingIntentOnFinishedForTesting(PendingIntent.OnFinished onFinished) {
        mOnFinishedForTesting = onFinished;
        ResettersForTesting.register(() -> mOnFinishedForTesting = null);
    }

    @Override
    public boolean isOpenedByChrome() {
        return mIsOpenedByChrome;
    }

    @Override
    @BrowserServicesIntentDataProvider.CustomTabsUiType
    public int getUiType() {
        return mUiType;
    }

    /**
     * @return See {@link #EXTRA_MEDIA_VIEWER_URL}.
     */
    @Override
    public @Nullable String getMediaViewerUrl() {
        return mMediaViewerUrl;
    }

    @Override
    public boolean shouldEnableEmbeddedMediaExperience() {
        return mEnableEmbeddedMediaExperience;
    }

    @Override
    public boolean isFromMediaLauncherActivity() {
        return mIsFromMediaLauncherActivity;
    }

    @Override
    public boolean shouldShowStarButton() {
        return !mDisableStar;
    }

    @Override
    public boolean shouldShowDownloadButton() {
        return !mDisableDownload;
    }

    @Nullable
    @Override
    public TrustedWebActivityDisplayMode getTwaDisplayMode() {
        return mTrustedWebActivityDisplayMode;
    }

    @Override
    @Nullable
    public List<String> getTrustedWebActivityAdditionalOrigins() {
        return mTrustedWebActivityAdditionalOrigins;
    }

    @Override
    @Nullable
    public Set<Origin> getAllTrustedWebActivityOrigins() {
        // Lazily compute this, since `getUrlToLoad()` requires native to be loaded.
        if (mAllTrustedWebActivityOrigins != null) {
            return mAllTrustedWebActivityOrigins;
        }

        mAllTrustedWebActivityOrigins = new HashSet<Origin>();
        Origin initialOrigin = Origin.create(getUrlToLoad());
        if (initialOrigin != null) mAllTrustedWebActivityOrigins.add(initialOrigin);
        if (mTrustedWebActivityAdditionalOrigins != null) {
            for (String originAsString : mTrustedWebActivityAdditionalOrigins) {
                Origin origin = Origin.create(originAsString);
                if (origin == null) continue;

                mAllTrustedWebActivityOrigins.add(origin);
            }
        }

        return mAllTrustedWebActivityOrigins;
    }

    @Override
    public @Nullable String getTranslateLanguage() {
        return shouldAutoTranslate() ? mAutoTranslateLanguage : mTranslateLanguage;
    }

    @Override
    public boolean shouldAutoTranslate() {
        return mAutoTranslateLanguage != null && isAllowedToAutoTranslate();
    }

    private static boolean isPackageNameInList(String packageName, String pipeDelimitedList) {
        if (packageName == null || TextUtils.isEmpty(pipeDelimitedList)) return false;
        for (String p : pipeDelimitedList.split("\\|")) {
            if (packageName.equals(p)) return true;
        }
        return false;
    }

    private boolean isAllowedToAutoTranslate() {
        if (!ChromeFeatureList.sCctAutoTranslate.isEnabled()) return false;
        if (mIsTrustedIntent && AUTO_TRANSLATE_ALLOW_ALL_FIRST_PARTIES.getValue()) return true;
        return isPackageNameInList(
                getClientPackageName(), AUTO_TRANSLATE_PACKAGE_NAME_ALLOWLIST.getValue());
    }

    @Override
    public @Nullable ShareTarget getShareTarget() {
        Bundle bundle =
                IntentUtils.safeGetBundleExtra(
                        getIntent(), TrustedWebActivityIntentBuilder.EXTRA_SHARE_TARGET);
        if (bundle == null) return null;
        try {
            return ShareTarget.fromBundle(bundle);
        } catch (Throwable e) {
            // Catch unparcelling errors.
            return null;
        }
    }

    @Override
    public @Nullable ShareData getShareData() {
        Bundle bundle =
                IntentUtils.safeGetParcelableExtra(
                        getIntent(), TrustedWebActivityIntentBuilder.EXTRA_SHARE_DATA);
        if (bundle == null) return null;
        try {
            return ShareData.fromBundle(bundle);
        } catch (Throwable e) {
            // Catch unparcelling errors.
            return null;
        }
    }

    @TwaDisclosureUi
    @Override
    public int getTwaDisclosureUi() {
        int version = mIntent.getIntExtra(EXTRA_TWA_DISCLOSURE_UI, TwaDisclosureUi.DEFAULT);

        if (version != TwaDisclosureUi.V1_INFOBAR
                && version != TwaDisclosureUi.V2_NOTIFICATION_OR_SNACKBAR) {
            return TwaDisclosureUi.DEFAULT;
        }

        return version;
    }

    @ActivitySideSheetDecorationType
    @Override
    public int getActivitySideSheetDecorationType() {
        return mSideSheetDecorationType;
    }

    @ActivitySideSheetRoundedCornersPosition
    @Override
    public int getActivitySideSheetRoundedCornersPosition() {
        return mSideSheetRoundedCornersPosition;
    }

    @Override
    @Nullable
    public int[] getGsaExperimentIds() {
        return mGsaExperimentIds;
    }

    @Override
    public @Px int getInitialActivityHeight() {
        return getInitialActivityHeight(
                mIsTrustedIntent, mInitialActivityHeight, getClientPackageName());
    }

    @Override
    public @Px int getInitialActivityWidth() {
        return getInitialActivityWidth(
                mIsTrustedIntent, mInitialActivityWidth, getClientPackageName());
    }

    @Override
    public int getActivityBreakPoint() {
        return mBreakPointDp;
    }

    static boolean isAllowedThirdParty(String packageName) {
        if (packageName == null) return false;
        String defaultPolicy = THIRD_PARTIES_DEFAULT_POLICY.getValue();
        if (defaultPolicy.equals(DEFAULT_POLICY_USE_ALLOWLIST)) {
            return isPackageNameInList(packageName, ALLOWLIST_ENTRIES.getValue());
        } else if (defaultPolicy.equals(DEFAULT_POLICY_USE_DENYLIST)) {
            return !isPackageNameInList(packageName, DENYLIST_ENTRIES.getValue());
        }
        assert false : "We can't get here since the default policy is use denylist.";
        return false;
    }

    @Override
    public @CloseButtonPosition int getCloseButtonPosition() {
        return IntentUtils.safeGetIntExtra(
                mIntent, EXTRA_CLOSE_BUTTON_POSITION, CLOSE_BUTTON_POSITION_DEFAULT);
    }

    @Override
    public boolean shouldSuppressAppMenu() {
        // The media viewer has no default menu items, so if there are also no custom items, we
        // should disable the menu altogether.
        return isMediaViewer() && getMenuTitles().isEmpty();
    }

    @Override
    public int getPartialTabToolbarCornerRadius() {
        return mPartialTabToolbarCornerRadius;
    }

    @Override
    public boolean isPartialCustomTabFixedHeight() {
        return mIsPartialCustomTabFixedHeight;
    }

    @Override
    public boolean canInteractWithBackground() {
        return mInteractWithBackground;
    }

    @Override
    public boolean showSideSheetMaximizeButton() {
        return IntentUtils.safeGetBooleanExtra(
                mIntent, EXTRA_ACTIVITY_SIDE_SHEET_ENABLE_MAXIMIZATION, false);
    }

    @Override
    public int getSideSheetSlideInBehavior() {
        return ACTIVITY_SIDE_SHEET_SLIDE_IN_FROM_SIDE;
    }

    @Override
    public int getSideSheetPosition() {
        int position = CustomTabsIntent.getActivitySideSheetPosition(mIntent);
        return position == ACTIVITY_SIDE_SHEET_POSITION_DEFAULT
                ? ACTIVITY_SIDE_SHEET_POSITION_END
                : position;
    }

    @Override
    public boolean isInteractiveOmniboxAllowed() {
        if (!ChromeFeatureList.sSearchInCCT.isEnabled()) return false;
        if (isOffTheRecord()) return false;
        if (isPartialCustomTab()) return false;
        if (BuildInfo.getInstance().isAutomotive) return false;

        return isPackageNameInList(
                getClientPackageName(), OMNIBOX_ALLOWED_PACKAGE_NAMES.getValue());
    }

    @Override
    public long getTargetNetwork() {
        return mNetwork != null ? mNetwork.getNetworkHandle() : NetId.INVALID;
    }

    @Override
    public boolean isAuthTab() {
        // TODO(crbug.com/345627627): Remove this and set this to return true in a new
        //     intent data provider.
        boolean isAuthTab = false;
        return ChromeFeatureList.sCctAuthTab.isEnabled() && isAuthTab;
    }
}