blob: 8dcbb3ad497b00f1bd9a571cc7ece9ff292f5e6d [file] [log] [blame]
// Copyright 2020 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.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import org.chromium.base.Log;
import org.chromium.base.TraceEvent;
import org.chromium.base.compat.ApiHelperForQ;
import java.util.concurrent.Executor;
/** Implementation of ChildServiceConnection that does connect to a service. */
/* package */ class ChildServiceConnectionImpl
implements ChildServiceConnection, ServiceConnection {
private static final String TAG = "ChildServiceConn";
private final Context mContext;
private final Intent mBindIntent;
private final int mBindFlags;
private final Handler mHandler;
private final Executor mExecutor;
private ChildServiceConnectionDelegate mDelegate;
private final String mInstanceName;
private boolean mBound;
/* package */ ChildServiceConnectionImpl(Context context, Intent bindIntent, int bindFlags,
Handler handler, Executor executor, ChildServiceConnectionDelegate delegate,
String instanceName) {
mContext = context;
mBindIntent = bindIntent;
mBindFlags = bindFlags;
mHandler = handler;
mExecutor = executor;
mDelegate = delegate;
mInstanceName = instanceName;
}
@Override
public boolean bindServiceConnection() {
try {
TraceEvent.begin("ChildServiceConnectionImpl.bindServiceConnection");
mBound = BindService.doBindService(
mContext, mBindIntent, this, mBindFlags, mHandler, mExecutor, mInstanceName);
} finally {
TraceEvent.end("ChildServiceConnectionImpl.bindServiceConnection");
}
return mBound;
}
@Override
public void unbindServiceConnection() {
if (mBound) {
mContext.unbindService(this);
mBound = false;
}
}
@Override
public boolean isBound() {
return mBound;
}
@Override
public void updateGroupImportance(int group, int importanceInGroup) {
// ChildProcessConnection checks there is a real connection to the service before calling
// this, and this `isBound` check should in theory be unnecessary. However this is still
// tripped on some devices where another service connection bound successfully but this
// service connection failed in `bindServiceConnection`. Such a case is not expected OS
// behavior and is not handled. However, avoid crashing in `updateServiceGroup` by doing
// this check here.
if (!isBound()) {
return;
}
if (BindService.supportVariableConnections()) {
try {
ApiHelperForQ.updateServiceGroup(mContext, this, group, importanceInGroup);
} catch (IllegalArgumentException e) {
// There is an unavoidable race here binding might be removed for example due to a
// crash, which has not been processed on the launcher thread.
// Ignore these. See crbug.com/1026626 and crbug.com/1026626 for context.
return;
}
BindService.doBindService(
mContext, mBindIntent, this, mBindFlags, mHandler, mExecutor, mInstanceName);
}
}
@Override
public void retire() {
mDelegate = null;
unbindServiceConnection();
}
@Override
public void onServiceConnected(ComponentName className, final IBinder service) {
if (mDelegate == null) {
Log.w(TAG, "onServiceConnected after timeout " + className);
return;
}
mDelegate.onServiceConnected(service);
}
// Called on the main thread to notify that the child service did not disconnect gracefully.
@Override
public void onServiceDisconnected(ComponentName className) {
if (mDelegate != null) mDelegate.onServiceDisconnected();
}
}