1 package com.google.oboe.tests.unittestrunner;
2 
3 import androidx.annotation.NonNull;
4 import androidx.appcompat.app.AppCompatActivity;
5 import androidx.core.app.ActivityCompat;
6 
7 import android.Manifest;
8 import android.content.pm.PackageManager;
9 import android.content.res.AssetManager;
10 import android.os.Build;
11 import android.os.Bundle;
12 import android.util.Log;
13 import android.view.WindowManager;
14 import android.widget.ScrollView;
15 import android.widget.TextView;
16 import android.widget.Toast;
17 
18 import java.io.BufferedReader;
19 import java.io.File;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.OutputStream;
25 
26 public class MainActivity extends AppCompatActivity {
27 
28     private final String TAG = MainActivity.class.getName();
29     private static final String TEST_BINARY_FILENAME = "testOboe.so";
30     private static final int APP_PERMISSION_REQUEST = 0;
31 
32     private TextView outputText;
33     private ScrollView scrollView;
34 
35     @Override
onCreate(Bundle savedInstanceState)36     protected void onCreate(Bundle savedInstanceState) {
37         super.onCreate(savedInstanceState);
38         setContentView(R.layout.activity_main);
39         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
40 
41         outputText = findViewById(R.id.output_view_text);
42         scrollView = findViewById(R.id.scroll_view);
43         runCommand();
44     }
45 
runCommand()46     private void runCommand(){
47         if (!isRecordPermissionGranted()){
48             requestPermissions();
49         } else {
50             Log.d(TAG, "Got RECORD_AUDIO permission");
51             Thread commandThread = new Thread(new UnitTestCommand());
52             commandThread.start();
53         }
54     }
55 
executeBinary()56     private String executeBinary() {
57         StringBuilder output = new StringBuilder();
58 
59         try {
60             String executablePath;
61             if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
62                 executablePath = getApplicationInfo().nativeLibraryDir + "/" + TEST_BINARY_FILENAME;
63             } else {
64                 executablePath = getExecutablePathFromAssets();
65             }
66 
67             Log.d(TAG, "Attempting to execute " + executablePath);
68 
69             Process process = Runtime.getRuntime().exec(executablePath);
70 
71             BufferedReader stdInput = new BufferedReader(new
72                     InputStreamReader(process.getInputStream()));
73 
74             BufferedReader stdError = new BufferedReader(new
75                     InputStreamReader(process.getErrorStream()));
76 
77             // read the output from the command
78             String s;
79             while ((s = stdInput.readLine()) != null) {
80                 Log.d(TAG, s);
81                 output.append(s).append("\n");
82             }
83 
84             // read any errors from the attempted command
85             while ((s = stdError.readLine()) != null) {
86                 Log.e(TAG, "ERROR: " + s);
87                 output.append("ERROR: ").append(s).append("\n");
88             }
89 
90             process.waitFor();
91             Log.d(TAG, "Finished executing binary");
92         } catch (IOException e){
93             Log.e(TAG, "Could not execute binary ", e);
94         } catch (InterruptedException e) {
95             Log.e(TAG, "Interrupted", e);
96         }
97 
98         return output.toString();
99     }
100 
101     // Legacy method to get asset path.
102     // This will not work on more recent Android releases.
getExecutablePathFromAssets()103     private String getExecutablePathFromAssets() {
104         AssetManager assetManager = getAssets();
105 
106         String abi = Build.SUPPORTED_ABIS[0];
107         String extraStringForDebugBuilds = "-hwasan";
108         if (abi.endsWith(extraStringForDebugBuilds)) {
109             abi = abi.substring(0, abi.length() - extraStringForDebugBuilds.length());
110         }
111         String filesDir = getFilesDir().getPath();
112         String testBinaryPath = abi + "/" + TEST_BINARY_FILENAME;
113 
114         try {
115             InputStream inStream = assetManager.open(testBinaryPath);
116             Log.d(TAG, "Opened " + testBinaryPath);
117 
118             // Copy this file to an executable location
119             File outFile = new File(filesDir, TEST_BINARY_FILENAME);
120 
121             OutputStream outStream = new FileOutputStream(outFile);
122 
123             byte[] buffer = new byte[1024];
124             int read;
125             while ((read = inStream.read(buffer)) != -1) {
126                 outStream.write(buffer, 0, read);
127             }
128             inStream.close();
129             outStream.flush();
130             outStream.close();
131             Log.d(TAG, "Copied " + testBinaryPath + " to " + filesDir);
132 
133             String executablePath = filesDir + "/" + TEST_BINARY_FILENAME;
134             Log.d(TAG, "Setting execute permission on " + executablePath);
135             boolean success = new File(executablePath).setExecutable(true, false);
136             if (!success) {
137                 Log.d(TAG, "Could not set execute permission on " + executablePath);
138             }
139             return executablePath;
140         } catch (IOException e) {
141             e.printStackTrace();
142         }
143         return "";
144     }
145 
isRecordPermissionGranted()146     private boolean isRecordPermissionGranted() {
147         return (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) ==
148                 PackageManager.PERMISSION_GRANTED);
149     }
150 
requestPermissions()151     private void requestPermissions(){
152         ActivityCompat.requestPermissions(
153                 this,
154                 new String[]{Manifest.permission.RECORD_AUDIO},
155                 APP_PERMISSION_REQUEST);
156     }
157 
158     @Override
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)159     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
160                                            @NonNull int[] grantResults) {
161 
162         if (APP_PERMISSION_REQUEST != requestCode) {
163             super.onRequestPermissionsResult(requestCode, permissions, grantResults);
164             return;
165         }
166 
167         if (grantResults.length != 1 ||
168                 grantResults[0] != PackageManager.PERMISSION_GRANTED) {
169 
170             // User denied the permission, without this we cannot record audio
171             // Show a toast and update the status accordingly
172             outputText.setText(R.string.status_record_audio_denied);
173             Toast.makeText(getApplicationContext(),
174                     getString(R.string.need_record_audio_permission),
175                     Toast.LENGTH_SHORT)
176                     .show();
177         } else {
178             // Permission was granted, run the command
179             outputText.setText(R.string.status_record_audio_granted);
180             runCommand();
181         }
182     }
183 
184     class UnitTestCommand implements Runnable {
185 
186         @Override
run()187         public void run() {
188             final String output = executeBinary();
189 
190             runOnUiThread(() -> {
191                 outputText.setText(output);
192 
193                 // Scroll to the bottom so we can see the test result
194                 scrollView.postDelayed(() -> scrollView.scrollTo(0, outputText.getBottom()), 100);
195             });
196         }
197     }
198 }
199