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

Commit cfa214cb authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Change attribution integration points for SpeechRecognizer." into sc-dev am: 8a91fe36

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14234431

Change-Id: Ic842b44caac5a6415db67b38278ff19a37be1743
parents f3595be4 8a91fe36
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));
            }
        }