1a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa/*
2a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa * Copyright (C) 2014 The Android Open Source Project
3a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa *
4a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa * Licensed under the Apache License, Version 2.0 (the "License");
5a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa * you may not use this file except in compliance with the License.
6a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa * You may obtain a copy of the License at
7a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa *
8a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa *      http://www.apache.org/licenses/LICENSE-2.0
9a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa *
10a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa * Unless required by applicable law or agreed to in writing, software
11a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa * distributed under the License is distributed on an "AS IS" BASIS,
12a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa * See the License for the specific language governing permissions and
14a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa * limitations under the License
15a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa */
16a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
17a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaapackage android.hardware.location;
18a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
19a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaaimport android.Manifest;
20a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaaimport android.content.Context;
21a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaaimport android.os.RemoteCallbackList;
22a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaaimport android.os.RemoteException;
23a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaaimport android.text.TextUtils;
24a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaaimport android.util.Log;
25a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
26a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa/**
27a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa * A class that implements an {@link IActivityRecognitionHardware} backed up by the Activity
28a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa * Recognition HAL.
29a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa *
30a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa * @hide
31a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa */
32a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaapublic class ActivityRecognitionHardware extends IActivityRecognitionHardware.Stub {
33614612edb508dd5ef1a8f463105b16894906264edestradaa    private static final String TAG = "ActivityRecognitionHW";
34614612edb508dd5ef1a8f463105b16894906264edestradaa    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
35a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
36a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
37614612edb508dd5ef1a8f463105b16894906264edestradaa    private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
38614612edb508dd5ef1a8f463105b16894906264edestradaa            + HARDWARE_PERMISSION + "' not granted to access ActivityRecognitionHardware";
39614612edb508dd5ef1a8f463105b16894906264edestradaa
40a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private static final int INVALID_ACTIVITY_TYPE = -1;
41a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private static final int NATIVE_SUCCESS_RESULT = 0;
42614612edb508dd5ef1a8f463105b16894906264edestradaa    private static final int EVENT_TYPE_DISABLED = 0;
43614612edb508dd5ef1a8f463105b16894906264edestradaa    private static final int EVENT_TYPE_ENABLED = 1;
44614612edb508dd5ef1a8f463105b16894906264edestradaa
45614612edb508dd5ef1a8f463105b16894906264edestradaa    /**
46614612edb508dd5ef1a8f463105b16894906264edestradaa     * Contains the number of supported Event Types.
47614612edb508dd5ef1a8f463105b16894906264edestradaa     *
48614612edb508dd5ef1a8f463105b16894906264edestradaa     * NOTE: increment this counter every time a new EVENT_TYPE_ is added to
49614612edb508dd5ef1a8f463105b16894906264edestradaa     *       com.android.location.provider.ActivityRecognitionProvider
50614612edb508dd5ef1a8f463105b16894906264edestradaa     */
51614612edb508dd5ef1a8f463105b16894906264edestradaa    private static final int EVENT_TYPE_COUNT = 3;
52a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
53614612edb508dd5ef1a8f463105b16894906264edestradaa    private static ActivityRecognitionHardware sSingletonInstance;
54a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private static final Object sSingletonInstanceLock = new Object();
55a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
56a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private final Context mContext;
57614612edb508dd5ef1a8f463105b16894906264edestradaa    private final int mSupportedActivitiesCount;
58a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private final String[] mSupportedActivities;
59614612edb508dd5ef1a8f463105b16894906264edestradaa    private final int[][] mSupportedActivitiesEnabledEvents;
60614612edb508dd5ef1a8f463105b16894906264edestradaa    private final SinkList mSinks = new SinkList();
61a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
62a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private static class Event {
63a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        public int activity;
64a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        public int type;
65a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        public long timestamp;
66a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
67a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
68a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private ActivityRecognitionHardware(Context context) {
69a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        nativeInitialize();
70a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
71a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        mContext = context;
72a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        mSupportedActivities = fetchSupportedActivities();
73614612edb508dd5ef1a8f463105b16894906264edestradaa        mSupportedActivitiesCount = mSupportedActivities.length;
74614612edb508dd5ef1a8f463105b16894906264edestradaa        mSupportedActivitiesEnabledEvents = new int[mSupportedActivitiesCount][EVENT_TYPE_COUNT];
75a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
76a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
77a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    public static ActivityRecognitionHardware getInstance(Context context) {
78a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        synchronized (sSingletonInstanceLock) {
79a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            if (sSingletonInstance == null) {
80a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa                sSingletonInstance = new ActivityRecognitionHardware(context);
81a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            }
82a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
83a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            return sSingletonInstance;
84a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        }
85a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
86a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
87a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    public static boolean isSupported() {
88a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        return nativeIsSupported();
89a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
90a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
91a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    @Override
92a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    public String[] getSupportedActivities() {
93a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        checkPermissions();
94a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        return mSupportedActivities;
95a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
96a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
97a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    @Override
98a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    public boolean isActivitySupported(String activity) {
99a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        checkPermissions();
100a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        int activityType = getActivityType(activity);
101a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        return activityType != INVALID_ACTIVITY_TYPE;
102a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
103a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
104a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    @Override
105a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    public boolean registerSink(IActivityRecognitionHardwareSink sink) {
106a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        checkPermissions();
107a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        return mSinks.register(sink);
108a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
109a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
110a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    @Override
111a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    public boolean unregisterSink(IActivityRecognitionHardwareSink sink) {
112a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        checkPermissions();
113a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        return mSinks.unregister(sink);
114a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
115a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
116a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    @Override
117a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) {
118a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        checkPermissions();
119a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
120a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        int activityType = getActivityType(activity);
121a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        if (activityType == INVALID_ACTIVITY_TYPE) {
122a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            return false;
123a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        }
124a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
125a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs);
126614612edb508dd5ef1a8f463105b16894906264edestradaa        if (result == NATIVE_SUCCESS_RESULT) {
127614612edb508dd5ef1a8f463105b16894906264edestradaa            mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_ENABLED;
128614612edb508dd5ef1a8f463105b16894906264edestradaa            return true;
129614612edb508dd5ef1a8f463105b16894906264edestradaa        }
130614612edb508dd5ef1a8f463105b16894906264edestradaa        return false;
131a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
132a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
133a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    @Override
134a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    public boolean disableActivityEvent(String activity, int eventType) {
135a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        checkPermissions();
136a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
137a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        int activityType = getActivityType(activity);
138a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        if (activityType == INVALID_ACTIVITY_TYPE) {
139a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            return false;
140a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        }
141a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
142a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        int result = nativeDisableActivityEvent(activityType, eventType);
143614612edb508dd5ef1a8f463105b16894906264edestradaa        if (result == NATIVE_SUCCESS_RESULT) {
144614612edb508dd5ef1a8f463105b16894906264edestradaa            mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
145614612edb508dd5ef1a8f463105b16894906264edestradaa            return true;
146614612edb508dd5ef1a8f463105b16894906264edestradaa        }
147614612edb508dd5ef1a8f463105b16894906264edestradaa        return false;
148a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
149a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
150a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    @Override
151a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    public boolean flush() {
152a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        checkPermissions();
153a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        int result = nativeFlush();
154a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        return result == NATIVE_SUCCESS_RESULT;
155a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
156a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
157a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    /**
158a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa     * Called by the Activity-Recognition HAL.
159a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa     */
160a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private void onActivityChanged(Event[] events) {
1613b0224dc3c2e7ffb93dc56970395003d0e387545destradaa        if (events == null || events.length == 0) {
162614612edb508dd5ef1a8f463105b16894906264edestradaa            if (DEBUG) Log.d(TAG, "No events to broadcast for onActivityChanged.");
163a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            return;
164a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        }
165a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
166a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        int eventsLength = events.length;
167a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        ActivityRecognitionEvent activityRecognitionEventArray[] =
168a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa                new ActivityRecognitionEvent[eventsLength];
169a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        for (int i = 0; i < eventsLength; ++i) {
170a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            Event event = events[i];
171a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            String activityName = getActivityName(event.activity);
172a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            activityRecognitionEventArray[i] =
173a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa                    new ActivityRecognitionEvent(activityName, event.type, event.timestamp);
174a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        }
175a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        ActivityChangedEvent activityChangedEvent =
176a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa                new ActivityChangedEvent(activityRecognitionEventArray);
177a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
1783b0224dc3c2e7ffb93dc56970395003d0e387545destradaa        int size = mSinks.beginBroadcast();
179a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        for (int i = 0; i < size; ++i) {
180a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            IActivityRecognitionHardwareSink sink = mSinks.getBroadcastItem(i);
181a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            try {
182a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa                sink.onActivityChanged(activityChangedEvent);
183a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            } catch (RemoteException e) {
184a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa                Log.e(TAG, "Error delivering activity changed event.", e);
185a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            }
186a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        }
187a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        mSinks.finishBroadcast();
188a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
189a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
190a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private String getActivityName(int activityType) {
191a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        if (activityType < 0 || activityType >= mSupportedActivities.length) {
192a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            String message = String.format(
193a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa                    "Invalid ActivityType: %d, SupportedActivities: %d",
194a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa                    activityType,
195a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa                    mSupportedActivities.length);
196a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            Log.e(TAG, message);
197a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            return null;
198a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        }
199a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
200a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        return mSupportedActivities[activityType];
201a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
202a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
203a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private int getActivityType(String activity) {
204a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        if (TextUtils.isEmpty(activity)) {
205a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            return INVALID_ACTIVITY_TYPE;
206a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        }
207a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
2083b0224dc3c2e7ffb93dc56970395003d0e387545destradaa        int supportedActivitiesLength = mSupportedActivities.length;
2093b0224dc3c2e7ffb93dc56970395003d0e387545destradaa        for (int i = 0; i < supportedActivitiesLength; ++i) {
210a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            if (activity.equals(mSupportedActivities[i])) {
211a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa                return i;
212a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            }
213a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        }
214a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
215a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        return INVALID_ACTIVITY_TYPE;
216a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
217a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
218a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private void checkPermissions() {
219614612edb508dd5ef1a8f463105b16894906264edestradaa        mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
220a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
221a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
2223b0224dc3c2e7ffb93dc56970395003d0e387545destradaa    private String[] fetchSupportedActivities() {
223a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        String[] supportedActivities = nativeGetSupportedActivities();
224a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        if (supportedActivities != null) {
225a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            return supportedActivities;
226a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        }
227a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
228a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa        return new String[0];
229a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    }
230a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
231614612edb508dd5ef1a8f463105b16894906264edestradaa    private class SinkList extends RemoteCallbackList<IActivityRecognitionHardwareSink> {
232614612edb508dd5ef1a8f463105b16894906264edestradaa        @Override
233614612edb508dd5ef1a8f463105b16894906264edestradaa        public void onCallbackDied(IActivityRecognitionHardwareSink callback) {
234614612edb508dd5ef1a8f463105b16894906264edestradaa            int callbackCount = mSinks.getRegisteredCallbackCount();
235614612edb508dd5ef1a8f463105b16894906264edestradaa            if (DEBUG) Log.d(TAG, "RegisteredCallbackCount: " + callbackCount);
236614612edb508dd5ef1a8f463105b16894906264edestradaa            if (callbackCount != 0) {
237614612edb508dd5ef1a8f463105b16894906264edestradaa                return;
238614612edb508dd5ef1a8f463105b16894906264edestradaa            }
239614612edb508dd5ef1a8f463105b16894906264edestradaa            // currently there is only one client for this, so if all its sinks have died, we clean
240614612edb508dd5ef1a8f463105b16894906264edestradaa            // up after them, this ensures that the AR HAL is not out of sink
241614612edb508dd5ef1a8f463105b16894906264edestradaa            for (int activity = 0; activity < mSupportedActivitiesCount; ++activity) {
242614612edb508dd5ef1a8f463105b16894906264edestradaa                for (int event = 0; event < EVENT_TYPE_COUNT; ++event) {
243614612edb508dd5ef1a8f463105b16894906264edestradaa                    disableActivityEventIfEnabled(activity, event);
244614612edb508dd5ef1a8f463105b16894906264edestradaa                }
245614612edb508dd5ef1a8f463105b16894906264edestradaa            }
246614612edb508dd5ef1a8f463105b16894906264edestradaa        }
247614612edb508dd5ef1a8f463105b16894906264edestradaa
248614612edb508dd5ef1a8f463105b16894906264edestradaa        private void disableActivityEventIfEnabled(int activityType, int eventType) {
249614612edb508dd5ef1a8f463105b16894906264edestradaa            if (mSupportedActivitiesEnabledEvents[activityType][eventType] != EVENT_TYPE_ENABLED) {
250614612edb508dd5ef1a8f463105b16894906264edestradaa                return;
251614612edb508dd5ef1a8f463105b16894906264edestradaa            }
252614612edb508dd5ef1a8f463105b16894906264edestradaa
253614612edb508dd5ef1a8f463105b16894906264edestradaa            int result = nativeDisableActivityEvent(activityType, eventType);
254614612edb508dd5ef1a8f463105b16894906264edestradaa            mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
255614612edb508dd5ef1a8f463105b16894906264edestradaa            String message = String.format(
256614612edb508dd5ef1a8f463105b16894906264edestradaa                    "DisableActivityEvent: activityType=%d, eventType=%d, result=%d",
257614612edb508dd5ef1a8f463105b16894906264edestradaa                    activityType,
258614612edb508dd5ef1a8f463105b16894906264edestradaa                    eventType,
259614612edb508dd5ef1a8f463105b16894906264edestradaa                    result);
260614612edb508dd5ef1a8f463105b16894906264edestradaa            Log.e(TAG, message);
261614612edb508dd5ef1a8f463105b16894906264edestradaa        }
262614612edb508dd5ef1a8f463105b16894906264edestradaa    }
263614612edb508dd5ef1a8f463105b16894906264edestradaa
264a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    // native bindings
265a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    static { nativeClassInit(); }
266a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa
267a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private static native void nativeClassInit();
268a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa    private static native boolean nativeIsSupported();
2693b0224dc3c2e7ffb93dc56970395003d0e387545destradaa
2703b0224dc3c2e7ffb93dc56970395003d0e387545destradaa    private native void nativeInitialize();
2713b0224dc3c2e7ffb93dc56970395003d0e387545destradaa    private native void nativeRelease();
2723b0224dc3c2e7ffb93dc56970395003d0e387545destradaa    private native String[] nativeGetSupportedActivities();
2733b0224dc3c2e7ffb93dc56970395003d0e387545destradaa    private native int nativeEnableActivityEvent(
274a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            int activityType,
275a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            int eventType,
276a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa            long reportLatenceNs);
2773b0224dc3c2e7ffb93dc56970395003d0e387545destradaa    private native int nativeDisableActivityEvent(int activityType, int eventType);
2783b0224dc3c2e7ffb93dc56970395003d0e387545destradaa    private native int nativeFlush();
279a4fa3b5aa53cf677b623fe346c585cb8a0c1ce26destradaa}
280