1 /*
2  * Copyright (C) 2021 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.android.cts.verifier.audio;
18 
19 import android.os.Bundle;
20 import android.util.Log;
21 import android.view.View;
22 import android.widget.Button;
23 import android.widget.RadioButton;
24 import android.widget.TextView;
25 
26 import com.android.cts.verifier.PassFailButtons;
27 import com.android.cts.verifier.R;
28 
29 import org.hyphonate.megaaudio.common.BuilderBase;
30 
31 public abstract class AudioColdStartBaseActivity
32         extends PassFailButtons.Activity
33         implements View.OnClickListener {
34     private static final String TAG = "AudioColdStartBaseActivity";
35 
36     // JNI load
37     static {
38         try {
39             System.loadLibrary("megaaudio_jni");
40         } catch (UnsatisfiedLinkError e) {
41             Log.e(TAG, "Error loading MegaAudio JNI library");
42             Log.e(TAG, "e: " + e);
43             e.printStackTrace();
44         }
45 
46         /* TODO: gracefully fail/notify if the library can't be loaded */
47     }
48 
49     // Test State
50     protected boolean mIsTestRunning;
51 
52     // Audio Attributes
53     protected static final int NUM_CHANNELS = 2;
54     protected int mSampleRate;
55     protected int mNumBufferFrames;
56 
57     protected int mAudioApi = BuilderBase.TYPE_OBOE;
58 
59     // (all times in nanoseconds)
60     protected long mPreOpenTime;
61     protected long mPostOpenTime;
62     protected long mPreStartTime;
63     protected long mPostStartTime;
64 
65     protected double mColdStartlatencyMS;
66 
67     // Widgets
68     Button mStartBtn;
69     Button mStopBtn;
70 
71     TextView mAttributesTxt;
72     TextView mOpenTimeTxt;
73     TextView mStartTimeTxt;
74     TextView mLatencyTxt;
75     TextView mResultsTxt;
76 
77     // Time-base conversions
nanosToMs(double nanos)78     protected double nanosToMs(double nanos) {
79         return nanos / 1000000.0;
80     }
81 
msToNanos(double ms)82     protected long msToNanos(double ms) {
83         return (long) (ms * 1000000.0);
84     }
85 
86     //
87     // UI Helpers
88     //
89     private final String msFormat = "%.2f ms";
90 
makeMSString(double ms)91     protected String makeMSString(double ms) {
92         return String.format(msFormat, ms);
93     }
94 
95     //
96     // UI
97     //
showAttributes()98     void showAttributes() {
99         mAttributesTxt.setText("" + mSampleRate + " Hz " + mNumBufferFrames + " Frames");
100     }
101 
showOpenTime()102     void showOpenTime() {
103         double timeMs = nanosToMs(mPostOpenTime - mPreOpenTime);
104         mOpenTimeTxt.setText("Open: " + makeMSString(timeMs));
105     }
106 
showStartTime()107     void showStartTime() {
108         double timeMs = nanosToMs(mPostStartTime - mPreStartTime);
109         mStartTimeTxt.setText("Start: " + makeMSString(timeMs));
110     }
111 
showColdStartLatency()112     void showColdStartLatency() {
113         mLatencyTxt.setText("Latency: " + mColdStartlatencyMS);
114 
115         if (mColdStartlatencyMS <= getRecommendedTimeMS()) {
116             mResultsTxt.setText("PASS. Meets RECOMMENDED latency of "
117                     + getRecommendedTimeMS() + "ms");
118         } else if (mColdStartlatencyMS <= getRequiredTimeMS()) {
119             mResultsTxt.setText("PASS. Meets REQUIRED latency of " + getRequiredTimeMS() + "ms");
120         } else {
121             mResultsTxt.setText("FAIL. Did not meet REQUIRED latency of " + getRequiredTimeMS()
122                     + "ms");
123         }
124     }
125 
clearResults()126     protected void clearResults() {
127         mAttributesTxt.setText("");
128         mOpenTimeTxt.setText("");
129         mStartTimeTxt.setText("");
130         mLatencyTxt.setText("");
131         mResultsTxt.setText("");
132     }
133 
134     @Override
onCreate(Bundle savedInstanceState)135     protected void onCreate(Bundle savedInstanceState) {
136         super.onCreate(savedInstanceState);
137 
138         ((RadioButton) findViewById(R.id.audioJavaApiBtn)).setOnClickListener(this);
139         RadioButton nativeApiRB = findViewById(R.id.audioNativeApiBtn);
140         nativeApiRB.setChecked(true);
141         nativeApiRB.setOnClickListener(this);
142 
143         mStartBtn = (Button) findViewById(R.id.coldstart_start_btn);
144         mStartBtn.setOnClickListener(this);
145         mStopBtn = (Button) findViewById(R.id.coldstart_stop_btn);
146         mStopBtn.setOnClickListener(this);
147         mStopBtn.setEnabled(false);
148 
149         mAttributesTxt = ((TextView) findViewById(R.id.coldstart_attributesTxt));
150         mOpenTimeTxt = ((TextView) findViewById(R.id.coldstart_openTimeTxt));
151         mStartTimeTxt = ((TextView) findViewById(R.id.coldstart_startTimeTxt));
152         mLatencyTxt = (TextView) findViewById(R.id.coldstart_coldLatencyTxt);
153         mResultsTxt = (TextView) findViewById(R.id.coldstart_coldResultsTxt);
154     }
155 
getRequiredTimeMS()156     abstract int getRequiredTimeMS();
getRecommendedTimeMS()157     abstract int getRecommendedTimeMS();
158 
startAudioTest()159     abstract boolean startAudioTest();
stopAudioTest()160     abstract void stopAudioTest();
161 
updateTestStateButtons()162     protected void updateTestStateButtons() {
163         mStartBtn.setEnabled(!mIsTestRunning);
164         mStopBtn.setEnabled(mIsTestRunning);
165     }
166 
167     //
168     // View.OnClickListener overrides
169     //
170     @Override
onClick(View v)171     public void onClick(View v) {
172         switch (v.getId()) {
173             case R.id.audioJavaApiBtn:
174                 stopAudioTest();
175                 updateTestStateButtons();
176                 clearResults();
177                 mAudioApi = BuilderBase.TYPE_JAVA;
178                 break;
179 
180             case R.id.audioNativeApiBtn:
181                 stopAudioTest();
182                 updateTestStateButtons();
183                 clearResults();
184                 mAudioApi = BuilderBase.TYPE_OBOE;
185                 break;
186 
187             case R.id.coldstart_start_btn:
188                 startAudioTest();
189 
190                 showAttributes();
191                 showOpenTime();
192                 showStartTime();
193 
194                 updateTestStateButtons();
195                 break;
196 
197             case R.id.coldstart_stop_btn:
198                 stopAudioTest();
199 
200                 updateTestStateButtons();
201                 break;
202         }
203     }
204 }
205