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 = "ActivityRecognitionHW";
     34     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     35 
     36     private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
     37     private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
     38             + HARDWARE_PERMISSION + "' not granted to access ActivityRecognitionHardware";
     39 
     40     private static final int INVALID_ACTIVITY_TYPE = -1;
     41     private static final int NATIVE_SUCCESS_RESULT = 0;
     42     private static final int EVENT_TYPE_DISABLED = 0;
     43     private static final int EVENT_TYPE_ENABLED = 1;
     44 
     45     /**
     46      * Contains the number of supported Event Types.
     47      *
     48      * NOTE: increment this counter every time a new EVENT_TYPE_ is added to
     49      *       com.android.location.provider.ActivityRecognitionProvider
     50      */
     51     private static final int EVENT_TYPE_COUNT = 3;
     52 
     53     private static ActivityRecognitionHardware sSingletonInstance;
     54     private static final Object sSingletonInstanceLock = new Object();
     55 
     56     private final Context mContext;
     57     private final int mSupportedActivitiesCount;
     58     private final String[] mSupportedActivities;
     59     private final int[][] mSupportedActivitiesEnabledEvents;
     60     private final SinkList mSinks = new SinkList();
     61 
     62     private static class Event {
     63         public int activity;
     64         public int type;
     65         public long timestamp;
     66     }
     67 
     68     private ActivityRecognitionHardware(Context context) {
     69         nativeInitialize();
     70 
     71         mContext = context;
     72         mSupportedActivities = fetchSupportedActivities();
     73         mSupportedActivitiesCount = mSupportedActivities.length;
     74         mSupportedActivitiesEnabledEvents = new int[mSupportedActivitiesCount][EVENT_TYPE_COUNT];
     75     }
     76 
     77     public static ActivityRecognitionHardware getInstance(Context context) {
     78         synchronized (sSingletonInstanceLock) {
     79             if (sSingletonInstance == null) {
     80                 sSingletonInstance = new ActivityRecognitionHardware(context);
     81             }
     82 
     83             return sSingletonInstance;
     84         }
     85     }
     86 
     87     public static boolean isSupported() {
     88         return nativeIsSupported();
     89     }
     90 
     91     @Override
     92     public String[] getSupportedActivities() {
     93         checkPermissions();
     94         return mSupportedActivities;
     95     }
     96 
     97     @Override
     98     public boolean isActivitySupported(String activity) {
     99         checkPermissions();
    100         int activityType = getActivityType(activity);
    101         return activityType != INVALID_ACTIVITY_TYPE;
    102     }
    103 
    104     @Override
    105     public boolean registerSink(IActivityRecognitionHardwareSink sink) {
    106         checkPermissions();
    107         return mSinks.register(sink);
    108     }
    109 
    110     @Override
    111     public boolean unregisterSink(IActivityRecognitionHardwareSink sink) {
    112         checkPermissions();
    113         return mSinks.unregister(sink);
    114     }
    115 
    116     @Override
    117     public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) {
    118         checkPermissions();
    119 
    120         int activityType = getActivityType(activity);
    121         if (activityType == INVALID_ACTIVITY_TYPE) {
    122             return false;
    123         }
    124 
    125         int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs);
    126         if (result == NATIVE_SUCCESS_RESULT) {
    127             mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_ENABLED;
    128             return true;
    129         }
    130         return false;
    131     }
    132 
    133     @Override
    134     public boolean disableActivityEvent(String activity, int eventType) {
    135         checkPermissions();
    136 
    137         int activityType = getActivityType(activity);
    138         if (activityType == INVALID_ACTIVITY_TYPE) {
    139             return false;
    140         }
    141 
    142         int result = nativeDisableActivityEvent(activityType, eventType);
    143         if (result == NATIVE_SUCCESS_RESULT) {
    144             mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
    145             return true;
    146         }
    147         return false;
    148     }
    149 
    150     @Override
    151     public boolean flush() {
    152         checkPermissions();
    153         int result = nativeFlush();
    154         return result == NATIVE_SUCCESS_RESULT;
    155     }
    156 
    157     /**
    158      * Called by the Activity-Recognition HAL.
    159      */
    160     private void onActivityChanged(Event[] events) {
    161         if (events == null || events.length == 0) {
    162             if (DEBUG) Log.d(TAG, "No events to broadcast for onActivityChanged.");
    163             return;
    164         }
    165 
    166         int eventsLength = events.length;
    167         ActivityRecognitionEvent activityRecognitionEventArray[] =
    168                 new ActivityRecognitionEvent[eventsLength];
    169         for (int i = 0; i < eventsLength; ++i) {
    170             Event event = events[i];
    171             String activityName = getActivityName(event.activity);
    172             activityRecognitionEventArray[i] =
    173                     new ActivityRecognitionEvent(activityName, event.type, event.timestamp);
    174         }
    175         ActivityChangedEvent activityChangedEvent =
    176                 new ActivityChangedEvent(activityRecognitionEventArray);
    177 
    178         int size = mSinks.beginBroadcast();
    179         for (int i = 0; i < size; ++i) {
    180             IActivityRecognitionHardwareSink sink = mSinks.getBroadcastItem(i);
    181             try {
    182                 sink.onActivityChanged(activityChangedEvent);
    183             } catch (RemoteException e) {
    184                 Log.e(TAG, "Error delivering activity changed event.", e);
    185             }
    186         }
    187         mSinks.finishBroadcast();
    188     }
    189 
    190     private String getActivityName(int activityType) {
    191         if (activityType < 0 || activityType >= mSupportedActivities.length) {
    192             String message = String.format(
    193                     "Invalid ActivityType: %d, SupportedActivities: %d",
    194                     activityType,
    195                     mSupportedActivities.length);
    196             Log.e(TAG, message);
    197             return null;
    198         }
    199 
    200         return mSupportedActivities[activityType];
    201     }
    202 
    203     private int getActivityType(String activity) {
    204         if (TextUtils.isEmpty(activity)) {
    205             return INVALID_ACTIVITY_TYPE;
    206         }
    207 
    208         int supportedActivitiesLength = mSupportedActivities.length;
    209         for (int i = 0; i < supportedActivitiesLength; ++i) {
    210             if (activity.equals(mSupportedActivities[i])) {
    211                 return i;
    212             }
    213         }
    214 
    215         return INVALID_ACTIVITY_TYPE;
    216     }
    217 
    218     private void checkPermissions() {
    219         mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
    220     }
    221 
    222     private String[] fetchSupportedActivities() {
    223         String[] supportedActivities = nativeGetSupportedActivities();
    224         if (supportedActivities != null) {
    225             return supportedActivities;
    226         }
    227 
    228         return new String[0];
    229     }
    230 
    231     private class SinkList extends RemoteCallbackList<IActivityRecognitionHardwareSink> {
    232         @Override
    233         public void onCallbackDied(IActivityRecognitionHardwareSink callback) {
    234             int callbackCount = mSinks.getRegisteredCallbackCount();
    235             if (DEBUG) Log.d(TAG, "RegisteredCallbackCount: " + callbackCount);
    236             if (callbackCount != 0) {
    237                 return;
    238             }
    239             // currently there is only one client for this, so if all its sinks have died, we clean
    240             // up after them, this ensures that the AR HAL is not out of sink
    241             for (int activity = 0; activity < mSupportedActivitiesCount; ++activity) {
    242                 for (int event = 0; event < EVENT_TYPE_COUNT; ++event) {
    243                     disableActivityEventIfEnabled(activity, event);
    244                 }
    245             }
    246         }
    247 
    248         private void disableActivityEventIfEnabled(int activityType, int eventType) {
    249             if (mSupportedActivitiesEnabledEvents[activityType][eventType] != EVENT_TYPE_ENABLED) {
    250                 return;
    251             }
    252 
    253             int result = nativeDisableActivityEvent(activityType, eventType);
    254             mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
    255             String message = String.format(
    256                     "DisableActivityEvent: activityType=%d, eventType=%d, result=%d",
    257                     activityType,
    258                     eventType,
    259                     result);
    260             Log.e(TAG, message);
    261         }
    262     }
    263 
    264     // native bindings
    265     static { nativeClassInit(); }
    266 
    267     private static native void nativeClassInit();
    268     private static native boolean nativeIsSupported();
    269 
    270     private native void nativeInitialize();
    271     private native void nativeRelease();
    272     private native String[] nativeGetSupportedActivities();
    273     private native int nativeEnableActivityEvent(
    274             int activityType,
    275             int eventType,
    276             long reportLatenceNs);
    277     private native int nativeDisableActivityEvent(int activityType, int eventType);
    278     private native int nativeFlush();
    279 }
    280