ConnectionManager.java
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.health.platform.client.impl.ipc.internal;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import java.util.HashMap;
import java.util.Map;
/**
* Manages connections to a service in a different process.
*
* @hide
*/
@RestrictTo(Scope.LIBRARY)
public final class ConnectionManager implements Handler.Callback, ServiceConnection.Callback {
private static final String TAG = "ConnectionManager";
private static final int MSG_CONNECTED = 1;
private static final int MSG_DISCONNECTED = 2;
private static final int MSG_EXECUTE = 3;
private static final int MSG_REGISTER_LISTENER = 4;
private static final int MSG_UNREGISTER_LISTENER = 5;
private final Context mContext;
private final Handler mHandler;
private final Map<String, ServiceConnection> mServiceConnectionMap = new HashMap<>();
private boolean mBindToSelfEnabled;
public ConnectionManager(Context context, Looper looper) {
this.mContext = context;
this.mHandler = new Handler(looper, this);
}
/**
* Schedules operation for execution
*
* @param operation Operation prepared for scheduling on the connection queue.
*/
public void scheduleForExecution(QueueOperation operation) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_EXECUTE, operation));
}
/**
* Registers a listener by executing an operation represented by the {@link QueueOperation}.
*
* @param listenerKey Key based on which listeners will be distinguished.
* @param registerOperation Queue operation executed against the corresponding connection to
* register the listener. Will be used to re-register when connection is lost.
*/
public void registerListener(ListenerKey listenerKey, QueueOperation registerOperation) {
mHandler.sendMessage(
mHandler.obtainMessage(
MSG_REGISTER_LISTENER, new ListenerHolder(listenerKey, registerOperation)));
}
/**
* Unregisters a listener by executing an operation represented by the {@link QueueOperation}.
*
* @param listenerKey Key based on which listeners will be distinguished.
* @param unregisterOperation Queue operation executed against the corresponding connection to
* unregister the listener.
*/
public void unregisterListener(ListenerKey listenerKey, QueueOperation unregisterOperation) {
mHandler.sendMessage(
mHandler.obtainMessage(
MSG_UNREGISTER_LISTENER,
new ListenerHolder(listenerKey, unregisterOperation)));
}
@Override
public void onConnected(ServiceConnection connection) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECTED, connection));
}
@Override
public void onDisconnected(ServiceConnection connection, long reconnectDelayMs) {
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MSG_DISCONNECTED, connection), reconnectDelayMs);
}
@Override
public boolean isBindToSelfEnabled() {
return mBindToSelfEnabled;
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_CONNECTED:
ServiceConnection serviceConnection = ((ServiceConnection) msg.obj);
serviceConnection.reRegisterAllListeners();
serviceConnection.refreshServiceVersion();
serviceConnection.flushQueue();
return true;
case MSG_DISCONNECTED:
((ServiceConnection) msg.obj).maybeReconnect();
return true;
case MSG_EXECUTE:
QueueOperation queueOperation = (QueueOperation) msg.obj;
getConnection(queueOperation.getConnectionConfiguration()).enqueue(queueOperation);
return true;
case MSG_REGISTER_LISTENER:
ListenerHolder registerListenerHolder = (ListenerHolder) msg.obj;
getConnection(
registerListenerHolder
.getListenerOperation()
.getConnectionConfiguration())
.registerListener(
registerListenerHolder.getListenerKey(),
registerListenerHolder.getListenerOperation());
return true;
case MSG_UNREGISTER_LISTENER:
ListenerHolder unregisterListenerHolder = (ListenerHolder) msg.obj;
getConnection(
unregisterListenerHolder
.getListenerOperation()
.getConnectionConfiguration())
.unregisterListener(
unregisterListenerHolder.getListenerKey(),
unregisterListenerHolder.getListenerOperation());
return true;
default:
Log.e(TAG, "Received unknown message: " + msg.what);
return false;
}
}
public void setBindToSelf(boolean bindToSelfEnabled) {
this.mBindToSelfEnabled = bindToSelfEnabled;
}
private ServiceConnection getConnection(ConnectionConfiguration connectionConfiguration) {
String connectionKey = connectionConfiguration.getKey();
ServiceConnection serviceConnection = mServiceConnectionMap.get(connectionKey);
if (serviceConnection == null) {
serviceConnection =
new ServiceConnection(
mContext, connectionConfiguration, new DefaultExecutionTracker(), this);
mServiceConnectionMap.put(connectionKey, serviceConnection);
}
return serviceConnection;
}
private static class ListenerHolder {
private final ListenerKey mListenerKey;
private final QueueOperation mListenerOperation;
ListenerHolder(ListenerKey listenerKey, QueueOperation listenerOperation) {
this.mListenerKey = listenerKey;
this.mListenerOperation = listenerOperation;
}
ListenerKey getListenerKey() {
return mListenerKey;
}
QueueOperation getListenerOperation() {
return mListenerOperation;
}
}
}