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