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

Commit 722fa092 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "[Pandora] More HFP tests"

parents 0899e552 9183a100
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -7,10 +7,18 @@ class Modem:
    def __init__(self, port):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(("127.0.0.1", port))
        self.active_calls = []
        self.socket = s

    def close(self):
        for phone_number in self.active_calls:
            self.socket.sendall(b'REM0\r\nAT+REMOTECALL=6,0,0,"' + str(phone_number).encode("utf-8") + b'",0\r\n')
        self.socket.close()

    def call(self, phone_number):
        self.active_calls.append(phone_number)
        self.socket.sendall(b'REM0\r\nAT+REMOTECALL=4,0,0,"' + str(phone_number).encode("utf-8") + b'",129\r\n')

    def answer_outgoing_call(self, phone_number):
        self.active_calls.append(phone_number)
        self.socket.sendall(b'REM0\r\nAT+REMOTECALL=0,0,0,"' + str(phone_number).encode("utf-8") + b'",129\r\n')
+393 −10
Original line number Diff line number Diff line
@@ -13,11 +13,12 @@
# limitations under the License.
"""HFP proxy module."""

from mmi2grpc._helpers import assert_description
from mmi2grpc._helpers import assert_description, match_description
from mmi2grpc._proxy import ProfileProxy

from pandora_experimental.hfp_grpc import HFP
from pandora_experimental.host_grpc import Host
from pandora_experimental.host_pb2 import ConnectabilityMode
from pandora_experimental.security_grpc import Security
from pandora_experimental.hfp_pb2 import AudioPath

@@ -29,9 +30,10 @@ import time
WAIT_DELAY_BEFORE_CONNECTION = 2

# The tests needs the MMI to accept pairing confirmation request.
NEEDS_WAIT_CONNECTION_BEFORE_TEST = {'HFP/AG/WBS/BV-01-I', 'HFP/AG/SLC/BV-05-I'}
NEEDS_WAIT_CONNECTION_BEFORE_TEST = {"HFP/AG/WBS/BV-01-I", "HFP/AG/SLC/BV-05-I"}

IXIT_PHONE_NUMBER = 42
IXIT_SECOND_PHONE_NUMBER = 43


class HFPProxy(ProfileProxy):
@@ -56,7 +58,7 @@ class HFPProxy(ProfileProxy):
        def waitConnectionCallback(self, pts_addr):
            self.connection = self.host.WaitConnection(address=pts_addr).connection

        print(f'HFP placeholder mmi: asyncWaitConnection', file=sys.stderr)
        print(f"HFP placeholder mmi: asyncWaitConnection", file=sys.stderr)
        th = threading.Timer(interval=delay, function=waitConnectionCallback, args=(self, pts_addr))
        th.start()

@@ -77,15 +79,26 @@ class HFPProxy(ProfileProxy):
        return "OK"

    @assert_description
    def TSC_iut_enable_slc(self, pts_addr: bytes, **kwargs):
    def TSC_iut_enable_slc(self, test: str, pts_addr: bytes, **kwargs):
        """
        Click Ok, then initiate a service level connection from the
        Implementation Under Test (IUT) to the PTS.
        """

        def enable_slc():
            time.sleep(2)

            if test == "HFP/AG/SLC/BV-02-C":
                self.host.SetConnectabilityMode(connectability=ConnectabilityMode.CONNECTABILITY_CONNECTABLE)
                self.connection = self.host.Connect(address=pts_addr).connection
            else:
                if not self.connection:
                    self.connection = self.host.Connect(address=pts_addr).connection

            self.hfp.EnableSlc(connection=self.connection)

        threading.Thread(target=enable_slc).start()

        return "OK"

    @assert_description
@@ -118,8 +131,7 @@ class HFPProxy(ProfileProxy):
        Make the Implementation Under Test (IUT) connectable, then click Ok.
        """

        if "HFP/AG/SLC/BV-03-C" in test:
            self.connection = self.host.WaitConnection(pts_addr).connection
        self.host.SetConnectabilityMode(connectability=ConnectabilityMode.CONNECTABILITY_CONNECTABLE)

        return "OK"

@@ -184,6 +196,7 @@ class HFPProxy(ProfileProxy):
        """

        # TODO
        time.sleep(2)  # give it time for SCO to come up

        return "OK"

