1/* 2 * Copyright (C) 2016 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 17package android.hardware.location; 18 19import java.io.FileDescriptor; 20import java.io.PrintWriter; 21import java.nio.ByteBuffer; 22import java.nio.ByteOrder; 23import java.util.ArrayList; 24import java.util.HashMap; 25 26import android.Manifest; 27import android.content.Context; 28import android.content.pm.PackageManager; 29import android.os.RemoteCallbackList; 30import android.os.RemoteException; 31import android.os.ServiceManager; 32import android.service.vr.IVrManager; 33import android.service.vr.IVrStateCallbacks; 34import android.util.Log; 35 36import java.io.FileDescriptor; 37import java.io.PrintWriter; 38import java.util.ArrayList; 39import java.util.concurrent.ConcurrentHashMap; 40 41/** 42 * @hide 43 */ 44public class ContextHubService extends IContextHubService.Stub { 45 public static final String CONTEXTHUB_SERVICE = "contexthub_service"; 46 47 private static final String TAG = "ContextHubService"; 48 private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE; 49 private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '" 50 + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware"; 51 52 53 public static final int ANY_HUB = -1; 54 public static final int MSG_LOAD_NANO_APP = 3; 55 public static final int MSG_UNLOAD_NANO_APP = 4; 56 57 private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown"; 58 private static final String PRE_LOADED_APP_NAME = PRE_LOADED_GENERIC_UNKNOWN; 59 private static final String PRE_LOADED_APP_PUBLISHER = PRE_LOADED_GENERIC_UNKNOWN; 60 private static final int PRE_LOADED_APP_MEM_REQ = 0; 61 62 private static final int MSG_HEADER_SIZE = 4; 63 private static final int HEADER_FIELD_MSG_TYPE = 0; 64 private static final int HEADER_FIELD_MSG_VERSION = 1; 65 private static final int HEADER_FIELD_HUB_HANDLE = 2; 66 private static final int HEADER_FIELD_APP_INSTANCE = 3; 67 68 private static final int HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE; 69 private static final int HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1; 70 private static final int MSG_LOAD_APP_HEADER_SIZE = MSG_HEADER_SIZE + 2; 71 72 private static final int OS_APP_INSTANCE = -1; 73 74 private static final long APP_ID_ACTIVITY_RECOGNITION = 0x476f6f676c001000L; 75 76 private final Context mContext; 77 private final ConcurrentHashMap<Integer, NanoAppInstanceInfo> mNanoAppHash = 78 new ConcurrentHashMap<>(); 79 private final ContextHubInfo[] mContextHubInfo; 80 private final RemoteCallbackList<IContextHubCallback> mCallbacksList = 81 new RemoteCallbackList<>(); 82 83 private native int nativeSendMessage(int[] header, byte[] data); 84 private native ContextHubInfo[] nativeInitialize(); 85 86 private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { 87 @Override 88 public void onVrStateChanged(boolean enabled) { 89 for (NanoAppInstanceInfo app : mNanoAppHash.values()) { 90 if (app.getAppId() == APP_ID_ACTIVITY_RECOGNITION) { 91 sendVrStateChangeMessageToApp(app, enabled); 92 break; 93 } 94 } 95 } 96 }; 97 98 public ContextHubService(Context context) { 99 mContext = context; 100 mContextHubInfo = nativeInitialize(); 101 102 for (int i = 0; i < mContextHubInfo.length; i++) { 103 Log.d(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId() 104 + ", name: " + mContextHubInfo[i].getName()); 105 } 106 107 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { 108 IVrManager vrManager = 109 IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager")); 110 if (vrManager != null) { 111 try { 112 vrManager.registerListener(mVrStateCallbacks); 113 } catch (RemoteException e) { 114 Log.e(TAG, "VR state listener registration failed", e); 115 } 116 } 117 } 118 } 119 120 @Override 121 public int registerCallback(IContextHubCallback callback) throws RemoteException { 122 checkPermissions(); 123 mCallbacksList.register(callback); 124 return 0; 125 } 126 127 @Override 128 public int[] getContextHubHandles() throws RemoteException { 129 checkPermissions(); 130 int[] returnArray = new int[mContextHubInfo.length]; 131 132 for (int i = 0; i < returnArray.length; ++i) { 133 returnArray[i] = i; 134 Log.d(TAG, String.format("Hub %s is mapped to %d", 135 mContextHubInfo[i].getName(), returnArray[i])); 136 } 137 138 return returnArray; 139 } 140 141 @Override 142 public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException { 143 checkPermissions(); 144 if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) { 145 return null; // null means fail 146 } 147 148 return mContextHubInfo[contextHubHandle]; 149 } 150 151 // TODO(b/30808791): Remove this when NanoApp's API is correctly treating 152 // app IDs as 64-bits. 153 private static long parseAppId(NanoApp app) { 154 // NOTE: If this shifting seems odd (since it's actually "ONAN"), note 155 // that it matches how this is defined in context_hub.h. 156 final int HEADER_MAGIC = 157 (((int)'N' << 0) | 158 ((int)'A' << 8) | 159 ((int)'N' << 16) | 160 ((int)'O' << 24)); 161 final int HEADER_MAGIC_OFFSET = 4; 162 final int HEADER_APP_ID_OFFSET = 8; 163 164 ByteBuffer header = ByteBuffer.wrap(app.getAppBinary()) 165 .order(ByteOrder.LITTLE_ENDIAN); 166 167 try { 168 if (header.getInt(HEADER_MAGIC_OFFSET) == HEADER_MAGIC) { 169 // This is a legitimate nanoapp header. Let's grab the app ID. 170 return header.getLong(HEADER_APP_ID_OFFSET); 171 } 172 } catch (IndexOutOfBoundsException e) { 173 // The header is undersized. We'll fall through to our code 174 // path below, which handles being unable to parse the header. 175 } 176 // We failed to parse the header. Even through it's probably wrong, 177 // let's give NanoApp's idea of our ID. This is at least consistent. 178 return app.getAppId(); 179 } 180 181 @Override 182 public int loadNanoApp(int contextHubHandle, NanoApp app) throws RemoteException { 183 checkPermissions(); 184 185 if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) { 186 Log.e(TAG, "Invalid contextHubhandle " + contextHubHandle); 187 return -1; 188 } 189 190 int[] msgHeader = new int[MSG_LOAD_APP_HEADER_SIZE]; 191 msgHeader[HEADER_FIELD_HUB_HANDLE] = contextHubHandle; 192 msgHeader[HEADER_FIELD_APP_INSTANCE] = OS_APP_INSTANCE; 193 msgHeader[HEADER_FIELD_MSG_VERSION] = 0; 194 msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_LOAD_NANO_APP; 195 196 long appId = app.getAppId(); 197 // TODO(b/30808791): Remove this hack when the NanoApp API is fixed, 198 // and getAppId() returns a 'long' instead of an 'int'. 199 if ((appId >> 32) != 0) { 200 // We're unlikely to notice this warning, but at least 201 // we can avoid running our hack logic. 202 Log.w(TAG, "Code has not been updated since API fix."); 203 } else { 204 appId = parseAppId(app); 205 } 206 207 msgHeader[HEADER_FIELD_LOAD_APP_ID_LO] = (int)(appId & 0xFFFFFFFF); 208 msgHeader[HEADER_FIELD_LOAD_APP_ID_HI] = (int)((appId >> 32) & 0xFFFFFFFF); 209 210 int errVal = nativeSendMessage(msgHeader, app.getAppBinary()); 211 if (errVal != 0) { 212 Log.e(TAG, "Send Message returns error" + contextHubHandle); 213 return -1; 214 } 215 216 // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app 217 return 0; 218 } 219 220 @Override 221 public int unloadNanoApp(int nanoAppInstanceHandle) throws RemoteException { 222 checkPermissions(); 223 NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstanceHandle); 224 if (info == null) { 225 return -1; //means failed 226 } 227 228 // Call Native interface here 229 int[] msgHeader = new int[MSG_HEADER_SIZE]; 230 msgHeader[HEADER_FIELD_HUB_HANDLE] = ANY_HUB; 231 msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppInstanceHandle; 232 msgHeader[HEADER_FIELD_MSG_VERSION] = 0; 233 msgHeader[HEADER_FIELD_MSG_TYPE] = MSG_UNLOAD_NANO_APP; 234 235 byte msg[] = new byte[0]; 236 237 if (nativeSendMessage(msgHeader, msg) != 0) { 238 return -1; 239 } 240 241 // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app 242 return 0; 243 } 244 245 @Override 246 public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle) 247 throws RemoteException { 248 checkPermissions(); 249 // This assumes that all the nanoAppInfo is current. This is reasonable 250 // for the use cases for tightly controlled nanoApps. 251 if (mNanoAppHash.containsKey(nanoAppInstanceHandle)) { 252 return mNanoAppHash.get(nanoAppInstanceHandle); 253 } else { 254 return null; 255 } 256 } 257 258 @Override 259 public int[] findNanoAppOnHub(int hubHandle, NanoAppFilter filter) throws RemoteException { 260 checkPermissions(); 261 ArrayList<Integer> foundInstances = new ArrayList<Integer>(); 262 263 for (Integer nanoAppInstance: mNanoAppHash.keySet()) { 264 NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance); 265 266 if (filter.testMatch(info)) { 267 foundInstances.add(nanoAppInstance); 268 } 269 } 270 271 int[] retArray = new int[foundInstances.size()]; 272 for (int i = 0; i < foundInstances.size(); i++) { 273 retArray[i] = foundInstances.get(i).intValue(); 274 } 275 276 return retArray; 277 } 278 279 @Override 280 public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage msg) 281 throws RemoteException { 282 checkPermissions(); 283 284 int[] msgHeader = new int[MSG_HEADER_SIZE]; 285 msgHeader[HEADER_FIELD_HUB_HANDLE] = hubHandle; 286 msgHeader[HEADER_FIELD_APP_INSTANCE] = nanoAppHandle; 287 msgHeader[HEADER_FIELD_MSG_VERSION] = msg.getVersion(); 288 msgHeader[HEADER_FIELD_MSG_TYPE] = msg.getMsgType(); 289 290 return nativeSendMessage(msgHeader, msg.getData()); 291 } 292 293 @Override 294 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 295 if (mContext.checkCallingOrSelfPermission("android.permission.DUMP") 296 != PackageManager.PERMISSION_GRANTED) { 297 pw.println("Permission Denial: can't dump contexthub_service"); 298 return; 299 } 300 301 pw.println("Dumping ContextHub Service"); 302 303 pw.println(""); 304 // dump ContextHubInfo 305 pw.println("=================== CONTEXT HUBS ===================="); 306 for (int i = 0; i < mContextHubInfo.length; i++) { 307 pw.println("Handle " + i + " : " + mContextHubInfo[i].toString()); 308 } 309 pw.println(""); 310 pw.println("=================== NANOAPPS ===================="); 311 // Dump nanoAppHash 312 for (Integer nanoAppInstance: mNanoAppHash.keySet()) { 313 pw.println(nanoAppInstance + " : " + mNanoAppHash.get(nanoAppInstance).toString()); 314 } 315 316 // dump eventLog 317 } 318 319 private void checkPermissions() { 320 mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE); 321 } 322 323 private int onMessageReceipt(int[] header, byte[] data) { 324 if (header == null || data == null || header.length < MSG_HEADER_SIZE) { 325 return -1; 326 } 327 int callbacksCount = mCallbacksList.beginBroadcast(); 328 if (callbacksCount < 1) { 329 Log.v(TAG, "No message callbacks registered."); 330 return 0; 331 } 332 333 ContextHubMessage msg = new ContextHubMessage(header[HEADER_FIELD_MSG_TYPE], 334 header[HEADER_FIELD_MSG_VERSION], 335 data); 336 for (int i = 0; i < callbacksCount; ++i) { 337 IContextHubCallback callback = mCallbacksList.getBroadcastItem(i); 338 try { 339 callback.onMessageReceipt( 340 header[HEADER_FIELD_HUB_HANDLE], 341 header[HEADER_FIELD_APP_INSTANCE], 342 msg); 343 } catch (RemoteException e) { 344 Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ")."); 345 continue; 346 } 347 } 348 mCallbacksList.finishBroadcast(); 349 return 0; 350 } 351 352 private int addAppInstance(int hubHandle, int appInstanceHandle, long appId, int appVersion) { 353 // App Id encodes vendor & version 354 NanoAppInstanceInfo appInfo = new NanoAppInstanceInfo(); 355 356 appInfo.setAppId(appId); 357 appInfo.setAppVersion(appVersion); 358 appInfo.setName(PRE_LOADED_APP_NAME); 359 appInfo.setContexthubId(hubHandle); 360 appInfo.setHandle(appInstanceHandle); 361 appInfo.setPublisher(PRE_LOADED_APP_PUBLISHER); 362 appInfo.setNeededExecMemBytes(PRE_LOADED_APP_MEM_REQ); 363 appInfo.setNeededReadMemBytes(PRE_LOADED_APP_MEM_REQ); 364 appInfo.setNeededWriteMemBytes(PRE_LOADED_APP_MEM_REQ); 365 366 String action; 367 if (mNanoAppHash.containsKey(appInstanceHandle)) { 368 action = "Updated"; 369 } else { 370 action = "Added"; 371 } 372 373 mNanoAppHash.put(appInstanceHandle, appInfo); 374 Log.d(TAG, action + " app instance " + appInstanceHandle + " with id " 375 + appId + " version " + appVersion); 376 377 return 0; 378 } 379 380 private int deleteAppInstance(int appInstanceHandle) { 381 if (mNanoAppHash.remove(appInstanceHandle) == null) { 382 return -1; 383 } 384 385 return 0; 386 } 387 388 private void sendVrStateChangeMessageToApp(NanoAppInstanceInfo app, boolean vrModeEnabled) { 389 int[] msgHeader = new int[MSG_HEADER_SIZE]; 390 msgHeader[HEADER_FIELD_MSG_TYPE] = 0; 391 msgHeader[HEADER_FIELD_MSG_VERSION] = 0; 392 msgHeader[HEADER_FIELD_HUB_HANDLE] = ANY_HUB; 393 msgHeader[HEADER_FIELD_APP_INSTANCE] = app.getHandle(); 394 395 byte[] data = new byte[1]; 396 data[0] = (byte) ((vrModeEnabled) ? 1 : 0); 397 int ret = nativeSendMessage(msgHeader, data); 398 if (ret != 0) { 399 Log.e(TAG, "Couldn't send VR state change notification (" + ret + ")!"); 400 } 401 } 402} 403