Merge "Explicitly close sound model file descriptors" into tm-dev am: 4b5f06df78

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

Change-Id: I30d2deaa03e030e3ca6c5ac24582fba927f91f11
This commit is contained in:
Ytai Ben-tsvi
2022-02-23 19:33:39 +00:00
committed by Automerger Merge Worker
4 changed files with 111 additions and 43 deletions

View File

@@ -37,6 +37,8 @@ import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import java.io.IOException;
/**
* The SoundTriggerModule provides APIs to control sound models and sound detection
* on a given sound trigger hardware module.
@@ -137,13 +139,39 @@ public class SoundTriggerModule {
if (model instanceof SoundTrigger.GenericSoundModel) {
SoundModel aidlModel = ConversionUtil.api2aidlGenericSoundModel(
(SoundTrigger.GenericSoundModel) model);
soundModelHandle[0] = mService.loadModel(aidlModel);
try {
soundModelHandle[0] = mService.loadModel(aidlModel);
} finally {
// TODO(b/219825762): We should be able to use the entire object in a
// try-with-resources
// clause, instead of having to explicitly close internal fields.
if (aidlModel.data != null) {
try {
aidlModel.data.close();
} catch (IOException e) {
Log.e(TAG, "Failed to close file", e);
}
}
}
return SoundTrigger.STATUS_OK;
}
if (model instanceof SoundTrigger.KeyphraseSoundModel) {
PhraseSoundModel aidlModel = ConversionUtil.api2aidlPhraseSoundModel(
(SoundTrigger.KeyphraseSoundModel) model);
soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
try {
soundModelHandle[0] = mService.loadPhraseModel(aidlModel);
} finally {
// TODO(b/219825762): We should be able to use the entire object in a
// try-with-resources
// clause, instead of having to explicitly close internal fields.
if (aidlModel.common.data != null) {
try {
aidlModel.common.data.close();
} catch (IOException e) {
Log.e(TAG, "Failed to close file", e);
}
}
}
return SoundTrigger.STATUS_OK;
}
return SoundTrigger.STATUS_BAD_VALUE;

View File

@@ -29,7 +29,9 @@ import android.os.IBinder;
import android.os.IHwBinder;
import android.os.RemoteException;
import android.system.OsConstants;
import android.util.Log;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@@ -54,6 +56,8 @@ import java.util.concurrent.atomic.AtomicReference;
* </ul>
*/
final class SoundTriggerHw2Compat implements ISoundTriggerHal {
private static final String TAG = "SoundTriggerHw2Compat";
private final @NonNull Runnable mRebootRunnable;
private final @NonNull IHwBinder mBinder;
private @NonNull android.hardware.soundtrigger.V2_0.ISoundTriggerHw mUnderlying_2_0;
@@ -226,6 +230,16 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHal {
return handle.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
// TODO(b/219825762): We should be able to use the entire object in a try-with-resources
// clause, instead of having to explicitly close internal fields.
if (hidlModel.data != null) {
try {
hidlModel.data.close();
} catch (IOException e) {
Log.e(TAG, "Failed to close file", e);
}
}
}
}
@@ -252,6 +266,16 @@ final class SoundTriggerHw2Compat implements ISoundTriggerHal {
return handle.get();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
// TODO(b/219825762): We should be able to use the entire object in a try-with-resources
// clause, instead of having to explicitly close internal fields.
if (hidlModel.common.data != null) {
try {
hidlModel.common.data.close();
} catch (IOException e) {
Log.e(TAG, "Failed to close file", e);
}
}
}
}

View File

