Fix race in RecognizerService teardown.

Use a WeakReference to refer to the outer class to prevent leaks.
Ensure atomicity of access to the reference.

Bug: 17584947
Change-Id: I7ad7c7793b60fa125e04fc4d803ed905e8a00a95
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 45eb0bf..dcdbba78 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -28,6 +28,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.lang.ref.WeakReference;
+
 /**
  * This class provides a base class for recognition service implementations. This class should be
  * extended only in case you wish to implement a new speech recognizer. Please note that the
@@ -315,40 +317,46 @@
     }
 
     /** Binder of the recognition service */
-    private static class RecognitionServiceBinder extends IRecognitionService.Stub {
-        private RecognitionService mInternalService;
+    private static final class RecognitionServiceBinder extends IRecognitionService.Stub {
+        private final WeakReference<RecognitionService> mServiceRef;
 
         public RecognitionServiceBinder(RecognitionService service) {
-            mInternalService = service;
+            mServiceRef = new WeakReference<RecognitionService>(service);
         }
 
+        @Override
         public void startListening(Intent recognizerIntent, IRecognitionListener listener) {
             if (DBG) Log.d(TAG, "startListening called by:" + listener.asBinder());
-            if (mInternalService != null && mInternalService.checkPermissions(listener)) {
-                mInternalService.mHandler.sendMessage(Message.obtain(mInternalService.mHandler,
-                        MSG_START_LISTENING, mInternalService.new StartListeningArgs(
+            final RecognitionService service = mServiceRef.get();
+            if (service != null && service.checkPermissions(listener)) {
+                service.mHandler.sendMessage(Message.obtain(service.mHandler,
+                        MSG_START_LISTENING, service.new StartListeningArgs(
                                 recognizerIntent, listener)));
             }
         }
 
+        @Override
         public void stopListening(IRecognitionListener listener) {
             if (DBG) Log.d(TAG, "stopListening called by:" + listener.asBinder());
-            if (mInternalService != null && mInternalService.checkPermissions(listener)) {
-                mInternalService.mHandler.sendMessage(Message.obtain(mInternalService.mHandler,
+            final RecognitionService service = mServiceRef.get();
+            if (service != null && service.checkPermissions(listener)) {
+                service.mHandler.sendMessage(Message.obtain(service.mHandler,
                         MSG_STOP_LISTENING, listener));
             }
         }
 
+        @Override
         public void cancel(IRecognitionListener listener) {
             if (DBG) Log.d(TAG, "cancel called by:" + listener.asBinder());
-            if (mInternalService != null && mInternalService.checkPermissions(listener)) {
-                mInternalService.mHandler.sendMessage(Message.obtain(mInternalService.mHandler,
+            final RecognitionService service = mServiceRef.get();
+            if (service != null && service.checkPermissions(listener)) {
+                service.mHandler.sendMessage(Message.obtain(service.mHandler,
                         MSG_CANCEL, listener));
             }
         }
 
         public void clearReference() {
-            mInternalService = null;
+            mServiceRef.clear();
         }
     }
 }