@@ -234,13 +247,383 @@ class HFPProxy(ProfileProxy):

        return "OK"

    @assert_description
    def TSC_iut_enable_audio(self, **kwargs):
        """
        Click Ok, then initiate an audio connection (SCO) from the
        Implementation Under Test (IUT) to the PTS.
        """

        def enable_audio():
            time.sleep(2)
            self.hfp.SetAudioPath(audio_path=AudioPath.AUDIO_PATH_HANDSFREE)

        threading.Thread(target=enable_audio).start()

        return "OK"

    @assert_description
    def TSC_iut_disable_audio_slc_down_ok(self, pts_addr: bytes, **kwargs):
        """
        Click OK, then close the audio connection (SCO) between the
        Implementation Under Test (IUT) and the PTS.  If necessary, it is OK to
        close the service level connection. Do not power-off the IUT.
        """

        self.connection = self.host.GetConnection(address=pts_addr).connection

        def disable_slc():
            time.sleep(2)
            self.hfp.DisableSlc(connection=self.connection)

        threading.Thread(target=disable_slc).start()

        return "OK"

    @assert_description
    def TSC_ag_iut_call_no_slc(self, **kwargs):
        """
        Place a call from an external line to the Implementation Under Test
        (IUT).  When the call is active, click Ok.
        """

        self.modem.call(IXIT_PHONE_NUMBER)
        time.sleep(5)  # there's a delay before Android registers the call
        self.hfp.AnswerCall()
        time.sleep(2)

        return "OK"

    @assert_description
    def TSC_ag_iut_enable_second_call(self, **kwargs):
        """
        Click Ok, then place a second call from an external line to the
        Implementation Under Test (IUT). Do not answer the call unless prompted
        to do so.
        """

        def enable_second_call():
            time.sleep(2)
            self.modem.call(IXIT_SECOND_PHONE_NUMBER)

        threading.Thread(target=enable_second_call).start()

        return "OK"

    @assert_description
    def TSC_ag_iut_call_swap(self, **kwargs):
        """
        Click Ok, then place the current call on hold and make the incoming/held
        call active using the Implementation Under Test (IUT).
        """

        self.hfp.SwapActiveCall()

        return "OK"

    @assert_description
    def TSC_verify_audio_second_call(self, **kwargs):
        """
        Verify the audio is returned to the 2nd call and then click Ok.  Resume
        action may be needed.  If the audio is not returned to the 2nd call,
        click Cancel.
        """

        return "OK"

    @assert_description
    def TSC_ag_iut_disable_call_after_verdict(self, **kwargs):
        """
        After the test verdict  is given, end all active calls using the
        external line or the Implementation Under Test (IUT).  Click OK to
        continue.
        """

        self.hfp.DeclineCall()

        return "OK"

    @assert_description
    def TSC_verify_no_ecnr(self, **kwargs):
        """
        Verify that EC and NR functionality is disabled, then click Ok.
        """

        return "OK"

    @assert_description
    def TSC_disable_inband_ring(self, **kwargs):
        """
        Click Ok, then disable the in-band ringtone using the Implemenation
        Under Test (IUT).
        """

        self.hfp.SetInBandRingtone(enabled=False)
        self.host.SoftReset()

        return "OK"

    @assert_description
    def TSC_wait_until_ringing(self, **kwargs):
        """
        When the Implementation Under Test (IUT) alerts the incoming call, click
        Ok.
        """

        # we are triggering a call from modem_simulator, so the alert is immediate

        return "OK"

    @assert_description
    def TSC_verify_incoming_call_ag(self, **kwargs):
        """
        Verify that there is an incoming call on the Implementation Under Test
        (IUT).
        """

        # we are triggering a call from modem_simulator, so this is guaranteed

        return "OK"

    @assert_description
    def TSC_disable_ag_cellular_network_expect_notification(self, pts_addr: bytes, **kwargs):
        """
        Click OK. Then, disable the control channel, such that the AG is de-
        registered.
        """

        self.connection = self.host.GetConnection(address=pts_addr).connection

        def disable_slc():
            time.sleep(2)
            self.hfp.DisableSlc(connection=self.connection)

        threading.Thread(target=disable_slc).start()

        return "OK"

    @assert_description
    def TSC_adjust_ag_battery_level_expect_no_notification(self, **kwargs):
        """
        Adjust the battery level on the AG to a level that should cause a
        battery level indication to be sent to HF. Then, click OK.
        """

        self.hfp.SetBatteryLevel(connection=self.connection, battery_percentage=42)

        return "OK"

    @assert_description
    def TSC_verify_subscriber_number(self, **kwargs):
        """
        Using the Implementation Under Test (IUT), verify that the following is
        a valid Audio Gateway (AG) subscriber number, then click
        Ok."+15551234567"nnNOTE: Subscriber service type is 145
        """

        return "OK"

    def TSC_ag_prepare_at_bldn(self, **kwargs):
        r"""
        Place the Implemenation Under Test (IUT) in a state which will accept an
        outgoing call set-up request from the PTS, then click OK.

        Note:  The
        PTS will send a request to establish an outgoing call from the IUT to
        the last dialed number.  Answer the incoming call when alerted.
        """

        self.hfp.MakeCall(number=str(IXIT_PHONE_NUMBER))
        self.log("Calling")
        time.sleep(2)
        self.hfp.DeclineCall()
        self.log("Declining")
        time.sleep(2)

        return "OK"

    @assert_description
    def TSC_ag_iut_prepare_for_atd(self, **kwargs):
        """
        Place the Implementation Under Test (IUT) in a mode that will allow an
        outgoing call initiated by the PTS, and click Ok.
        """

        return "OK"

    @assert_description
    def TSC_terminal_answer_call(self, **kwargs):
        """
        Click Ok, then answer the incoming call on the external terminal.
        """

        def answer_call():
            time.sleep(2)
            self.log("Answering")
            self.modem.answer_outgoing_call(IXIT_PHONE_NUMBER)

        threading.Thread(target=answer_call).start()

        return "OK"

    @match_description
    def TSC_signal_strength_verify(self, **kwargs):
        """
        Verify that the signal reported on the Implementaion Under Test \(IUT\) is
        proportional to the value \(out of 5\), then click Ok.[0-9]
        """

        return "OK"

    @assert_description
    def TSC_signal_strength_impair(self, **kwargs):
        """
        Impair the cellular signal by placing the Implementation Under Test
        (IUT) under partial RF shielding, then click Ok.
        """

        return "OK"

    @assert_description
    def TSC_verify_network_operator(self, **kwargs):
        """
        Verify the following information matches the network operator reported
        on the Implementation Under Test (IUT), then click Ok:"Android Virtual "
        """

        return "OK"

    @assert_description
    def TSC_INFO_slc_with_30_seconds_wait(self, **kwargs):
        """
        After clicking the OK button, PTS will connect to the IUT and then be
        idle for 30 seconds as part of the test procedure.

        Click OK to proceed.
        """

        return "OK"

    @assert_description
    def TSC_ag_iut_disable_call(self, **kwargs):
        """
        Click Ok, then end the call using the Implemention Under Test IUT).
        """

        def disable_call():
            time.sleep(2)
            self.hfp.DeclineCall()

        threading.Thread(target=disable_call).start()

        return "OK"

    @match_description
    def TSC_dtmf_verify(self, **kwargs):
        """
        Verify the DTMF code, then click Ok. .
        """

        return "OK"

    @assert_description
    def TSC_TWC_instructions(self, **kwargs):
        """
        NOTE: The following rules apply for this test case:

        1.
        TSPX_phone_number - the 1st call
        2. TSPX_second_phone_number - the 2nd
        call

        Edits can be made within the IXIT settings for the above phone
        numbers.
        """

        return "OK"

    def TSC_call_swap_and_disable_held_tester(self, **kwargs):
        """
        Set the Implementation Under Test (IUT) in a state that will allow the
        PTS to initiate a AT+CHLD=1 operation,  then click Ok.

        Note: Upon
        receiving the said command, the IUT will simultaneously drop the active
        call and make the held call active.
        """

        return "OK"

    @assert_description
    def TSC_verify_audio_first_call(self, **kwargs):
        """
        Verify the audio is returned to the 1st call and click Ok. Resume action
        my be needed.  If the audio is not present in the 1st call, click
        Cancel.
        """

        # TODO

        return "OK"

    @assert_description
    def TSC_ag_iut_dial_out_second(self, **kwargs):
        """
        Verify that the last number dialed on the Implementation Under Test
        (IUT) matches the TSPX_Second_phone_number entered in the IXIT settings.
        """

        # TODO

        return "OK"

    @assert_description
    def TSC_prepare_iut_for_vra(self, pts_addr: bytes, **kwargs):
        """
        Place the Implementation Under Test (IUT) in a state which will allow a
        request from the PTS to activate voice recognition, then click Ok.
        """

        self.hfp.SetVoiceRecognition(
            enabled=True,
            connection=self.host.GetConnection(address=pts_addr).connection,
        )

        return "OK"

    @assert_description
    def TSC_ag_iut_clear_call_history(self, **kwargs):
        """
        Clear the call history on  the Implementation Under Test (IUT) such that
        there are zero records of any numbers dialed, then click Ok.
        """

        self.hfp.ClearCallHistory()

        return "OK"

    @assert_description
    def TSC_reject_call(self, **kwargs):
        """
        Click Ok, then reject the incoming call using the Implemention Under
        Test (IUT).
        """

        def reject_call():
            time.sleep(2)
            self.hfp.DeclineCall()

        threading.Thread(target=reject_call).start()

        return "OK"

    def _auto_confirm_requests(self, times=None):

        def task():
            cnt = 0
            pairing_events = self.security.OnPairing()
            for event in pairing_events:
                if event.WhichOneof('method') in {"just_works", "numeric_comparison"}:
                if event.WhichOneof("method") in {"just_works", "numeric_comparison"}:
                    if times is None or cnt < times:
                        cnt += 1
                        pairing_events.send(event=event, confirm=True)
