accessing aidl to consume the custom service apis + android - android

There was a service in framework level, they are binding and starting that service from the framework only. I need to access that service and consume those APIs from the client (Android) side. I have gone through the most of the examples which are having code for create/start service connection from the client and whenever service connected in that connected listener using IBinder, we can access those APIs.
I tried, like adding aidl file at client side with the same package structure and added following code.
val DevManager =applicationContext.getSystemService("servicename")
val clazz = Class.forName(DevManager.javaClass.name)
val method = DevManager.javaClass.getDeclaredMethod(
"getIDevManager",
Context::class.java
)
method.isAccessible = true
val DevService: IDevManager =
method.invoke(DevManager) as IDevManager
var status = DevManager.devStatus
We are getting NoSuchMethodException.
Please suggest how can we achieve this, thanks in advance.

try {
val testManager = TestApplication.context!!.getSystemService(SERVICE_NAME)
val status = testManager.javaClass.getDeclaredMethod("methodName")
status.isAccessible = true
result = (status.invoke(testManager)).toString()
} catch (e: Exception) {
Log.d(TAG, " exception: ${e.message}")
e.printStackTrace()
}

Related

Using CompanionDeviceManager to read the devices information

I am using BluetoothLeScanner to scan for BLE devices and get a list of objects representing the devices to show inside my app (not connecting to any of them).
I am interested in doing the same, but using the CompanionDeviceManager now. Its callback CompanionDeviceManager.Callback.onDeviceFound(chooserLauncher: IntentSender?) unfortunately does not return any human readable form of found devices... the closest it gets is the IntentSender.writeToParcel method, but I am not sure how to use it in this situation.
I am not constrained to use the CompanionDeviceManager but I wanted to follow the OS version specific guidelines, we are supposed to use CompanionDeviceManager for Bluetooth devices scanning starting from API 26, but it seems useless in my case... so is there any way to get devices data from that callback, or should I just ditch it and stay with BluetoothLeScanner for all OS versions?
Late answer but it might help someone else. You can create a bluetooth device picker in combination with ActivityResultContracts.StartIntentSenderForResult() in order to get the BluetoothDevice. From there you will have access to all the device info that you need. Recent changes added some Android 12 permissions like android.permission.BLUETOOTH_CONNECT. Your mileage may vary.
val context = LocalContext.current
// Get the device manager instance
val deviceManager: CompanionDeviceManager by lazy {
ContextCompat.getSystemService(
context,
CompanionDeviceManager::class.java
) as CompanionDeviceManager
}
// Create a filter of your choice. Here I just look for specific device names
val deviceFilter: BluetoothDeviceFilter by lazy {
BluetoothDeviceFilter.Builder()
.setNamePattern(Pattern.compile(supportedDevices))
.build()
}
// Create a pairing request with your filter from the last step
val pairingRequest: AssociationRequest = AssociationRequest.Builder()
.addDeviceFilter(deviceFilter)
.build()
// Create a picker for discovered bluetooth devices
val bluetoothDevicePicker = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartIntentSenderForResult(),
onResult = {
val device: BluetoothDevice? =
it.data?.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE)
try {
// Now that you have the desired device, do what you need to with it
device?.apply {
when {
name?.matches(Regex(firstDevicePattern)) == true -> {
Log.i(TAG, "${this.name} connected")
onFirstDeviceDiscovered(device)
}
name?.matches(Regex(secondDevicePattern)) == true -> {
Log.i(TAG, "${this.name} connected")
onSecondDeviceDiscovered(device)
}
}
}
} catch (e: SecurityException) {
e.printStackTrace()
//TODO: handle the security exception (this is possibly a bug)
// https://issuetracker.google.com/issues/198986283
}
}
)
// A utility function to centralize calling associate (optional)
val associateDevice: (AssociationRequest) -> Unit = { request ->
// Attempt to associate device(s)
deviceManager.associate(
request,
object : CompanionDeviceManager.Callback() {
override fun onDeviceFound(chooserLauncher: IntentSender) {
val sender = IntentSenderRequest.Builder(chooserLauncher)
.build()
bluetoothDevicePicker.launch(sender)
}
override fun onFailure(error: CharSequence?) {
//TODO: handle association failure
}
}, null
)
}

How to set GOOGLE_APPLICATION_CREDENTIALS? [duplicate]

