1fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta/*
2fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta * Copyright (C) 2014 The Android Open Source Project
3fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta *
4fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta * Licensed under the Apache License, Version 2.0 (the "License");
5fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta * you may not use this file except in compliance with the License.
6fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta * You may obtain a copy of the License at
7fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta *
8fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta *      http://www.apache.org/licenses/LICENSE-2.0
9fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta *
10fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta * Unless required by applicable law or agreed to in writing, software
11fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta * distributed under the License is distributed on an "AS IS" BASIS,
12fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta * See the License for the specific language governing permissions and
14fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta * limitations under the License.
15fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta */
16fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
17fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavettapackage android.hardware.soundtrigger;
18fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
19c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport static org.mockito.Matchers.any;
20c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport static org.mockito.Matchers.eq;
21a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavettaimport static org.mockito.Mockito.reset;
22c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport static org.mockito.Mockito.spy;
23c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport static org.mockito.Mockito.timeout;
24c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport static org.mockito.Mockito.verify;
25fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
26fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavettaimport android.content.Context;
27c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport android.hardware.soundtrigger.SoundTrigger.GenericRecognitionEvent;
28fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavettaimport android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
29c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
30c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
31fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavettaimport android.media.soundtrigger.SoundTriggerManager;
32fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavettaimport android.os.ParcelUuid;
33fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavettaimport android.os.ServiceManager;
34fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavettaimport android.test.AndroidTestCase;
35c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport android.test.suitebuilder.annotation.LargeTest;
36fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavettaimport android.test.suitebuilder.annotation.SmallTest;
37fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
38fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavettaimport com.android.internal.app.ISoundTriggerService;
39fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
40c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport java.io.DataOutputStream;
41c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport java.net.InetAddress;
42c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport java.net.Socket;
43c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport java.util.ArrayList;
44a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavettaimport java.util.HashSet;
45fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavettaimport java.util.Random;
46fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavettaimport java.util.UUID;
47fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
48c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavettaimport org.mockito.MockitoAnnotations;
49c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
50fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavettapublic class GenericSoundModelTest extends AndroidTestCase {
51c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    static final int MSG_DETECTION_ERROR = -1;
52c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    static final int MSG_DETECTION_RESUME = 0;
53c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    static final int MSG_DETECTION_PAUSE = 1;
54c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    static final int MSG_KEYPHRASE_TRIGGER = 2;
55c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    static final int MSG_GENERIC_TRIGGER = 4;
56c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
57c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    private Random random = new Random();
58a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta    private HashSet<UUID> loadedModelUuids;
59c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    private ISoundTriggerService soundTriggerService;
60c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    private SoundTriggerManager soundTriggerManager;
61c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
62c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    @Override
63c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    public void setUp() throws Exception {
64c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        super.setUp();
65c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        MockitoAnnotations.initMocks(this);
66fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
67fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta        Context context = getContext();
68c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        soundTriggerService = ISoundTriggerService.Stub.asInterface(
69c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta                ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
70c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        soundTriggerManager = (SoundTriggerManager) context.getSystemService(
71c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta                Context.SOUND_TRIGGER_SERVICE);
72c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
73a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        loadedModelUuids = new HashSet<UUID>();
74c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    }
75c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
76c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    @Override
77c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    public void tearDown() throws Exception {
78c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        for (UUID modelUuid : loadedModelUuids) {
79c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta            soundTriggerService.deleteSoundModel(new ParcelUuid(modelUuid));
80c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        }
81c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        super.tearDown();
82c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    }
83fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
84c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    GenericSoundModel new_sound_model() {
85c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        // Create sound model
86fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta        byte[] data = new byte[1024];
87c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        random.nextBytes(data);
88fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta        UUID modelUuid = UUID.randomUUID();
89fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta        UUID mVendorUuid = UUID.randomUUID();
90c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        return new GenericSoundModel(modelUuid, mVendorUuid, data);
91c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    }
92fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
93c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    @SmallTest
94c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    public void testUpdateGenericSoundModel() throws Exception {
95c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        GenericSoundModel model = new_sound_model();
96fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
97c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        // Update sound model
98c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        soundTriggerService.updateSoundModel(model);
99c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        loadedModelUuids.add(model.uuid);
100fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
101c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        // Confirm it was updated
102c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        GenericSoundModel returnedModel =
103c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta                soundTriggerService.getSoundModel(new ParcelUuid(model.uuid));
104c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        assertEquals(model, returnedModel);
105fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta    }
106fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
107fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta    @SmallTest
108fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta    public void testDeleteGenericSoundModel() throws Exception {
109c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        GenericSoundModel model = new_sound_model();
110fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
111c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        // Update sound model
112c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        soundTriggerService.updateSoundModel(model);
113c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        loadedModelUuids.add(model.uuid);
114fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
115c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        // Delete sound model
116c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        soundTriggerService.deleteSoundModel(new ParcelUuid(model.uuid));
117c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        loadedModelUuids.remove(model.uuid);
118fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta
119c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        // Confirm it was deleted
120fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta        GenericSoundModel returnedModel =
121c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta                soundTriggerService.getSoundModel(new ParcelUuid(model.uuid));
122fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta        assertEquals(null, returnedModel);
123fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta    }
124c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
125c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    @LargeTest
126c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    public void testStartStopGenericSoundModel() throws Exception {
127c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        GenericSoundModel model = new_sound_model();
128c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
129c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        boolean captureTriggerAudio = true;
130c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        boolean allowMultipleTriggers = true;
131c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
132c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta                null, null);
133c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
134c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
135c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        // Update and start sound model recognition
136c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        soundTriggerService.updateSoundModel(model);
137c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        loadedModelUuids.add(model.uuid);
138c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        int r = soundTriggerService.startRecognition(new ParcelUuid(model.uuid), spyCallback,
139c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta                config);
140c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        assertEquals("Could Not Start Recognition with code: " + r,
141c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta                android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
142c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
143c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        // Stop recognition
144c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        r = soundTriggerService.stopRecognition(new ParcelUuid(model.uuid), spyCallback);
145c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        assertEquals("Could Not Stop Recognition with code: " + r,
146c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta                android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
147c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    }
148c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
149c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    @LargeTest
150c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    public void testTriggerGenericSoundModel() throws Exception {
151c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        GenericSoundModel model = new_sound_model();
152c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
153c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        boolean captureTriggerAudio = true;
154c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        boolean allowMultipleTriggers = true;
155c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
156c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta                null, null);
157c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
158c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
159c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        // Update and start sound model
160c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        soundTriggerService.updateSoundModel(model);
161c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        loadedModelUuids.add(model.uuid);
162c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        soundTriggerService.startRecognition(new ParcelUuid(model.uuid), spyCallback, config);
163c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
164c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        // Send trigger to stub HAL
165c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
166c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        DataOutputStream out = new DataOutputStream(socket.getOutputStream());
167c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        out.writeBytes("trig " + model.uuid.toString() + "\r\n");
168c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        out.flush();
169c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        socket.close();
170c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
171c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        // Verify trigger was received
172c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
173c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    }
174c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
175a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta    /**
176a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta     * Tests a more complicated pattern of loading, unloading, triggering, starting and stopping
177a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta     * recognition. Intended to find unexpected errors that occur in unexpected states.
178a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta     */
179a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta    @LargeTest
180a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta    public void testFuzzGenericSoundModel() throws Exception {
181a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        int numModels = 2;
182a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta
183a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        final int STATUS_UNLOADED = 0;
184a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        final int STATUS_LOADED = 1;
185a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        final int STATUS_STARTED = 2;
186a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta
187a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        class ModelInfo {
188a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            int status;
189a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            GenericSoundModel model;
190a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta
191a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            public ModelInfo(GenericSoundModel model, int status) {
192a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                this.status = status;
193a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                this.model = model;
194a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            }
195a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        }
196a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta
197a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        Random predictableRandom = new Random(100);
198a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta
199a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        ArrayList modelInfos = new ArrayList<ModelInfo>();
200a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        for(int i=0; i<numModels; i++) {
201a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            // Create sound model
202a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            byte[] data = new byte[1024];
203a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            predictableRandom.nextBytes(data);
204a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            UUID modelUuid = UUID.randomUUID();
205a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            UUID mVendorUuid = UUID.randomUUID();
206a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
207a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            ModelInfo modelInfo = new ModelInfo(model, STATUS_UNLOADED);
208a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            modelInfos.add(modelInfo);
209a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        }
210a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta
211a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        boolean captureTriggerAudio = true;
212a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        boolean allowMultipleTriggers = true;
213a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
214a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                null, null);
215a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
216a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta
217a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta
218a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        int numOperationsToRun = 100;
219a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        for(int i=0; i<numOperationsToRun; i++) {
220a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            // Select a random model
221a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            int modelInfoIndex = predictableRandom.nextInt(modelInfos.size());
222a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            ModelInfo modelInfo = (ModelInfo) modelInfos.get(modelInfoIndex);
223a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta
224a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            // Perform a random operation
225a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            int operation = predictableRandom.nextInt(5);
226a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta
227a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            if (operation == 0 && modelInfo.status == STATUS_UNLOADED) {
228a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                // Update and start sound model
229a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                soundTriggerService.updateSoundModel(modelInfo.model);
230a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                loadedModelUuids.add(modelInfo.model.uuid);
231a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                modelInfo.status = STATUS_LOADED;
232a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            } else if (operation == 1 && modelInfo.status == STATUS_LOADED) {
233a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                // Start the sound model
234a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                int r = soundTriggerService.startRecognition(new ParcelUuid(modelInfo.model.uuid),
235a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                        spyCallback, config);
236a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                assertEquals("Could Not Start Recognition with code: " + r,
237a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                        android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
238a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                modelInfo.status = STATUS_STARTED;
239a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            } else if (operation == 2 && modelInfo.status == STATUS_STARTED) {
240a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                // Send trigger to stub HAL
241a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
242a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                DataOutputStream out = new DataOutputStream(socket.getOutputStream());
243a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                out.writeBytes("trig " + modelInfo.model.uuid + "\r\n");
244a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                out.flush();
245a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                socket.close();
246a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta
247a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                // Verify trigger was received
248a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
249a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                reset(spyCallback);
250a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            } else if (operation == 3 && modelInfo.status == STATUS_STARTED) {
251a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                // Stop recognition
252a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                int r = soundTriggerService.stopRecognition(new ParcelUuid(modelInfo.model.uuid),
253a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                        spyCallback);
254a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                assertEquals("Could Not Stop Recognition with code: " + r,
255a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                        android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
256a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                modelInfo.status = STATUS_LOADED;
257a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            } else if (operation == 4 && modelInfo.status != STATUS_UNLOADED) {
258a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                // Delete sound model
259a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.uuid));
260a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                loadedModelUuids.remove(modelInfo.model.uuid);
261a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta
262a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                // Confirm it was deleted
263a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                GenericSoundModel returnedModel =
264a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                        soundTriggerService.getSoundModel(new ParcelUuid(modelInfo.model.uuid));
265a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                assertEquals(null, returnedModel);
266a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta                modelInfo.status = STATUS_UNLOADED;
267a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta            }
268a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta        }
269a0319cb5c48a36953eff33664a429dc1a9c6dbdaRyan Bavetta    }
270c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
271c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    public class TestRecognitionStatusCallback extends IRecognitionStatusCallback.Stub {
272c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        @Override
273c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        public void onGenericSoundTriggerDetected(GenericRecognitionEvent recognitionEvent) {
274c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        }
275c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
276c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        @Override
277c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        public void onKeyphraseDetected(KeyphraseRecognitionEvent recognitionEvent) {
278c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        }
279c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
280c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        @Override
281c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        public void onError(int status) {
282c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        }
283c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
284c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        @Override
285c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        public void onRecognitionPaused() {
286c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        }
287c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta
288c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        @Override
289c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        public void onRecognitionResumed() {
290c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta        }
291c92bbd7cdb9fba1438695d57307b1e94688b296dRyan Bavetta    }
292fc3ad3e4abeb83a679b5515a7f6ec618b8bcdca4Ryan Bavetta}
293