Loading android/app/AndroidManifest.xml +8 −0 Original line number Diff line number Diff line Loading @@ -422,6 +422,14 @@ <action android:name="android.bluetooth.IBluetoothHearingAid"/> </intent-filter> </service> <service android:process="@string/process" android:name=".hap.HapClientService" android:enabled="@bool/profile_supported_hap_client" android:exported="true"> <intent-filter> <action android:name="android.bluetooth.IBluetoothHapClient"/> </intent-filter> </service> <service android:process="@string/process" android:name=".vc.VolumeControlService" Loading android/app/res/values/config.xml +1 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ <bool name="profile_supported_mcp_server">true</bool> <bool name="profile_supported_csip_set_coordinator">true</bool> <bool name="profile_supported_le_call_control">true</bool> <bool name="profile_supported_hap_client">true</bool> <!-- If true, we will require location to be enabled on the device to fire Bluetooth LE scan result callbacks in addition to having one Loading android/app/src/com/android/bluetooth/btservice/Config.java +3 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import com.android.bluetooth.avrcp.AvrcpTargetService; import com.android.bluetooth.avrcpcontroller.AvrcpControllerService; import com.android.bluetooth.csip.CsipSetCoordinatorService; import com.android.bluetooth.gatt.GattService; import com.android.bluetooth.hap.HapClientService; import com.android.bluetooth.hearingaid.HearingAidService; import com.android.bluetooth.hfp.HeadsetService; import com.android.bluetooth.hfpclient.HeadsetClientService; Loading Loading @@ -132,6 +133,8 @@ public class Config { new ProfileConfig(CsipSetCoordinatorService.class, R.bool.profile_supported_csip_set_coordinator, (1 << BluetoothProfile.CSIP_SET_COORDINATOR)), new ProfileConfig(HapClientService.class, R.bool.profile_supported_hap_client, (1 << BluetoothProfile.HAP_CLIENT)), }; private static Class[] sSupportedProfiles = new Class[0]; Loading android/app/src/com/android/bluetooth/hap/HapClientService.java 0 → 100644 +373 −0 Original line number Diff line number Diff line /* * Copyright 2021 HIMSA II K/S - www.himsa.com. * Represented by EHIMA - www.ehima.com * * 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.bluetooth.hap; import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetoothHapClient; import android.content.AttributionSource; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.ProfileService; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.SynchronousResultReceiver; import java.util.ArrayList; import java.util.List; /** * Provides Bluetooth Hearing Access profile, as a service. * @hide */ public class HapClientService extends ProfileService { private static final boolean DBG = true; private static final String TAG = "HapClientService"; private static HapClientService sHapClient; private static synchronized void setHapClient(HapClientService instance) { if (DBG) { Log.d(TAG, "setHapClient(): set to: " + instance); } sHapClient = instance; } /** * Get the HapClientService instance * @return HapClientService instance */ public static synchronized HapClientService getHapClientService() { if (sHapClient == null) { Log.w(TAG, "getHapClientService(): service is NULL"); return null; } if (!sHapClient.isAvailable()) { Log.w(TAG, "getHapClientService(): service is not available"); return null; } return sHapClient; } @Override protected void create() { if (DBG) { Log.d(TAG, "create()"); } } @Override protected void cleanup() { if (DBG) { Log.d(TAG, "cleanup()"); } } @Override protected IProfileServiceBinder initBinder() { return new BluetoothHapClientBinder(this); } @Override protected boolean start() { if (DBG) { Log.d(TAG, "start()"); } if (sHapClient != null) { throw new IllegalStateException("start() called twice"); } // Mark service as started setHapClient(this); return true; } @Override protected boolean stop() { if (DBG) { Log.d(TAG, "stop()"); } if (sHapClient == null) { Log.w(TAG, "stop() called before start()"); return true; } // Marks service as stopped setHapClient(null); return true; } @Override public void dump(StringBuilder sb) { super.dump(sb); } /** * Binder object: must be a static class or memory leak may occur */ @VisibleForTesting static class BluetoothHapClientBinder extends IBluetoothHapClient.Stub implements IProfileServiceBinder { private HapClientService mService; BluetoothHapClientBinder(HapClientService svc) { mService = svc; } private HapClientService getService(AttributionSource source) { if (!Utils.checkCallerIsSystemOrActiveUser(TAG) || !Utils.checkServiceAvailable(mService, TAG) || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { Log.w(TAG, "Hearing Access call not allowed for non-active user"); return null; } if (mService != null && mService.isAvailable()) { return mService; } return null; } @Override public void cleanup() { mService = null; } @Override public void connect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void disconnect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getConnectedDevices(AttributionSource source, SynchronousResultReceiver receiver) { try { List<BluetoothDevice> defaultValue = new ArrayList<>(); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getDevicesMatchingConnectionStates(int[] states, AttributionSource source, SynchronousResultReceiver receiver) { try { List<BluetoothDevice> defaultValue = new ArrayList<>(); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getConnectionState(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { int defaultValue = BluetoothProfile.STATE_DISCONNECTED; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getConnectionPolicy(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { int defaultValue = BluetoothProfile.CONNECTION_POLICY_UNKNOWN; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getActivePresetIndex(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getHapGroup(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { int defaultValue = BluetoothCsipSetCoordinator.GROUP_ID_INVALID; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void selectActivePreset(BluetoothDevice device, int presetIndex, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void groupSelectActivePreset(int groupId, int presetIndex, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void nextActivePreset(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void groupNextActivePreset(int groupId, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void previousActivePreset(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void groupPreviousActivePreset(int groupId, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getPresetInfo(BluetoothDevice device, int presetIndex, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getAllPresetsInfo(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getFeatures(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void setPresetName(BluetoothDevice device, int presetIndex, String name, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void groupSetPresetName(int groupId, int presetIndex, String name, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } } } framework/java/android/bluetooth/BluetoothProfile.java +9 −1 Original line number Diff line number Diff line Loading @@ -246,13 +246,19 @@ public interface BluetoothProfile { */ int LE_CALL_CONTROL = 27; /* * Hearing Access Profile Client * */ int HAP_CLIENT = 28; /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ int MAX_PROFILE_ID = 27; int MAX_PROFILE_ID = 28; /** * Default priority for devices that we try to auto-connect to and Loading Loading @@ -452,6 +458,8 @@ public interface BluetoothProfile { return "HEARING_AID"; case LE_AUDIO: return "LE_AUDIO"; case HAP_CLIENT: return "HAP_CLIENT"; default: return "UNKNOWN_PROFILE"; } Loading Loading
android/app/AndroidManifest.xml +8 −0 Original line number Diff line number Diff line Loading @@ -422,6 +422,14 @@ <action android:name="android.bluetooth.IBluetoothHearingAid"/> </intent-filter> </service> <service android:process="@string/process" android:name=".hap.HapClientService" android:enabled="@bool/profile_supported_hap_client" android:exported="true"> <intent-filter> <action android:name="android.bluetooth.IBluetoothHapClient"/> </intent-filter> </service> <service android:process="@string/process" android:name=".vc.VolumeControlService" Loading
android/app/res/values/config.xml +1 −0 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ <bool name="profile_supported_mcp_server">true</bool> <bool name="profile_supported_csip_set_coordinator">true</bool> <bool name="profile_supported_le_call_control">true</bool> <bool name="profile_supported_hap_client">true</bool> <!-- If true, we will require location to be enabled on the device to fire Bluetooth LE scan result callbacks in addition to having one Loading
android/app/src/com/android/bluetooth/btservice/Config.java +3 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import com.android.bluetooth.avrcp.AvrcpTargetService; import com.android.bluetooth.avrcpcontroller.AvrcpControllerService; import com.android.bluetooth.csip.CsipSetCoordinatorService; import com.android.bluetooth.gatt.GattService; import com.android.bluetooth.hap.HapClientService; import com.android.bluetooth.hearingaid.HearingAidService; import com.android.bluetooth.hfp.HeadsetService; import com.android.bluetooth.hfpclient.HeadsetClientService; Loading Loading @@ -132,6 +133,8 @@ public class Config { new ProfileConfig(CsipSetCoordinatorService.class, R.bool.profile_supported_csip_set_coordinator, (1 << BluetoothProfile.CSIP_SET_COORDINATOR)), new ProfileConfig(HapClientService.class, R.bool.profile_supported_hap_client, (1 << BluetoothProfile.HAP_CLIENT)), }; private static Class[] sSupportedProfiles = new Class[0]; Loading
android/app/src/com/android/bluetooth/hap/HapClientService.java 0 → 100644 +373 −0 Original line number Diff line number Diff line /* * Copyright 2021 HIMSA II K/S - www.himsa.com. * Represented by EHIMA - www.ehima.com * * 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.bluetooth.hap; import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetoothHapClient; import android.content.AttributionSource; import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.ProfileService; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.SynchronousResultReceiver; import java.util.ArrayList; import java.util.List; /** * Provides Bluetooth Hearing Access profile, as a service. * @hide */ public class HapClientService extends ProfileService { private static final boolean DBG = true; private static final String TAG = "HapClientService"; private static HapClientService sHapClient; private static synchronized void setHapClient(HapClientService instance) { if (DBG) { Log.d(TAG, "setHapClient(): set to: " + instance); } sHapClient = instance; } /** * Get the HapClientService instance * @return HapClientService instance */ public static synchronized HapClientService getHapClientService() { if (sHapClient == null) { Log.w(TAG, "getHapClientService(): service is NULL"); return null; } if (!sHapClient.isAvailable()) { Log.w(TAG, "getHapClientService(): service is not available"); return null; } return sHapClient; } @Override protected void create() { if (DBG) { Log.d(TAG, "create()"); } } @Override protected void cleanup() { if (DBG) { Log.d(TAG, "cleanup()"); } } @Override protected IProfileServiceBinder initBinder() { return new BluetoothHapClientBinder(this); } @Override protected boolean start() { if (DBG) { Log.d(TAG, "start()"); } if (sHapClient != null) { throw new IllegalStateException("start() called twice"); } // Mark service as started setHapClient(this); return true; } @Override protected boolean stop() { if (DBG) { Log.d(TAG, "stop()"); } if (sHapClient == null) { Log.w(TAG, "stop() called before start()"); return true; } // Marks service as stopped setHapClient(null); return true; } @Override public void dump(StringBuilder sb) { super.dump(sb); } /** * Binder object: must be a static class or memory leak may occur */ @VisibleForTesting static class BluetoothHapClientBinder extends IBluetoothHapClient.Stub implements IProfileServiceBinder { private HapClientService mService; BluetoothHapClientBinder(HapClientService svc) { mService = svc; } private HapClientService getService(AttributionSource source) { if (!Utils.checkCallerIsSystemOrActiveUser(TAG) || !Utils.checkServiceAvailable(mService, TAG) || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { Log.w(TAG, "Hearing Access call not allowed for non-active user"); return null; } if (mService != null && mService.isAvailable()) { return mService; } return null; } @Override public void cleanup() { mService = null; } @Override public void connect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void disconnect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getConnectedDevices(AttributionSource source, SynchronousResultReceiver receiver) { try { List<BluetoothDevice> defaultValue = new ArrayList<>(); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getDevicesMatchingConnectionStates(int[] states, AttributionSource source, SynchronousResultReceiver receiver) { try { List<BluetoothDevice> defaultValue = new ArrayList<>(); receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getConnectionState(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { int defaultValue = BluetoothProfile.STATE_DISCONNECTED; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getConnectionPolicy(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { int defaultValue = BluetoothProfile.CONNECTION_POLICY_UNKNOWN; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getActivePresetIndex(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getHapGroup(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { int defaultValue = BluetoothCsipSetCoordinator.GROUP_ID_INVALID; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void selectActivePreset(BluetoothDevice device, int presetIndex, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void groupSelectActivePreset(int groupId, int presetIndex, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void nextActivePreset(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void groupNextActivePreset(int groupId, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void previousActivePreset(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void groupPreviousActivePreset(int groupId, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getPresetInfo(BluetoothDevice device, int presetIndex, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getAllPresetsInfo(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void getFeatures(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void setPresetName(BluetoothDevice device, int presetIndex, String name, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } @Override public void groupSetPresetName(int groupId, int presetIndex, String name, AttributionSource source, SynchronousResultReceiver receiver) { try { boolean defaultValue = false; receiver.send(defaultValue); } catch (RuntimeException e) { receiver.propagateException(e); } } } }
framework/java/android/bluetooth/BluetoothProfile.java +9 −1 Original line number Diff line number Diff line Loading @@ -246,13 +246,19 @@ public interface BluetoothProfile { */ int LE_CALL_CONTROL = 27; /* * Hearing Access Profile Client * */ int HAP_CLIENT = 28; /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ int MAX_PROFILE_ID = 27; int MAX_PROFILE_ID = 28; /** * Default priority for devices that we try to auto-connect to and Loading Loading @@ -452,6 +458,8 @@ public interface BluetoothProfile { return "HEARING_AID"; case LE_AUDIO: return "LE_AUDIO"; case HAP_CLIENT: return "HAP_CLIENT"; default: return "UNKNOWN_PROFILE"; } Loading