1 /* 2 * Copyright (C) 2020 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.nn.crashtest.app; 18 19 20 import static com.android.nn.crashtest.app.CrashTestStatus.TestResult.HANG; 21 22 import static java.util.concurrent.TimeUnit.MILLISECONDS; 23 24 import android.annotation.SuppressLint; 25 import android.app.Activity; 26 import android.content.Intent; 27 import android.os.Bundle; 28 import android.os.RemoteException; 29 import android.util.Log; 30 import android.view.View; 31 import android.view.WindowManager; 32 import android.widget.Button; 33 import android.widget.TextView; 34 35 import com.android.nn.benchmark.app.R; 36 import com.android.nn.benchmark.core.TestModels; 37 import com.android.nn.crashtest.core.CrashTestCoordinator; 38 import com.android.nn.crashtest.core.test.RunModelsInParallel; 39 40 import java.time.Duration; 41 42 43 public class NNParallelTestActivity extends Activity { 44 public static final int SHUTDOWN_TIMEOUT = 20000; 45 46 private static String TAG = "NNParallelTestActivity"; 47 48 public static final String EXTRA_TEST_DURATION_MILLIS = "duration"; 49 public static final String EXTRA_THREAD_COUNT = "thread_count"; 50 public static final String EXTRA_TEST_LIST = "test_list"; 51 public static final String EXTRA_RUN_IN_SEPARATE_PROCESS = "run_in_separate_process"; 52 public static final String EXTRA_TEST_NAME = "test_name"; 53 public static final String EXTRA_ACCELERATOR_NAME = "accelerator_name"; 54 public static final String EXTRA_IGNORE_UNSUPPORTED_MODELS = "ignore_unsupported_models"; 55 public static final String EXTRA_RUN_MODEL_COMPILATION_ONLY = "run_model_compilation_only"; 56 public static final String EXTRA_MEMORY_MAP_MODEL = "memory_map_model"; 57 public static final String EXTRA_USE_NNAPI_SL = "use_nnapi_sl"; 58 public static final String EXTRA_EXTRACT_NNAPI_SL = "extract_nnapi_sl"; 59 60 // Not using AtomicBoolean to have the concept of unset status 61 private CrashTestCoordinator mCoordinator; 62 private TextView mTestResultView; 63 private Button mStopTestButton; 64 private String mTestName; 65 66 private final CrashTestStatus mTestStatus = new CrashTestStatus(this::showMessage); 67 68 @SuppressLint("SetTextI18n") 69 @Override onCreate(Bundle savedInstanceState)70 protected void onCreate(Bundle savedInstanceState) { 71 super.onCreate(savedInstanceState); 72 setContentView(R.layout.interruptable_test); 73 mTestResultView = findViewById(R.id.parallel_test_result); 74 mStopTestButton = findViewById(R.id.stop_test); 75 mStopTestButton.setEnabled(false); 76 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 77 } 78 showMessage(String msg)79 protected void showMessage(String msg) { 80 runOnUiThread(() -> mTestResultView.append(msg)); 81 } 82 83 84 @Override onResume()85 protected void onResume() { 86 super.onResume(); 87 88 if (mTestStatus.isTestCompleted()) { 89 // test was completed before resuming 90 return; 91 } 92 if (mCoordinator != null) { 93 // test is already running. 94 return; 95 } 96 97 final Intent intent = getIntent(); 98 99 final int[] testList = intent.getIntArrayExtra(EXTRA_TEST_LIST); 100 101 final int threadCount = intent.getIntExtra(EXTRA_THREAD_COUNT, 10); 102 final long testDurationMillis = intent.getLongExtra(EXTRA_TEST_DURATION_MILLIS, 103 1000 * 60 * 10); 104 final boolean runInSeparateProcess = intent.getBooleanExtra(EXTRA_RUN_IN_SEPARATE_PROCESS, 105 true); 106 mTestName = intent.getStringExtra(EXTRA_TEST_NAME) != null 107 ? intent.getStringExtra(EXTRA_TEST_NAME) : "no-name"; 108 109 mCoordinator = new CrashTestCoordinator(getApplicationContext()); 110 111 String acceleratorName = intent.getStringExtra(EXTRA_ACCELERATOR_NAME); 112 boolean ignoreUnsupportedModels = intent.getBooleanExtra(EXTRA_IGNORE_UNSUPPORTED_MODELS, 113 false); 114 boolean mmapModel = intent.getBooleanExtra(EXTRA_MEMORY_MAP_MODEL, false); 115 boolean useNnapiSl = intent.getBooleanExtra(EXTRA_USE_NNAPI_SL, false); 116 boolean extractNnapiSl = intent.getBooleanExtra(EXTRA_EXTRACT_NNAPI_SL, false); 117 118 final boolean runModelCompilationOnly = intent.getBooleanExtra( 119 EXTRA_RUN_MODEL_COMPILATION_ONLY, false); 120 121 mCoordinator.startTest(RunModelsInParallel.class, 122 RunModelsInParallel.intentInitializer(testList, threadCount, 123 Duration.ofMillis(testDurationMillis), mTestName, acceleratorName, 124 ignoreUnsupportedModels, runModelCompilationOnly, mmapModel, 125 TestModels.getModelFilterRegex(), useNnapiSl, extractNnapiSl), 126 mTestStatus, runInSeparateProcess, mTestName); 127 128 mStopTestButton.setEnabled(true); 129 } 130 endTests()131 private void endTests() { 132 mCoordinator.shutdown(); 133 mCoordinator = null; 134 } 135 136 // This method blocks until the tests complete and returns true if all tests completed 137 // successfully 138 @SuppressLint("DefaultLocale") testResult()139 public CrashTestStatus.TestResult testResult() { 140 try { 141 final Intent intent = getIntent(); 142 final long testDurationMillis = intent.getLongExtra(EXTRA_TEST_DURATION_MILLIS, 143 60 * 10); 144 // Giving the test a bit of time to wrap up 145 final long testResultTimeout = testDurationMillis + SHUTDOWN_TIMEOUT; 146 boolean completed = mTestStatus.waitForCompletion(testResultTimeout, MILLISECONDS); 147 if (!completed) { 148 showMessage(String.format( 149 "Ending test '%s' since test result collection timeout of %d " 150 + "millis is expired", 151 mTestName, testResultTimeout)); 152 endTests(); 153 } 154 } catch (InterruptedException ignored) { 155 Thread.currentThread().interrupt(); 156 } 157 158 // If no result is available, assuming HANG 159 mTestStatus.compareAndSetResult(null, HANG); 160 return mTestStatus.result(); 161 } 162 onStopTestClicked(View view)163 public void onStopTestClicked(View view) { 164 showMessage("Stopping tests"); 165 endTests(); 166 } 167 168 /** 169 * Kills the process running the tests. 170 * 171 * @throws IllegalStateException if the method is called for an in-process test. 172 * @throws RemoteException if the test service is not reachable 173 */ killTestProcess()174 public void killTestProcess() throws RemoteException { 175 final Intent intent = getIntent(); 176 177 final boolean runInSeparateProcess = intent.getBooleanExtra(EXTRA_RUN_IN_SEPARATE_PROCESS, 178 true); 179 180 if (!runInSeparateProcess) { 181 throw new IllegalStateException("Cannot kill the test process in an in-process test!"); 182 } 183 184 Log.i(TAG, "Asking coordinator to kill test process"); 185 mCoordinator.killCrashTestService(); 186 } 187 } 188