Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f304205b authored by Jack He's avatar Jack He Committed by Automerger Merge Worker
Browse files

Merge "hap: Add Hearing Access Profile boilerplate" am: 8ee9cbb7

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/1884652

Change-Id: Icf45308621a129615f872b2c8b7e109ef7774be6
parents 9f5236d1 8ee9cbb7
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -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"
+1 −0
Original line number Diff line number Diff line
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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];
+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);
            }
        }
    }
}
+9 −1
Original line number Diff line number Diff line
@@ -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
@@ -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