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