blob: 0ada30fbcc4b8cc9f68903ab4506f863cbbb327f [file] [log] [blame]
// 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.base.compat.ApiHelperForQ;
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 ApiHelperForQ.bindIsolatedService(
context, 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() {}
}