chromium/base/android/java/src/org/chromium/base/process_launcher/BindService.java

// Copyright 2018 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.base.process_launcher;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;

import androidx.annotation.RequiresApi;

import org.chromium.build.BuildConfig;

import java.lang.reflect.Method;
import java.util.concurrent.Executor;

/** Class of static helper methods to call Context.bindService variants. */
final class BindService {
    private static Method sBindServiceAsUserMethod;

    static boolean supportVariableConnections() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
                && !BuildConfig.IS_INCREMENTAL_INSTALL;
    }

    // Note that handler is not guaranteed to be used, and client still need to correctly handle
    // callbacks on the UI thread.
    static boolean doBindService(
            Context context,
            Intent intent,
            ServiceConnection connection,
            int flags,
            Handler handler,
            Executor executor,
            String instanceName) {
        if (supportVariableConnections() && instanceName != null) {
            return context.bindIsolatedService(intent, flags, instanceName, executor, connection);
        }

        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
            return bindServiceByCall(context, intent, connection, flags);
        }

        try {
            return bindServiceByReflection(context, intent, connection, flags, handler);
        } catch (ReflectiveOperationException reflectionException) {
            try {
                return bindServiceByCall(context, intent, connection, flags);
            } catch (RuntimeException runtimeException) {
                // Include the reflectionException in crash reports.
                throw new RuntimeException(runtimeException.getMessage(), reflectionException);
            }
        }
    }

    private static boolean bindServiceByCall(
            Context context, Intent intent, ServiceConnection connection, int flags) {
        return context.bindService(intent, connection, flags);
    }

    @RequiresApi(Build.VERSION_CODES.N)
    @SuppressLint("DiscouragedPrivateApi")
    private static boolean bindServiceByReflection(
            Context context,
            Intent intent,
            ServiceConnection connection,
            int flags,
            Handler handler)
            throws ReflectiveOperationException {
        if (sBindServiceAsUserMethod == null) {
            sBindServiceAsUserMethod =
                    Context.class.getDeclaredMethod(
                            "bindServiceAsUser",
                            Intent.class,
                            ServiceConnection.class,
                            int.class,
                            Handler.class,
                            UserHandle.class);
        }
        // No need for null checks or worry about infinite looping here. Otherwise a regular calls
        // into the ContextWrapper would lead to problems as well.
        while (context instanceof ContextWrapper) {
            context = ((ContextWrapper) context).getBaseContext();
        }
        return (Boolean)
                sBindServiceAsUserMethod.invoke(
                        context, intent, connection, flags, handler, Process.myUserHandle());
    }

    private BindService() {}
}