I am trying to build an app that use google speech cloud api in android kotlin
here is my code
launch {
googleTextToSpeech = TextToSpeechClient.create()
googleTextToSpeech?.let {
viewModel.speakGoogle(googleTextToSpeech!!, totalMessage, player)
}
}
fun speakGoogle(textToSpeech: com.google.cloud.texttospeech.v1.TextToSpeechClient, message: String, player: MediaPlayer) {
val filePath = Environment.getExternalStorageDirectory().absolutePath + "/google_" + System.currentTimeMillis() + ".mp3"
launch {
var msg = android.text.Html.fromHtml(message).toString()
msg = msg.replace("\"", "")
var input = SynthesisInput.newBuilder().setText(msg).build()
var voice = VoiceSelectionParams.newBuilder().setLanguageCode("en-US").setSsmlGender(SsmlVoiceGender.FEMALE).build()
var audio = AudioConfig.newBuilder().setAudioEncoding(AudioEncoding.MP3).build()
var response = textToSpeech.synthesizeSpeech(input, voice, audio)
response?.let {
try {
val inputStream = response.audioContent.toByteArray()
File(filePath).outputStream().use { inputStream }
player.setDataSource(filePath)
player.prepare()
player.start()
} catch (e: IOException) {
Log.i("AMIRA00000", e.toString())
} catch (e: IllegalStateException) {
Log.i("AMIRA00000", e.toString())
}
}
}
}
I am also having my google-service.json file included in the app
but I am getting the following error
java.io.IOException: The Application Default Credentials are not available. They are available if running in Google Compute Engine. Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
at com.google.auth.oauth2.DefaultCredentialsProvider.getDefaultCredentials(DefaultCredentialsProvider.java:134)
at com.google.auth.oauth2.GoogleCredentials.getApplicationDefault(GoogleCredentials.java:119)
at com.google.auth.oauth2.GoogleCredentials.getApplicationDefault(GoogleCredentials.java:91)
at com.google.api.gax.core.GoogleCredentialsProvider.getCredentials(GoogleCredentialsProvider.java:67)
at com.google.api.gax.rpc.ClientContext.create(ClientContext.java:135)
at com.google.cloud.texttospeech.v1.stub.GrpcTextToSpeechStub.create(GrpcTextToSpeechStub.java:74)
at com.google.cloud.texttospeech.v1.stub.TextToSpeechStubSettings.createStub(TextToSpeechStubSettings.java:100)
at com.google.cloud.texttospeech.v1.TextToSpeechClient.<init>(TextToSpeechClient.java:128)
at com.google.cloud.texttospeech.v1.TextToSpeechClient.create(TextToSpeechClient.java:109)
at com.google.cloud.texttospeech.v1.TextToSpeechClient.create(TextToSpeechClient.java:101)
at com.sbs16.ensofia.view.main.MainFragment$setupBinding$3$$special$$inlined$let$lambda$2.invokeSuspend(MainFragment.kt:186)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
I am also having another json file thathave private key and other credential settings but I do not know how to use it
I'm not an Android developer, but my feeling is that you shouldn't call the TextToSpeech directly from your Android app. You should call it through a backend (On AppEngine for example, or on Firebase Functions).
This backend is authenticated by the TextToSpeech API, and your Android client is authenticated on your backend.
Thereby, you can control who use your app, and your TextToSpeech feature. In any case, never put the service account secret key file in your app, either anybody that download you app can steal the key and perform call on the service account behalf and you will pay the bill!

Android AIDL Check Client Identity upon connecting

