OSDN Git Service

Clean up dead objects when detected in the SoundTriggerHelper
authorChris Thornton <thorntonc@google.com>
Wed, 22 Mar 2017 20:52:50 +0000 (13:52 -0700)
committerChris Thornton <thorntonc@google.com>
Thu, 23 Mar 2017 04:28:31 +0000 (21:28 -0700)
If an app has died while being connected to the sound trigger helper/the
binder goes away, we should stop and unload the model since there's no
way to get a handle on it again (without reloading the same UUID at some
point).

Test: Force stop the SoundTriggerTestApp while it has a model started,
trigger the sound model, then watch it get unloaded when the helper
notices that the binder is dead.

Change-Id: Iddfaaa03482d56e71f23e5982d4bd579f386b51d

services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java

index ca37631..1aa952c 100644 (file)
@@ -34,6 +34,7 @@ import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
 import android.hardware.soundtrigger.SoundTrigger.SoundModel;
 import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
 import android.hardware.soundtrigger.SoundTriggerModule;
+import android.os.DeadObjectException;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.telephony.PhoneStateListener;
@@ -46,6 +47,8 @@ import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
 import java.util.UUID;
 
 /**
@@ -376,7 +379,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
 
             ModelData modelData = getKeyphraseModelDataLocked(keyphraseId);
             if (modelData == null || !modelData.isKeyphraseModel()) {
-                Slog.e(TAG, "No model exists for given keyphrase Id.");
+                Slog.e(TAG, "No model exists for given keyphrase Id " + keyphraseId);
                 return STATUS_ERROR;
             }
 
@@ -609,13 +612,16 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
             return;
         }
 
+        model.setStopped();
         try {
             callback.onGenericSoundTriggerDetected((GenericRecognitionEvent) event);
+        } catch (DeadObjectException e) {
+            forceStopAndUnloadModel(model, e);
+            return;
         } catch (RemoteException e) {
             Slog.w(TAG, "RemoteException in onGenericSoundTriggerDetected", e);
         }
 
-        model.setStopped();
         RecognitionConfig config = model.getRecognitionConfig();
         if (config == null) {
             Slog.w(TAG, "Generic recognition event: Null RecognitionConfig for model handle: " +
@@ -699,6 +705,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
             modelData.setStopped();
             try {
                 modelData.getCallback().onRecognitionPaused();
+            } catch (DeadObjectException e) {
+                forceStopAndUnloadModel(modelData, e);
             } catch (RemoteException e) {
                 Slog.w(TAG, "RemoteException in onRecognitionPaused", e);
             }
@@ -710,8 +718,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
         MetricsLogger.count(mContext, "sth_recognition_failure_event", 1);
         try {
             sendErrorCallbacksToAll(STATUS_ERROR);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "RemoteException in onError", e);
         } finally {
             internalClearModelStateLocked();
             internalClearGlobalStateLocked();
@@ -748,15 +754,17 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
             Slog.w(TAG, "Received onRecognition event without callback for keyphrase model.");
             return;
         }
+        modelData.setStopped();
 
         try {
             modelData.getCallback().onKeyphraseDetected((KeyphraseRecognitionEvent) event);
+        } catch (DeadObjectException e) {
+            forceStopAndUnloadModel(modelData, e);
+            return;
         } catch (RemoteException e) {
             Slog.w(TAG, "RemoteException in onKeyphraseDetected", e);
         }
 
-        modelData.setStopped();
-
         RecognitionConfig config = modelData.getRecognitionConfig();
         if (config != null) {
             // Whether we should continue by starting this again.
@@ -791,10 +799,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
 
     private void onServiceDiedLocked() {
         try {
-          MetricsLogger.count(mContext, "sth_service_died", 1);
+            MetricsLogger.count(mContext, "sth_service_died", 1);
             sendErrorCallbacksToAll(SoundTrigger.STATUS_DEAD_OBJECT);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "RemoteException in onError", e);
         } finally {
             internalClearModelStateLocked();
             internalClearGlobalStateLocked();
@@ -879,11 +885,48 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
     }
 
     // Sends an error callback to all models with a valid registered callback.
-    private void sendErrorCallbacksToAll(int errorCode) throws RemoteException {
+    private void sendErrorCallbacksToAll(int errorCode) {
         for (ModelData modelData : mModelDataMap.values()) {
             IRecognitionStatusCallback callback = modelData.getCallback();
             if (callback != null) {
-                callback.onError(STATUS_ERROR);
+                try {
+                    callback.onError(errorCode);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "RemoteException sendErrorCallbacksToAll for model handle " +
+                            modelData.getHandle(), e);
+                }
+            }
+        }
+    }
+
+    private void forceStopAndUnloadModel(ModelData modelData, Exception exception) {
+        if (exception != null) {
+          Slog.e(TAG, "forceStopAndUnloadModel", exception);
+        }
+        if (modelData.isModelStarted()) {
+            Slog.d(TAG, "Stopping previously started dangling model " + modelData.getHandle());
+            if (mModule.stopRecognition(modelData.getHandle()) != STATUS_OK) {
+                modelData.setStopped();
+                modelData.setRequested(false);
+            } else {
+                Slog.e(TAG, "Failed to stop model " + modelData.getHandle());
+            }
+        }
+        if (modelData.isModelLoaded()) {
+            Slog.d(TAG, "Unloading previously loaded dangling model " + modelData.getHandle());
+            if (mModule.unloadSoundModel(modelData.getHandle()) == STATUS_OK) {
+                // Remove the model data from existence.
+                mModelDataMap.remove(modelData.getModelId());
+                Iterator it = mKeyphraseUuidMap.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry pair = (Map.Entry) it.next();
+                    if (pair.getValue().equals(modelData.getModelId())) {
+                        it.remove();
+                    }
+                }
+                modelData.clearState();
+            } else {
+                Slog.e(TAG, "Failed to unload model " + modelData.getHandle());
             }
         }
     }
@@ -976,6 +1019,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
             if (notify) {
                 try {
                     callback.onError(status);
+                } catch (DeadObjectException e) {
+                    forceStopAndUnloadModel(modelData, e);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "RemoteException in onError", e);
                 }
@@ -988,6 +1033,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
             if (notify) {
                 try {
                     callback.onRecognitionResumed();
+                } catch (DeadObjectException e) {
+                    forceStopAndUnloadModel(modelData, e);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "RemoteException in onRecognitionResumed", e);
                 }
@@ -1013,6 +1060,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
             if (notify) {
                 try {
                     callback.onError(status);
+                } catch (DeadObjectException e) {
+                    forceStopAndUnloadModel(modelData, e);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "RemoteException in onError", e);
                 }
@@ -1024,6 +1073,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
             if (notify) {
                 try {
                     callback.onRecognitionPaused();
+                } catch (DeadObjectException e) {
+                    forceStopAndUnloadModel(modelData, e);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "RemoteException in onRecognitionPaused", e);
                 }