I am trying to extend RecognitionService to try out different Speech to Text services other than given by google. In order to check if SpeechRecognizer initializes correctly dummy implementations are given now. I get "RecognitionService: call for recognition service without RECORD_AUDIO permissions" when below check is done inside RecognitionService#checkPermissions().
if (PermissionChecker.checkCallingPermissionForDataDelivery(this,
android.Manifest.permission.RECORD_AUDIO, packageName, featureId,
null /*message*/)
== PermissionChecker.PERMISSION_GRANTED) {
return true;
}
Note that checked similar reported issue and I verified that inside my extended service, this permission exists when checked with below.
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)
Android manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.texttospeech">
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<queries>
<package android:name="com.google.android.googlequicksearchbox"/>
</queries>
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".SampleSpeechRecognizerService"
android:exported="true"
android:foregroundServiceType="microphone"
android:permission="android.permission.RECORD_AUDIO">
<intent-filter>
<action android:name="android.speech.RecognitionService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest>
MainActivity
package com.example.texttospeech;
import android.Manifest;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.Bundle;
import android.speech.RecognitionListener;
import android.speech.RecognitionService;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
private static final String TAG = AppCompatActivity.class.getSimpleName();
private Intent speechRecognizerIntent;
public static final int PERMISSION_REQUEST_RECORD_AUDIO = 1;
private SpeechRecognizer speechRecognizer;
private EditText editText;
private ImageView micButton;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = findViewById(R.id.text);
micButton = findViewById(R.id.button);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
checkPermission();
} else {
configureSpeechListener();
}
boolean isSupported = SpeechRecognizer.isRecognitionAvailable(this);
if (!isSupported) {
Log.i(TAG, "Device has no Speech support");
}
micButton.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
speechRecognizer.stopListening();
}
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
micButton.setImageResource(R.drawable.ic_mic_black_24dp);
speechRecognizer.startListening(speechRecognizerIntent);
}
return false;
}
});
}
private void configureSpeechListener() {
//speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this);
ComponentName currentRecognitionCmp = null;
List<ResolveInfo> list = getPackageManager().queryIntentServices(
new Intent(RecognitionService.SERVICE_INTERFACE), 0);
for (ResolveInfo info : list) {
currentRecognitionCmp = new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
}
speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this, currentRecognitionCmp);
speechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
speechRecognizer.setRecognitionListener(new SampleSpeechRecognitionListener());
}
@Override
protected void onDestroy() {
super.onDestroy();
speechRecognizer.destroy();
}
private void checkPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, PERMISSION_REQUEST_RECORD_AUDIO);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISSION_REQUEST_RECORD_AUDIO:
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
configureSpeechListener();
} else {
Toast.makeText(this, "Microphone permission required to proceed", Toast.LENGTH_SHORT).show();
}
return;
}
}
private class SampleSpeechRecognitionListener implements RecognitionListener {
@Override
public void onReadyForSpeech(Bundle params) {
Log.i("Sample", "ReadyForSpeech");
}
@Override
public void onBeginningOfSpeech() {
editText.setText("");
editText.setHint("Listening...");
Log.i("Sample", "onBeginningOfSpeech");
}
@Override
public void onRmsChanged(float rmsdB) {
}
@Override
public void onBufferReceived(byte[] buffer) {
}
@Override
public void onEndOfSpeech() {
Log.i("Sample", "onEndOfSpeech");
}
@Override
public void onError(int error) {
Log.e("Sample", "Error occured.." + error);
}
@Override
public void onResults(Bundle bundle) {
Log.i("Sample", "onResults");
micButton.setImageResource(R.drawable.ic_mic_black_off);
ArrayList<String> data = bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
editText.setText(data.get(0));
Log.i("Sample", data.get(0));
}
@Override
public void onPartialResults(Bundle partialResults) {
Log.i("Sample", "onPartialResults");
}
@Override
public void onEvent(int eventType, Bundle params) {
Log.i("Sample", "onEvent");
}
}
}
SampleSpeechRecognizerService
package com.example.texttospeech;
import static com.example.texttospeech.App.CHANNEL_ID;
import android.app.Notification;
import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.speech.RecognitionService;
import android.speech.SpeechRecognizer;
import android.util.Log;
import java.util.ArrayList;
public class SampleSpeechRecognizerService extends RecognitionService {
private RecognitionService.Callback mListener;
private Bundle mExtras;
@Override
public void onCreate() {
super.onCreate();
Log.i("Sample", "Service started");
startForeground(new Intent(),1,1);
}
private int startForeground(Intent intent, int flags, int startId) {
Notification notification = new Notification.Builder(this, CHANNEL_ID)
.setContentTitle("Speech Service")
.setContentText("Speech to Text conversion is ongoing")
.setSmallIcon(R.drawable.ic_android)
.build();
startForeground(1, notification);
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("Sample", "Service stopped");
}
@Override
protected void onStartListening(Intent recognizerIntent, Callback listener) {
mListener = listener;
Log.i("Sample", "onStartListening");
mExtras = recognizerIntent.getExtras();
if (mExtras == null) {
mExtras = new Bundle();
}
onReadyForSpeech(new Bundle());
onBeginningOfSpeech();
}
@Override
protected void onCancel(Callback listener) {
Log.i("Sample", "onCancel");
onResults(new Bundle());
}
@Override
protected void onStopListening(Callback listener) {
Log.i("Sample", "onStopListening");
onEndOfSpeech();
}
protected void onReadyForSpeech(Bundle bundle) {
try {
mListener.readyForSpeech(bundle);
} catch (RemoteException e) {
// Ignored
}
}
protected void afterRecording(ArrayList<String> results) {
Log.i("Sample", "afterRecording");
for (String item : results) {
Log.i("RESULT", item);
}
}
protected void onRmsChanged(float rms) {
try {
mListener.rmsChanged(rms);
} catch (RemoteException e) {
// Ignored
}
}
protected void onResults(Bundle bundle) {
try {
mListener.results(bundle);
} catch (RemoteException e) {
// Ignored
}
}
protected void onPartialResults(Bundle bundle) {
try {
mListener.partialResults(bundle);
} catch (RemoteException e) {
// Ignored
}
}
protected void onBeginningOfSpeech() {
try {
mListener.beginningOfSpeech();
} catch (RemoteException e) {
// Ignored
}
}
protected void onEndOfSpeech() {
try {
mListener.endOfSpeech();
} catch (RemoteException e) {
// Ignored
}
ArrayList<String> results = new ArrayList<>();
results.add("1");
results.add("2");
results.add("3");
Bundle bundle = new Bundle();
bundle.putStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION, results);
afterRecording(results);
}
protected void onBufferReceived(byte[] buffer) {
try {
mListener.bufferReceived(buffer);
} catch (RemoteException e) {
// Ignored
}
}
}
I am running on Android 11 in Google Pixel 4XL. As there are privacy restrictions for microphone access in Android 11, ran the extended service as foreground service as well. Still getting same error. Anyone faced this issue with Android 11? Thanks in advance
As mentioned in the comments above, it was resolved after moved the service to run on a separate process (by specifying service with android:process in manifest)
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".SampleSpeechRecognizerService"
android:exported="true"
android:foregroundServiceType="microphone"
android:process=":speechProcess"
android:permission="android.permission.RECORD_AUDIO">
<intent-filter>
<action android:name="android.speech.RecognitionService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With