@@ -31,7 +31,6 @@ import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -49,7 +48,6 @@ import android.os.IBinder;
import android.os.IHwBinder;
import android.os.IHwInterface;
import android.os.RemoteException;
import android.system.OsConstants;
import org.junit.After;
import org.junit.Before;
@@ -60,6 +58,7 @@ import org.mockito.ArgumentCaptor;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@RunWith(Parameterized.class)
public class SoundHw2CompatTest {
@@ -240,14 +239,15 @@ public class SoundHw2CompatTest {
(android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
final int handle = 29;
ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> modelCaptor =
ArgumentCaptor.forClass(
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel.class);
AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel> model =
new AtomicReference<>();
ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
ArgumentCaptor.forClass(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
doAnswer(invocation -> {
// We need to dup the model, as it gets invalidated after the call returns.
model.set(TestUtil.dupModel_2_1(invocation.getArgument(0)));
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadSoundModel_2_1Callback
resultCallback = invocation.getArgument(3);
@@ -259,10 +259,9 @@ public class SoundHw2CompatTest {
assertEquals(handle,
mCanonical.loadSoundModel(TestUtil.createGenericSoundModel(), canonicalCallback));
verify(driver_2_1).loadSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
anyInt(), any());
verify(driver_2_1).loadSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(), any());
TestUtil.validateGenericSoundModel_2_1(modelCaptor.getValue());
TestUtil.validateGenericSoundModel_2_1(model.get());
validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
return handle;
}
@@ -355,14 +354,16 @@ public class SoundHw2CompatTest {
(android.hardware.soundtrigger.V2_1.ISoundTriggerHw) mHalDriver;
final int handle = 29;
ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel>
modelCaptor = ArgumentCaptor.forClass(
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel.class);
AtomicReference<android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel> model =
new AtomicReference<>();
ArgumentCaptor<android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback> callbackCaptor =
ArgumentCaptor.forClass(
android.hardware.soundtrigger.V2_1.ISoundTriggerHwCallback.class);
doAnswer(invocation -> {
// We need to dup the model, as it gets invalidated after the call returns.
model.set(TestUtil.dupPhraseModel_2_1(invocation.getArgument(0)));
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.loadPhraseSoundModel_2_1Callback
resultCallback = invocation.getArgument(3);
@@ -374,10 +375,10 @@ public class SoundHw2CompatTest {
assertEquals(handle, mCanonical.loadPhraseSoundModel(TestUtil.createPhraseSoundModel(),
canonicalCallback));
verify(driver_2_1).loadPhraseSoundModel_2_1(modelCaptor.capture(), callbackCaptor.capture(),
anyInt(), any());
verify(driver_2_1).loadPhraseSoundModel_2_1(any(), callbackCaptor.capture(), anyInt(),
any());
TestUtil.validatePhraseSoundModel_2_1(modelCaptor.getValue());
TestUtil.validatePhraseSoundModel_2_1(model.get());
validateCallback_2_1(callbackCaptor.getValue(), canonicalCallback);
return handle;
}

View File

@@ -47,6 +47,7 @@ import android.os.HidlMemoryUtil;
import android.os.ParcelFileDescriptor;
import android.os.SharedMemory;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
@@ -82,6 +83,19 @@ class TestUtil {
HidlMemoryUtil.hidlMemoryToByteArray(model.data));
}
static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dupModel_2_1(
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel model) {
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel dup =
new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.SoundModel();
dup.header = model.header;
try {
dup.data = model.data.dup();
} catch (IOException e) {
throw new RuntimeException(e);
}
return dup;
}
private static void validateSoundModel_2_0(
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.SoundModel model, int type) {
assertEquals(type, model.type);
@@ -121,6 +135,15 @@ class TestUtil {
validatePhrases_2_0(model.phrases);
}
static android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dupPhraseModel_2_1(
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel model) {
android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel dup =
new android.hardware.soundtrigger.V2_1.ISoundTriggerHw.PhraseSoundModel();
dup.common = dupModel_2_1(model.common);
dup.phrases = model.phrases;
return dup;
}
static void validatePhraseSoundModel_2_0(
android.hardware.soundtrigger.V2_0.ISoundTriggerHw.PhraseSoundModel model) {
validateSoundModel_2_0(model.common,
@@ -190,31 +213,27 @@ class TestUtil {
properties.maxKeyPhrases = 567;
properties.maxUsers = 678;
properties.recognitionModes =
RecognitionMode.VOICE_TRIGGER
| RecognitionMode.USER_IDENTIFICATION
| RecognitionMode.USER_AUTHENTICATION
| RecognitionMode.GENERIC_TRIGGER;
RecognitionMode.VOICE_TRIGGER | RecognitionMode.USER_IDENTIFICATION
| RecognitionMode.USER_AUTHENTICATION | RecognitionMode.GENERIC_TRIGGER;
properties.captureTransition = true;
properties.maxBufferMs = 321;
properties.concurrentCapture = supportConcurrentCapture;
properties.triggerInEvent = true;
properties.powerConsumptionMw = 432;
properties.supportedModelArch = "supportedModelArch";
properties.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
| AudioCapabilities.NOISE_SUPPRESSION;
properties.audioCapabilities =
AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
return properties;
}
static void validateDefaultProperties(Properties properties,
boolean supportConcurrentCapture) {
static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture) {
validateDefaultProperties(properties, supportConcurrentCapture,
AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
"supportedModelArch");
}
static void validateDefaultProperties(Properties properties,
boolean supportConcurrentCapture, @AudioCapabilities int audioCapabilities,
@NonNull String supportedModelArch) {
static void validateDefaultProperties(Properties properties, boolean supportConcurrentCapture,
@AudioCapabilities int audioCapabilities, @NonNull String supportedModelArch) {
assertEquals("implementor", properties.implementor);
assertEquals("description", properties.description);
assertEquals(123, properties.version);
@@ -222,10 +241,9 @@ class TestUtil {
assertEquals(456, properties.maxSoundModels);
assertEquals(567, properties.maxKeyPhrases);
assertEquals(678, properties.maxUsers);
assertEquals(RecognitionMode.GENERIC_TRIGGER
| RecognitionMode.USER_AUTHENTICATION
| RecognitionMode.USER_IDENTIFICATION
| RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
assertEquals(RecognitionMode.GENERIC_TRIGGER | RecognitionMode.USER_AUTHENTICATION
| RecognitionMode.USER_IDENTIFICATION | RecognitionMode.VOICE_TRIGGER,
properties.recognitionModes);
assertTrue(properties.captureTransition);
assertEquals(321, properties.maxBufferMs);
assertEquals(supportConcurrentCapture, properties.concurrentCapture);
@@ -246,8 +264,8 @@ class TestUtil {
config.phraseRecognitionExtras[0].levels[0].userId = 234;
config.phraseRecognitionExtras[0].levels[0].levelPercent = 34;
config.data = new byte[]{5, 4, 3, 2, 1};
config.audioCapabilities = AudioCapabilities.ECHO_CANCELLATION
| AudioCapabilities.NOISE_SUPPRESSION;
config.audioCapabilities =
AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION;
return config;
}
@@ -295,13 +313,12 @@ class TestUtil {
int captureHandle) {
validateRecognitionConfig_2_1(config.base, captureDevice, captureHandle);
assertEquals(AudioCapabilities.ECHO_CANCELLATION
| AudioCapabilities.NOISE_SUPPRESSION, config.audioCapabilities);
assertEquals(AudioCapabilities.ECHO_CANCELLATION | AudioCapabilities.NOISE_SUPPRESSION,
config.audioCapabilities);
}
static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_0(
int hwHandle,
int status) {
int hwHandle, int status) {
android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent halEvent =
new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.RecognitionEvent();
halEvent.status = status;
@@ -351,8 +368,7 @@ class TestUtil {
return event;
}
static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(
int hwHandle,
static ISoundTriggerHwCallback.RecognitionEvent createRecognitionEvent_2_1(int hwHandle,
int status) {
ISoundTriggerHwCallback.RecognitionEvent halEvent =
new ISoundTriggerHwCallback.RecognitionEvent();
@@ -386,8 +402,7 @@ class TestUtil {
PhraseRecognitionExtra extra = new PhraseRecognitionExtra();
extra.id = 123;
extra.confidenceLevel = 52;
extra.recognitionModes = RecognitionMode.VOICE_TRIGGER
| RecognitionMode.GENERIC_TRIGGER;
extra.recognitionModes = RecognitionMode.VOICE_TRIGGER | RecognitionMode.GENERIC_TRIGGER;
ConfidenceLevel level = new ConfidenceLevel();
level.userId = 31;
level.levelPercent = 43;
@@ -396,8 +411,8 @@ class TestUtil {
return event;
}
static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent
createPhraseRecognitionEvent_2_0(int hwHandle, int status) {
static android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent createPhraseRecognitionEvent_2_0(
int hwHandle, int status) {
android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent halEvent =
new android.hardware.soundtrigger.V2_0.ISoundTriggerHwCallback.PhraseRecognitionEvent();
halEvent.common = createRecognitionEvent_2_0(hwHandle, status);