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