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

Commit 04fa2534 authored by Sergey Volnov's avatar Sergey Volnov
Browse files

Change attribution integration points for SpeechRecognizer.

I've deleted the extra attribution for now just to fix the bug asap, but
we need a long-term sync to make sure attribution works properly. For
both cases when the RS app decides to integrate with the new attribution
framework OR decides to ignore it.

Bug: 184963112
Test: atest CtsVoiceRecognitionTestCases
Change-Id: I20bd8174c1a188da6c5af55cdd3bebee8a638d2d
parent 1b2c6656
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -58,7 +58,6 @@ oneway interface IRecognitionService {
     * Cancels the speech recognition.
     *
     * @param listener to receive callbacks, note that this must be non-null
     * @param packageName the package name calling this API
     */
    void cancel(in IRecognitionListener listener, boolean isShutdown);
}
+59 −88
Original line number Diff line number Diff line
@@ -34,7 +34,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;

@@ -66,7 +65,12 @@ public abstract class RecognitionService extends Service {
    private static final String TAG = "RecognitionService";

    /** Debugging flag */
    private static final boolean DBG = false;
    private static final boolean DBG = true;

    private static final String RECORD_AUDIO_APP_OP =
            AppOpsManager.permissionToOp(Manifest.permission.RECORD_AUDIO);
    private static final int RECORD_AUDIO_APP_OP_CODE =
            AppOpsManager.permissionToOpCode(Manifest.permission.RECORD_AUDIO);

    /** Binder of the recognition service */
    private RecognitionServiceBinder mBinder = new RecognitionServiceBinder(this);
@@ -97,7 +101,7 @@ public abstract class RecognitionService extends Service {
                    dispatchStopListening((IRecognitionListener) msg.obj);
                    break;
                case MSG_CANCEL:
                    dispatchCancel((IRecognitionListener) msg.obj, msg.arg1 == 1);
                    dispatchCancel((IRecognitionListener) msg.obj);
                    break;
                case MSG_RESET:
                    dispatchClearCallback();
@@ -108,19 +112,21 @@ public abstract class RecognitionService extends Service {

    private void dispatchStartListening(Intent intent, final IRecognitionListener listener,
            @NonNull AttributionSource attributionSource) {
        try {
            if (mCurrentCallback == null) {
            if (DBG) Log.d(TAG, "created new mCurrentCallback, listener = " + listener.asBinder());
                if (DBG) {
                    Log.d(TAG, "created new mCurrentCallback, listener = " + listener.asBinder());
                }
                mCurrentCallback = new Callback(listener, attributionSource);

                RecognitionService.this.onStartListening(intent, mCurrentCallback);
            } else {
            try {
                listener.onError(SpeechRecognizer.ERROR_RECOGNIZER_BUSY);
                Log.i(TAG, "concurrent startListening received - ignoring this call");
            }
        } catch (RemoteException e) {
            Log.d(TAG, "onError call from startListening failed");
        }
            Log.i(TAG, "concurrent startListening received - ignoring this call");
        }
    }

    private void dispatchStopListening(IRecognitionListener listener) {
@@ -139,16 +145,13 @@ public abstract class RecognitionService extends Service {
        }
    }

    private void dispatchCancel(IRecognitionListener listener, boolean shutDown) {
    private void dispatchCancel(IRecognitionListener listener) {
        if (mCurrentCallback == null) {
            if (DBG) Log.d(TAG, "cancel called with no preceding startListening - ignoring");
        } else if (mCurrentCallback.mListener.asBinder() != listener.asBinder()) {
            Log.w(TAG, "cancel called by client who did not call startListening - ignoring");
        } else { // the correct state
            RecognitionService.this.onCancel(mCurrentCallback);
            if (shutDown) {
                mCurrentCallback.finishRecordAudioOpAttributionToCallerIfNeeded();
            }
            mCurrentCallback = null;
            if (DBG) Log.d(TAG, "canceling - setting mCurrentCallback to null");
        }
@@ -172,47 +175,6 @@ public abstract class RecognitionService extends Service {
        }
    }

    /**
     * Checks whether the caller has sufficient permissions
     * 
     * @param listener to send the error message to in case of error
     * @param forDataDelivery If the permission check is for delivering the sensitive data.
     * @param packageName the package name of the caller
     * @param featureId The feature in the package
     * @return {@code true} if the caller has enough permissions, {@code false} otherwise
     */
    private boolean checkPermissions(IRecognitionListener listener, boolean forDataDelivery,
            @NonNull String packageName, @Nullable String featureId) {
        if (DBG) Log.d(TAG, "checkPermissions");

        final int callingUid = Binder.getCallingUid();
        if (callingUid == Process.SYSTEM_UID) {
            // Assuming system has verified permissions of the caller.
            return true;
        }

        if (forDataDelivery) {
            if (PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(this,
                    android.Manifest.permission.RECORD_AUDIO, packageName, featureId,
                    null /*message*/) == PermissionChecker.PERMISSION_GRANTED) {
                return true;
            }
        } else {
            if (PermissionChecker.checkCallingOrSelfPermissionForPreflight(this,
                    android.Manifest.permission.RECORD_AUDIO)
                            == PermissionChecker.PERMISSION_GRANTED) {
                return true;
            }
        }
        try {
            Log.e(TAG, "call for recognition service without RECORD_AUDIO permissions");
            listener.onError(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
        } catch (RemoteException re) {
            Log.e(TAG, "sending ERROR_INSUFFICIENT_PERMISSIONS message failed", re);
        }
        return false;
    }

    /**
     * Notifies the service that it should start listening for speech.
     * 
@@ -281,7 +243,6 @@ public abstract class RecognitionService extends Service {
         *        single channel audio stream. The sample rate is implementation dependent.
         */
        public void bufferReceived(byte[] buffer) throws RemoteException {
            startRecordAudioOpAttributionToCallerIfNeeded();
            mListener.onBufferReceived(buffer);
        }

@@ -314,7 +275,6 @@ public abstract class RecognitionService extends Service {
         *        {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter
         */
        public void partialResults(Bundle partialResults) throws RemoteException {
            startRecordAudioOpAttributionToCallerIfNeeded();
            mListener.onPartialResults(partialResults);
        }

@@ -336,7 +296,6 @@ public abstract class RecognitionService extends Service {
         *        {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter
         */
        public void results(Bundle results) throws RemoteException {
            startRecordAudioOpAttributionToCallerIfNeeded();
            Message.obtain(mHandler, MSG_RESET).sendToTarget();
            mListener.onResults(results);
        }
@@ -366,9 +325,6 @@ public abstract class RecognitionService extends Service {
         * and passing this identity to {@link
         * android.content.ContextParams.Builder#setNextAttributionSource(AttributionSource)}.
         *
         *
         *
         *
         * @return The permission identity of the calling app.
         *
         * @see android.content.ContextParams.Builder#setNextAttributionSource(
@@ -379,40 +335,55 @@ public abstract class RecognitionService extends Service {
            return mCallingAttributionSource;
        }

        private void startRecordAudioOpAttributionToCallerIfNeeded() throws RemoteException {
            if (!isProxyingRecordAudioToCaller()) {
                final int result = PermissionChecker.checkPermissionAndStartDataDelivery(
                        RecognitionService.this, Manifest.permission.RECORD_AUDIO,
                        getAttributionContextForCaller().getAttributionSource(),
                        /*message*/ null);
                if (result == PermissionChecker.PERMISSION_GRANTED) {
                    return;
                }
                error(SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS);
        boolean maybeStartAttribution() {
            if (DBG) {
                Log.i(TAG, "Starting attribution");
            }

            if (DBG && isProxyingRecordAudioToCaller()) {
                Log.i(TAG, "Proxying already in progress, not starting the attribution");
            }

        private @NonNull Context getAttributionContextForCaller() {
            if (mAttributionContext == null) {
            if (!isProxyingRecordAudioToCaller()) {
                mAttributionContext = createContext(new ContextParams.Builder()
                        .setNextAttributionSource(mCallingAttributionSource)
                        .build());

                final int result = PermissionChecker.checkPermissionAndStartDataDelivery(
                        RecognitionService.this,
                        Manifest.permission.RECORD_AUDIO,
                        mAttributionContext.getAttributionSource(),
                        /*message*/ null);

                return result == PermissionChecker.PERMISSION_GRANTED;
            }
            return mAttributionContext;
            return false;
        }

        void maybeFinishAttribution() {
            if (DBG) {
                Log.i(TAG, "Finishing attribution");
            }

            if (DBG && !isProxyingRecordAudioToCaller()) {
                Log.i(TAG, "Not proxying currently, not finishing the attribution");
            }

        void finishRecordAudioOpAttributionToCallerIfNeeded() {
            if (isProxyingRecordAudioToCaller()) {
                final String op = AppOpsManager.permissionToOp(Manifest.permission.RECORD_AUDIO);
                PermissionChecker.finishDataDelivery(RecognitionService.this,
                        op, getAttributionContextForCaller().getAttributionSource());
                PermissionChecker.finishDataDelivery(
                        RecognitionService.this,
                        RECORD_AUDIO_APP_OP,
                        mAttributionContext.getAttributionSource());

                mAttributionContext = null;
            }
        }

        private boolean isProxyingRecordAudioToCaller() {
            final int op = AppOpsManager.permissionToOpCode(Manifest.permission.RECORD_AUDIO);
            final AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
            return appOpsManager.isProxying(op, getAttributionTag(),
            return appOpsManager.isProxying(
                    RECORD_AUDIO_APP_OP_CODE,
                    getAttributionTag(),
                    mCallingAttributionSource.getUid(),
                    mCallingAttributionSource.getPackageName());
        }
@@ -423,7 +394,7 @@ public abstract class RecognitionService extends Service {
        private final WeakReference<RecognitionService> mServiceRef;

        public RecognitionServiceBinder(RecognitionService service) {
            mServiceRef = new WeakReference<RecognitionService>(service);
            mServiceRef = new WeakReference<>(service);
        }

        @Override
@@ -445,8 +416,8 @@ public abstract class RecognitionService extends Service {
            if (DBG) Log.d(TAG, "stopListening called by:" + listener.asBinder());
            final RecognitionService service = mServiceRef.get();
            if (service != null) {
                service.mHandler.sendMessage(Message.obtain(service.mHandler,
                        MSG_STOP_LISTENING, listener));
                service.mHandler.sendMessage(
                        Message.obtain(service.mHandler, MSG_STOP_LISTENING, listener));
            }
        }

@@ -455,8 +426,8 @@ public abstract class RecognitionService extends Service {
            if (DBG) Log.d(TAG, "cancel called by:" + listener.asBinder());
            final RecognitionService service = mServiceRef.get();
            if (service != null) {
                service.mHandler.sendMessage(Message.obtain(service.mHandler,
                        MSG_CANCEL, isShutdown ? 1 : 0, 0, listener));
                service.mHandler.sendMessage(
                        Message.obtain(service.mHandler, MSG_CANCEL, listener));
            }
        }