FileDocCategorySizeDatePackage
ActivityRecognitionHardware.javaAPI DocAndroid 5.1 API7389Thu Mar 12 22:22:10 GMT 2015android.hardware.location

ActivityRecognitionHardware.java

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package android.hardware.location;

import android.Manifest;
import android.content.Context;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;

/**
 * A class that implements an {@link IActivityRecognitionHardware} backed up by the Activity
 * Recognition HAL.
 *
 * @hide
 */
public class ActivityRecognitionHardware extends IActivityRecognitionHardware.Stub {
    private static final String TAG = "ActivityRecognitionHardware";

    private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
    private static final int INVALID_ACTIVITY_TYPE = -1;
    private static final int NATIVE_SUCCESS_RESULT = 0;

    private static ActivityRecognitionHardware sSingletonInstance = null;
    private static final Object sSingletonInstanceLock = new Object();

    private final Context mContext;
    private final String[] mSupportedActivities;

    private final RemoteCallbackList<IActivityRecognitionHardwareSink> mSinks =
            new RemoteCallbackList<IActivityRecognitionHardwareSink>();

    private static class Event {
        public int activity;
        public int type;
        public long timestamp;
    }

    private ActivityRecognitionHardware(Context context) {
        nativeInitialize();

        mContext = context;
        mSupportedActivities = fetchSupportedActivities();
    }

    public static ActivityRecognitionHardware getInstance(Context context) {
        synchronized (sSingletonInstanceLock) {
            if (sSingletonInstance == null) {
                sSingletonInstance = new ActivityRecognitionHardware(context);
            }

            return sSingletonInstance;
        }
    }

    public static boolean isSupported() {
        return nativeIsSupported();
    }

    @Override
    public String[] getSupportedActivities() {
        checkPermissions();
        return mSupportedActivities;
    }

    @Override
    public boolean isActivitySupported(String activity) {
        checkPermissions();
        int activityType = getActivityType(activity);
        return activityType != INVALID_ACTIVITY_TYPE;
    }

    @Override
    public boolean registerSink(IActivityRecognitionHardwareSink sink) {
        checkPermissions();
        return mSinks.register(sink);
    }

    @Override
    public boolean unregisterSink(IActivityRecognitionHardwareSink sink) {
        checkPermissions();
        return mSinks.unregister(sink);
    }

    @Override
    public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) {
        checkPermissions();

        int activityType = getActivityType(activity);
        if (activityType == INVALID_ACTIVITY_TYPE) {
            return false;
        }

        int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs);
        return result == NATIVE_SUCCESS_RESULT;
    }

    @Override
    public boolean disableActivityEvent(String activity, int eventType) {
        checkPermissions();

        int activityType = getActivityType(activity);
        if (activityType == INVALID_ACTIVITY_TYPE) {
            return false;
        }

        int result = nativeDisableActivityEvent(activityType, eventType);
        return result == NATIVE_SUCCESS_RESULT;
    }

    @Override
    public boolean flush() {
        checkPermissions();
        int result = nativeFlush();
        return result == NATIVE_SUCCESS_RESULT;
    }

    /**
     * Called by the Activity-Recognition HAL.
     */
    private void onActivityChanged(Event[] events) {
        if (events == null || events.length == 0) {
            Log.d(TAG, "No events to broadcast for onActivityChanged.");
            return;
        }

        int eventsLength = events.length;
        ActivityRecognitionEvent activityRecognitionEventArray[] =
                new ActivityRecognitionEvent[eventsLength];
        for (int i = 0; i < eventsLength; ++i) {
            Event event = events[i];
            String activityName = getActivityName(event.activity);
            activityRecognitionEventArray[i] =
                    new ActivityRecognitionEvent(activityName, event.type, event.timestamp);
        }
        ActivityChangedEvent activityChangedEvent =
                new ActivityChangedEvent(activityRecognitionEventArray);

        int size = mSinks.beginBroadcast();
        for (int i = 0; i < size; ++i) {
            IActivityRecognitionHardwareSink sink = mSinks.getBroadcastItem(i);
            try {
                sink.onActivityChanged(activityChangedEvent);
            } catch (RemoteException e) {
                Log.e(TAG, "Error delivering activity changed event.", e);
            }
        }
        mSinks.finishBroadcast();

    }

    private String getActivityName(int activityType) {
        if (activityType < 0 || activityType >= mSupportedActivities.length) {
            String message = String.format(
                    "Invalid ActivityType: %d, SupportedActivities: %d",
                    activityType,
                    mSupportedActivities.length);
            Log.e(TAG, message);
            return null;
        }

        return mSupportedActivities[activityType];
    }

    private int getActivityType(String activity) {
        if (TextUtils.isEmpty(activity)) {
            return INVALID_ACTIVITY_TYPE;
        }

        int supportedActivitiesLength = mSupportedActivities.length;
        for (int i = 0; i < supportedActivitiesLength; ++i) {
            if (activity.equals(mSupportedActivities[i])) {
                return i;
            }
        }

        return INVALID_ACTIVITY_TYPE;
    }

    private void checkPermissions() {
        String message = String.format(
                "Permission '%s' not granted to access ActivityRecognitionHardware",
                HARDWARE_PERMISSION);
        mContext.enforceCallingPermission(HARDWARE_PERMISSION, message);
    }

    private String[] fetchSupportedActivities() {
        String[] supportedActivities = nativeGetSupportedActivities();
        if (supportedActivities != null) {
            return supportedActivities;
        }

        return new String[0];
    }

    // native bindings
    static { nativeClassInit(); }

    private static native void nativeClassInit();
    private static native boolean nativeIsSupported();

    private native void nativeInitialize();
    private native void nativeRelease();
    private native String[] nativeGetSupportedActivities();
    private native int nativeEnableActivityEvent(
            int activityType,
            int eventType,
            long reportLatenceNs);
    private native int nativeDisableActivityEvent(int activityType, int eventType);
    private native int nativeFlush();
}