[4/X] Introduce BluetoothCompanionDeviceConnectionListener
Bug: 211398735
Test: atest CtsCompanionDeviceManagerCoreTestCases
Test: atest CtsCompanionDeviceManagerUiAutomationTestCases
Test: atest CtsOsTestCases:CompanionDeviceManagerTest
Change-Id: If0ffc8d5ddaf8609febbe0b59b1bc67e0c7aeb98
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
new file mode 100644
index 0000000..a4fa1c1
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 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.android.server.companion.presence;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.companion.AssociationInfo;
+import android.net.MacAddress;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.util.Log;
+
+import com.android.server.companion.AssociationStore;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@SuppressLint("LongLogTag")
+class BluetoothCompanionDeviceConnectionListener
+ extends BluetoothAdapter.BluetoothConnectionCallback
+ implements AssociationStore.OnChangeListener {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "CompanionDevice_PresenceMonitor_BT";
+
+ interface Callback {
+ void onBluetoothCompanionDeviceConnected(int associationId);
+
+ void onBluetoothCompanionDeviceDisconnected(int associationId);
+ }
+
+ private final @NonNull AssociationStore mAssociationStore;
+ private final @NonNull Callback mCallback;
+ /** A set of ALL connected BT device (not only companion.) */
+ private final @NonNull Map<MacAddress, BluetoothDevice> mAllConnectedDevices = new HashMap<>();
+
+ BluetoothCompanionDeviceConnectionListener(@NonNull AssociationStore associationStore,
+ @NonNull Callback callback) {
+ mAssociationStore = associationStore;
+ mCallback = callback;
+ }
+
+ public void init(@NonNull BluetoothAdapter btAdapter) {
+ if (DEBUG) Log.i(TAG, "init()");
+
+ btAdapter.registerBluetoothConnectionCallback(
+ new HandlerExecutor(Handler.getMain()), /* callback */this);
+ mAssociationStore.registerListener(this);
+ }
+
+ /**
+ * Overrides
+ * {@link BluetoothAdapter.BluetoothConnectionCallback#onDeviceConnected(BluetoothDevice)}.
+ */
+ @Override
+ public void onDeviceConnected(@NonNull BluetoothDevice device) {
+ if (DEBUG) Log.i(TAG, "onDevice_Connected() " + toString(device));
+
+ final MacAddress macAddress = MacAddress.fromString(device.getAddress());
+ if (mAllConnectedDevices.put(macAddress, device) != null) {
+ if (DEBUG) Log.w(TAG, "Device " + toString(device) + " is already connected.");
+ return;
+ }
+
+ onDeviceConnectivityChanged(device, true);
+ }
+
+ /**
+ * Overrides
+ * {@link BluetoothAdapter.BluetoothConnectionCallback#onDeviceConnected(BluetoothDevice)}.
+ * Also invoked when user turns BT off while the device is connected.
+ */
+ @Override
+ public void onDeviceDisconnected(@NonNull BluetoothDevice device,
+ @DisconnectReason int reason) {
+ if (DEBUG) {
+ Log.i(TAG, "onDevice_Disconnected() " + toString(device));
+ Log.d(TAG, " reason=" + disconnectReasonText(reason));
+ }
+
+ final MacAddress macAddress = MacAddress.fromString(device.getAddress());
+ if (mAllConnectedDevices.remove(macAddress) == null) {
+ if (DEBUG) Log.w(TAG, "The device wasn't tracked as connected " + toString(device));
+ return;
+ }
+
+ onDeviceConnectivityChanged(device, false);
+ }
+
+ private void onDeviceConnectivityChanged(@NonNull BluetoothDevice device, boolean connected) {
+ final List<AssociationInfo> associations =
+ mAssociationStore.getAssociationsByAddress(device.getAddress());
+
+ if (DEBUG) {
+ Log.d(TAG, "onDevice_ConnectivityChanged() " + toString(device)
+ + " connected=" + connected);
+ if (associations.isEmpty()) {
+ Log.d(TAG, " > No CDM associations");
+ } else {
+ Log.d(TAG, " > associations=" + Arrays.toString(associations.toArray()));
+ }
+ }
+
+ for (AssociationInfo association : associations) {
+ final int id = association.getId();
+ if (connected) {
+ mCallback.onBluetoothCompanionDeviceConnected(id);
+ } else {
+ mCallback.onBluetoothCompanionDeviceDisconnected(id);
+ }
+ }
+ }
+
+ @Override
+ public void onAssociationAdded(AssociationInfo association) {
+ if (DEBUG) Log.d(TAG, "onAssociation_Added() " + association);
+
+ if (mAllConnectedDevices.containsKey(association.getDeviceMacAddress())) {
+ mCallback.onBluetoothCompanionDeviceConnected(association.getId());
+ }
+ }
+
+ @Override
+ public void onAssociationUpdated(AssociationInfo association, boolean addressChanged) {
+ if (DEBUG) {
+ Log.d(TAG, "onAssociation_Updated() addrChange=" + addressChanged
+ + " " + association);
+ }
+
+ if (!addressChanged) {
+ // Don't need to do anything.
+ return;
+ }
+
+ // At the moment CDM does allow changing association addresses, so we will never come here.
+ // This will be implemented when CDM support updating addresses.
+ throw new IllegalArgumentException("Address changes are not supported.");
+ }
+
+ private static String toString(@NonNull BluetoothDevice btDevice) {
+ final StringBuilder sb = new StringBuilder(btDevice.getAddress());
+
+ sb.append(" [name=");
+ final String name = btDevice.getName();
+ if (name != null) {
+ sb.append('\'').append(name).append('\'');
+ } else {
+ sb.append("null");
+ }
+
+ final String alias = btDevice.getAlias();
+ if (alias != null) {
+ sb.append(", alias='").append(alias).append("'");
+ }
+
+ return sb.append(']').toString();
+ }
+}