• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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.google.sample.oboe.manualtest;
18 
19 import android.Manifest;
20 import android.content.pm.PackageInfo;
21 import android.content.pm.PackageManager;
22 import android.os.Build;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.support.annotation.NonNull;
27 import android.support.v4.app.ActivityCompat;
28 import android.support.v4.content.ContextCompat;
29 import android.util.Log;
30 import android.view.View;
31 import android.widget.Button;
32 import android.widget.TextView;
33 import android.widget.Toast;
34 
35 import java.io.File;
36 import java.io.FileOutputStream;
37 import java.io.IOException;
38 import java.io.OutputStreamWriter;
39 import java.io.Writer;
40 
41 /**
42  * Activity to measure latency on a full duplex stream.
43  */
44 public class AnalyzerActivity extends TestInputActivity {
45 
46     private static final int MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE = 1001;
47 
48     public static final String KEY_IN_SHARING = "in_sharing";
49     public static final String KEY_OUT_SHARING = "out_sharing";
50     public static final String VALUE_SHARING_EXCLUSIVE = "exclusive";
51     public static final String VALUE_SHARING_SHARED = "shared";
52 
53     public static final String KEY_IN_PERF = "in_perf";
54     public static final String KEY_OUT_PERF = "out_perf";
55     public static final String VALUE_PERF_LOW_LATENCY = "lowlat";
56     public static final String VALUE_PERF_POWERSAVE = "powersave";
57     public static final String VALUE_PERF_NONE = "none";
58 
59     public static final String KEY_IN_CHANNELS = "in_channels";
60     public static final String KEY_OUT_CHANNELS = "out_channels";
61     public static final int VALUE_DEFAULT_CHANNELS = 2;
62 
63     public static final String KEY_SAMPLE_RATE = "sample_rate";
64     public static final int VALUE_DEFAULT_SAMPLE_RATE = 48000;
65 
66     protected static final String KEY_FILE_NAME = "file";
67     protected static final String KEY_BUFFER_BURSTS = "buffer_bursts";
68 
69     public static final String VALUE_UNSPECIFIED = "unspecified";
70     public static final String KEY_IN_API = "in_api";
71     public static final String KEY_OUT_API = "out_api";
72     public static final String VALUE_API_AAUDIO = "aaudio";
73     public static final String VALUE_API_OPENSLES = "opensles";
74 
75     AudioOutputTester mAudioOutTester;
76     protected BufferSizeView mBufferSizeView;
77     protected String mResultFileName;
78     private String mTestResults;
79 
80     // Note that these string must match the enum result_code in LatencyAnalyzer.h
resultCodeToString(int resultCode)81     String resultCodeToString(int resultCode) {
82         switch (resultCode) {
83             case 0:
84                 return "OK";
85             case -99:
86                 return "ERROR_NOISY";
87             case -98:
88                 return "ERROR_VOLUME_TOO_LOW";
89             case -97:
90                 return "ERROR_VOLUME_TOO_HIGH";
91             case -96:
92                 return "ERROR_CONFIDENCE";
93             case -95:
94                 return "ERROR_INVALID_STATE";
95             case -94:
96                 return "ERROR_GLITCHES";
97             case -93:
98                 return "ERROR_NO_LOCK";
99             default:
100                 return "UNKNOWN";
101         }
102     }
103 
getAnalyzerState()104     public native int getAnalyzerState();
isAnalyzerDone()105     public native boolean isAnalyzerDone();
getMeasuredResult()106     public native int getMeasuredResult();
getResetCount()107     public native int getResetCount();
108 
109     @NonNull
getCommonTestReport()110     protected String getCommonTestReport() {
111         StringBuffer report = new StringBuffer();
112         // Add some extra information for the remote tester.
113         report.append("build.fingerprint = " + Build.FINGERPRINT + "\n");
114         try {
115             PackageInfo pinfo = getPackageManager().getPackageInfo(getPackageName(), 0);
116             report.append(String.format("test.version = %s\n", pinfo.versionName));
117             report.append(String.format("test.version.code = %d\n", pinfo.versionCode));
118         } catch (PackageManager.NameNotFoundException e) {
119         }
120         report.append("time.millis = " + System.currentTimeMillis() + "\n");
121 
122         // INPUT
123         report.append(mAudioInputTester.actualConfiguration.dump());
124         AudioStreamBase inStream = mAudioInputTester.getCurrentAudioStream();
125         report.append(String.format("in.burst.frames = %d\n", inStream.getFramesPerBurst()));
126         report.append(String.format("in.xruns = %d\n", inStream.getXRunCount()));
127 
128         // OUTPUT
129         report.append(mAudioOutTester.actualConfiguration.dump());
130         AudioStreamBase outStream = mAudioOutTester.getCurrentAudioStream();
131         report.append(String.format("out.burst.frames = %d\n", outStream.getFramesPerBurst()));
132         int bufferSize = outStream.getBufferSizeInFrames();
133         report.append(String.format("out.buffer.size.frames = %d\n", bufferSize));
134         int bufferCapacity = outStream.getBufferCapacityInFrames();
135         report.append(String.format("out.buffer.capacity.frames = %d\n", bufferCapacity));
136         report.append(String.format("out.xruns = %d\n", outStream.getXRunCount()));
137 
138         return report.toString();
139     }
140 
141     @Override
onCreate(Bundle savedInstanceState)142     protected void onCreate(Bundle savedInstanceState) {
143         super.onCreate(savedInstanceState);
144         mAudioOutTester = addAudioOutputTester();
145         mBufferSizeView = (BufferSizeView) findViewById(R.id.buffer_size_view);
146     }
147 
148     @Override
resetConfiguration()149     protected void resetConfiguration() {
150         super.resetConfiguration();
151         mAudioOutTester.reset();
152 
153         StreamContext streamContext = getFirstInputStreamContext();
154         if (streamContext != null) {
155             if (streamContext.configurationView != null) {
156                 streamContext.configurationView.setFormat(StreamConfiguration.AUDIO_FORMAT_PCM_FLOAT);
157                 streamContext.configurationView.setFormatConversionAllowed(true);
158             }
159         }
160         streamContext = getFirstOutputStreamContext();
161         if (streamContext != null) {
162             if (streamContext.configurationView != null) {
163                 streamContext.configurationView.setFormat(StreamConfiguration.AUDIO_FORMAT_PCM_FLOAT);
164                 streamContext.configurationView.setFormatConversionAllowed(true);
165             }
166         }
167     }
168 
169     @Override
openAudio()170     public void openAudio() throws IOException {
171         super.openAudio();
172         if (mBufferSizeView != null) {
173             mBufferSizeView.onStreamOpened((OboeAudioStream) mAudioOutTester.getCurrentAudioStream());
174         }
175     }
176 
onStreamClosed()177     public void onStreamClosed() {
178         Toast.makeText(getApplicationContext(),
179                 "Stream was closed or disconnected!",
180                 Toast.LENGTH_SHORT)
181                 .show();
182         stopAudioTest();
183     }
184 
stopAudioTest()185     public void stopAudioTest() {
186     }
187 
getApiFromText(String text)188     private int getApiFromText(String text) {
189         if (VALUE_API_AAUDIO.equals(text)) {
190             return StreamConfiguration.NATIVE_API_AAUDIO;
191         } else if (VALUE_API_OPENSLES.equals(text)) {
192             return StreamConfiguration.NATIVE_API_OPENSLES;
193         } else {
194             return StreamConfiguration.NATIVE_API_UNSPECIFIED;
195         }
196     }
197 
getPerfFromText(String text)198     private int getPerfFromText(String text) {
199         if (VALUE_PERF_NONE.equals(text)) {
200             return StreamConfiguration.PERFORMANCE_MODE_NONE;
201         } else if (VALUE_PERF_POWERSAVE.equals(text)) {
202             return StreamConfiguration.PERFORMANCE_MODE_POWER_SAVING;
203         } else {
204             return StreamConfiguration.PERFORMANCE_MODE_LOW_LATENCY;
205         }
206     }
207 
getSharingFromText(String text)208     private int getSharingFromText(String text) {
209         if (VALUE_SHARING_SHARED.equals(text)) {
210             return StreamConfiguration.SHARING_MODE_SHARED;
211         } else {
212             return StreamConfiguration.SHARING_MODE_EXCLUSIVE;
213         }
214     }
215 
configureStreamsFromBundle(Bundle bundle)216     void configureStreamsFromBundle(Bundle bundle) {
217         // Configure settings
218         StreamConfiguration requestedInConfig = mAudioInputTester.requestedConfiguration;
219         StreamConfiguration requestedOutConfig = mAudioOutTester.requestedConfiguration;
220 
221         requestedInConfig.reset();
222         requestedOutConfig.reset();
223 
224         // OpenSL ES or AAudio API
225         String text = bundle.getString(KEY_IN_API, VALUE_UNSPECIFIED);
226         int audioApi = getApiFromText(text);
227         requestedInConfig.setNativeApi(audioApi);
228         text = bundle.getString(KEY_OUT_API, VALUE_UNSPECIFIED);
229         audioApi = getApiFromText(text);
230         requestedOutConfig.setNativeApi(audioApi);
231 
232         // channnels
233         int inChannels = bundle.getInt(KEY_IN_CHANNELS, VALUE_DEFAULT_CHANNELS);
234         requestedInConfig.setChannelCount(inChannels);
235         int outChannels = bundle.getInt(KEY_OUT_CHANNELS, VALUE_DEFAULT_CHANNELS);
236         requestedOutConfig.setChannelCount(outChannels);
237 
238         // performance mode
239         text = bundle.getString(KEY_IN_PERF, VALUE_PERF_LOW_LATENCY);
240         int perfMode = getPerfFromText(text);
241         requestedInConfig.setPerformanceMode(perfMode);
242         text = bundle.getString(KEY_OUT_PERF, VALUE_PERF_LOW_LATENCY);
243         perfMode = getPerfFromText(text);
244         requestedOutConfig.setPerformanceMode(perfMode);
245 
246         int sampleRate = bundle.getInt(KEY_SAMPLE_RATE, VALUE_DEFAULT_SAMPLE_RATE);
247         requestedInConfig.setSampleRate(sampleRate);
248         requestedOutConfig.setSampleRate(sampleRate);
249 
250         text = bundle.getString(KEY_IN_SHARING, VALUE_SHARING_EXCLUSIVE);
251         int sharingMode = getSharingFromText(text);
252         requestedInConfig.setSharingMode(sharingMode);
253         text = bundle.getString(KEY_OUT_SHARING, VALUE_SHARING_EXCLUSIVE);
254         sharingMode = getSharingFromText(text);
255         requestedOutConfig.setSharingMode(sharingMode);
256     }
257 
writeTestResultIfPermitted(String resultString)258     void writeTestResultIfPermitted(String resultString) {
259         // Here, thisActivity is the current activity
260         if (ContextCompat.checkSelfPermission(this,
261                 Manifest.permission.WRITE_EXTERNAL_STORAGE)
262                 != PackageManager.PERMISSION_GRANTED) {
263             mTestResults = resultString;
264             ActivityCompat.requestPermissions(this,
265                     new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
266                     MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE);
267         } else {
268             // Permission has already been granted
269             writeTestResult(resultString);
270         }
271     }
272 
maybeWriteTestResult(String resultString)273     void maybeWriteTestResult(String resultString) {
274         if (mResultFileName == null) return;
275         writeTestResultIfPermitted(resultString);
276     }
277 
278     @Override
onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)279     public void onRequestPermissionsResult(int requestCode,
280                                            String[] permissions,
281                                            int[] grantResults) {
282         switch (requestCode) {
283             case MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {
284                 // If request is cancelled, the result arrays are empty.
285                 if (grantResults.length > 0
286                         && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
287                     writeTestResult(mTestResults);
288                 } else {
289                     showToast("Writing external storage needed for test results.");
290                 }
291                 return;
292             }
293         }
294     }
295 
writeTestInBackground(final String resultString)296     private void writeTestInBackground(final String resultString) {
297         new Thread() {
298             public void run() {
299                 writeTestResult(resultString);
300             }
301         }.start();
302     }
303 
304     // Run this in a background thread.
writeTestResult(String resultString)305     private void writeTestResult(String resultString) {
306         File resultFile = new File(mResultFileName);
307         Writer writer = null;
308         try {
309             writer = new OutputStreamWriter(new FileOutputStream(resultFile));
310             writer.write(resultString);
311         } catch (
312                 IOException e) {
313             e.printStackTrace();
314             showErrorToast(" writing result file. " + e.getMessage());
315         } finally {
316             if (writer != null) {
317                 try {
318                     writer.close();
319                 } catch (IOException e) {
320                     e.printStackTrace();
321                 }
322             }
323         }
324 
325         mResultFileName = null;
326     }
327 
328 
329 }
330