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 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