Loading android/pandora/mmi2grpc/mmi2grpc/opp.py +80 −4 Original line number Diff line number Diff line Loading @@ -116,3 +116,79 @@ class OPPProxy(ProfileProxy): """ return "OK" @assert_description def TSC_OBEX_MMI_iut_initiate_slc_connect_rfcomm(self, pts_addr: bytes, **kwargs): """ Take action to create an rfcomm channel for an OBEX connection. """ self._android.SendFile() return "OK" @assert_description def TSC_OBEX_MMI_iut_initiate_slc_connect_l2cap(self, pts_addr: bytes, **kwargs): """ Take action to create an l2cap channel for an OBEX connection. """ self._android.SendFile() return "OK" @assert_description def TSC_OPP_mmi_user_verify_opp_format_indication(self, **kwargs): """ Does the IUT display that the tester (OPP server) supports the following Object Push Formats: vCards, vCal, vNote, vMsg and Other content Note: If the IUT does not support format indication, please press 'Yes' now. Note: Do not connect to the tester until requested. """ return "OK" @assert_description def TSC_OBEX_MMI_iut_initiate_connect_OPP(self, **kwargs): """ Take action to initiate an OBEX CONNECT REQ for OPP. """ return "OK" @assert_description def TSC_OBEX_MMI_iut_initiate_put(self, **kwargs): """ Take action to send a PUT request. Then allow the operation to complete as normal. """ return "OK" @assert_description def TSC_OPP_mmi_user_verify_client_pushed_file(self, **kwargs): """ Does the file named 'x-ms-bmp' in the recently opened window represent the file just pushed by the IUT? """ return "OK" @assert_description def TSC_OBEX_MMI_iut_initiate_slc_disconnect(self, **kwargs): """ Take action to disconnect the transport channel. """ return "OK" @assert_description def TSC_OBEX_MMI_iut_initiate_disconnect(self, **kwargs): """ Take action to initiate an OBEX DISCONNECT REQ. """ return "OK" android/pandora/server/configs/pts_bot_tests_config.json +4 −4 Original line number Diff line number Diff line Loading @@ -392,6 +392,10 @@ "MAP/MSE/MSM/BV-06-I", "MAP/MSE/MSM/BV-07-I", "MAP/MSE/MSM/BV-08-I", "OPP/CL/GOEP/BC/BV-02-I", "OPP/CL/GOEP/CON/BV-01-C", "OPP/CL/OPH/BV-01-I", "OPP/CL/OPH/BV-34-I", "OPP/SR/GOEP/BC/BV-01-I", "OPP/SR/GOEP/CON/BV-02-C", "OPP/SR/GOEP/ROB/BV-01-C", Loading Loading @@ -792,10 +796,6 @@ "MAP/MSE/MMN/BV-14-I", "MAP/MSE/MMU/BV-02-I", "PBAP/PSE/SSM/BV-07-C", "OPP/CL/GOEP/BC/BV-02-I", "OPP/CL/GOEP/CON/BV-01-C", "OPP/CL/OPH/BV-01-I", "OPP/CL/OPH/BV-34-I", "OPP/SR/BCP/BV-02-I", "OPP/SR/GOEP/ROB/BV-02-C", "OPP/SR/OPH/BV-10-I", Loading android/pandora/server/src/com/android/pandora/AndroidInternal.kt +94 −0 Original line number Diff line number Diff line Loading @@ -20,11 +20,21 @@ import android.util.Log import android.content.Context import com.google.protobuf.Empty import io.grpc.stub.StreamObserver import android.graphics.Bitmap import android.os.Environment import android.provider.Telephony.* import android.telephony.SmsManager import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.content.Intent import android.content.ComponentName import android.content.ContentUris import java.io.File import java.io.FileOutputStream import android.provider.MediaStore.Images.Media import android.provider.MediaStore.MediaColumns import android.net.Uri import kotlinx.coroutines.async import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothManager Loading @@ -48,14 +58,29 @@ class AndroidInternal(val context: Context) : AndroidImplBase() { private val INCOMING_FILE_TITLE = "Incoming file" private val INCOMING_FILE_WAIT_TIMEOUT = 2000L private val BT_PKG_NAME = "com.android.bluetooth" private val BT_OPP_LAUNCHER_ACTIVITY = "com.android.bluetooth.opp.BluetoothOppLauncherActivity" private val BT_DEVICE_SELECT_WAIT_TIMEOUT = 3000L private val IMAGE_FILE_NAME = "OPP_TEST_IMAGE.bmp" private val bluetoothManager = context.getSystemService(BluetoothManager::class.java)!! private val bluetoothAdapter = bluetoothManager.adapter private var telephonyManager = context.getSystemService(TelephonyManager::class.java) private val DEFAULT_MESSAGE_LEN = 130 private var device: UiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) init { createImageFile() } fun deinit() { scope.cancel() val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), IMAGE_FILE_NAME) if (file.exists()) { file.delete() } } override fun log(request: LogRequest, responseObserver: StreamObserver<LogResponse>) { Loading Loading @@ -97,4 +122,73 @@ class AndroidInternal(val context: Context) : AndroidImplBase() { Empty.getDefaultInstance() } } override fun sendFile(request: Empty, responseObserver: StreamObserver<Empty>) { grpcUnary<Empty>(scope, responseObserver) { initiateSendFile(getImageId(IMAGE_FILE_NAME), "image/bmp") waitAndSelectBluetoothDevice() Empty.getDefaultInstance() } } suspend private fun waitAndSelectBluetoothDevice() { var selectJob = scope.async { device.wait(Until.findObject(By.textContains("Cuttlefish")), BT_DEVICE_SELECT_WAIT_TIMEOUT).click() } selectJob.await() } private fun initiateSendFile(imageId: Long, type: String) { val contentUri = ContentUris.withAppendedId(Media.EXTERNAL_CONTENT_URI, imageId) try { var sendingIntent = Intent(Intent.ACTION_SEND) sendingIntent.setType(type) sendingIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK sendingIntent.setComponent(ComponentName(BT_PKG_NAME, BT_OPP_LAUNCHER_ACTIVITY)) sendingIntent.putExtra(Intent.EXTRA_STREAM, contentUri) context.startActivity(sendingIntent) } catch(e: Exception) { } } private fun getImageId(fileName: String): Long { val selection = MediaColumns.DISPLAY_NAME + "=?" val selectionArgs = arrayOf(fileName) val cursor = context.getContentResolver().query(Media.EXTERNAL_CONTENT_URI, null, selection, selectionArgs, null) cursor?.use { it.let { it.moveToFirst() return it.getLong(it.getColumnIndexOrThrow(Media._ID)) } } return 0L } private fun createImageFile() { val bitmapImage = Bitmap.createBitmap(30, 20, Bitmap.Config.ARGB_8888) val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), IMAGE_FILE_NAME) var fileOutputStream: FileOutputStream? = null if (file.exists()) { file.delete() } file.createNewFile() try { fileOutputStream = FileOutputStream(file) bitmapImage.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream) fileOutputStream.flush() } catch (e: Exception) { e.printStackTrace() } finally { try { if (fileOutputStream != null) { fileOutputStream.close() } } catch (e: Exception) { e.printStackTrace() } } } } pandora/interfaces/pandora_experimental/_android.proto +2 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ service Android { rpc SendSMS(google.protobuf.Empty) returns (google.protobuf.Empty); // Accept incoming file rpc AcceptIncomingFile(google.protobuf.Empty) returns (google.protobuf.Empty); // Send file rpc SendFile(google.protobuf.Empty) returns (google.protobuf.Empty); } message LogRequest { Loading Loading
android/pandora/mmi2grpc/mmi2grpc/opp.py +80 −4 Original line number Diff line number Diff line Loading @@ -116,3 +116,79 @@ class OPPProxy(ProfileProxy): """ return "OK" @assert_description def TSC_OBEX_MMI_iut_initiate_slc_connect_rfcomm(self, pts_addr: bytes, **kwargs): """ Take action to create an rfcomm channel for an OBEX connection. """ self._android.SendFile() return "OK" @assert_description def TSC_OBEX_MMI_iut_initiate_slc_connect_l2cap(self, pts_addr: bytes, **kwargs): """ Take action to create an l2cap channel for an OBEX connection. """ self._android.SendFile() return "OK" @assert_description def TSC_OPP_mmi_user_verify_opp_format_indication(self, **kwargs): """ Does the IUT display that the tester (OPP server) supports the following Object Push Formats: vCards, vCal, vNote, vMsg and Other content Note: If the IUT does not support format indication, please press 'Yes' now. Note: Do not connect to the tester until requested. """ return "OK" @assert_description def TSC_OBEX_MMI_iut_initiate_connect_OPP(self, **kwargs): """ Take action to initiate an OBEX CONNECT REQ for OPP. """ return "OK" @assert_description def TSC_OBEX_MMI_iut_initiate_put(self, **kwargs): """ Take action to send a PUT request. Then allow the operation to complete as normal. """ return "OK" @assert_description def TSC_OPP_mmi_user_verify_client_pushed_file(self, **kwargs): """ Does the file named 'x-ms-bmp' in the recently opened window represent the file just pushed by the IUT? """ return "OK" @assert_description def TSC_OBEX_MMI_iut_initiate_slc_disconnect(self, **kwargs): """ Take action to disconnect the transport channel. """ return "OK" @assert_description def TSC_OBEX_MMI_iut_initiate_disconnect(self, **kwargs): """ Take action to initiate an OBEX DISCONNECT REQ. """ return "OK"
android/pandora/server/configs/pts_bot_tests_config.json +4 −4 Original line number Diff line number Diff line Loading @@ -392,6 +392,10 @@ "MAP/MSE/MSM/BV-06-I", "MAP/MSE/MSM/BV-07-I", "MAP/MSE/MSM/BV-08-I", "OPP/CL/GOEP/BC/BV-02-I", "OPP/CL/GOEP/CON/BV-01-C", "OPP/CL/OPH/BV-01-I", "OPP/CL/OPH/BV-34-I", "OPP/SR/GOEP/BC/BV-01-I", "OPP/SR/GOEP/CON/BV-02-C", "OPP/SR/GOEP/ROB/BV-01-C", Loading Loading @@ -792,10 +796,6 @@ "MAP/MSE/MMN/BV-14-I", "MAP/MSE/MMU/BV-02-I", "PBAP/PSE/SSM/BV-07-C", "OPP/CL/GOEP/BC/BV-02-I", "OPP/CL/GOEP/CON/BV-01-C", "OPP/CL/OPH/BV-01-I", "OPP/CL/OPH/BV-34-I", "OPP/SR/BCP/BV-02-I", "OPP/SR/GOEP/ROB/BV-02-C", "OPP/SR/OPH/BV-10-I", Loading
android/pandora/server/src/com/android/pandora/AndroidInternal.kt +94 −0 Original line number Diff line number Diff line Loading @@ -20,11 +20,21 @@ import android.util.Log import android.content.Context import com.google.protobuf.Empty import io.grpc.stub.StreamObserver import android.graphics.Bitmap import android.os.Environment import android.provider.Telephony.* import android.telephony.SmsManager import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.content.Intent import android.content.ComponentName import android.content.ContentUris import java.io.File import java.io.FileOutputStream import android.provider.MediaStore.Images.Media import android.provider.MediaStore.MediaColumns import android.net.Uri import kotlinx.coroutines.async import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothManager Loading @@ -48,14 +58,29 @@ class AndroidInternal(val context: Context) : AndroidImplBase() { private val INCOMING_FILE_TITLE = "Incoming file" private val INCOMING_FILE_WAIT_TIMEOUT = 2000L private val BT_PKG_NAME = "com.android.bluetooth" private val BT_OPP_LAUNCHER_ACTIVITY = "com.android.bluetooth.opp.BluetoothOppLauncherActivity" private val BT_DEVICE_SELECT_WAIT_TIMEOUT = 3000L private val IMAGE_FILE_NAME = "OPP_TEST_IMAGE.bmp" private val bluetoothManager = context.getSystemService(BluetoothManager::class.java)!! private val bluetoothAdapter = bluetoothManager.adapter private var telephonyManager = context.getSystemService(TelephonyManager::class.java) private val DEFAULT_MESSAGE_LEN = 130 private var device: UiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) init { createImageFile() } fun deinit() { scope.cancel() val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), IMAGE_FILE_NAME) if (file.exists()) { file.delete() } } override fun log(request: LogRequest, responseObserver: StreamObserver<LogResponse>) { Loading Loading @@ -97,4 +122,73 @@ class AndroidInternal(val context: Context) : AndroidImplBase() { Empty.getDefaultInstance() } } override fun sendFile(request: Empty, responseObserver: StreamObserver<Empty>) { grpcUnary<Empty>(scope, responseObserver) { initiateSendFile(getImageId(IMAGE_FILE_NAME), "image/bmp") waitAndSelectBluetoothDevice() Empty.getDefaultInstance() } } suspend private fun waitAndSelectBluetoothDevice() { var selectJob = scope.async { device.wait(Until.findObject(By.textContains("Cuttlefish")), BT_DEVICE_SELECT_WAIT_TIMEOUT).click() } selectJob.await() } private fun initiateSendFile(imageId: Long, type: String) { val contentUri = ContentUris.withAppendedId(Media.EXTERNAL_CONTENT_URI, imageId) try { var sendingIntent = Intent(Intent.ACTION_SEND) sendingIntent.setType(type) sendingIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK sendingIntent.setComponent(ComponentName(BT_PKG_NAME, BT_OPP_LAUNCHER_ACTIVITY)) sendingIntent.putExtra(Intent.EXTRA_STREAM, contentUri) context.startActivity(sendingIntent) } catch(e: Exception) { } } private fun getImageId(fileName: String): Long { val selection = MediaColumns.DISPLAY_NAME + "=?" val selectionArgs = arrayOf(fileName) val cursor = context.getContentResolver().query(Media.EXTERNAL_CONTENT_URI, null, selection, selectionArgs, null) cursor?.use { it.let { it.moveToFirst() return it.getLong(it.getColumnIndexOrThrow(Media._ID)) } } return 0L } private fun createImageFile() { val bitmapImage = Bitmap.createBitmap(30, 20, Bitmap.Config.ARGB_8888) val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), IMAGE_FILE_NAME) var fileOutputStream: FileOutputStream? = null if (file.exists()) { file.delete() } file.createNewFile() try { fileOutputStream = FileOutputStream(file) bitmapImage.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream) fileOutputStream.flush() } catch (e: Exception) { e.printStackTrace() } finally { try { if (fileOutputStream != null) { fileOutputStream.close() } } catch (e: Exception) { e.printStackTrace() } } } }
pandora/interfaces/pandora_experimental/_android.proto +2 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ service Android { rpc SendSMS(google.protobuf.Empty) returns (google.protobuf.Empty); // Accept incoming file rpc AcceptIncomingFile(google.protobuf.Empty) returns (google.protobuf.Empty); // Send file rpc SendFile(google.protobuf.Empty) returns (google.protobuf.Empty); } message LogRequest { Loading