1 // Copyright 2018 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base.process_launcher; 6 7 import android.annotation.SuppressLint; 8 import android.content.Context; 9 import android.content.ContextWrapper; 10 import android.content.Intent; 11 import android.content.ServiceConnection; 12 import android.os.Build; 13 import android.os.Handler; 14 import android.os.Process; 15 import android.os.UserHandle; 16 17 import androidx.annotation.RequiresApi; 18 19 import org.chromium.base.compat.ApiHelperForQ; 20 import org.chromium.build.BuildConfig; 21 22 import java.lang.reflect.Method; 23 import java.util.concurrent.Executor; 24 25 /** 26 * Class of static helper methods to call Context.bindService variants. 27 */ 28 final class BindService { 29 private static Method sBindServiceAsUserMethod; 30 supportVariableConnections()31 static boolean supportVariableConnections() { 32 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q 33 && !BuildConfig.IS_INCREMENTAL_INSTALL; 34 } 35 36 // Note that handler is not guaranteed to be used, and client still need to correctly handle 37 // callbacks on the UI thread. doBindService(Context context, Intent intent, ServiceConnection connection, int flags, Handler handler, Executor executor, String instanceName)38 static boolean doBindService(Context context, Intent intent, ServiceConnection connection, 39 int flags, Handler handler, Executor executor, String instanceName) { 40 if (supportVariableConnections() && instanceName != null) { 41 return ApiHelperForQ.bindIsolatedService( 42 context, intent, flags, instanceName, executor, connection); 43 } 44 45 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) { 46 return bindServiceByCall(context, intent, connection, flags); 47 } 48 49 try { 50 return bindServiceByReflection(context, intent, connection, flags, handler); 51 } catch (ReflectiveOperationException reflectionException) { 52 try { 53 return bindServiceByCall(context, intent, connection, flags); 54 } catch (RuntimeException runtimeException) { 55 // Include the reflectionException in crash reports. 56 throw new RuntimeException(runtimeException.getMessage(), reflectionException); 57 } 58 } 59 } 60 bindServiceByCall( Context context, Intent intent, ServiceConnection connection, int flags)61 private static boolean bindServiceByCall( 62 Context context, Intent intent, ServiceConnection connection, int flags) { 63 return context.bindService(intent, connection, flags); 64 } 65 66 @RequiresApi(Build.VERSION_CODES.N) 67 @SuppressLint("DiscouragedPrivateApi") bindServiceByReflection(Context context, Intent intent, ServiceConnection connection, int flags, Handler handler)68 private static boolean bindServiceByReflection(Context context, Intent intent, 69 ServiceConnection connection, int flags, Handler handler) 70 throws ReflectiveOperationException { 71 if (sBindServiceAsUserMethod == null) { 72 sBindServiceAsUserMethod = 73 Context.class.getDeclaredMethod("bindServiceAsUser", Intent.class, 74 ServiceConnection.class, int.class, Handler.class, UserHandle.class); 75 } 76 // No need for null checks or worry about infinite looping here. Otherwise a regular calls 77 // into the ContextWrapper would lead to problems as well. 78 while (context instanceof ContextWrapper) { 79 context = ((ContextWrapper) context).getBaseContext(); 80 } 81 return (Boolean) sBindServiceAsUserMethod.invoke( 82 context, intent, connection, flags, handler, Process.myUserHandle()); 83 } 84 BindService()85 private BindService() {} 86 } 87