Loading core/java/android/speech/IRecognitionService.aidl +0 −1 Original line number Diff line number Diff line Loading @@ -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); } core/java/android/speech/RecognitionService.java +59 −88 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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(); Loading @@ -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) { Loading @@ -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"); } Loading @@ -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. * Loading Loading @@ -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); } Loading Loading @@ -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); } Loading @@ -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); } Loading Loading @@ -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( Loading @@ -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()); } Loading @@ -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 Loading @@ -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)); } } Loading @@ -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)); } } Loading Loading
core/java/android/speech/IRecognitionService.aidl +0 −1 Original line number Diff line number Diff line Loading @@ -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); }
core/java/android/speech/RecognitionService.java +59 −88 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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(); Loading @@ -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) { Loading @@ -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"); } Loading @@ -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. * Loading Loading @@ -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); } Loading Loading @@ -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); } Loading @@ -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); } Loading Loading @@ -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( Loading @@ -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()); } Loading @@ -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 Loading @@ -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)); } } Loading @@ -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)); } } Loading