chromium/components/browser_ui/device_lock/android/java/src/org/chromium/components/browser_ui/device_lock/DeviceLockBridge.java

// Copyright 2023 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.components.browser_ui.device_lock;

import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Context;

import androidx.annotation.NonNull;

import org.jni_zero.CalledByNative;
import org.jni_zero.NativeMethods;

import org.chromium.base.ContextUtils;
import org.chromium.ui.base.WindowAndroid;

/** This bridge allows native web C++ code to launch DeviceLockActivity. */
public class DeviceLockBridge {
    /**
     * Whether the Device Lock page has been shown to the user and acknowledged with a device lock
     * present. This is used to determine whether to show the device lock page when the user is
     * interacting with sensitive personal data on the device.
     */
    public static final String DEVICE_LOCK_PAGE_HAS_BEEN_PASSED =
            "Chrome.DeviceLockPage.HasBeenPassed";

    private long mNativeDeviceLockBridge;

    private DeviceLockBridge(long nativeDeviceLockBridge) {
        mNativeDeviceLockBridge = nativeDeviceLockBridge;
    }

    @CalledByNative
    static DeviceLockBridge create(long nativeDeviceLockBridge) {
        return new DeviceLockBridge(nativeDeviceLockBridge);
    }

    /**
     * Launches DeviceLockActivity (explainer dialog and PIN/password setup flow) before allowing
     * users to continue if the user's device is not secure (ex: no PIN or password set).
     *
     * <p>TODO(crbug.com/40927226): Handle edge case where Chrome is killed when switching to OS PIN
     * flow.
     */
    @CalledByNative
    private void launchDeviceLockUiBeforeRunningCallback(@NonNull WindowAndroid windowAndroid) {
        if (mNativeDeviceLockBridge == 0) {
            return;
        }
        final Context context = windowAndroid.getContext().get();
        if (context != null) {
            DeviceLockActivityLauncher deviceLockActivityLauncher =
                    DeviceLockActivityLauncherSupplier.from(windowAndroid).get();
            deviceLockActivityLauncher.launchDeviceLockActivity(
                    context,
                    null,
                    false,
                    windowAndroid,
                    (resultCode, unused) ->
                            DeviceLockBridgeJni.get()
                                    .onDeviceLockUiFinished(
                                            mNativeDeviceLockBridge,
                                            resultCode == Activity.RESULT_OK),
                    DeviceLockActivityLauncher.Source.AUTOFILL);
        } else {
            DeviceLockBridgeJni.get().onDeviceLockUiFinished(mNativeDeviceLockBridge, false);
        }
    }

    @CalledByNative
    private void clearNativePointer() {
        mNativeDeviceLockBridge = 0;
    }

    @CalledByNative
    private static boolean isDeviceSecure() {
        return ((KeyguardManager)
                        ContextUtils.getApplicationContext()
                                .getSystemService(Context.KEYGUARD_SERVICE))
                .isDeviceSecure();
    }

    @CalledByNative
    public static boolean deviceLockPageHasBeenPassed() {
        return ContextUtils.getAppSharedPreferences()
                .getBoolean(DEVICE_LOCK_PAGE_HAS_BEEN_PASSED, false);
    }

    /** C++ method signatures. */
    @NativeMethods
    interface Natives {
        void onDeviceLockUiFinished(long nativeDeviceLockBridge, boolean isDeviceLockSet);
    }
}