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.google.android.utils.chre; 18 19 import android.app.Instrumentation; 20 import android.content.Context; 21 import android.hardware.location.ContextHubInfo; 22 import android.hardware.location.ContextHubManager; 23 import android.hardware.location.ContextHubTransaction; 24 import android.hardware.location.NanoAppBinary; 25 import android.hardware.location.NanoAppState; 26 import android.os.ParcelFileDescriptor; 27 28 import androidx.test.InstrumentationRegistry; 29 30 import org.junit.Assert; 31 32 import java.io.BufferedReader; 33 import java.io.FileInputStream; 34 import java.io.IOException; 35 import java.io.InputStream; 36 import java.io.InputStreamReader; 37 import java.nio.charset.StandardCharsets; 38 import java.util.List; 39 import java.util.concurrent.TimeUnit; 40 import java.util.concurrent.TimeoutException; 41 42 /** 43 * A set of helper functions for PTS CHRE tests. 44 */ 45 public class ChreTestUtil { 46 // Various timeouts for Context Hub operations. 47 private static final long TIMEOUT_LOAD_NANOAPP_SECONDS = 5; 48 private static final long TIMEOUT_UNLOAD_NANOAPP_SECONDS = 5; 49 private static final long QUERY_NANOAPPS_TIMEOUT_SECONDS = 5; 50 51 /** 52 * Read the nanoapp to an InputStream object. 53 * 54 * @param context the Context to find the asset resources 55 * @param fileName the fileName of the nanoapp 56 * @return the InputStream of the nanoapp 57 */ getNanoAppInputStream(Context context, String fileName)58 public static InputStream getNanoAppInputStream(Context context, String fileName) { 59 InputStream inputStream = null; 60 try { 61 inputStream = context.getAssets().open(fileName); 62 } catch (IOException e) { 63 Assert.fail("Could not find asset " + fileName + ": " + e.toString()); 64 } 65 return inputStream; 66 } 67 68 /** 69 * Creates a NanoAppBinary object from the nanoapp fileName. 70 * 71 * @param fileName the fileName of the nanoapp 72 * @return the NanoAppBinary object 73 */ createNanoAppBinary(String fileName)74 public static NanoAppBinary createNanoAppBinary(String fileName) { 75 Context context = InstrumentationRegistry.getTargetContext(); 76 77 InputStream stream = getNanoAppInputStream(context, fileName); 78 byte[] binary = null; 79 try { 80 binary = new byte[stream.available()]; 81 stream.read(binary); 82 } catch (IOException e) { 83 Assert.fail("IOException while reading binary for " + fileName + ": " + e.getMessage()); 84 } 85 86 return new NanoAppBinary(binary); 87 } 88 89 /** 90 * Loads a nanoapp. 91 * 92 * @param manager The ContextHubManager to use to load the nanoapp. 93 * @param info The ContextHubInfo describing the Context Hub to load the nanoapp to. 94 * @param nanoAppBinary The nanoapp binary to load. 95 * @return true if the load succeeded. 96 */ loadNanoApp( ContextHubManager manager, ContextHubInfo info, NanoAppBinary nanoAppBinary)97 public static boolean loadNanoApp( 98 ContextHubManager manager, ContextHubInfo info, NanoAppBinary nanoAppBinary) { 99 ContextHubTransaction<Void> txn = manager.loadNanoApp(info, nanoAppBinary); 100 ContextHubTransaction.Response<Void> resp = null; 101 try { 102 resp = txn.waitForResponse(TIMEOUT_LOAD_NANOAPP_SECONDS, TimeUnit.SECONDS); 103 } catch (TimeoutException | InterruptedException e) { 104 Assert.fail(e.getMessage()); 105 } 106 107 return resp != null && resp.getResult() == ContextHubTransaction.RESULT_SUCCESS; 108 } 109 110 /** 111 * Same as loadNanoApp(), but asserts that it succeeds. 112 */ loadNanoAppAssertSuccess( ContextHubManager manager, ContextHubInfo info, NanoAppBinary nanoAppBinary)113 public static void loadNanoAppAssertSuccess( 114 ContextHubManager manager, ContextHubInfo info, NanoAppBinary nanoAppBinary) { 115 if (!loadNanoApp(manager, info, nanoAppBinary)) { 116 Assert.fail("Failed to load nanoapp"); 117 } 118 } 119 120 /** 121 * Unloads a nanoapp. 122 * 123 * @param manager The ContextHubManager to use to unload the nanoapp. 124 * @param info The ContextHubInfo describing the Context Hub to unload the nanoapp from. 125 * @param nanoAppId The 64-bit ID of the nanoapp to unload. 126 * @return true if the unload succeeded. 127 */ unloadNanoApp( ContextHubManager manager, ContextHubInfo info, long nanoAppId)128 public static boolean unloadNanoApp( 129 ContextHubManager manager, ContextHubInfo info, long nanoAppId) { 130 ContextHubTransaction<Void> txn = manager.unloadNanoApp(info, nanoAppId); 131 ContextHubTransaction.Response<Void> resp = null; 132 try { 133 resp = txn.waitForResponse(TIMEOUT_UNLOAD_NANOAPP_SECONDS, TimeUnit.SECONDS); 134 } catch (TimeoutException | InterruptedException e) { 135 Assert.fail(e.getMessage()); 136 } 137 138 return resp != null && resp.getResult() == ContextHubTransaction.RESULT_SUCCESS; 139 } 140 /** 141 * Same as unloadNanoApp(), but asserts that it succeeds. 142 */ unloadNanoAppAssertSuccess( ContextHubManager manager, ContextHubInfo info, long nanoAppId)143 public static void unloadNanoAppAssertSuccess( 144 ContextHubManager manager, ContextHubInfo info, long nanoAppId) { 145 if (!unloadNanoApp(manager, info, nanoAppId)) { 146 Assert.fail("Failed to unload nanoapp"); 147 } 148 } 149 150 /** 151 * Executes a given shell command. 152 * 153 * @param instrumentation The instrumentation to use. 154 * @param command The shell command to execute. 155 * @return The string output. 156 */ executeShellCommand(Instrumentation instrumentation, String command)157 public static String executeShellCommand(Instrumentation instrumentation, String command) { 158 final ParcelFileDescriptor pfd = instrumentation.getUiAutomation() 159 .executeShellCommand(command); 160 try (InputStream in = new FileInputStream(pfd.getFileDescriptor())) { 161 return readFromInputStream(in); 162 } catch (Exception e) { 163 Assert.fail(e.getMessage()); 164 } finally { 165 closeOrAssert(pfd); 166 } 167 return null; 168 } 169 170 /** 171 * Executes a given shell command using the app context rather than the shells so that the app's 172 * permissions are used. 173 * 174 * @param command The shell command to execute. 175 * @return The string output. 176 */ executeShellCommandWithAppPerms(String command)177 public static String executeShellCommandWithAppPerms(String command) throws Exception { 178 final Process process = Runtime.getRuntime().exec(command); 179 process.waitFor(); 180 return readFromInputStream(process.getInputStream()); 181 } 182 183 /** 184 * @param input The string input of an integer. 185 * @return The converted integer. 186 */ convertToIntegerOrFail(String input)187 public static int convertToIntegerOrFail(String input) { 188 try { 189 return Integer.parseInt(input); 190 } catch (NumberFormatException e) { 191 Assert.fail(e.getMessage()); 192 } 193 194 return -1; 195 } 196 197 /** 198 * Get all the nanoapps currently loaded on device. 199 * 200 * @return The nanoapps loaded currently. 201 */ queryNanoAppsAssertSuccess( ContextHubManager contextHubManager, ContextHubInfo contextHubInfo)202 public static List<NanoAppState> queryNanoAppsAssertSuccess( 203 ContextHubManager contextHubManager, ContextHubInfo contextHubInfo) { 204 ContextHubTransaction<List<NanoAppState>> transaction = 205 contextHubManager.queryNanoApps(contextHubInfo); 206 assertTransactionSuccessSync(transaction, QUERY_NANOAPPS_TIMEOUT_SECONDS); 207 ContextHubTransaction.Response<List<NanoAppState>> response = null; 208 try { 209 response = transaction.waitForResponse(QUERY_NANOAPPS_TIMEOUT_SECONDS, 210 TimeUnit.SECONDS); 211 } catch (InterruptedException e) { 212 Assert.fail("InterruptedException while getting query response"); 213 } catch (TimeoutException e) { 214 Assert.fail("TimeoutException while getting query response"); 215 } 216 return response.getContents(); 217 } 218 219 /** 220 * Queries for the nanoapp version. 221 * 222 * @param contextHubManager The ContextHubManager to use. 223 * @param contextHubInfo The ContextHubInfo describing the Context Hub to query. 224 * @param nanoAppId The ID of the nanoapp to get the version for. 225 * @return The nanoapp version. 226 */ getNanoAppVersion(ContextHubManager contextHubManager, ContextHubInfo contextHubInfo, long nanoAppId)227 public static int getNanoAppVersion(ContextHubManager contextHubManager, 228 ContextHubInfo contextHubInfo, long nanoAppId) { 229 List<NanoAppState> stateList = queryNanoAppsAssertSuccess(contextHubManager, 230 contextHubInfo); 231 for (NanoAppState state : stateList) { 232 if (state.getNanoAppId() == nanoAppId) { 233 return (int) state.getNanoAppVersion(); 234 } 235 } 236 237 Assert.fail("Could not query for nanoapp with ID 0x" + Long.toHexString(nanoAppId)); 238 return -1; 239 } 240 241 /** 242 * @param closeable The object to close. 243 */ closeOrAssert(AutoCloseable closeable)244 private static void closeOrAssert(AutoCloseable closeable) { 245 try { 246 closeable.close(); 247 } catch (Exception e) { 248 Assert.fail(e.getMessage()); 249 } 250 } 251 readFromInputStream(InputStream in)252 private static String readFromInputStream(InputStream in) throws Exception { 253 StringBuilder out = new StringBuilder(); 254 BufferedReader br = new BufferedReader(new InputStreamReader(in, 255 StandardCharsets.UTF_8)); 256 String str = null; 257 while ((str = br.readLine()) != null) { 258 out.append(str); 259 } 260 261 closeOrAssert(br); 262 return out.toString(); 263 } 264 265 /** 266 * Assert that the context hub transaction gets a successful response. 267 * 268 * @param transaction The context hub transaction 269 * @param timeoutInSeconds The timeout while waiting for the transaction response in seconds 270 */ assertTransactionSuccessSync( ContextHubTransaction<?> transaction, long timeoutInSeconds)271 private static void assertTransactionSuccessSync( 272 ContextHubTransaction<?> transaction, long timeoutInSeconds) throws AssertionError { 273 if (transaction == null) { 274 Assert.fail("ContextHubTransaction cannot be null"); 275 } 276 277 String type = ContextHubTransaction.typeToString(transaction.getType(), 278 true /* upperCase */); 279 ContextHubTransaction.Response<?> response = null; 280 try { 281 response = transaction.waitForResponse(timeoutInSeconds, TimeUnit.SECONDS); 282 } catch (InterruptedException e) { 283 Assert.fail("InterruptedException while waiting for " + type + " transaction"); 284 } catch (TimeoutException e) { 285 Assert.fail("TimeoutException while waiting for " + type + " transaction"); 286 } 287 288 Assert.assertTrue(type + " transaction failed with error code " + response.getResult(), 289 response.getResult() == ContextHubTransaction.RESULT_SUCCESS); 290 } 291 } 292