1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.nearby.fastpair.provider.bluetooth; 18 19 import android.annotation.TargetApi; 20 import android.bluetooth.BluetoothGattCharacteristic; 21 import android.bluetooth.BluetoothGattService; 22 23 import androidx.annotation.Nullable; 24 25 import com.android.server.nearby.common.bluetooth.BluetoothConsts; 26 27 import java.util.ArrayList; 28 import java.util.HashMap; 29 import java.util.List; 30 import java.util.Map; 31 import java.util.Map.Entry; 32 import java.util.UUID; 33 34 /** Configuration of a GATT server. */ 35 @TargetApi(18) 36 public class BluetoothGattServerConfig { 37 private final Map<UUID, ServiceConfig> mServiceConfigs = new HashMap<UUID, ServiceConfig>(); 38 39 @Nullable 40 private BluetoothGattServerHelper.Listener mServerlistener = null; 41 addService(UUID uuid, ServiceConfig serviceConfig)42 public BluetoothGattServerConfig addService(UUID uuid, ServiceConfig serviceConfig) { 43 mServiceConfigs.put(uuid, serviceConfig); 44 return this; 45 } 46 setServerConnectionListener( BluetoothGattServerHelper.Listener listener)47 public BluetoothGattServerConfig setServerConnectionListener( 48 BluetoothGattServerHelper.Listener listener) { 49 mServerlistener = listener; 50 return this; 51 } 52 53 @Nullable getServerListener()54 public BluetoothGattServerHelper.Listener getServerListener() { 55 return mServerlistener; 56 } 57 58 /** 59 * Adds a service and a characteristic to indicate that the server has dynamic services. 60 * This is a workaround for b/21587710. 61 * TODO(lingjunl): remove them when b/21587710 is fixed. 62 */ addSelfDefinedDynamicService()63 public BluetoothGattServerConfig addSelfDefinedDynamicService() { 64 ServiceConfig serviceConfig = new ServiceConfig().addCharacteristic( 65 new BluetoothGattServlet() { 66 @Override 67 public BluetoothGattCharacteristic getCharacteristic() { 68 return new BluetoothGattCharacteristic( 69 BluetoothConsts.SERVICE_DYNAMIC_CHARACTERISTIC, 70 BluetoothGattCharacteristic.PROPERTY_READ, 71 BluetoothGattCharacteristic.PERMISSION_READ); 72 } 73 }); 74 return addService(BluetoothConsts.SERVICE_DYNAMIC_SERVICE, serviceConfig); 75 } 76 getBluetoothGattServices()77 public List<BluetoothGattService> getBluetoothGattServices() { 78 List<BluetoothGattService> result = new ArrayList<BluetoothGattService>(); 79 for (Entry<UUID, ServiceConfig> serviceEntry : mServiceConfigs.entrySet()) { 80 UUID serviceUuid = serviceEntry.getKey(); 81 ServiceConfig serviceConfig = serviceEntry.getValue(); 82 if (serviceUuid == null || serviceConfig == null) { 83 // This is not supposed to happen 84 throw new IllegalStateException(); 85 } 86 BluetoothGattService gattService = new BluetoothGattService(serviceUuid, 87 BluetoothGattService.SERVICE_TYPE_PRIMARY); 88 for (Entry<BluetoothGattCharacteristic, BluetoothGattServlet> servletEntry : 89 serviceConfig.getServlets().entrySet()) { 90 BluetoothGattCharacteristic characteristic = servletEntry.getKey(); 91 if (characteristic == null) { 92 // This is not supposed to happen 93 throw new IllegalStateException(); 94 } 95 gattService.addCharacteristic(characteristic); 96 } 97 result.add(gattService); 98 } 99 return result; 100 } 101 getAdvertisedUuids()102 public List<UUID> getAdvertisedUuids() { 103 List<UUID> result = new ArrayList<UUID>(); 104 for (Entry<UUID, ServiceConfig> serviceEntry : mServiceConfigs.entrySet()) { 105 UUID serviceUuid = serviceEntry.getKey(); 106 ServiceConfig serviceConfig = serviceEntry.getValue(); 107 if (serviceUuid == null || serviceConfig == null) { 108 // This is not supposed to happen 109 throw new IllegalStateException(); 110 } 111 if (serviceConfig.isAdvertised()) { 112 result.add(serviceUuid); 113 } 114 } 115 return result; 116 } 117 getServlets()118 public Map<BluetoothGattCharacteristic, BluetoothGattServlet> getServlets() { 119 Map<BluetoothGattCharacteristic, BluetoothGattServlet> result = 120 new HashMap<BluetoothGattCharacteristic, BluetoothGattServlet>(); 121 for (ServiceConfig serviceConfig : mServiceConfigs.values()) { 122 result.putAll(serviceConfig.getServlets()); 123 } 124 return result; 125 } 126 127 /** Configuration of a GATT service. */ 128 public static class ServiceConfig { 129 private final Map<BluetoothGattCharacteristic, BluetoothGattServlet> mServlets = 130 new HashMap<BluetoothGattCharacteristic, BluetoothGattServlet>(); 131 private boolean mAdvertise = false; 132 addCharacteristic(BluetoothGattServlet servlet)133 public ServiceConfig addCharacteristic(BluetoothGattServlet servlet) { 134 mServlets.put(servlet.getCharacteristic(), servlet); 135 return this; 136 } 137 setAdvertise(boolean advertise)138 public ServiceConfig setAdvertise(boolean advertise) { 139 mAdvertise = advertise; 140 return this; 141 } 142 getServlets()143 public Map<BluetoothGattCharacteristic, BluetoothGattServlet> getServlets() { 144 return mServlets; 145 } 146 isAdvertised()147 public boolean isAdvertised() { 148 return mAdvertise; 149 } 150 } 151 } 152