csdcorp / speech_to_text

A Flutter plugin that exposes device specific text to speech recognition capability.
BSD 3-Clause "New" or "Revised" License
387 stars 235 forks source link

Fix the Chinese version of Samsung S9 crash #153

Closed tmxdyf closed 3 years ago

tmxdyf commented 3 years ago

issue:

java.lang.SecurityException: Not allowed to bind to service Intent { act=android.speech.RecognitionService cmp=com.samsung.android.bixby.agent/.mainui.voiceinteraction.MainVoiceInteractionService }

fix: SpeechToTextPlugin.kt

    private fun Context.findComponentName(): ComponentName? {
        val list: List<ResolveInfo> = packageManager.queryIntentServices(Intent(RecognitionService.SERVICE_INTERFACE), 0)
        return list.firstOrNull()?.serviceInfo?.let { ComponentName(it.packageName, it.name) }
    }

    private fun createRecognizer() {
        if (null != speechRecognizer) {
            return
        }
        debugLog("Creating recognizer")
        speechRecognizer = createSpeechRecognizer(pluginContext,pluginContext?.findComponentName()).apply {
            debugLog("Setting listener")
            setRecognitionListener(this@SpeechToTextPlugin)
        }
        if (null == speechRecognizer) {
            Log.e(logTag, "Speech recognizer null")
            activeResult.error(
                    SpeechToTextErrors.recognizerNotAvailable.name,
                    "Speech recognizer null", "")
            activeResult = null
        }

        debugLog("before setup intent")
        setupRecognizerIntent(defaultLanguageTag, true, ListenMode.deviceDefault, false)
        debugLog("after setup intent")
    }
sowens-csd commented 3 years ago

Thank you very much for the detailed fix and the patch, I appreciate it.

Can you give me a brief description of why createSpeechRecognizer needs a specific intent name and how we know the first matching intent is the right one? The Android docs here https://developer.android.com/reference/android/speech/SpeechRecognizer#createSpeechRecognizer(android.content.Context,%20android.content.ComponentName) say "Normally you would not use this; use createSpeechRecognizer(android.content.Context) instead to use the system default recognition service.". This change will affect all devices so we'll have to make sure it is correct for all cases.

A small note about your patch, looks like you patched master instead of main? If you could put a patch in against main it should be much smaller and therefore easier to review.

tmxdyf commented 3 years ago
  1. Thank you very much for your reply. When I am adapting to the Chinese version of Samsung S9 speech recognition. createSpeechRecognizer will throw an exception if the intent is not specified. This is the exception information: java.lang.SecurityException: Not allowed to bind to service Intent {act=android.speech.RecognitionService cmp=com.samsung.android.bixby.agent/.mainui.voiceinteraction.MainVoiceInteractionService}
  2. Sorry, I want to make changes in the main branch, but the operation made a mistake.
tmxdyf commented 3 years ago

This is crash information: image

sowens-csd commented 3 years ago

Thank you, I understand the crash. Can you give me some documentation on the fix? Looking up the recognizer name seems risky for a general release based on the Android documentation I've seen.

There are three options:

  1. Detect something about the Android version or device that turns on the name lookup
  2. Add a flag to initialize that turns name lookup on. That adds another option that a user of the plugin has to make which is bad.
  3. Find a safe way to always do the name lookup for all devices.

Option 3 would be my preference, and is how you have done it in the patch. But that option is not what the Android documentation suggests. Can you point me at any docs that suggest it would be safe?

tmxdyf commented 3 years ago

I'm sorry. I don't know how to reply to you. I hope you can find a proper way to fix this problem. Thank you.

sowens-csd commented 3 years ago

Version 3.1.0 which is in the repo now has an experimental change that allows the client to specify whether to lookup the intent or not. Use this to lookup the intent component name.

    bool hasSpeech = await speech.initialize(
        onError: errorListener,
        onStatus: statusListener,
        options: [
          SpeechToText.androidIntentLookup,
        ]);

If you have a chance please give it a try and let me know if it resolves your issue. This uses your patch although I didn't just merge the patch because of the branching issue.

tmxdyf commented 3 years ago

Thank you very much, you are very good, you have helped me solve this problem and tolerated my mistakes. speech can work normally. Your work is really great, thank you.