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

Commit c5184cb8 authored by Rahul Arya's avatar Rahul Arya Committed by Automerger Merge Worker
Browse files

Merge changes from topic "modem-simulator-pts" am: 2f1c9e79

parents 11ce53d1 2f1c9e79
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -35,11 +35,13 @@ from mmi2grpc.sdp import SDPProxy
from mmi2grpc.sm import SMProxy
from mmi2grpc._helpers import format_proxy
from mmi2grpc._rootcanal import RootCanal
from mmi2grpc._modem import Modem

from pandora_experimental.host_grpc import Host

PANDORA_SERVER_PORT = 8999
ROOTCANAL_CONTROL_PORT = 6212
MODEM_SIMULATOR_PORT = 4242
MAX_RETRIES = 10
GRPC_SERVER_INIT_TIMEOUT = 10  # seconds

@@ -60,8 +62,10 @@ class IUT:
        """
        self.pandora_server_port = int(args[0]) if len(args) > 0 else PANDORA_SERVER_PORT
        self.rootcanal_control_port = int(args[1]) if len(args) > 1 else ROOTCANAL_CONTROL_PORT
        self.modem_simulator_port = int(args[2]) if len(args) > 2 else MODEM_SIMULATOR_PORT
        self.test = test
        self.rootcanal = None
        self.modem = None

        # Profile proxies.
        self._a2dp = None
@@ -81,6 +85,8 @@ class IUT:
        self.rootcanal = RootCanal(port=self.rootcanal_control_port)
        self.rootcanal.reconnect_phone()

        self.modem = Modem(port=self.modem_simulator_port)

        # Note: we don't keep a single gRPC channel instance in the IUT class
        # because reset is allowed to close the gRPC server.
        with grpc.insecure_channel(f'localhost:{self.pandora_server_port}') as channel:
@@ -90,6 +96,9 @@ class IUT:
        self.rootcanal.close()
        self.rootcanal = None

        self.modem.close()
        self.modem = None

        self._a2dp = None
        self._avrcp = None
        self._gatt = None
@@ -178,7 +187,8 @@ class IUT:
        # Handles HFP MMIs.
        if profile in ('HFP'):
            if not self._hfp:
                self._hfp = HFPProxy(grpc.insecure_channel(f'localhost:{self.pandora_server_port}'))
                self._hfp = HFPProxy(test, grpc.insecure_channel(f'localhost:{self.pandora_server_port}'),
                                     self.rootcanal, self.modem)
            return self._hfp.interact(test, interaction, description, pts_address)
        # Handles HID MMIs.
        if profile in ('HID'):
+16 −0
Original line number Diff line number Diff line
import os
import socket


class Modem:

    def __init__(self, port):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(("127.0.0.1", port))
        self.socket = s

    def close(self):
        self.socket.close()

    def call(self, phone_number):
        self.socket.sendall(b'REM0\r\nAT+REMOTECALL=4,0,0,"' + str(phone_number).encode("utf-8") + b'",129\r\n')
+11 −1
Original line number Diff line number Diff line
@@ -27,6 +27,16 @@
                    <action android:name="android.media.browse.MediaBrowserService"/>
               </intent-filter>
          </service>

      <service
          android:name=".Hfp$PandoraInCallService"
          android:permission="android.permission.BIND_INCALL_SERVICE"
          android:exported="true">
        <intent-filter>
          <action android:name="android.telecom.InCallService" />
        </intent-filter>
      </service>

    </application>

  <uses-permission android:name="android.permission.INTERNET" />
+56 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.pandora

import android.annotation.SuppressLint
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothHeadset
import android.bluetooth.BluetoothManager
@@ -23,6 +24,10 @@ import android.bluetooth.BluetoothProfile
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.IBinder
import android.telecom.CallAudioState
import android.telecom.InCallService
import android.telecom.TelecomManager
import com.google.protobuf.Empty
import io.grpc.stub.StreamObserver
import kotlinx.coroutines.CoroutineScope
@@ -34,30 +39,48 @@ import kotlinx.coroutines.flow.shareIn
import pandora.HFPGrpc.HFPImplBase
import pandora.HfpProto.*

private const val TAG = "PandoraHfp"

@kotlinx.coroutines.ExperimentalCoroutinesApi
class Hfp(val context: Context) : HFPImplBase() {
  private val TAG = "PandoraHfp"

  private val scope: CoroutineScope
  private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
  private val flow: Flow<Intent>

  private val bluetoothManager = context.getSystemService(BluetoothManager::class.java)!!
  private val telecomManager = context.getSystemService(TelecomManager::class.java)!!
  private val bluetoothAdapter = bluetoothManager.adapter

  private val bluetoothHfp = getProfileProxy<BluetoothHeadset>(context, BluetoothProfile.HEADSET)

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

  init {
    scope = CoroutineScope(Dispatchers.Default)

    val intentFilter = IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
    flow = intentFlow(context, intentFilter).shareIn(scope, SharingStarted.Eagerly)

    // kill any existing call
    telecomManager.endCall()
  }

  fun deinit() {
    // kill any existing call
    telecomManager.endCall()

    bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, bluetoothHfp)
    scope.cancel()
  }

  class PandoraInCallService : InCallService() {
    override fun onBind(intent: Intent?): IBinder? {
      inCallService = this
      return super.onBind(intent)
    }
  }

  override fun enableSlc(request: EnableSlcRequest, responseObserver: StreamObserver<Empty>) {
    grpcUnary<Empty>(scope, responseObserver) {
      val device = request.connection.toBluetoothDevice(bluetoothAdapter)
@@ -80,7 +103,7 @@ class Hfp(val context: Context) : HFPImplBase() {

  override fun setBatteryLevel(
    request: SetBatteryLevelRequest,
    responseObserver: StreamObserver<Empty>
    responseObserver: StreamObserver<Empty>,
  ) {
    grpcUnary<Empty>(scope, responseObserver) {
      val action = "android.intent.action.BATTERY_CHANGED"
@@ -88,4 +111,32 @@ class Hfp(val context: Context) : HFPImplBase() {
      Empty.getDefaultInstance()
    }
  }

  override fun declineCall(
    request: DeclineCallRequest,
    responseObserver: StreamObserver<DeclineCallResponse>,
  ) {
    grpcUnary(scope, responseObserver) {
      telecomManager.endCall()
      DeclineCallResponse.getDefaultInstance()
    }
  }

  override fun setAudioPath(
    request: SetAudioPathRequest,
    responseObserver: StreamObserver<SetAudioPathResponse>,
  ) {
    grpcUnary(scope, responseObserver) {
      when (request.audioPath!!) {
        AudioPath.AUDIO_PATH_UNKNOWN,
        AudioPath.UNRECOGNIZED -> {}
        AudioPath.AUDIO_PATH_HANDSFREE -> {
          check(bluetoothHfp.getActiveDevice() != null)
          inCallService.setAudioRoute(CallAudioState.ROUTE_BLUETOOTH)
        }
        AudioPath.AUDIO_PATH_SPEAKERS -> inCallService.setAudioRoute(CallAudioState.ROUTE_SPEAKER)
      }
      SetAudioPathResponse.getDefaultInstance()
    }
  }
}
+20 −0
Original line number Diff line number Diff line
@@ -15,6 +15,10 @@ service HFP {
  rpc DisableSlc(DisableSlcRequest) returns (google.protobuf.Empty);
  // Change the battery level to the one requested
  rpc SetBatteryLevel(SetBatteryLevelRequest) returns (google.protobuf.Empty);
  // Decline a call
  rpc DeclineCall(DeclineCallRequest) returns (DeclineCallResponse);
  // Set the audio path
  rpc SetAudioPath(SetAudioPathRequest) returns (SetAudioPathResponse);
}

// Request of the `EnableSlc` method.
@@ -36,3 +40,19 @@ message SetBatteryLevelRequest {
  // Battery level to be set on the DUT
  int32 battery_percentage = 2;
}

message DeclineCallRequest {}

message DeclineCallResponse {}

enum AudioPath {
  AUDIO_PATH_UNKNOWN = 0;
  AUDIO_PATH_SPEAKERS = 1;
  AUDIO_PATH_HANDSFREE = 2;
}

message SetAudioPathRequest {
  AudioPath audio_path = 1;
}

message SetAudioPathResponse {}
Loading