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