+3 −3
Original line number Diff line number Diff line
@@ -22,12 +22,12 @@
    <test class="com.android.tradefed.testtype.pandora.PtsBotTest" >
        <!-- Creates a randomized temp dir for pts-bot binaries and avoid
             conflicts when running multiple pts-bot on the same machine -->
        <option name="create-bin-temp-dir" value="true"/>
        <!-- <option name="create-bin-temp-dir" value="true"/> -->
        <!-- mmi2grpc is contained inside pts-bot folder -->
        <option name="mmi2grpc" value="pts-bot" />
        <option name="tests-config-file" value="pts_bot_tests_config.json" />
        <option name="max-flaky-tests" value="2" />
        <option name="max-retries-per-test" value="3" />
        <option name="max-flaky-tests" value="0" />
        <option name="max-retries-per-test" value="0" />
        <option name="physical" value="false" />
        <option name="profile" value="A2DP/SNK" />
        <option name="profile" value="A2DP/SRC" />
+35 −35
Original line number Diff line number Diff line
@@ -236,14 +236,40 @@
    "HFP/AG/DIS/BV-01-I",
    "HFP/AG/ACC/BV-08-I",
    "HFP/AG/ACC/BV-09-I",
    "HFP/AG/ACC/BV-15-I",
    "HFP/AG/ACR/BV-01-I",
    "HFP/AG/ACR/BV-02-I",
    "HFP/AG/ACS/BI-14-I",
    "HFP/AG/ACS/BV-04-I",
    "HFP/AG/ACS/BV-08-I",
    "HFP/AG/ACS/BV-11-I",
    "HFP/AG/ATA/BV-02-I",
    "HFP/AG/ATH/BV-03-I",
    "HFP/AG/ATH/BV-04-I",
    "HFP/AG/ATH/BV-06-I",
    "HFP/AG/CLI/BV-01-I",
    "HFP/AG/ECS/BV-03-I",
    "HFP/AG/ENO/BV-01-I",
    "HFP/AG/HFI/BV-02-I",
    "HFP/AG/ICA/BV-07-I",
    "HFP/AG/ICA/BV-08-I",
    "HFP/AG/ICA/BV-09-I",
    "HFP/AG/NUM/BV-01-I",
    "HFP/AG/OCL/BV-02-I",
    "HFP/AG/PSI/BV-03-C",
    "HFP/AG/PSI/BV-04-I",
    "HFP/AG/SDP/BV-01-I",
    "HFP/AG/SLC/BV-01-C",
    "HFP/AG/SLC/BV-03-C",
    "HFP/AG/SLC/BV-09-I",
    "HFP/AG/SLC/BV-10-I",
    "HFP/AG/TCA/BV-02-I",
    "HFP/AG/TCA/BV-03-I",
    "HFP/AG/TCA/BV-04-I",
    "HFP/AG/TCA/BV-05-I",
    "HFP/AG/TDC/BV-01-I",
    "HFP/AG/TWC/BV-02-I",
    "HFP/AG/TWC/BV-03-I",
    "HFP/AG/WBS/BV-01-I",
    "HID/HOS/DAT/BV-01-C",
    "HID/HOS/HCE/BV-01-I",
