Home | History | Annotate | Download | only in location
      1 /*
      2  * Copyright (C) 2014 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.os.RemoteCallbackList;
     22 import android.os.RemoteException;
     23 import android.text.TextUtils;
     24 import android.util.Log;
     25 
     26 /**
     27  * A class that implements an {@link IActivityRecognitionHardware} backed up by the Activity
     28  * Recognition HAL.
     29  *
     30  * @hide
     31  */
     32 public class ActivityRecognitionHardware extends IActivityRecognitionHardware.Stub {
     33     private static final String TAG = "ActivityRecognitionHardware";
     34 
     35     private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
     36     private static final int INVALID_ACTIVITY_TYPE = -1;
     37     private static final int NATIVE_SUCCESS_RESULT = 0;
     38 
     39     private static ActivityRecognitionHardware sSingletonInstance = null;
     40     private static final Object sSingletonInstanceLock = new Object();
     41 
     42     private final Context mContext;
     43     private final String[] mSupportedActivities;
     44 
     45     private final RemoteCallbackList<IActivityRecognitionHardwareSink> mSinks =
     46             new RemoteCallbackList<IActivityRecognitionHardwareSink>();
     47 
     48     private static class Event {
     49         public int activity;
     50         public int type;
     51         public long timestamp;
     52     }
     53 
     54     private ActivityRecognitionHardware(Context context) {
     55         nativeInitialize();
     56 
     57         mContext = context;
     58         mSupportedActivities = fetchSupportedActivities();
     59     }
     60 
     61     public static ActivityRecognitionHardware getInstance(Context context) {
     62         synchronized (sSingletonInstanceLock) {
     63             if (sSingletonInstance == null) {
     64                 sSingletonInstance = new ActivityRecognitionHardware(context);
     65             }
     66 
     67             return sSingletonInstance;
     68         }
     69     }
     70 
     71     public static boolean isSupported() {
     72         return nativeIsSupported();
     73     }
     74 
     75     @Override
     76     public String[] getSupportedActivities() {
     77         checkPermissions();
     78         return mSupportedActivities;
     79     }
     80 
     81     @Override
     82     public boolean isActivitySupported(String activity) {
     83         checkPermissions();
     84         int activityType = getActivityType(activity);
     85         return activityType != INVALID_ACTIVITY_TYPE;
     86     }
     87 
     88     @Override
     89     public boolean registerSink(IActivityRecognitionHardwareSink sink) {
     90         checkPermissions();
     91         return mSinks.register(sink);
     92     }
     93 
     94     @Override
     95     public boolean unregisterSink(IActivityRecognitionHardwareSink sink) {
     96         checkPermissions();
     97         return mSinks.unregister(sink);
     98     }
     99 
    100     @Override
    101     public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) {
    102         checkPermissions();
    103 
    104         int activityType = getActivityType(activity);
    105         if (activityType == INVALID_ACTIVITY_TYPE) {
    106             return false;
    107         }
    108 
    109         int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs);
    110         return result == NATIVE_SUCCESS_RESULT;
    111     }
    112 
    113     @Override
    114     public boolean disableActivityEvent(String activity, int eventType) {
    115         checkPermissions();
    116 
    117         int activityType = getActivityType(activity);
    118         if (activityType == INVALID_ACTIVITY_TYPE) {
    119             return false;
    120         }
    121 
    122         int result = nativeDisableActivityEvent(activityType, eventType);
    123         return result == NATIVE_SUCCESS_RESULT;
    124     }
    125 
    126     @Override
    127     public boolean flush() {
    128         checkPermissions();
    129         int result = nativeFlush();
    130         return result == NATIVE_SUCCESS_RESULT;
    131     }
    132 
    133     /**
    134      * Called by the Activity-Recognition HAL.
    135      */
    136     private void onActivityChanged(Event[] events) {
    137         if (events == null || events.length == 0) {
    138             Log.d(TAG, "No events to broadcast for onActivityChanged.");
    139             return;
    140         }
    141 
    142         int eventsLength = events.length;
    143         ActivityRecognitionEvent activityRecognitionEventArray[] =
    144                 new ActivityRecognitionEvent[eventsLength];
    145         for (int i = 0; i < eventsLength; ++i) {
    146             Event event = events[i];
    147             String activityName = getActivityName(event.activity);
    148             activityRecognitionEventArray[i] =
    149                     new ActivityRecognitionEvent(activityName, event.type, event.timestamp);
    150         }
    151         ActivityChangedEvent activityChangedEvent =
    152                 new ActivityChangedEvent(activityRecognitionEventArray);
    153 
    154         int size = mSinks.beginBroadcast();
    155         for (int i = 0; i < size; ++i) {
    156             IActivityRecognitionHardwareSink sink = mSinks.getBroadcastItem(i);
    157             try {
    158                 sink.onActivityChanged(activityChangedEvent);
    159             } catch (RemoteException e) {
    160                 Log.e(TAG, "Error delivering activity changed event.", e);
    161             }
    162         }
    163         mSinks.finishBroadcast();
    164 
    165     }
    166 
    167     private String getActivityName(int activityType) {
    168         if (activityType < 0 || activityType >= mSupportedActivities.length) {
    169             String message = String.format(
    170                     "Invalid ActivityType: %d, SupportedActivities: %d",
    171                     activityType,
    172                     mSupportedActivities.length);
    173             Log.e(TAG, message);
    174             return null;
    175         }
    176 
    177         return mSupportedActivities[activityType];
    178     }
    179 
    180     private int getActivityType(String activity) {
    181         if (TextUtils.isEmpty(activity)) {
    182             return INVALID_ACTIVITY_TYPE;
    183         }
    184 
    185         int supportedActivitiesLength = mSupportedActivities.length;
    186         for (int i = 0; i < supportedActivitiesLength; ++i) {
    187             if (activity.equals(mSupportedActivities[i])) {
    188                 return i;
    189             }
    190         }
    191 
    192         return INVALID_ACTIVITY_TYPE;
    193     }
    194 
    195     private void checkPermissions() {
    196         String message = String.format(
    197                 "Permission '%s' not granted to access ActivityRecognitionHardware",
    198                 HARDWARE_PERMISSION);
    199         mContext.enforceCallingPermission(HARDWARE_PERMISSION, message);
    200     }
    201 
    202     private String[] fetchSupportedActivities() {
    203         String[] supportedActivities = nativeGetSupportedActivities();
    204         if (supportedActivities != null) {
    205             return supportedActivities;
    206         }
    207 
    208         return new String[0];
    209     }
    210 
    211     // native bindings
    212     static { nativeClassInit(); }
    213 
    214     private static native void nativeClassInit();
    215     private static native boolean nativeIsSupported();
    216 
    217     private native void nativeInitialize();
    218     private native void nativeRelease();
    219     private native String[] nativeGetSupportedActivities();
    220     private native int nativeEnableActivityEvent(
    221             int activityType,
    222             int eventType,
    223             long reportLatenceNs);
    224     private native int nativeDisableActivityEvent(int activityType, int eventType);
    225     private native int nativeFlush();
    226 }
    227