chromium/components/permissions/android/java/src/org/chromium/components/permissions/nfc/NfcSystemLevelSetting.java

// Copyright 2019 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.permissions.nfc;

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.nfc.NfcAdapter;
import android.os.Process;
import android.provider.Settings;

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

import org.chromium.base.ContextUtils;
import org.chromium.base.ResettersForTesting;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskTraits;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.base.WindowAndroid;

/**
 * Provides methods for querying NFC sytem-level setting on Android.
 *
 * This class should be used only on the UI thread.
 */
public class NfcSystemLevelSetting {
    private static Boolean sNfcSupportForTesting;
    private static Boolean sSystemNfcSettingForTesting;

    @CalledByNative
    public static boolean isNfcAccessPossible() {
        if (sNfcSupportForTesting != null) {
            return sNfcSupportForTesting;
        }

        Context context = ContextUtils.getApplicationContext();
        int permission =
                context.checkPermission(Manifest.permission.NFC, Process.myPid(), Process.myUid());
        if (permission != PackageManager.PERMISSION_GRANTED) {
            return false;
        }

        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(context);
        return nfcAdapter != null;
    }

    @CalledByNative
    public static boolean isNfcSystemLevelSettingEnabled() {
        if (sSystemNfcSettingForTesting != null) {
            return sSystemNfcSettingForTesting;
        }

        if (!isNfcAccessPossible()) return false;

        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(ContextUtils.getApplicationContext());
        return nfcAdapter.isEnabled();
    }

    @CalledByNative
    private static void promptToEnableNfcSystemLevelSetting(
            WebContents webContents, final long nativeCallback) {
        WindowAndroid window = webContents.getTopLevelNativeWindow();
        if (window == null) {
            // Consuming code may not expect a sync callback to happen.
            PostTask.postTask(
                    TaskTraits.UI_DEFAULT,
                    () ->
                            NfcSystemLevelSettingJni.get()
                                    .onNfcSystemLevelPromptCompleted(nativeCallback));
            return;
        }

        NfcSystemLevelPrompt prompt = new NfcSystemLevelPrompt();
        prompt.show(
                window,
                () ->
                        NfcSystemLevelSettingJni.get()
                                .onNfcSystemLevelPromptCompleted(nativeCallback));
    }

    public static Intent getNfcSystemLevelSettingIntent() {
        Intent intent = new Intent(Settings.ACTION_NFC_SETTINGS);
        Context context = ContextUtils.getApplicationContext();
        if (intent.resolveActivity(context.getPackageManager()) == null) {
            return null;
        }
        return intent;
    }

    /** Disable/enable Android NFC setting for testing use only. */
    public static void setNfcSettingForTesting(Boolean enabled) {
        sSystemNfcSettingForTesting = enabled;
        ResettersForTesting.register(() -> sSystemNfcSettingForTesting = null);
    }

    /** Disable/enable Android NFC support for testing use only. */
    public static void setNfcSupportForTesting(Boolean enabled) {
        sNfcSupportForTesting = enabled;
        ResettersForTesting.register(() -> sNfcSupportForTesting = null);
    }

    @NativeMethods
    interface Natives {
        void onNfcSystemLevelPromptCompleted(long callback);
    }
}