I have an app that acts as a service, and the second app needs to connect to it, so I'm using Android Interface Defenition Language (AIDL).
What is the best approach to limit the service accepting only that specific app? and in which method the identity of the client app should happen?
I know a client should have a copy of .aidl file, but I need more ways to check who is connecting to the service.
There are ways to check the identity of the app connecting to your aidl service.
By checking the signing of the apps:
If you want only the apps that are signed by the same key as your app or SystemApps can connect to your service refer kotlin code is below :
Same Key Sign and IsSyetm app:
private fun isSameKeySinged(packageManager: PackageManager, packageNameOfTheOtherApp: String): Boolean {
return packageManager.checkSignatures(
BuildConfig.APPLICATION_ID, packageNameOfTheOtherApp
) == PackageManager.SIGNATURE_MATCH
}
private fun isSystemApp(packageManager: PackageManager, packageNameOfTheOtherApp: String): Boolean {
try {
val applicationInfo = packageManager.getApplicationInfo(
packageNameOfTheOtherApp, 0
)
if (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0 || applicationInfo.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP != 0) {
return true
}
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
return false
}
By Checking the caller App: You can retrieve the package name of the caller using getCalledId inside the methods which other app call:
val callingApp = packageManager.getNameForUid(Binder.getCallingUid())

DatagramSocket not receiving some packets on Android 9

I want to receive callerId callbacks in my Android application. I've followed an example provided by it's creators which is available here.
My listening function looks like this (exact copy but in Kotlin):
private fun startListening() {
val socket = DatagramSocket(null)
val address = InetSocketAddress("255.255.255.255", 3520)
socket.reuseAddress = true
socket.broadcast = true
socket.bind(address)
var looping = true
var buffer = ByteArray(65507)
while (looping) {
val dp = DatagramPacket(buffer, buffer.size)
try {
log.debug("Waiting for call")
socket.receive(dp)
val recString = String(dp.data, 0, dp.length)
log.debug("Received new message: $recString")
_callReceivedLive.postValue(recString)
} catch (e: Exception) {
log.error("Exception in CallerIdListener", e)
looping = false
}
}
}
After installing it on device with Android 5.1.1 Version everything seems fine. Every single call I mock using Ethernet Emulator is received by the application.
Now after firing this app on Samsung Galaxy Tab with Android Version 9 it only receives like 30% of calls.
Any idea what might be wrong?

How to send and receive strings through TCP connection using kotlin

I have a TCP Server on Windows, and I want to send and receive text strings between the server and my Android device.
I spent alot of time searching for an example using Kotlin but I didn't find any useful code, so I'm now only able to create the socket and connect.
fun connect() {
try{
val soc = Socket("192.168.1.5", 1419)
val dout = DataOutputStream(soc.getOutputStream())
dout.writeUTF("1")
dout.flush()
dout.close()
soc.close()
}
catch (e:Exception){
e.printStackTrace()
}
}
You can check this simple example. Hope it'll help you!
Server:
fun main() {
val server = ServerSocket(9999)
println("Server running on port ${server.localPort}")
val client = server.accept()
println("Client connected : ${client.inetAddress.hostAddress}")
val scanner = Scanner(client.inputStream)
while (scanner.hasNextLine()) {
println(scanner.nextLine())
break
}
server.close()
}
Client:
fun main() {
val client = Socket("127.0.0.1", 9999)
client.outputStream.write("Hello from the client!".toByteArray())
client.close()
}
You can also do it with ktor, it's a kotlin based asynchronous framework. It uses coroutines natively which allow concurrency.
Use Kotlin 1.4 and ktor 1.6.0, add it to your build.gradle.kts:
plugins {
kotlin("jvm") version "1.4.32"
}
dependencies {
implementation("io.ktor:ktor-server-netty:1.6.0")
implementation("io.ktor:ktor-network:1.6.0")
}
Then you can use the sockets, it's still a bit experimental but it's getting there, with newer version ktor-network is now necessary.
Here is the code:
Server:
suspend fun server() {
val server = aSocket(ActorSelectorManager(Executors.newCachedThreadPool().asCoroutineDispatcher())).tcp()
.bind(InetSocketAddress("127.0.0.1", 2323))
println("Server running: ${server.localAddress}")
val socket = server.accept()
println("Socket accepted: ${socket.remoteAddress}")
val input = socket.openReadChannel()
val output = socket.openWriteChannel(autoFlush = true)
val line = input.readUTF8Line()
println("received '$line' from ${socket.remoteAddress}")
output.writeFully("$line back\r\n".toByteArray())
}
Client:
suspend fun client() {
val socket = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp()
.connect(InetSocketAddress("127.0.0.1", 2323))
val input = socket.openReadChannel()
val output = socket.openWriteChannel(autoFlush = true)
output.writeFully("hello\r\n".toByteArray())
println("Server said: '${input.readUTF8Line()}'")
}
Run them both:
fun main() {
CoroutineScope(Dispatchers.Default).launch { server() }
runBlocking { client() }
}
When you run them, the client will send a message, the server will respond and you should see something like this:
Server running: /127.0.0.1:2323
Socket accepted: /127.0.0.1:56215
received 'hello' from /127.0.0.1:56215
Server said: 'hello back'
Find more example on their documentation simple echo server
There are 2 important things based on my experiment:
get permission in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
create the socket from a background thread, the following works for me:
Executors.newSingleThreadExecutor().execute {
val socket = Socket("192.168.0.15", 50000)
val scanner = Scanner(socket.getInputStream())
val printWriter = PrintWriter(socket.getOutputStream())
while (scanner.hasNextLine()) {
Log.d(TAG, "${ scanner.nextLine() }")
}
}
This is the source code in GitHub.
There is a video of my experiment.

Categories

Resources