• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.mobileer.oboetester;
18 
19 import android.content.Context;
20 import android.media.AudioManager;
21 import android.os.Bundle;
22 import android.support.annotation.Nullable;
23 
24 import java.io.IOException;
25 import java.util.ArrayList;
26 
27 public class BaseAutoGlitchActivity extends GlitchActivity {
28 
29     private static final int SETUP_TIME_SECONDS = 4; // Time for the stream to settle.
30     protected static final int DEFAULT_DURATION_SECONDS = 8; // Run time for each test.
31     private static final int DEFAULT_GAP_MILLIS = 400; // Idle time between each test.
32     private static final String TEXT_SKIP = "SKIP";
33     public static final String TEXT_PASS = "PASS";
34     public static final String TEXT_FAIL = "FAIL !!!!";
35 
36     protected int mDurationSeconds = DEFAULT_DURATION_SECONDS;
37     protected int mGapMillis = DEFAULT_GAP_MILLIS;
38     private String mTestName = "";
39 
40     protected ArrayList<TestResult> mTestResults = new ArrayList<TestResult>();
41 
logDeviceInfo()42     void logDeviceInfo() {
43         log("\n############################");
44         log("\nDevice Info:");
45         AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
46         log(AudioQueryTools.getAudioManagerReport(audioManager));
47         log(AudioQueryTools.getAudioFeatureReport(getPackageManager()));
48         log(AudioQueryTools.getAudioPropertyReport());
49         log("\n############################");
50     }
51 
setTestName(String name)52     void setTestName(String name) {
53         mTestName = name;
54     }
55 
56     private static class TestDirection {
57         public final int channelUsed;
58         public final int channelCount;
59         public final int deviceId;
60         public final int mmapUsed;
61         public final int performanceMode;
62         public final int sharingMode;
63 
TestDirection(StreamConfiguration configuration, int channelUsed)64         public TestDirection(StreamConfiguration configuration, int channelUsed) {
65             this.channelUsed = channelUsed;
66             channelCount = configuration.getChannelCount();
67             deviceId = configuration.getDeviceId();
68             mmapUsed = configuration.isMMap() ? 1 : 0;
69             performanceMode = configuration.getPerformanceMode();
70             sharingMode = configuration.getSharingMode();
71         }
72 
countDifferences(TestDirection other)73         int countDifferences(TestDirection other) {
74             int count = 0;
75             count += (channelUsed != other.channelUsed) ? 1 : 0;
76             count += (channelCount != other.channelCount) ? 1 : 0;
77             count += (deviceId != other.deviceId) ? 1 : 0;
78             count += (mmapUsed != other.mmapUsed) ? 1 : 0;
79             count += (performanceMode != other.performanceMode) ? 1 : 0;
80             count += (sharingMode != other.sharingMode) ? 1 : 0;
81             return count;
82         }
83 
comparePassedDirection(String prefix, TestDirection passed)84         public String comparePassedDirection(String prefix, TestDirection passed) {
85             StringBuffer text = new StringBuffer();
86             text.append(TestDataPathsActivity.comparePassedField(prefix, this, passed, "channelUsed"));
87             text.append(TestDataPathsActivity.comparePassedField(prefix,this, passed, "channelCount"));
88             text.append(TestDataPathsActivity.comparePassedField(prefix,this, passed, "deviceId"));
89             text.append(TestDataPathsActivity.comparePassedField(prefix,this, passed, "mmapUsed"));
90             text.append(TestDataPathsActivity.comparePassedField(prefix,this, passed, "performanceMode"));
91             text.append(TestDataPathsActivity.comparePassedField(prefix,this, passed, "sharingMode"));
92             return text.toString();
93         }
94         @Override
toString()95         public String toString() {
96             return "D=" + deviceId
97                     + ", " + ((mmapUsed > 0) ? "MMAP" : "Lgcy")
98                     + ", ch=" + channelText(channelUsed, channelCount)
99                     + "," + StreamConfiguration.convertPerformanceModeToText(performanceMode)
100                     + "," + StreamConfiguration.convertSharingModeToText(sharingMode);
101         }
102     }
103 
104     protected static class TestResult {
105         final int testIndex;
106         final TestDirection input;
107         final TestDirection output;
108         public final int inputPreset;
109         public final int sampleRate;
110         final String testName; // name or purpose of test
111 
112         int result = TEST_RESULT_SKIPPED; // TEST_RESULT_FAILED, etc
113         private String mComments = ""; // additional info, ideas for why it failed
114 
TestResult(int testIndex, String testName, StreamConfiguration inputConfiguration, int inputChannel, StreamConfiguration outputConfiguration, int outputChannel)115         public TestResult(int testIndex,
116                           String testName,
117                           StreamConfiguration inputConfiguration,
118                           int inputChannel,
119                           StreamConfiguration outputConfiguration,
120                           int outputChannel) {
121             this.testIndex = testIndex;
122             this.testName = testName;
123             input = new TestDirection(inputConfiguration, inputChannel);
124             output = new TestDirection(outputConfiguration, outputChannel);
125             sampleRate = outputConfiguration.getSampleRate();
126             this.inputPreset = inputConfiguration.getInputPreset();
127         }
128 
countDifferences(TestResult other)129         int countDifferences(TestResult other) {
130             int count = 0;
131             count += input.countDifferences((other.input));
132             count += output.countDifferences((other.output));
133             count += (sampleRate != other.sampleRate) ? 1 : 0;
134             count += (inputPreset != other.inputPreset) ? 1 : 0;
135             return count;
136         }
137 
failed()138         public boolean failed() {
139             return result == TEST_RESULT_FAILED;
140         }
141 
passed()142         public boolean passed() {
143             return result == TEST_RESULT_PASSED;
144         }
145 
comparePassed(TestResult passed)146         public String comparePassed(TestResult passed) {
147             StringBuffer text = new StringBuffer();
148             text.append("Compare with passed test #" + passed.testIndex + "\n");
149             text.append(input.comparePassedDirection("IN", passed.input));
150             text.append(TestDataPathsActivity.comparePassedField("IN", this, passed, "inputPreset"));
151             text.append(output.comparePassedDirection("OUT", passed.output));
152             text.append(TestDataPathsActivity.comparePassedField("I/O",this, passed, "sampleRate"));
153 
154             return text.toString();
155         }
156 
157         @Override
toString()158         public String toString() {
159             return "IN:  " + input + ", ip=" + inputPreset + "\n"
160                     + "OUT: " + output + ", sr=" + sampleRate
161                     + mComments;
162         }
163 
addComment(String comment)164         public void addComment(String comment) {
165             mComments += "\n";
166             mComments += comment;
167         }
168 
setResult(int result)169         public void setResult(int result) {
170             this.result = result;
171         }
getResult(int result)172         public int getResult(int result) {
173             return result;
174         }
175     }
176 
177     @Override
onCreate(Bundle savedInstanceState)178     protected void onCreate(Bundle savedInstanceState) {
179         super.onCreate(savedInstanceState);
180 
181         mAutomatedTestRunner = findViewById(R.id.auto_test_runner);
182         mAutomatedTestRunner.setActivity(this);
183     }
184 
log(String text)185     protected void log(String text) {
186         mAutomatedTestRunner.log(text);
187     }
188 
appendFailedSummary(String text)189     protected void appendFailedSummary(String text) {
190         mAutomatedTestRunner.appendFailedSummary(text);
191     }
192 
appendSummary(String text)193     protected void appendSummary(String text) {
194         mAutomatedTestRunner.appendSummary(text);
195     }
196 
197     @Override
onStopTest()198     public void onStopTest() {
199         mAutomatedTestRunner.stopTest();
200     }
201 
channelText(int index, int count)202     static String channelText(int index, int count) {
203         return index + "/" + count;
204     }
205 
getConfigText(StreamConfiguration config)206     protected String getConfigText(StreamConfiguration config) {
207         int channel = (config.getDirection() == StreamConfiguration.DIRECTION_OUTPUT)
208                 ? getOutputChannel() : getInputChannel();
209         return ((config.getDirection() == StreamConfiguration.DIRECTION_OUTPUT) ? "OUT" : "INP")
210                 + (config.isMMap() ? "-M" : "-L")
211                 + ", ID = " + String.format("%2d", config.getDeviceId())
212                 + ", SR = " + String.format("%5d", config.getSampleRate())
213                 + ", Perf = " + StreamConfiguration.convertPerformanceModeToText(
214                 config.getPerformanceMode())
215                 + ", " + StreamConfiguration.convertSharingModeToText(config.getSharingMode())
216                 + ", ch = " + channelText(channel, config.getChannelCount());
217     }
218 
getStreamText(AudioStreamBase stream)219     protected String getStreamText(AudioStreamBase stream) {
220         return ("burst=" + stream.getFramesPerBurst()
221                 + ", size=" + stream.getBufferSizeInFrames()
222                 + ", cap=" + stream.getBufferCapacityInFrames()
223         );
224     }
225 
226     public final static int TEST_RESULT_FAILED = -2;
227     public final static int TEST_RESULT_WARNING = -1;
228     public final static int TEST_RESULT_SKIPPED = 0;
229     public final static int TEST_RESULT_PASSED = 1;
230 
231     // Run one test based on the requested input/output configurations.
232     @Nullable
testConfigurations()233     protected TestResult testConfigurations() throws InterruptedException {
234         int result = TEST_RESULT_SKIPPED;
235         mAutomatedTestRunner.incrementTestCount();
236         if ((getSingleTestIndex() >= 0) && (mAutomatedTestRunner.getTestCount() != getSingleTestIndex())) {
237             return null;
238         }
239 
240         log("========================== #" + mAutomatedTestRunner.getTestCount());
241 
242         StreamConfiguration requestedInConfig = mAudioInputTester.requestedConfiguration;
243         StreamConfiguration requestedOutConfig = mAudioOutTester.requestedConfiguration;
244 
245         StreamConfiguration actualInConfig = mAudioInputTester.actualConfiguration;
246         StreamConfiguration actualOutConfig = mAudioOutTester.actualConfiguration;
247 
248         log("Requested:");
249         log("  " + getConfigText(requestedInConfig));
250         log("  " + getConfigText(requestedOutConfig));
251 
252         String reason = "";
253         boolean openFailed = false;
254         try {
255             openAudio(); // this will fill in actualConfig
256             log("Actual:");
257             // Set output size to a level that will avoid glitches.
258             AudioStreamBase outStream = mAudioOutTester.getCurrentAudioStream();
259             int sizeFrames = outStream.getBufferCapacityInFrames() / 2;
260             sizeFrames = Math.max(sizeFrames, 2 * outStream.getFramesPerBurst());
261             outStream.setBufferSizeInFrames(sizeFrames);
262             AudioStreamBase inStream = mAudioInputTester.getCurrentAudioStream();
263             log("  " + getConfigText(actualInConfig));
264             log("      " + getStreamText(inStream));
265             log("  " + getConfigText(actualOutConfig));
266             log("      " + getStreamText(outStream));
267         } catch (Exception e) {
268             openFailed = true;
269             log(e.getMessage());
270             reason = e.getMessage();
271         }
272 
273         TestResult testResult = new TestResult(
274                 mAutomatedTestRunner.getTestCount(),
275                 mTestName,
276                 mAudioInputTester.actualConfiguration,
277                 getInputChannel(),
278                 mAudioOutTester.actualConfiguration,
279                 getOutputChannel()
280         );
281 
282         // The test would only be worth running if we got the configuration we requested on input or output.
283         String skipReason = shouldTestBeSkipped();
284         boolean skipped = skipReason.length() > 0;
285         boolean valid = !openFailed && !skipped;
286         boolean startFailed = false;
287         if (valid) {
288             try {
289                 startAudioTest();
290             } catch (IOException e) {
291                 e.printStackTrace();
292                 valid = false;
293                 startFailed = true;
294                 log(e.getMessage());
295                 reason = e.getMessage();
296             }
297         }
298         mAutomatedTestRunner.flushLog();
299 
300         if (valid) {
301             // Check for early return until we reach full duration.
302             long now = System.currentTimeMillis();
303             long startedAt = now;
304             long endTime = System.currentTimeMillis() + (mDurationSeconds * 1000);
305             boolean finishedEarly = false;
306             while (now < endTime && !finishedEarly) {
307                 Thread.sleep(100); // Let test run.
308                 now = System.currentTimeMillis();
309                 finishedEarly = isFinishedEarly();
310                 if (finishedEarly) {
311                     log("Finished early after " + (now - startedAt) + " msec.");
312                 }
313             }
314         }
315         int inXRuns = 0;
316         int outXRuns = 0;
317 
318         if (!openFailed) {
319             // get xRuns before closing the streams.
320             inXRuns = mAudioInputTester.getCurrentAudioStream().getXRunCount();
321             outXRuns = mAudioOutTester.getCurrentAudioStream().getXRunCount();
322 
323             super.stopAudioTest();
324         }
325 
326         if (openFailed || startFailed) {
327             appendFailedSummary("------ #" + mAutomatedTestRunner.getTestCount() + "\n");
328             appendFailedSummary(getConfigText(requestedInConfig) + "\n");
329             appendFailedSummary(getConfigText(requestedOutConfig) + "\n");
330             appendFailedSummary(reason + "\n");
331             mAutomatedTestRunner.incrementFailCount();
332         } else if (skipped) {
333             log(TEXT_SKIP + " - " + skipReason);
334         } else {
335             log("Result:");
336             reason += didTestFail();
337             boolean passed = reason.length() == 0;
338 
339             String resultText = getShortReport();
340             resultText += ", xruns = " + inXRuns + "/" + outXRuns;
341             resultText += ", " + (passed ? TEXT_PASS : TEXT_FAIL);
342             resultText += reason;
343             log("  " + resultText);
344             if (!passed) {
345                 appendFailedSummary("------ #" + mAutomatedTestRunner.getTestCount() + "\n");
346                 appendFailedSummary("  " + getConfigText(actualInConfig) + "\n");
347                 appendFailedSummary("  " + getConfigText(actualOutConfig) + "\n");
348                 appendFailedSummary("    " + resultText + "\n");
349                 mAutomatedTestRunner.incrementFailCount();
350                 result = TEST_RESULT_FAILED;
351             } else {
352                 mAutomatedTestRunner.incrementPassCount();
353                 result = TEST_RESULT_PASSED;
354             }
355         }
356         mAutomatedTestRunner.flushLog();
357 
358         // Give hardware time to settle between tests.
359         Thread.sleep(mGapMillis);
360 
361         if (valid) {
362             testResult.setResult(result);
363             mTestResults.add(testResult);
364         }
365 
366         return testResult;
367     }
368 
isFinishedEarly()369     protected boolean isFinishedEarly() {
370         return false;
371     }
372 
shouldTestBeSkipped()373     protected String shouldTestBeSkipped() {
374         String why = "";
375         StreamConfiguration requestedInConfig = mAudioInputTester.requestedConfiguration;
376         StreamConfiguration requestedOutConfig = mAudioOutTester.requestedConfiguration;
377         StreamConfiguration actualInConfig = mAudioInputTester.actualConfiguration;
378         StreamConfiguration actualOutConfig = mAudioOutTester.actualConfiguration;
379         // No point running the test if we don't get the sharing mode we requested.
380         if (actualInConfig.getSharingMode() != requestedInConfig.getSharingMode()
381                 || actualOutConfig.getSharingMode() != requestedOutConfig.getSharingMode()) {
382             log("Did not get requested sharing mode.");
383             why += "share";
384         }
385         // We don't skip based on performance mode because if you request LOW_LATENCY you might
386         // get a smaller burst than if you request NONE.
387         return why;
388     }
389 
didTestFail()390     public String didTestFail() {
391         String why = "";
392         if (getMaxSecondsWithNoGlitch() <= (mDurationSeconds - SETUP_TIME_SECONDS)) {
393             why += ", glitch";
394         }
395         return why;
396     }
397 
logAnalysis(String text)398     void logAnalysis(String text) {
399         appendFailedSummary(text + "\n");
400     }
401 
analyzeTestResults()402     protected void analyzeTestResults() {
403         logAnalysis("\n==== ANALYSIS ===========");
404         logAnalysis("Compare failed configuration with closest one that passed.");
405         // Analyze each failed test.
406         for (TestResult testResult : mTestResults) {
407             if (testResult.failed()) {
408                 logAnalysis("-------------------- #" + testResult.testIndex + " FAILED");
409                 String name = testResult.testName;
410                 if (name.length() > 0) {
411                     logAnalysis(name);
412                 }
413                 TestResult[] closest = findClosestPassingTestResults(testResult);
414                 for (TestResult other : closest) {
415                     logAnalysis(testResult.comparePassed(other));
416                 }
417                 logAnalysis(testResult.toString());
418             }
419         }
420     }
421 
422     @Nullable
findClosestPassingTestResults(TestResult testResult)423     private TestResult[] findClosestPassingTestResults(TestResult testResult) {
424         int minDifferences = Integer.MAX_VALUE;
425         for (TestResult other : mTestResults) {
426             if (other.passed()) {
427                 int numDifferences = testResult.countDifferences(other);
428                 if (numDifferences < minDifferences) {
429                     minDifferences = numDifferences;
430                 }
431             }
432         }
433         // Now find all the tests that are just as close as the closest.
434         ArrayList<TestResult> list = new ArrayList<TestResult>();
435         for (TestResult other : mTestResults) {
436             if (other.passed()) {
437                 int numDifferences = testResult.countDifferences(other);
438                 if (numDifferences == minDifferences) {
439                     list.add(other);
440                 }
441             }
442         }
443         return list.toArray(new TestResult[0]);
444     }
445 
446 }
447