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

Commit f6fa43b7 authored by Sergey Volnov's avatar Sergey Volnov
Browse files

Add utility for to allow to overwrite on-device speech recognition

temporarily.

I had to define a new permission in order for it to work (which makes
sense, since we only want to allow shell to overwrite the temp service).

Test: adding CTS
Bug: 177915986
Change-Id: I3ac8f2366595f502b42c9ad93b331d0c0039d16c
parent 86cde2c6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -147,6 +147,7 @@ package android {
    field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
    field public static final String MANAGE_SMARTSPACE = "android.permission.MANAGE_SMARTSPACE";
    field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
    field public static final String MANAGE_SPEECH_RECOGNITION = "android.permission.MANAGE_SPEECH_RECOGNITION";
    field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
    field public static final String MANAGE_TEST_NETWORKS = "android.permission.MANAGE_TEST_NETWORKS";
    field public static final String MANAGE_TIME_AND_ZONE_DETECTION = "android.permission.MANAGE_TIME_AND_ZONE_DETECTION";
+8 −0
Original line number Diff line number Diff line
@@ -2123,6 +2123,14 @@ package android.service.watchdog {

}

package android.speech {

  public class SpeechRecognizer {
    method public void setTemporaryOnDeviceRecognizer(@Nullable android.content.ComponentName);
  }

}

package android.telecom {

  public static class Call.Details {
+2 −0
Original line number Diff line number Diff line
@@ -31,4 +31,6 @@ oneway interface IRecognitionServiceManager {
        in IBinder clientToken,
        boolean onDevice,
        in IRecognitionServiceManagerCallback callback);

    void setTemporaryComponent(in ComponentName componentName);
}
+94 −27
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package android.speech;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -35,6 +37,8 @@ import android.util.Log;
import android.util.Slog;

import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * This class provides access to the speech recognition service. This service allows access to the
@@ -52,7 +56,7 @@ import java.util.List;
 */
public class SpeechRecognizer {
    /** DEBUG value to enable verbose debug prints */
    private final static boolean DBG = false;
    private static final boolean DBG = false;

    /** Log messages identifier */
    private static final String TAG = "SpeechRecognizer";
@@ -113,10 +117,11 @@ public class SpeechRecognizer {
    public static final int ERROR_SERVER_DISCONNECTED = 11;

    /** action codes */
    private final static int MSG_START = 1;
    private final static int MSG_STOP = 2;
    private final static int MSG_CANCEL = 3;
    private final static int MSG_CHANGE_LISTENER = 4;
    private static final int MSG_START = 1;
    private static final int MSG_STOP = 2;
    private static final int MSG_CANCEL = 3;
    private static final int MSG_CHANGE_LISTENER = 4;
    private static final int MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT = 5;

    /** The actual RecognitionService endpoint */
    private IRecognitionService mService;
@@ -134,6 +139,7 @@ public class SpeechRecognizer {

    /** Handler that will execute the main tasks */
    private Handler mHandler = new Handler(Looper.getMainLooper()) {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
@@ -149,10 +155,19 @@ public class SpeechRecognizer {
                case MSG_CHANGE_LISTENER:
                    handleChangeListener((RecognitionListener) msg.obj);
                    break;
                case MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT:
                    handleSetTemporaryComponent((ComponentName) msg.obj);
                    break;
            }
        }
    };

    /**
     * Temporary queue, saving the messages until the connection will be established, afterwards,
     * only mHandler will receive the messages
     */
    private final Queue<Message> mPendingTasks = new LinkedBlockingQueue<>();

    /** The Listener that will receive all the callbacks */
    private final InternalListener mListener = new InternalListener();

@@ -287,11 +302,9 @@ public class SpeechRecognizer {

        if (mService == null) {
            // First time connection: first establish a connection, then dispatch #startListening.
            connectToSystemService(
                    () -> putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent)));
        } else {
            putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
            connectToSystemService();
        }
        putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
    }

    /**
@@ -336,6 +349,22 @@ public class SpeechRecognizer {
        putMessage(Message.obtain(mHandler, MSG_CANCEL));
    }

    /**
     * Sets a temporary component to power on-device speech recognizer.
     *
     * <p>This is only expected to be called in tests, system would reject calls from client apps.
     *
     * @param componentName name of the component to set temporary replace speech recognizer. {@code
     *        null} value resets the recognizer to default.
     *
     * @hide
     */
    @TestApi
    public void setTemporaryOnDeviceRecognizer(@Nullable ComponentName componentName) {
        mHandler.sendMessage(
                Message.obtain(mHandler, MSG_SET_TEMPORARY_ON_DEVICE_COMPONENT, componentName));
    }

    private static void checkIsCalledFromMainThread() {
        if (Looper.myLooper() != Looper.getMainLooper()) {
            throw new RuntimeException(
@@ -344,8 +373,12 @@ public class SpeechRecognizer {
    }

    private void putMessage(Message msg) {
        if (mService == null) {
            mPendingTasks.offer(msg);
        } else {
            mHandler.sendMessage(msg);
        }
    }

    /** sends the actual message to the service */
    private void handleStartListening(Intent recognizerIntent) {
@@ -395,6 +428,22 @@ public class SpeechRecognizer {
        }
    }

    private void handleSetTemporaryComponent(ComponentName componentName) {
        if (DBG) {
            Log.d(TAG, "handleSetTemporaryComponent, componentName=" + componentName);
        }

        if (!maybeInitializeManagerService()) {
            return;
        }

        try {
            mManagerService.setTemporaryComponent(componentName);
        } catch (final RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }

    private boolean checkOpenConnection() {
        if (mService != null) {
            return true;
@@ -422,16 +471,13 @@ public class SpeechRecognizer {
        }

        mService = null;
        mPendingTasks.clear();
        mListener.mInternalListener = null;
    }

    /** Establishes a connection to system server proxy and initializes the session. */
    private void connectToSystemService(Runnable onSuccess) {
        mManagerService = IRecognitionServiceManager.Stub.asInterface(
                ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE));

        if (mManagerService == null) {
            mListener.onError(ERROR_CLIENT);
    private void connectToSystemService() {
        if (!maybeInitializeManagerService()) {
            return;
        }

@@ -450,13 +496,19 @@ public class SpeechRecognizer {
                    new IRecognitionServiceManagerCallback.Stub(){
                        @Override
                        public void onSuccess(IRecognitionService service) throws RemoteException {
                            if (DBG) {
                                Log.i(TAG, "Connected to speech recognition service");
                            }
                            mService = service;
                            onSuccess.run();
                            while (!mPendingTasks.isEmpty()) {
                                mHandler.sendMessage(mPendingTasks.poll());
                            }
                        }

                        @Override
                        public void onError(int errorCode) throws RemoteException {
                            Log.e(TAG, "Bind to system recognition service failed");
                            Log.e(TAG, "Bind to system recognition service failed with error "
                                    + errorCode);
                            mListener.onError(errorCode);
                        }
                    });
@@ -465,6 +517,21 @@ public class SpeechRecognizer {
        }
    }

    private boolean maybeInitializeManagerService() {
        if (mManagerService != null) {
            return true;
        }

        mManagerService = IRecognitionServiceManager.Stub.asInterface(
                ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE));

        if (mManagerService == null && mListener != null) {
            mListener.onError(ERROR_CLIENT);
            return false;
        }
        return true;
    }

    /**
     * Returns the component name to be used for establishing a connection, based on the parameters
     * used during initialization.
@@ -505,15 +572,15 @@ public class SpeechRecognizer {
    private static class InternalListener extends IRecognitionListener.Stub {
        private RecognitionListener mInternalListener;

        private final static int MSG_BEGINNING_OF_SPEECH = 1;
        private final static int MSG_BUFFER_RECEIVED = 2;
        private final static int MSG_END_OF_SPEECH = 3;
        private final static int MSG_ERROR = 4;
        private final static int MSG_READY_FOR_SPEECH = 5;
        private final static int MSG_RESULTS = 6;
        private final static int MSG_PARTIAL_RESULTS = 7;
        private final static int MSG_RMS_CHANGED = 8;
        private final static int MSG_ON_EVENT = 9;
        private static final int MSG_BEGINNING_OF_SPEECH = 1;
        private static final int MSG_BUFFER_RECEIVED = 2;
        private static final int MSG_END_OF_SPEECH = 3;
        private static final int MSG_ERROR = 4;
        private static final int MSG_READY_FOR_SPEECH = 5;
        private static final int MSG_RESULTS = 6;
        private static final int MSG_PARTIAL_RESULTS = 7;
        private static final int MSG_RMS_CHANGED = 8;
        private static final int MSG_ON_EVENT = 9;

        private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) {
            @Override
+7 −2
Original line number Diff line number Diff line
@@ -5257,6 +5257,11 @@
    <permission android:name="android.permission.MANAGE_MUSIC_RECOGNITION"
        android:protectionLevel="signature|privileged" />

    <!-- @SystemApi Allows an application to manage speech recognition service.
     @hide  <p>Not for use by third-party applications.</p> -->
    <permission android:name="android.permission.MANAGE_SPEECH_RECOGNITION"
        android:protectionLevel="signature" />

    <!-- @SystemApi Allows an application to manage the content suggestions service.
         @hide  <p>Not for use by third-party applications.</p> -->
    <permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS"
Loading