Home | History | Annotate | Download | only in location
      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 com.android.server.location;
     18 
     19 import android.Manifest;
     20 import android.content.Context;
     21 import android.content.pm.PackageManager;
     22 import android.hardware.location.ContextHubInfo;
     23 import android.hardware.location.ContextHubManager;
     24 import android.hardware.location.ContextHubMessage;
     25 import android.hardware.location.IContextHubService;
     26 import android.hardware.location.IContextHubCallback;
     27 import android.hardware.location.NanoAppFilter;
     28 import android.hardware.location.NanoApp;
     29 import android.hardware.location.NanoAppInstanceInfo;
     30 import android.os.RemoteCallbackList;
     31 import android.os.RemoteException;
     32 import android.util.Log;
     33 
     34 import com.android.internal.util.DumpUtils;
     35 
     36 import java.io.FileDescriptor;
     37 import java.io.PrintWriter;
     38 import java.nio.ByteBuffer;
     39 import java.nio.ByteOrder;
     40 import java.util.ArrayList;
     41 import java.util.concurrent.ConcurrentHashMap;
     42 import java.util.HashMap;
     43 
     44 /**
     45  * @hide
     46  */
     47 public 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