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.server.location.contexthub; 18 19 import android.Manifest; 20 import android.content.Context; 21 import android.hardware.contexthub.V1_0.AsyncEventType; 22 import android.hardware.contexthub.V1_0.ContextHubMsg; 23 import android.hardware.contexthub.V1_0.HostEndPoint; 24 import android.hardware.contexthub.V1_0.Result; 25 import android.hardware.contexthub.V1_2.HubAppInfo; 26 import android.hardware.location.ContextHubInfo; 27 import android.hardware.location.ContextHubTransaction; 28 import android.hardware.location.NanoAppBinary; 29 import android.hardware.location.NanoAppMessage; 30 import android.hardware.location.NanoAppRpcService; 31 import android.hardware.location.NanoAppState; 32 import android.util.Log; 33 34 import java.time.Instant; 35 import java.time.ZoneId; 36 import java.time.format.DateTimeFormatter; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Collection; 40 import java.util.HashMap; 41 import java.util.List; 42 43 /** 44 * A class encapsulating helper functions used by the ContextHubService class 45 */ 46 /* package */ class ContextHubServiceUtil { 47 private static final String TAG = "ContextHubServiceUtil"; 48 private static final String CONTEXT_HUB_PERMISSION = Manifest.permission.ACCESS_CONTEXT_HUB; 49 50 /** 51 * A host endpoint that is reserved to identify a broadcasted message. 52 */ 53 private static final char HOST_ENDPOINT_BROADCAST = 0xFFFF; 54 55 56 /* 57 * The format for printing to logs. 58 */ 59 private static final String DATE_FORMAT = "MM/dd HH:mm:ss.SSS"; 60 61 /** 62 * The DateTimeFormatter for printing to logs. 63 */ 64 private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT) 65 .withZone(ZoneId.systemDefault()); 66 67 /** 68 * Creates a ConcurrentHashMap of the Context Hub ID to the ContextHubInfo object given an 69 * ArrayList of HIDL ContextHub objects. 70 * 71 * @param hubList the ContextHub ArrayList 72 * @return the HashMap object 73 */ 74 /* package */ createContextHubInfoMap(List<ContextHubInfo> hubList)75 static HashMap<Integer, ContextHubInfo> createContextHubInfoMap(List<ContextHubInfo> hubList) { 76 HashMap<Integer, ContextHubInfo> contextHubIdToInfoMap = new HashMap<>(); 77 for (ContextHubInfo contextHubInfo : hubList) { 78 contextHubIdToInfoMap.put(contextHubInfo.getId(), contextHubInfo); 79 } 80 81 return contextHubIdToInfoMap; 82 } 83 84 /** 85 * Copies a primitive byte array to a ArrayList<Byte>. 86 * 87 * @param inputArray the primitive byte array 88 * @param outputArray the ArrayList<Byte> array to append 89 */ 90 /* package */ copyToByteArrayList(byte[] inputArray, ArrayList<Byte> outputArray)91 static void copyToByteArrayList(byte[] inputArray, ArrayList<Byte> outputArray) { 92 outputArray.clear(); 93 outputArray.ensureCapacity(inputArray.length); 94 for (byte element : inputArray) { 95 outputArray.add(element); 96 } 97 } 98 99 /** 100 * Creates a byte array given a ArrayList<Byte> and copies its contents. 101 * 102 * @param array the ArrayList<Byte> object 103 * @return the byte array 104 */ 105 /* package */ createPrimitiveByteArray(ArrayList<Byte> array)106 static byte[] createPrimitiveByteArray(ArrayList<Byte> array) { 107 byte[] primitiveArray = new byte[array.size()]; 108 for (int i = 0; i < array.size(); i++) { 109 primitiveArray[i] = array.get(i); 110 } 111 112 return primitiveArray; 113 } 114 115 /** 116 * Creates a primitive integer array given a Collection<Integer>. 117 * 118 * @param collection the collection to iterate 119 * @return the primitive integer array 120 */ createPrimitiveIntArray(Collection<Integer> collection)121 static int[] createPrimitiveIntArray(Collection<Integer> collection) { 122 int[] primitiveArray = new int[collection.size()]; 123 124 int i = 0; 125 for (int contextHubId : collection) { 126 primitiveArray[i++] = contextHubId; 127 } 128 129 return primitiveArray; 130 } 131 132 /** 133 * Generates the Context Hub HAL's HIDL NanoAppBinary object from the client-facing 134 * android.hardware.location.NanoAppBinary object. 135 * 136 * @param nanoAppBinary the client-facing NanoAppBinary object 137 * @return the Context Hub HAL's HIDL NanoAppBinary object 138 */ 139 /* package */ createHidlNanoAppBinary( NanoAppBinary nanoAppBinary)140 static android.hardware.contexthub.V1_0.NanoAppBinary createHidlNanoAppBinary( 141 NanoAppBinary nanoAppBinary) { 142 android.hardware.contexthub.V1_0.NanoAppBinary hidlNanoAppBinary = 143 new android.hardware.contexthub.V1_0.NanoAppBinary(); 144 145 hidlNanoAppBinary.appId = nanoAppBinary.getNanoAppId(); 146 hidlNanoAppBinary.appVersion = nanoAppBinary.getNanoAppVersion(); 147 hidlNanoAppBinary.flags = nanoAppBinary.getFlags(); 148 hidlNanoAppBinary.targetChreApiMajorVersion = nanoAppBinary.getTargetChreApiMajorVersion(); 149 hidlNanoAppBinary.targetChreApiMinorVersion = nanoAppBinary.getTargetChreApiMinorVersion(); 150 151 // Log exceptions while processing the binary, but continue to pass down the binary 152 // since the error checking is deferred to the Context Hub. 153 try { 154 copyToByteArrayList(nanoAppBinary.getBinaryNoHeader(), hidlNanoAppBinary.customBinary); 155 } catch (IndexOutOfBoundsException e) { 156 Log.w(TAG, e.getMessage()); 157 } catch (NullPointerException e) { 158 Log.w(TAG, "NanoApp binary was null"); 159 } 160 161 return hidlNanoAppBinary; 162 } 163 164 /** 165 * Generates the Context Hub HAL's AIDL NanoAppBinary object from the client-facing 166 * android.hardware.location.NanoAppBinary object. 167 * 168 * @param nanoAppBinary the client-facing NanoAppBinary object 169 * @return the Context Hub HAL's AIDL NanoAppBinary object 170 */ 171 /* package */ createAidlNanoAppBinary( NanoAppBinary nanoAppBinary)172 static android.hardware.contexthub.NanoappBinary createAidlNanoAppBinary( 173 NanoAppBinary nanoAppBinary) { 174 android.hardware.contexthub.NanoappBinary aidlNanoAppBinary = 175 new android.hardware.contexthub.NanoappBinary(); 176 177 aidlNanoAppBinary.nanoappId = nanoAppBinary.getNanoAppId(); 178 aidlNanoAppBinary.nanoappVersion = nanoAppBinary.getNanoAppVersion(); 179 aidlNanoAppBinary.flags = nanoAppBinary.getFlags(); 180 aidlNanoAppBinary.targetChreApiMajorVersion = nanoAppBinary.getTargetChreApiMajorVersion(); 181 aidlNanoAppBinary.targetChreApiMinorVersion = nanoAppBinary.getTargetChreApiMinorVersion(); 182 // This explicit definition is required to avoid erroneous behavior at the binder. 183 aidlNanoAppBinary.customBinary = new byte[0]; 184 185 // Log exceptions while processing the binary, but continue to pass down the binary 186 // since the error checking is deferred to the Context Hub. 187 try { 188 aidlNanoAppBinary.customBinary = nanoAppBinary.getBinaryNoHeader(); 189 } catch (IndexOutOfBoundsException e) { 190 Log.w(TAG, e.getMessage()); 191 } catch (NullPointerException e) { 192 Log.w(TAG, "NanoApp binary was null"); 193 } 194 195 return aidlNanoAppBinary; 196 } 197 198 /** 199 * Generates a client-facing NanoAppState array from a HAL HubAppInfo array. 200 * 201 * @param nanoAppInfoList the array of HubAppInfo objects 202 * @return the corresponding array of NanoAppState objects 203 */ 204 /* package */ createNanoAppStateList( List<HubAppInfo> nanoAppInfoList)205 static List<NanoAppState> createNanoAppStateList( 206 List<HubAppInfo> nanoAppInfoList) { 207 ArrayList<NanoAppState> nanoAppStateList = new ArrayList<>(); 208 for (HubAppInfo appInfo : nanoAppInfoList) { 209 nanoAppStateList.add( 210 new NanoAppState(appInfo.info_1_0.appId, appInfo.info_1_0.version, 211 appInfo.info_1_0.enabled, appInfo.permissions)); 212 } 213 214 return nanoAppStateList; 215 } 216 217 /** 218 * Generates a client-facing NanoAppState array from a AIDL NanoappInfo array. 219 * 220 * @param nanoAppInfoList the array of NanoappInfo objects 221 * @return the corresponding array of NanoAppState objects 222 */ 223 /* package */ createNanoAppStateList( android.hardware.contexthub.NanoappInfo[] nanoAppInfoList)224 static List<NanoAppState> createNanoAppStateList( 225 android.hardware.contexthub.NanoappInfo[] nanoAppInfoList) { 226 ArrayList<NanoAppState> nanoAppStateList = new ArrayList<>(); 227 for (android.hardware.contexthub.NanoappInfo appInfo : nanoAppInfoList) { 228 ArrayList<NanoAppRpcService> rpcServiceList = new ArrayList<>(); 229 for (android.hardware.contexthub.NanoappRpcService service : appInfo.rpcServices) { 230 rpcServiceList.add(new NanoAppRpcService(service.id, service.version)); 231 } 232 nanoAppStateList.add( 233 new NanoAppState(appInfo.nanoappId, appInfo.nanoappVersion, 234 appInfo.enabled, new ArrayList<>(Arrays.asList(appInfo.permissions)), 235 rpcServiceList)); 236 } 237 238 return nanoAppStateList; 239 } 240 241 /** 242 * Creates a HIDL ContextHubMsg object to send to a nanoapp. 243 * 244 * @param hostEndPoint the ID of the client sending the message 245 * @param message the client-facing NanoAppMessage object describing the message 246 * @return the HIDL ContextHubMsg object 247 */ 248 /* package */ createHidlContextHubMessage(short hostEndPoint, NanoAppMessage message)249 static ContextHubMsg createHidlContextHubMessage(short hostEndPoint, NanoAppMessage message) { 250 ContextHubMsg hidlMessage = new ContextHubMsg(); 251 252 hidlMessage.appName = message.getNanoAppId(); 253 hidlMessage.hostEndPoint = hostEndPoint; 254 hidlMessage.msgType = message.getMessageType(); 255 copyToByteArrayList(message.getMessageBody(), hidlMessage.msg); 256 257 return hidlMessage; 258 } 259 260 /** 261 * Creates an AIDL ContextHubMessage object to send to a nanoapp. 262 * 263 * @param hostEndPoint the ID of the client sending the message 264 * @param message the client-facing NanoAppMessage object describing the message 265 * @return the AIDL ContextHubMessage object 266 */ 267 /* package */ createAidlContextHubMessage( short hostEndPoint, NanoAppMessage message)268 static android.hardware.contexthub.ContextHubMessage createAidlContextHubMessage( 269 short hostEndPoint, NanoAppMessage message) { 270 android.hardware.contexthub.ContextHubMessage aidlMessage = 271 new android.hardware.contexthub.ContextHubMessage(); 272 273 aidlMessage.nanoappId = message.getNanoAppId(); 274 aidlMessage.hostEndPoint = (char) hostEndPoint; 275 aidlMessage.messageType = message.getMessageType(); 276 aidlMessage.messageBody = message.getMessageBody(); 277 // This explicit definition is required to avoid erroneous behavior at the binder. 278 aidlMessage.permissions = new String[0]; 279 280 return aidlMessage; 281 } 282 283 /** 284 * Creates a client-facing NanoAppMessage object to send to a client. 285 * 286 * @param message the HIDL ContextHubMsg object from a nanoapp 287 * @return the NanoAppMessage object 288 */ 289 /* package */ createNanoAppMessage(ContextHubMsg message)290 static NanoAppMessage createNanoAppMessage(ContextHubMsg message) { 291 byte[] messageArray = createPrimitiveByteArray(message.msg); 292 293 return NanoAppMessage.createMessageFromNanoApp( 294 message.appName, message.msgType, messageArray, 295 message.hostEndPoint == HostEndPoint.BROADCAST); 296 } 297 298 /** 299 * Creates a client-facing NanoAppMessage object to send to a client. 300 * 301 * @param message the AIDL ContextHubMessage object from a nanoapp 302 * @return the NanoAppMessage object 303 */ 304 /* package */ createNanoAppMessage( android.hardware.contexthub.ContextHubMessage message)305 static NanoAppMessage createNanoAppMessage( 306 android.hardware.contexthub.ContextHubMessage message) { 307 return NanoAppMessage.createMessageFromNanoApp( 308 message.nanoappId, message.messageType, message.messageBody, 309 message.hostEndPoint == HOST_ENDPOINT_BROADCAST); 310 } 311 312 /** 313 * Checks for ACCESS_CONTEXT_HUB permissions. 314 * 315 * @param context the context of the service 316 */ 317 /* package */ checkPermissions(Context context)318 static void checkPermissions(Context context) { 319 context.enforceCallingOrSelfPermission(CONTEXT_HUB_PERMISSION, 320 "ACCESS_CONTEXT_HUB permission required to use Context Hub"); 321 } 322 323 /** 324 * Helper function to convert from the HAL Result enum error code to the 325 * ContextHubTransaction.Result type. 326 * 327 * @param halResult the Result enum error code 328 * @return the ContextHubTransaction.Result equivalent 329 */ 330 @ContextHubTransaction.Result 331 /* package */ toTransactionResult(int halResult)332 static int toTransactionResult(int halResult) { 333 switch (halResult) { 334 case Result.OK: 335 return ContextHubTransaction.RESULT_SUCCESS; 336 case Result.BAD_PARAMS: 337 return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS; 338 case Result.NOT_INIT: 339 return ContextHubTransaction.RESULT_FAILED_UNINITIALIZED; 340 case Result.TRANSACTION_PENDING: 341 return ContextHubTransaction.RESULT_FAILED_BUSY; 342 case Result.TRANSACTION_FAILED: 343 case Result.UNKNOWN_FAILURE: 344 default: /* fall through */ 345 return ContextHubTransaction.RESULT_FAILED_UNKNOWN; 346 } 347 } 348 349 /** 350 * Converts old list of HubAppInfo received from the HAL to V1.2 HubAppInfo objects. 351 * 352 * @param oldInfoList list of V1.0 HubAppInfo objects 353 * @return list of V1.2 HubAppInfo objects 354 */ 355 /* package */ toHubAppInfo_1_2( ArrayList<android.hardware.contexthub.V1_0.HubAppInfo> oldInfoList)356 static ArrayList<HubAppInfo> toHubAppInfo_1_2( 357 ArrayList<android.hardware.contexthub.V1_0.HubAppInfo> oldInfoList) { 358 ArrayList newAppInfo = new ArrayList<HubAppInfo>(); 359 for (android.hardware.contexthub.V1_0.HubAppInfo oldInfo : oldInfoList) { 360 HubAppInfo newInfo = new HubAppInfo(); 361 newInfo.info_1_0.appId = oldInfo.appId; 362 newInfo.info_1_0.version = oldInfo.version; 363 newInfo.info_1_0.memUsage = oldInfo.memUsage; 364 newInfo.info_1_0.enabled = oldInfo.enabled; 365 newInfo.permissions = new ArrayList<String>(); 366 newAppInfo.add(newInfo); 367 } 368 return newAppInfo; 369 } 370 371 /** 372 * Converts a HIDL AsyncEventType to the corresponding ContextHubService.CONTEXT_HUB_EVENT_*. 373 * 374 * @param hidlEventType The AsyncEventType value. 375 * @return The converted event type. 376 */ 377 /* package */ toContextHubEvent(int hidlEventType)378 static int toContextHubEvent(int hidlEventType) { 379 switch (hidlEventType) { 380 case AsyncEventType.RESTARTED: 381 return ContextHubService.CONTEXT_HUB_EVENT_RESTARTED; 382 default: 383 Log.e(TAG, "toContextHubEvent: Unknown event type: " + hidlEventType); 384 return ContextHubService.CONTEXT_HUB_EVENT_UNKNOWN; 385 } 386 } 387 388 /** 389 * Converts an AIDL AsyncEventType to the corresponding ContextHubService.CONTEXT_HUB_EVENT_*. 390 * 391 * @param aidlEventType The AsyncEventType value. 392 * @return The converted event type. 393 */ 394 /* package */ toContextHubEventFromAidl(int aidlEventType)395 static int toContextHubEventFromAidl(int aidlEventType) { 396 switch (aidlEventType) { 397 case android.hardware.contexthub.AsyncEventType.RESTARTED: 398 return ContextHubService.CONTEXT_HUB_EVENT_RESTARTED; 399 default: 400 Log.e(TAG, "toContextHubEventFromAidl: Unknown event type: " + aidlEventType); 401 return ContextHubService.CONTEXT_HUB_EVENT_UNKNOWN; 402 } 403 } 404 405 /** 406 * Converts a timestamp in milliseconds to a properly-formatted date string for log output. 407 * 408 * @param timeStampInMs the timestamp in milliseconds 409 * @return the formatted date string 410 */ 411 /* package */ formatDateFromTimestamp(long timeStampInMs)412 static String formatDateFromTimestamp(long timeStampInMs) { 413 return DATE_FORMATTER.format(Instant.ofEpochMilli(timeStampInMs)); 414 } 415 } 416