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