1 // Copyright 2020 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.content.ComponentName; 8 import android.content.Context; 9 import android.content.Intent; 10 import android.content.ServiceConnection; 11 import android.os.Handler; 12 import android.os.IBinder; 13 14 import org.chromium.base.Log; 15 import org.chromium.base.TraceEvent; 16 import org.chromium.base.compat.ApiHelperForQ; 17 18 import java.util.concurrent.Executor; 19 20 /** Implementation of ChildServiceConnection that does connect to a service. */ 21 /* package */ class ChildServiceConnectionImpl 22 implements ChildServiceConnection, ServiceConnection { 23 private static final String TAG = "ChildServiceConn"; 24 25 private final Context mContext; 26 private final Intent mBindIntent; 27 private final int mBindFlags; 28 private final Handler mHandler; 29 private final Executor mExecutor; 30 private ChildServiceConnectionDelegate mDelegate; 31 private final String mInstanceName; 32 private boolean mBound; 33 ChildServiceConnectionImpl( Context context, Intent bindIntent, int bindFlags, Handler handler, Executor executor, ChildServiceConnectionDelegate delegate, String instanceName)34 /* package */ ChildServiceConnectionImpl( 35 Context context, 36 Intent bindIntent, 37 int bindFlags, 38 Handler handler, 39 Executor executor, 40 ChildServiceConnectionDelegate delegate, 41 String instanceName) { 42 mContext = context; 43 mBindIntent = bindIntent; 44 mBindFlags = bindFlags; 45 mHandler = handler; 46 mExecutor = executor; 47 mDelegate = delegate; 48 mInstanceName = instanceName; 49 } 50 51 @Override bindServiceConnection()52 public boolean bindServiceConnection() { 53 try { 54 TraceEvent.begin("ChildServiceConnectionImpl.bindServiceConnection"); 55 mBound = 56 BindService.doBindService( 57 mContext, 58 mBindIntent, 59 this, 60 mBindFlags, 61 mHandler, 62 mExecutor, 63 mInstanceName); 64 } finally { 65 TraceEvent.end("ChildServiceConnectionImpl.bindServiceConnection"); 66 } 67 return mBound; 68 } 69 70 @Override unbindServiceConnection()71 public void unbindServiceConnection() { 72 if (mBound) { 73 mContext.unbindService(this); 74 mBound = false; 75 } 76 } 77 78 @Override isBound()79 public boolean isBound() { 80 return mBound; 81 } 82 83 @Override updateGroupImportance(int group, int importanceInGroup)84 public void updateGroupImportance(int group, int importanceInGroup) { 85 // ChildProcessConnection checks there is a real connection to the service before calling 86 // this, and this `isBound` check should in theory be unnecessary. However this is still 87 // tripped on some devices where another service connection bound successfully but this 88 // service connection failed in `bindServiceConnection`. Such a case is not expected OS 89 // behavior and is not handled. However, avoid crashing in `updateServiceGroup` by doing 90 // this check here. 91 if (!isBound()) { 92 return; 93 } 94 if (BindService.supportVariableConnections()) { 95 try { 96 ApiHelperForQ.updateServiceGroup(mContext, this, group, importanceInGroup); 97 } catch (IllegalArgumentException e) { 98 // There is an unavoidable race here binding might be removed for example due to a 99 // crash, which has not been processed on the launcher thread. 100 // Ignore these. See crbug.com/1026626 and crbug.com/1026626 for context. 101 return; 102 } 103 BindService.doBindService( 104 mContext, mBindIntent, this, mBindFlags, mHandler, mExecutor, mInstanceName); 105 } 106 } 107 108 @Override retire()109 public void retire() { 110 mDelegate = null; 111 unbindServiceConnection(); 112 } 113 114 @Override onServiceConnected(ComponentName className, final IBinder service)115 public void onServiceConnected(ComponentName className, final IBinder service) { 116 if (mDelegate == null) { 117 Log.w(TAG, "onServiceConnected after timeout " + className); 118 return; 119 } 120 mDelegate.onServiceConnected(service); 121 } 122 123 // Called on the main thread to notify that the child service did not disconnect gracefully. 124 @Override onServiceDisconnected(ComponentName className)125 public void onServiceDisconnected(ComponentName className) { 126 if (mDelegate != null) mDelegate.onServiceDisconnected(); 127 } 128 } 129