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(Context context, Intent bindIntent, int bindFlags, 35 Handler handler, Executor executor, ChildServiceConnectionDelegate delegate, 36 String instanceName) { 37 mContext = context; 38 mBindIntent = bindIntent; 39 mBindFlags = bindFlags; 40 mHandler = handler; 41 mExecutor = executor; 42 mDelegate = delegate; 43 mInstanceName = instanceName; 44 } 45 46 @Override bindServiceConnection()47 public boolean bindServiceConnection() { 48 try { 49 TraceEvent.begin("ChildServiceConnectionImpl.bindServiceConnection"); 50 mBound = BindService.doBindService( 51 mContext, mBindIntent, this, mBindFlags, mHandler, mExecutor, mInstanceName); 52 } finally { 53 TraceEvent.end("ChildServiceConnectionImpl.bindServiceConnection"); 54 } 55 return mBound; 56 } 57 58 @Override unbindServiceConnection()59 public void unbindServiceConnection() { 60 if (mBound) { 61 mContext.unbindService(this); 62 mBound = false; 63 } 64 } 65 66 @Override isBound()67 public boolean isBound() { 68 return mBound; 69 } 70 71 @Override updateGroupImportance(int group, int importanceInGroup)72 public void updateGroupImportance(int group, int importanceInGroup) { 73 // ChildProcessConnection checks there is a real connection to the service before calling 74 // this, and this `isBound` check should in theory be unnecessary. However this is still 75 // tripped on some devices where another service connection bound successfully but this 76 // service connection failed in `bindServiceConnection`. Such a case is not expected OS 77 // behavior and is not handled. However, avoid crashing in `updateServiceGroup` by doing 78 // this check here. 79 if (!isBound()) { 80 return; 81 } 82 if (BindService.supportVariableConnections()) { 83 try { 84 ApiHelperForQ.updateServiceGroup(mContext, this, group, importanceInGroup); 85 } catch (IllegalArgumentException e) { 86 // There is an unavoidable race here binding might be removed for example due to a 87 // crash, which has not been processed on the launcher thread. 88 // Ignore these. See crbug.com/1026626 and crbug.com/1026626 for context. 89 return; 90 } 91 BindService.doBindService( 92 mContext, mBindIntent, this, mBindFlags, mHandler, mExecutor, mInstanceName); 93 } 94 } 95 96 @Override retire()97 public void retire() { 98 mDelegate = null; 99 unbindServiceConnection(); 100 } 101 102 @Override onServiceConnected(ComponentName className, final IBinder service)103 public void onServiceConnected(ComponentName className, final IBinder service) { 104 if (mDelegate == null) { 105 Log.w(TAG, "onServiceConnected after timeout " + className); 106 return; 107 } 108 mDelegate.onServiceConnected(service); 109 } 110 111 // Called on the main thread to notify that the child service did not disconnect gracefully. 112 @Override onServiceDisconnected(ComponentName className)113 public void onServiceDisconnected(ComponentName className) { 114 if (mDelegate != null) mDelegate.onServiceDisconnected(); 115 } 116 } 117