@@ -582,60 +608,32 @@
    "GATT/SR/GAW/BV-11-C",
    "HFP/AG/TRS/BV-01-C",
    "HFP/AG/PSI/BV-01-C",
    "HFP/AG/PSI/BV-04-I",
    "HFP/AG/ACS/BV-04-I",
    "HFP/AG/ACS/BV-08-I",
    "HFP/AG/ACS/BV-11-I",
    "HFP/AG/ACC/BI-13-I",
    "HFP/AG/ACS/BV-16-I",
    "HFP/AG/CLI/BV-01-I",
    "HFP/AG/ICA/BV-04-I",
    "HFP/AG/ICA/BV-07-I",
    "HFP/AG/ICA/BV-08-I",
    "HFP/AG/ICA/BV-09-I",
    "HFP/AG/TCA/BV-01-I",
    "HFP/AG/TCA/BV-02-I",
    "HFP/AG/TCA/BV-03-I",
    "HFP/AG/TCA/BV-05-I",
    "HFP/AG/ATH/BV-03-I",
    "HFP/AG/ATH/BV-04-I",
    "HFP/AG/ATH/BV-06-I",
    "HFP/AG/ATA/BV-01-I",
    "HFP/AG/ATA/BV-02-I",
    "HFP/AG/OCN/BV-01-I",
    "HFP/AG/OCL/BV-01-I",
    "HFP/AG/OCM/BV-01-I",
    "HFP/AG/OCM/BV-02-I",
    "HFP/AG/OCL/BV-01-I",
    "HFP/AG/OCL/BV-02-I",
    "HFP/AG/TWC/BV-02-I",
    "HFP/AG/TWC/BV-03-I",
    "HFP/AG/TWC/BV-05-I",
    "HFP/AG/ENO/BV-01-I",
    "HFP/AG/VRA/BV-01-I",
    "HFP/AG/TDC/BV-01-I",
    "HFP/AG/ECS/BV-03-I",
    "HFP/AG/NUM/BV-01-I",
    "HFP/AG/SLC/BV-01-C",
    "HFP/AG/SLC/BV-02-C",
    "HFP/AG/SLC/BV-03-C",
    "HFP/AG/SLC/BV-04-C",
    "HFP/AG/SLC/BV-05-I",
    "HFP/AG/SLC/BV-06-I",
    "HFP/AG/SLC/BV-07-I",
    "HFP/AG/ACC/BV-10-I",
    "HFP/AG/ACC/BV-11-I",
    "HFP/AG/ACC/BI-12-I",
    "HFP/AG/ACC/BI-13-I",
    "HFP/AG/ACC/BI-14-I",
    "HFP/AG/ACC/BV-15-I",
    "HFP/AG/SDP/BV-01-I",
    "HFP/AG/ICA/BV-06-I",
    "HFP/AG/IIA/BV-01-I",
    "HFP/AG/IIA/BV-02-I",
    "HFP/AG/IIC/BV-02-I",
    "HFP/AG/IID/BV-01-I",
    "HFP/AG/IID/BV-03-I",
    "HFP/AG/IIC/BV-01-I",
    "HFP/AG/IIC/BV-02-I",
    "HFP/AG/IIC/BV-03-I",
    "HFP/AG/HFI/BI-03-I",
    "HFP/AG/OCN/BV-01-I",
    "HFP/AG/SLC/BV-04-C",
    "HID/HOS/HCR/BV-01-I",
    "L2CAP/LE/CFC/BV-07-C",
    "L2CAP/LE/CFC/BV-11-C",
