chromium/android_webview/java/src/org/chromium/android_webview/AwNetLogsConnection.java

// Copyright 2024 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.android_webview;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;

import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;

import org.chromium.android_webview.common.services.INetLogService;
import org.chromium.android_webview.common.services.ServiceHelper;
import org.chromium.android_webview.common.services.ServiceNames;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.ThreadUtils;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskTraits;

// Connects to AwNetLogService service.
@JNINamespace("android_webview")
public class AwNetLogsConnection {
    private static final String TAG = "AwNetLogsConnection";

    // True if we should be logging. //
    private static boolean sLoggingEnabled;

    public static void startConnectNetLogService() {
        ThreadUtils.assertOnUiThread();
        sLoggingEnabled = true;
        final Context context = ContextUtils.getApplicationContext();
        final Intent intent = new Intent();
        intent.setClassName(AwBrowserProcess.getWebViewPackageName(), ServiceNames.NET_LOG_SERVICE);
        ServiceConnection connection =
                new ServiceConnection() {
                    @Override
                    public void onServiceConnected(ComponentName className, IBinder service) {
                        ThreadUtils.runOnUiThread(
                                () -> {
                                    if (!sLoggingEnabled) {
                                        context.unbindService(this);
                                        return;
                                    }
                                    PostTask.postTask(
                                            TaskTraits.BEST_EFFORT,
                                            () -> {
                                                try {
                                                    String packageName = context.getPackageName();
                                                    final long creationTime =
                                                            System.currentTimeMillis();
                                                    INetLogService netLogService =
                                                            INetLogService.Stub.asInterface(
                                                                    service);
                                                    ParcelFileDescriptor parcelFileDescriptor =
                                                            netLogService.streamLog(
                                                                    creationTime, packageName);
                                                    if (parcelFileDescriptor != null) {
                                                        int fd = parcelFileDescriptor.detachFd();
                                                        AwNetLogsConnectionJni.get()
                                                                .startNetLogs(fd);
                                                    }
                                                } catch (RemoteException e) {
                                                    Log.e(
                                                            TAG,
                                                            "Failed to get ParcelFileDescriptor"
                                                                    + " from NetLogService",
                                                            e);
                                                    return;
                                                } finally {
                                                    context.unbindService(this);
                                                }
                                            });
                                });
                    }

                    @Override
                    public void onServiceDisconnected(ComponentName className) {}
                };
        try {
            if (!ServiceHelper.bindService(context, intent, connection, Context.BIND_AUTO_CREATE)) {
                Log.w(TAG, "Could not bind to NetLogService " + intent);
            }
        } catch (RuntimeException e) {
            Log.e(TAG, "Exception during stream net log process", e);
        }
    }

    public static void stopNetLogService() {
        ThreadUtils.assertOnUiThread();
        if (sLoggingEnabled) {
            AwNetLogsConnectionJni.get().stopNetLogs();
        }
        sLoggingEnabled = false;
    }

    @NativeMethods
    interface Natives {
        void startNetLogs(int pfd);

        void stopNetLogs();
    }
}