1 /* 2 * Copyright 2021 HIMSA II K/S - www.himsa.com. 3 * Represented by EHIMA - www.ehima.com 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.bluetooth.csip; 19 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothDevice; 22 import android.util.Log; 23 24 import com.android.bluetooth.Utils; 25 import com.android.internal.annotations.GuardedBy; 26 import com.android.internal.annotations.VisibleForTesting; 27 28 import java.util.UUID; 29 30 /** 31 * CSIP Set Coordinator role native interface 32 */ 33 public class CsipSetCoordinatorNativeInterface { 34 private static final String TAG = "CsipSetCoordinatorNativeInterface"; 35 private static final boolean DBG = false; 36 private BluetoothAdapter mAdapter; 37 38 @GuardedBy("INSTANCE_LOCK") private static CsipSetCoordinatorNativeInterface sInstance; 39 private static final Object INSTANCE_LOCK = new Object(); 40 41 static { classInitNative()42 classInitNative(); 43 } 44 CsipSetCoordinatorNativeInterface()45 private CsipSetCoordinatorNativeInterface() { 46 mAdapter = BluetoothAdapter.getDefaultAdapter(); 47 if (mAdapter == null) { 48 Log.wtf(TAG, "No Bluetooth Adapter Available"); 49 } 50 } 51 52 /** 53 * Get singleton instance. 54 */ getInstance()55 public static CsipSetCoordinatorNativeInterface getInstance() { 56 synchronized (INSTANCE_LOCK) { 57 if (sInstance == null) { 58 sInstance = new CsipSetCoordinatorNativeInterface(); 59 } 60 return sInstance; 61 } 62 } 63 64 /** 65 * Initializes the native interface. 66 * 67 * priorities to configure. 68 */ 69 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) init()70 public void init() { 71 initNative(); 72 } 73 74 /** 75 * Cleanup the native interface. 76 */ 77 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) cleanup()78 public void cleanup() { 79 cleanupNative(); 80 } 81 82 /** 83 * Initiates CsipSetCoordinator connection to a remote device. 84 * 85 * @param device the remote device 86 * @return true on success, otherwise false. 87 */ 88 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) connect(BluetoothDevice device)89 public boolean connect(BluetoothDevice device) { 90 return connectNative(getByteAddress(device)); 91 } 92 93 /** 94 * Disconnects CsipSetCoordinator from a remote device. 95 * 96 * @param device the remote device 97 * @return true on success, otherwise false. 98 */ 99 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) disconnect(BluetoothDevice device)100 public boolean disconnect(BluetoothDevice device) { 101 return disconnectNative(getByteAddress(device)); 102 } 103 104 /** 105 * Get the device by the address 106 * 107 * @return the device 108 */ 109 @VisibleForTesting getDevice(byte[] address)110 public BluetoothDevice getDevice(byte[] address) { 111 return mAdapter.getRemoteDevice(address); 112 } 113 getByteAddress(BluetoothDevice device)114 private byte[] getByteAddress(BluetoothDevice device) { 115 if (device == null) { 116 return Utils.getBytesFromAddress("00:00:00:00:00:00"); 117 } 118 return Utils.getBytesFromAddress(device.getAddress()); 119 } 120 sendMessageToService(CsipSetCoordinatorStackEvent event)121 private void sendMessageToService(CsipSetCoordinatorStackEvent event) { 122 CsipSetCoordinatorService service = 123 CsipSetCoordinatorService.getCsipSetCoordinatorService(); 124 if (service != null) { 125 service.messageFromNative(event); 126 } else { 127 Log.e(TAG, "Event ignored, service not available: " + event); 128 } 129 } 130 131 // Callbacks from the native stack back into the Java framework. 132 // All callbacks are routed via the Service which will disambiguate which 133 // state machine the message should be routed to. 134 135 /** Device connection state change */ 136 @VisibleForTesting onConnectionStateChanged(byte[] address, int state)137 public void onConnectionStateChanged(byte[] address, int state) { 138 CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent( 139 CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 140 event.device = getDevice(address); 141 event.valueInt1 = state; 142 143 if (DBG) { 144 Log.d(TAG, "onConnectionStateChanged: " + event); 145 } 146 sendMessageToService(event); 147 } 148 149 /** Device availability */ 150 @VisibleForTesting onDeviceAvailable( byte[] address, int groupId, int groupSize, int rank, long uuidLsb, long uuidMsb)151 public void onDeviceAvailable( 152 byte[] address, int groupId, int groupSize, int rank, long uuidLsb, long uuidMsb) { 153 UUID uuid = new UUID(uuidMsb, uuidLsb); 154 CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent( 155 CsipSetCoordinatorStackEvent.EVENT_TYPE_DEVICE_AVAILABLE); 156 event.device = getDevice(address); 157 event.valueInt1 = groupId; 158 event.valueInt2 = groupSize; 159 event.valueInt3 = rank; 160 event.valueUuid1 = uuid; 161 162 if (DBG) { 163 Log.d(TAG, "onDeviceAvailable: " + event); 164 } 165 sendMessageToService(event); 166 } 167 168 // Callbacks from the native stack back into the Java framework. 169 // All callbacks are routed via the Service which will disambiguate which 170 // state machine the message should be routed to. 171 172 /** 173 * Set member available callback 174 */ 175 @VisibleForTesting onSetMemberAvailable(byte[] address, int groupId)176 public void onSetMemberAvailable(byte[] address, int groupId) { 177 CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent( 178 CsipSetCoordinatorStackEvent.EVENT_TYPE_SET_MEMBER_AVAILABLE); 179 event.device = getDevice(address); 180 event.valueInt1 = groupId; 181 if (DBG) { 182 Log.d(TAG, "onSetMemberAvailable: " + event); 183 } 184 sendMessageToService(event); 185 } 186 187 /** 188 * Group lock changed callback as a result of lock or unlock request or 189 * autonomous event. 190 * 191 * @param groupId group identifier 192 * @param locked whether group is locked or unlocked 193 * @param status status of a requested lock/unlock 194 */ 195 @VisibleForTesting onGroupLockChanged(int groupId, boolean locked, int status)196 public void onGroupLockChanged(int groupId, boolean locked, int status) { 197 CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent( 198 CsipSetCoordinatorStackEvent.EVENT_TYPE_GROUP_LOCK_CHANGED); 199 event.valueInt1 = groupId; 200 event.valueInt2 = status; 201 event.valueBool1 = locked; 202 if (DBG) { 203 Log.d(TAG, "onGroupLockChanged: " + event); 204 } 205 sendMessageToService(event); 206 } 207 208 /** 209 * Set lock on the group. 210 * @param groupId group identifier 211 * @param lock True for lock, false for unlock 212 */ 213 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) groupLockSet(int groupId, boolean lock)214 public void groupLockSet(int groupId, boolean lock) { 215 groupLockSetNative(groupId, lock); 216 } 217 218 // Native methods that call into the JNI interface classInitNative()219 private static native void classInitNative(); initNative()220 private native void initNative(); cleanupNative()221 private native void cleanupNative(); connectNative(byte[] address)222 private native boolean connectNative(byte[] address); disconnectNative(byte[] address)223 private native boolean disconnectNative(byte[] address); groupLockSetNative(int groupId, boolean lock)224 private native void groupLockSetNative(int groupId, boolean lock); 225 } 226