blob: 6798c1823d10bdb26d0421735a453794a5dfa653 [file] [log] [blame]
/*
* Copyright (C) 2020 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 com.google.android.connecteddevice.api;
import static com.google.android.connecteddevice.util.SafeLog.loge;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.connecteddevice.ConnectedDeviceManager;
import com.google.android.connecteddevice.ConnectedDeviceManager.ConnectionCallback;
import com.google.android.connecteddevice.ConnectedDeviceManager.DeviceAssociationCallback;
import com.google.android.connecteddevice.ConnectedDeviceManager.DeviceCallback;
import com.google.android.connecteddevice.logging.LoggingManager;
import com.google.android.connecteddevice.logging.LoggingManager.OnLogRequestedListener;
import com.google.android.connecteddevice.model.AssociatedDevice;
import com.google.android.connecteddevice.model.ConnectedDevice;
import com.google.android.connecteddevice.util.RemoteCallbackBinder;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/** Binder for exposing ConnectedDeviceManager to features. */
public class ConnectedDeviceManagerBinder extends IConnectedDeviceManager.Stub {
private static final String TAG = "ConnectedDeviceManagerBinder";
private final ConnectedDeviceManager connectedDeviceManager;
private final LoggingManager loggingManager;
// aidl callback binder -> connection callback
// Need to maintain a mapping in order to support unregistering callbacks.
private final ConcurrentHashMap<IBinder, ConnectionCallback> connectionCallbacks =
new ConcurrentHashMap<>();
// aidl callback binder -> device association callback
// Need to maintain a mapping in order to support unregistering callbacks.
private final ConcurrentHashMap<IBinder, DeviceAssociationCallback> associationCallbacks =
new ConcurrentHashMap<>();
// aidl callback binder -> device callback
// Need to maintain a mapping in order to support unregistering callbacks.
private final ConcurrentHashMap<IBinder, DeviceCallback> deviceCallbacks =
new ConcurrentHashMap<>();
// aidl listener binder -> log requested listener
// Need to maintain a mapping in order to support unregistering callbacks.
private final ConcurrentHashMap<IBinder, OnLogRequestedListener> logRequestedListeners =
new ConcurrentHashMap<>();
private final Executor callbackExecutor = Executors.newSingleThreadExecutor();
private final Set<RemoteCallbackBinder> callbackBinders = new CopyOnWriteArraySet<>();
public ConnectedDeviceManagerBinder(
ConnectedDeviceManager connectedDeviceManager, LoggingManager loggingManager) {
this.connectedDeviceManager = connectedDeviceManager;
this.loggingManager = loggingManager;
}
@Override
public List<ConnectedDevice> getActiveUserConnectedDevices() {
return connectedDeviceManager.getActiveUserConnectedDevices();
}
@Override
public void registerActiveUserConnectionCallback(@NonNull IConnectionCallback callback) {
ConnectionCallback connectionCallback =
new ConnectionCallback() {
@Override
public void onDeviceConnected(ConnectedDevice device) {
try {
callback.onDeviceConnected(device);
} catch (RemoteException exception) {
loge(TAG, "onDeviceConnected failed.", exception);
}
}
@Override
public void onDeviceDisconnected(ConnectedDevice device) {
try {
callback.onDeviceDisconnected(device);
} catch (RemoteException exception) {
loge(TAG, "onDeviceDisconnected failed.", exception);
}
}
};
connectedDeviceManager.registerActiveUserConnectionCallback(
connectionCallback, callbackExecutor);
RemoteCallbackBinder remoteBinder =
new RemoteCallbackBinder(
callback.asBinder(), iBinder -> unregisterConnectionCallback(callback));
callbackBinders.add(remoteBinder);
connectionCallbacks.put(callback.asBinder(), connectionCallback);
}
@Override
public void unregisterConnectionCallback(@NonNull IConnectionCallback callback) {
IBinder binder = callback.asBinder();
RemoteCallbackBinder remoteBinder = findRemoteCallbackBinder(binder);
if (remoteBinder == null) {
loge(
TAG,
"RemoteCallbackBinder is null, IConnectionCallback was not " + "previously registered.");
return;
}
ConnectionCallback connectionCallback = connectionCallbacks.get(binder);
if (connectionCallback == null) {
loge(TAG, "ConnectionCallback is null.");
return;
}
connectedDeviceManager.unregisterConnectionCallback(connectionCallback);
remoteBinder.cleanUp();
callbackBinders.remove(remoteBinder);
connectionCallbacks.remove(binder);
}
@Override
public void registerDeviceCallback(
@NonNull ConnectedDevice connectedDevice,
@NonNull ParcelUuid recipientId,
@NonNull IDeviceCallback callback) {
DeviceCallback deviceCallback =
new DeviceCallback() {
@Override
public void onSecureChannelEstablished(ConnectedDevice device) {
try {
callback.onSecureChannelEstablished(device);
} catch (RemoteException exception) {
loge(TAG, "onSecureChannelEstablished failed.", exception);
}
}
@Override
public void onMessageReceived(ConnectedDevice device, byte[] message) {
try {
callback.onMessageReceived(device, message);
} catch (RemoteException exception) {
loge(TAG, "onMessageReceived failed.", exception);
}
}
@Override
public void onDeviceError(ConnectedDevice device, int error) {
try {
callback.onDeviceError(device, error);
} catch (RemoteException exception) {
loge(TAG, "onDeviceError failed.", exception);
}
}
};
connectedDeviceManager.registerDeviceCallback(
connectedDevice,
recipientId.getUuid(),
deviceCallback,
callbackExecutor);
deviceCallbacks.put(callback.asBinder(), deviceCallback);
RemoteCallbackBinder remoteBinder =
new RemoteCallbackBinder(
callback.asBinder(),
iBinder -> unregisterDeviceCallback(connectedDevice, recipientId, callback));
callbackBinders.add(remoteBinder);
}
@Override
public void unregisterDeviceCallback(
@NonNull ConnectedDevice connectedDevice,
@NonNull ParcelUuid recipientId,
@NonNull IDeviceCallback callback) {
IBinder binder = callback.asBinder();
RemoteCallbackBinder remoteBinder = findRemoteCallbackBinder(binder);
if (remoteBinder == null) {
loge(
TAG,
"RemoteCallbackBinder is null, IDeviceCallback was not previously "
+ "registered. Ignoring call to unregister.");
return;
}
DeviceCallback deviceCallback = deviceCallbacks.remove(binder);
if (deviceCallback == null) {
loge(TAG, "No DeviceCallback associated with given callback. " + "Cannot unregister.");
return;
}
connectedDeviceManager.unregisterDeviceCallback(
connectedDevice, recipientId.getUuid(), deviceCallback);
remoteBinder.cleanUp();
callbackBinders.remove(remoteBinder);
}
@Override
public boolean sendMessageSecurely(
@NonNull ConnectedDevice connectedDevice,
@NonNull ParcelUuid recipientId,
@NonNull byte[] message) {
try {
connectedDeviceManager.sendMessageSecurely(
connectedDevice, recipientId.getUuid(), message);
return true;
} catch (IllegalStateException exception) {
loge(TAG, "Attempted to send message prior to secure channel established.", exception);
}
return false;
}
@Override
public void sendMessageUnsecurely(
@NonNull ConnectedDevice connectedDevice,
@NonNull ParcelUuid recipientId,
@NonNull byte[] message) {
connectedDeviceManager.sendMessageUnsecurely(
connectedDevice, recipientId.getUuid(), message);
}
@Override
public void registerDeviceAssociationCallback(@NonNull IDeviceAssociationCallback callback) {
DeviceAssociationCallback associationCallback =
new DeviceAssociationCallback() {
@Override
public void onAssociatedDeviceAdded(AssociatedDevice device) {
try {
callback.onAssociatedDeviceAdded(device);
} catch (RemoteException exception) {
loge(TAG, "onAssociatedDeviceAdded failed.", exception);
}
}
@Override
public void onAssociatedDeviceRemoved(AssociatedDevice device) {
try {
callback.onAssociatedDeviceRemoved(device);
} catch (RemoteException exception) {
loge(TAG, "onAssociatedDeviceRemoved failed.", exception);
}
}
@Override
public void onAssociatedDeviceUpdated(AssociatedDevice device) {
try {
callback.onAssociatedDeviceUpdated(device);
} catch (RemoteException exception) {
loge(TAG, "onAssociatedDeviceUpdated failed.", exception);
}
}
};
connectedDeviceManager.registerDeviceAssociationCallback(
associationCallback, callbackExecutor);
RemoteCallbackBinder remoteBinder =
new RemoteCallbackBinder(
callback.asBinder(), iBinder -> unregisterDeviceAssociationCallback(callback));
callbackBinders.add(remoteBinder);
associationCallbacks.put(callback.asBinder(), associationCallback);
}
@Override
public void unregisterDeviceAssociationCallback(@NonNull IDeviceAssociationCallback callback) {
IBinder binder = callback.asBinder();
RemoteCallbackBinder remoteBinder = findRemoteCallbackBinder(binder);
if (remoteBinder == null) {
loge(
TAG,
"RemoteCallbackBinder is null, IDeviceAssociationCallback was "
+ "not previously registered.");
return;
}
DeviceAssociationCallback associationCallback = associationCallbacks.remove(binder);
if (associationCallback == null) {
loge(TAG, "DeviceAssociationCallback is null.");
return;
}
connectedDeviceManager.unregisterDeviceAssociationCallback(associationCallback);
remoteBinder.cleanUp();
callbackBinders.remove(remoteBinder);
}
@Override
public void registerOnLogRequestedListener(
int loggerId, @NonNull IOnLogRequestedListener listener) {
OnLogRequestedListener onLogRequestedListener =
() -> {
try {
listener.onLogRecordsRequested();
} catch (RemoteException exception) {
loge(TAG, "Failed to notify log records requested.", exception);
}
};
loggingManager.addOnLogRequestedListener(loggerId, onLogRequestedListener, callbackExecutor);
IBinder listenerBinder = listener.asBinder();
RemoteCallbackBinder remoteBinder =
new RemoteCallbackBinder(
listenerBinder, iBinder -> unregisterOnLogRequestedListener(loggerId, listener));
callbackBinders.add(remoteBinder);
logRequestedListeners.put(listenerBinder, onLogRequestedListener);
}
@Override
public void unregisterOnLogRequestedListener(
int loggerId, @NonNull IOnLogRequestedListener listener) {
IBinder listenerBinder = listener.asBinder();
RemoteCallbackBinder remoteBinder = findRemoteCallbackBinder(listenerBinder);
if (remoteBinder == null) {
loge(
TAG,
"RemoteCallbackBinder is null, IOnLogRequestedListener "
+ listener
+ " is not previously registered.");
return;
}
OnLogRequestedListener onLogRequestedListener = logRequestedListeners.remove(listenerBinder);
if (onLogRequestedListener == null) {
loge(
TAG,
"OnLogRequestedListener is null, IOnLogRequestedListener "
+ listener
+ " is not previously registered.");
return;
}
loggingManager.removeOnLogRequestedListener(loggerId, onLogRequestedListener);
remoteBinder.cleanUp();
callbackBinders.remove(listenerBinder);
}
@Override
public void processLogRecords(int loggerId, @NonNull byte[] logRecords) {
loggingManager.prepareLocalLogRecords(loggerId, logRecords);
}
@Nullable
private RemoteCallbackBinder findRemoteCallbackBinder(@NonNull IBinder binder) {
for (RemoteCallbackBinder remoteBinder : callbackBinders) {
if (remoteBinder.getCallbackBinder().equals(binder)) {
return remoteBinder;
}
}
return null;
}
}