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 ActivityRecognitionHardware(Context context)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 getInstance(Context context)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 isSupported()87 public static boolean isSupported() { 88 return nativeIsSupported(); 89 } 90 91 @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) 92 @Override getSupportedActivities()93 public String[] getSupportedActivities() { 94 super.getSupportedActivities_enforcePermission(); 95 96 return mSupportedActivities; 97 } 98 99 @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) 100 @Override isActivitySupported(String activity)101 public boolean isActivitySupported(String activity) { 102 super.isActivitySupported_enforcePermission(); 103 104 int activityType = getActivityType(activity); 105 return activityType != INVALID_ACTIVITY_TYPE; 106 } 107 108 @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) 109 @Override registerSink(IActivityRecognitionHardwareSink sink)110 public boolean registerSink(IActivityRecognitionHardwareSink sink) { 111 super.registerSink_enforcePermission(); 112 113 return mSinks.register(sink); 114 } 115 116 @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) 117 @Override unregisterSink(IActivityRecognitionHardwareSink sink)118 public boolean unregisterSink(IActivityRecognitionHardwareSink sink) { 119 super.unregisterSink_enforcePermission(); 120 121 return mSinks.unregister(sink); 122 } 123 124 @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) 125 @Override enableActivityEvent(String activity, int eventType, long reportLatencyNs)126 public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) { 127 128 super.enableActivityEvent_enforcePermission(); 129 130 int activityType = getActivityType(activity); 131 if (activityType == INVALID_ACTIVITY_TYPE) { 132 return false; 133 } 134 135 int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs); 136 if (result == NATIVE_SUCCESS_RESULT) { 137 mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_ENABLED; 138 return true; 139 } 140 return false; 141 } 142 143 @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) 144 @Override disableActivityEvent(String activity, int eventType)145 public boolean disableActivityEvent(String activity, int eventType) { 146 147 super.disableActivityEvent_enforcePermission(); 148 149 int activityType = getActivityType(activity); 150 if (activityType == INVALID_ACTIVITY_TYPE) { 151 return false; 152 } 153 154 int result = nativeDisableActivityEvent(activityType, eventType); 155 if (result == NATIVE_SUCCESS_RESULT) { 156 mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED; 157 return true; 158 } 159 return false; 160 } 161 162 @android.annotation.EnforcePermission(android.Manifest.permission.LOCATION_HARDWARE) 163 @Override flush()164 public boolean flush() { 165 super.flush_enforcePermission(); 166 167 int result = nativeFlush(); 168 return result == NATIVE_SUCCESS_RESULT; 169 } 170 171 /** 172 * Called by the Activity-Recognition HAL. 173 */ onActivityChanged(Event[] events)174 private void onActivityChanged(Event[] events) { 175 if (events == null || events.length == 0) { 176 if (DEBUG) Log.d(TAG, "No events to broadcast for onActivityChanged."); 177 return; 178 } 179 180 int eventsLength = events.length; 181 ActivityRecognitionEvent activityRecognitionEventArray[] = 182 new ActivityRecognitionEvent[eventsLength]; 183 for (int i = 0; i < eventsLength; ++i) { 184 Event event = events[i]; 185 String activityName = getActivityName(event.activity); 186 activityRecognitionEventArray[i] = 187 new ActivityRecognitionEvent(activityName, event.type, event.timestamp); 188 } 189 ActivityChangedEvent activityChangedEvent = 190 new ActivityChangedEvent(activityRecognitionEventArray); 191 192 int size = mSinks.beginBroadcast(); 193 for (int i = 0; i < size; ++i) { 194 IActivityRecognitionHardwareSink sink = mSinks.getBroadcastItem(i); 195 try { 196 sink.onActivityChanged(activityChangedEvent); 197 } catch (RemoteException e) { 198 Log.e(TAG, "Error delivering activity changed event.", e); 199 } 200 } 201 mSinks.finishBroadcast(); 202 } 203 getActivityName(int activityType)204 private String getActivityName(int activityType) { 205 if (activityType < 0 || activityType >= mSupportedActivities.length) { 206 String message = String.format( 207 "Invalid ActivityType: %d, SupportedActivities: %d", 208 activityType, 209 mSupportedActivities.length); 210 Log.e(TAG, message); 211 return null; 212 } 213 214 return mSupportedActivities[activityType]; 215 } 216 getActivityType(String activity)217 private int getActivityType(String activity) { 218 if (TextUtils.isEmpty(activity)) { 219 return INVALID_ACTIVITY_TYPE; 220 } 221 222 int supportedActivitiesLength = mSupportedActivities.length; 223 for (int i = 0; i < supportedActivitiesLength; ++i) { 224 if (activity.equals(mSupportedActivities[i])) { 225 return i; 226 } 227 } 228 229 return INVALID_ACTIVITY_TYPE; 230 } 231 checkPermissions()232 private void checkPermissions() { 233 mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE); 234 } 235 fetchSupportedActivities()236 private String[] fetchSupportedActivities() { 237 String[] supportedActivities = nativeGetSupportedActivities(); 238 if (supportedActivities != null) { 239 return supportedActivities; 240 } 241 242 return new String[0]; 243 } 244 245 private class SinkList extends RemoteCallbackList<IActivityRecognitionHardwareSink> { 246 @Override onCallbackDied(IActivityRecognitionHardwareSink callback)247 public void onCallbackDied(IActivityRecognitionHardwareSink callback) { 248 int callbackCount = mSinks.getRegisteredCallbackCount(); 249 if (DEBUG) Log.d(TAG, "RegisteredCallbackCount: " + callbackCount); 250 if (callbackCount != 0) { 251 return; 252 } 253 // currently there is only one client for this, so if all its sinks have died, we clean 254 // up after them, this ensures that the AR HAL is not out of sink 255 for (int activity = 0; activity < mSupportedActivitiesCount; ++activity) { 256 for (int event = 0; event < EVENT_TYPE_COUNT; ++event) { 257 disableActivityEventIfEnabled(activity, event); 258 } 259 } 260 } 261 disableActivityEventIfEnabled(int activityType, int eventType)262 private void disableActivityEventIfEnabled(int activityType, int eventType) { 263 if (mSupportedActivitiesEnabledEvents[activityType][eventType] != EVENT_TYPE_ENABLED) { 264 return; 265 } 266 267 int result = nativeDisableActivityEvent(activityType, eventType); 268 mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED; 269 String message = String.format( 270 "DisableActivityEvent: activityType=%d, eventType=%d, result=%d", 271 activityType, 272 eventType, 273 result); 274 Log.e(TAG, message); 275 } 276 } 277 278 // native bindings nativeClassInit()279 static { nativeClassInit(); } 280 nativeClassInit()281 private static native void nativeClassInit(); nativeIsSupported()282 private static native boolean nativeIsSupported(); 283 nativeInitialize()284 private native void nativeInitialize(); nativeRelease()285 private native void nativeRelease(); nativeGetSupportedActivities()286 private native String[] nativeGetSupportedActivities(); nativeEnableActivityEvent( int activityType, int eventType, long reportLatenceNs)287 private native int nativeEnableActivityEvent( 288 int activityType, 289 int eventType, 290 long reportLatenceNs); nativeDisableActivityEvent(int activityType, int eventType)291 private native int nativeDisableActivityEvent(int activityType, int eventType); nativeFlush()292 private native int nativeFlush(); 293 } 294