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