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

Commit 2411a2c3 authored by Chris Thornton's avatar Chris Thornton
Browse files

Clean up dead objects when detected in the SoundTriggerHelper

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
parent c79d4388
Loading
Loading
Loading
Loading
+62 −11
Original line number Diff line number Diff line
@@ -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.
@@ -793,8 +801,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
        try {
            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);
                }