@@ -1216,7 +1214,9 @@
    "TSPC_HFP_2_3": true,
    "TSPC_HFP_2_3b": true,
    "TSPC_HFP_2_3c": true,
    "TSPC_HFP_2_4a": true,
    "TSPC_HFP_2_4b": true,
    "TSPC_HFP_2_5": true,
    "TSPC_HFP_2_6": true,
    "TSPC_HFP_2_7": true,
    "TSPC_HFP_2_7a": true,
@@ -1674,7 +1674,7 @@
    "HCI": {},
    "HFP": {
      "TSPX_phone_number": "42",
      "TSPX_second_phone_number": "42"
      "TSPX_second_phone_number": "43"
    },
    "HID": {},
    "HOGP": {},
+87 −3
Original line number Diff line number Diff line
@@ -24,10 +24,15 @@ import android.bluetooth.BluetoothProfile
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.os.Bundle
import android.os.IBinder
import android.provider.CallLog
import android.telecom.Call
import android.telecom.CallAudioState
import android.telecom.InCallService
import android.telecom.TelecomManager
import android.telecom.VideoProfile
import com.google.protobuf.Empty
import io.grpc.stub.StreamObserver
import kotlinx.coroutines.CoroutineScope
@@ -53,8 +58,7 @@ class Hfp(val context: Context) : HFPImplBase() {
  private val bluetoothHfp = getProfileProxy<BluetoothHeadset>(context, BluetoothProfile.HEADSET)

  companion object {
    @SuppressLint("StaticFieldLeak")
    private lateinit var inCallService: InCallService
    @SuppressLint("StaticFieldLeak") private lateinit var inCallService: InCallService
  }

  init {
@@ -64,6 +68,8 @@ class Hfp(val context: Context) : HFPImplBase() {

    // kill any existing call
    telecomManager.endCall()

    shell("su root setprop persist.bluetooth.disableinbandringing false")
  }

  fun deinit() {
@@ -129,7 +135,7 @@ class Hfp(val context: Context) : HFPImplBase() {
    grpcUnary(scope, responseObserver) {
      when (request.audioPath!!) {
        AudioPath.AUDIO_PATH_UNKNOWN,
        AudioPath.UNRECOGNIZED -> {}
        AudioPath.UNRECOGNIZED, -> {}
        AudioPath.AUDIO_PATH_HANDSFREE -> {
          check(bluetoothHfp.getActiveDevice() != null)
          inCallService.setAudioRoute(CallAudioState.ROUTE_BLUETOOTH)
@@ -139,4 +145,82 @@ class Hfp(val context: Context) : HFPImplBase() {
      SetAudioPathResponse.getDefaultInstance()
    }
  }

  override fun answerCall(
    request: AnswerCallRequest,
    responseObserver: StreamObserver<AnswerCallResponse>,
  ) {
    grpcUnary(scope, responseObserver) {
      telecomManager.acceptRingingCall()
      AnswerCallResponse.getDefaultInstance()
    }
  }

  override fun swapActiveCall(
    request: SwapActiveCallRequest,
    responseObserver: StreamObserver<SwapActiveCallResponse>,
  ) {
    grpcUnary(scope, responseObserver) {
      val callsToActivate = mutableListOf<Call>()
      for (call in inCallService.calls) {
        if (call.details.state == Call.STATE_ACTIVE) {
          call.hold()
        } else {
          callsToActivate.add(call)
        }
      }
      for (call in callsToActivate) {
        call.answer(VideoProfile.STATE_AUDIO_ONLY)
      }
      inCallService.calls[0].hold()
      inCallService.calls[1].unhold()
      SwapActiveCallResponse.getDefaultInstance()
    }
  }

  override fun setInBandRingtone(
    request: SetInBandRingtoneRequest,
    responseObserver: StreamObserver<SetInBandRingtoneResponse>,
  ) {
    grpcUnary(scope, responseObserver) {
      shell(
        "su root setprop persist.bluetooth.disableinbandringing " + (!request.enabled).toString()
      )
      SetInBandRingtoneResponse.getDefaultInstance()
    }
  }

  override fun makeCall(
    request: MakeCallRequest,
    responseObserver: StreamObserver<MakeCallResponse>
  ) {
    grpcUnary(scope, responseObserver) {
      telecomManager.placeCall(Uri.fromParts("tel", request.number, null), Bundle())
      MakeCallResponse.getDefaultInstance()
    }
  }

  override fun setVoiceRecognition(
    request: SetVoiceRecognitionRequest,
    responseObserver: StreamObserver<SetVoiceRecognitionResponse>
  ) {
    grpcUnary(scope, responseObserver) {
      if (request.enabled) {
        bluetoothHfp.startVoiceRecognition(request.connection.toBluetoothDevice(bluetoothAdapter))
      } else {
        bluetoothHfp.stopVoiceRecognition(request.connection.toBluetoothDevice(bluetoothAdapter))
      }
      SetVoiceRecognitionResponse.getDefaultInstance()
    }
  }

  override fun clearCallHistory(
    request: ClearCallHistoryRequest,
    responseObserver: StreamObserver<ClearCallHistoryResponse>
  ) {
    grpcUnary(scope, responseObserver) {
      context.contentResolver.delete(CallLog.Calls.CONTENT_URI, null, null)
      ClearCallHistoryResponse.getDefaultInstance()
    }
  }
}
Loading