• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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