/*
 * Copyright (C) 2012 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;

import static android.view.Display.DEFAULT_DISPLAY;

import android.os.RemoteException;
import android.os.ServiceManager;
import android.view.IRotationWatcher;
import android.view.IWindowManager;
import android.view.Surface;

import java.util.HashMap;
import java.util.List;

Helper class for implementing the legacy sensor manager API.
@hide
/** * Helper class for implementing the legacy sensor manager API. * @hide */
@SuppressWarnings("deprecation") final class LegacySensorManager { private static boolean sInitialized; private static IWindowManager sWindowManager; private static int sRotation = Surface.ROTATION_0; private final SensorManager mSensorManager; // List of legacy listeners. Guarded by mLegacyListenersMap. private final HashMap<SensorListener, LegacyListener> mLegacyListenersMap = new HashMap<SensorListener, LegacyListener>(); public LegacySensorManager(SensorManager sensorManager) { mSensorManager = sensorManager; synchronized (SensorManager.class) { if (!sInitialized) { sWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService("window")); if (sWindowManager != null) { // if it's null we're running in the system process // which won't get the rotated values try { sRotation = sWindowManager.watchRotation( new IRotationWatcher.Stub() { public void onRotationChanged(int rotation) { LegacySensorManager.onRotationChanged(rotation); } }, DEFAULT_DISPLAY); } catch (RemoteException e) { } } } } } public int getSensors() { int result = 0; final List<Sensor> fullList = mSensorManager.getFullSensorList(); for (Sensor i : fullList) { switch (i.getType()) { case Sensor.TYPE_ACCELEROMETER: result |= SensorManager.SENSOR_ACCELEROMETER; break; case Sensor.TYPE_MAGNETIC_FIELD: result |= SensorManager.SENSOR_MAGNETIC_FIELD; break; case Sensor.TYPE_ORIENTATION: result |= SensorManager.SENSOR_ORIENTATION | SensorManager.SENSOR_ORIENTATION_RAW; break; } } return result; } public boolean registerListener(SensorListener listener, int sensors, int rate) { if (listener == null) { return false; } boolean result = false; result = registerLegacyListener(SensorManager.SENSOR_ACCELEROMETER, Sensor.TYPE_ACCELEROMETER, listener, sensors, rate) || result; result = registerLegacyListener(SensorManager.SENSOR_MAGNETIC_FIELD, Sensor.TYPE_MAGNETIC_FIELD, listener, sensors, rate) || result; result = registerLegacyListener(SensorManager.SENSOR_ORIENTATION_RAW, Sensor.TYPE_ORIENTATION, listener, sensors, rate) || result; result = registerLegacyListener(SensorManager.SENSOR_ORIENTATION, Sensor.TYPE_ORIENTATION, listener, sensors, rate) || result; result = registerLegacyListener(SensorManager.SENSOR_TEMPERATURE, Sensor.TYPE_TEMPERATURE, listener, sensors, rate) || result; return result; } private boolean registerLegacyListener(int legacyType, int type, SensorListener listener, int sensors, int rate) { boolean result = false; // Are we activating this legacy sensor? if ((sensors & legacyType) != 0) { // if so, find a suitable Sensor Sensor sensor = mSensorManager.getDefaultSensor(type); if (sensor != null) { // We do all of this work holding the legacy listener lock to ensure // that the invariants around listeners are maintained. This is safe // because neither registerLegacyListener nor unregisterLegacyListener // are called reentrantly while sensors are being registered or unregistered. synchronized (mLegacyListenersMap) { // If we don't already have one, create a LegacyListener // to wrap this listener and process the events as // they are expected by legacy apps. LegacyListener legacyListener = mLegacyListenersMap.get(listener); if (legacyListener == null) { // we didn't find a LegacyListener for this client, // create one, and put it in our list. legacyListener = new LegacyListener(listener); mLegacyListenersMap.put(listener, legacyListener); } // register this legacy sensor with this legacy listener if (legacyListener.registerSensor(legacyType)) { // and finally, register the legacy listener with the new apis result = mSensorManager.registerListener(legacyListener, sensor, rate); } else { result = true; // sensor already enabled } } } } return result; } public void unregisterListener(SensorListener listener, int sensors) { if (listener == null) { return; } unregisterLegacyListener(SensorManager.SENSOR_ACCELEROMETER, Sensor.TYPE_ACCELEROMETER, listener, sensors); unregisterLegacyListener(SensorManager.SENSOR_MAGNETIC_FIELD, Sensor.TYPE_MAGNETIC_FIELD, listener, sensors); unregisterLegacyListener(SensorManager.SENSOR_ORIENTATION_RAW, Sensor.TYPE_ORIENTATION, listener, sensors); unregisterLegacyListener(SensorManager.SENSOR_ORIENTATION, Sensor.TYPE_ORIENTATION, listener, sensors); unregisterLegacyListener(SensorManager.SENSOR_TEMPERATURE, Sensor.TYPE_TEMPERATURE, listener, sensors); } private void unregisterLegacyListener(int legacyType, int type, SensorListener listener, int sensors) { // Are we deactivating this legacy sensor? if ((sensors & legacyType) != 0) { // if so, find the corresponding Sensor Sensor sensor = mSensorManager.getDefaultSensor(type); if (sensor != null) { // We do all of this work holding the legacy listener lock to ensure // that the invariants around listeners are maintained. This is safe // because neither registerLegacyListener nor unregisterLegacyListener // are called re-entrantly while sensors are being registered or unregistered. synchronized (mLegacyListenersMap) { // do we know about this listener? LegacyListener legacyListener = mLegacyListenersMap.get(listener); if (legacyListener != null) { // unregister this legacy sensor and if we don't // need the corresponding Sensor, unregister it too if (legacyListener.unregisterSensor(legacyType)) { // corresponding sensor not needed, unregister mSensorManager.unregisterListener(legacyListener, sensor); // finally check if we still need the legacyListener // in our mapping, if not, get rid of it too. if (!legacyListener.hasSensors()) { mLegacyListenersMap.remove(listener); } } } } } } } static void onRotationChanged(int rotation) { synchronized (SensorManager.class) { sRotation = rotation; } } static int getRotation() { synchronized (SensorManager.class) { return sRotation; } } private static final class LegacyListener implements SensorEventListener { private float[] mValues = new float[6]; private SensorListener mTarget; private int mSensors; private final LmsFilter mYawfilter = new LmsFilter(); LegacyListener(SensorListener target) { mTarget = target; mSensors = 0; } boolean registerSensor(int legacyType) { if ((mSensors & legacyType) != 0) { return false; } boolean alreadyHasOrientationSensor = hasOrientationSensor(mSensors); mSensors |= legacyType; if (alreadyHasOrientationSensor && hasOrientationSensor(legacyType)) { return false; // don't need to re-register the orientation sensor } return true; } boolean unregisterSensor(int legacyType) { if ((mSensors & legacyType) == 0) { return false; } mSensors &= ~legacyType; if (hasOrientationSensor(legacyType) && hasOrientationSensor(mSensors)) { return false; // can't unregister the orientation sensor just yet } return true; } boolean hasSensors() { return mSensors != 0; } private static boolean hasOrientationSensor(int sensors) { return (sensors & (SensorManager.SENSOR_ORIENTATION | SensorManager.SENSOR_ORIENTATION_RAW)) != 0; } public void onAccuracyChanged(Sensor sensor, int accuracy) { try { mTarget.onAccuracyChanged(getLegacySensorType(sensor.getType()), accuracy); } catch (AbstractMethodError e) { // old app that doesn't implement this method // just ignore it. } } public void onSensorChanged(SensorEvent event) { final float[] v = mValues; v[0] = event.values[0]; v[1] = event.values[1]; v[2] = event.values[2]; int type = event.sensor.getType(); int legacyType = getLegacySensorType(type); mapSensorDataToWindow(legacyType, v, LegacySensorManager.getRotation()); if (type == Sensor.TYPE_ORIENTATION) { if ((mSensors & SensorManager.SENSOR_ORIENTATION_RAW) != 0) { mTarget.onSensorChanged(SensorManager.SENSOR_ORIENTATION_RAW, v); } if ((mSensors & SensorManager.SENSOR_ORIENTATION) != 0) { v[0] = mYawfilter.filter(event.timestamp, v[0]); mTarget.onSensorChanged(SensorManager.SENSOR_ORIENTATION, v); } } else { mTarget.onSensorChanged(legacyType, v); } } /* * Helper function to convert the specified sensor's data to the windows's * coordinate space from the device's coordinate space. * * output: 3,4,5: values in the old API format * 0,1,2: transformed values in the old API format * */ private void mapSensorDataToWindow(int sensor, float[] values, int orientation) { float x = values[0]; float y = values[1]; float z = values[2]; switch (sensor) { case SensorManager.SENSOR_ORIENTATION: case SensorManager.SENSOR_ORIENTATION_RAW: z = -z; break; case SensorManager.SENSOR_ACCELEROMETER: x = -x; y = -y; z = -z; break; case SensorManager.SENSOR_MAGNETIC_FIELD: x = -x; y = -y; break; } values[0] = x; values[1] = y; values[2] = z; values[3] = x; values[4] = y; values[5] = z; if ((orientation & Surface.ROTATION_90) != 0) { // handles 90 and 270 rotation switch (sensor) { case SensorManager.SENSOR_ACCELEROMETER: case SensorManager.SENSOR_MAGNETIC_FIELD: values[0] = -y; values[1] = x; values[2] = z; break; case SensorManager.SENSOR_ORIENTATION: case SensorManager.SENSOR_ORIENTATION_RAW: values[0] = x + ((x < 270) ? 90 : -270); values[1] = z; values[2] = y; break; } } if ((orientation & Surface.ROTATION_180) != 0) { x = values[0]; y = values[1]; z = values[2]; // handles 180 (flip) and 270 (flip + 90) rotation switch (sensor) { case SensorManager.SENSOR_ACCELEROMETER: case SensorManager.SENSOR_MAGNETIC_FIELD: values[0] = -x; values[1] = -y; values[2] = z; break; case SensorManager.SENSOR_ORIENTATION: case SensorManager.SENSOR_ORIENTATION_RAW: values[0] = (x >= 180) ? (x - 180) : (x + 180); values[1] = -y; values[2] = -z; break; } } } private static int getLegacySensorType(int type) { switch (type) { case Sensor.TYPE_ACCELEROMETER: return SensorManager.SENSOR_ACCELEROMETER; case Sensor.TYPE_MAGNETIC_FIELD: return SensorManager.SENSOR_MAGNETIC_FIELD; case Sensor.TYPE_ORIENTATION: return SensorManager.SENSOR_ORIENTATION_RAW; case Sensor.TYPE_TEMPERATURE: return SensorManager.SENSOR_TEMPERATURE; } return 0; } } private static final class LmsFilter { private static final int SENSORS_RATE_MS = 20; private static final int COUNT = 12; private static final float PREDICTION_RATIO = 1.0f / 3.0f; private static final float PREDICTION_TIME = (SENSORS_RATE_MS * COUNT / 1000.0f) * PREDICTION_RATIO; private float[] mV = new float[COUNT * 2]; private long[] mT = new long[COUNT * 2]; private int mIndex; public LmsFilter() { mIndex = COUNT; } public float filter(long time, float in) { float v = in; final float ns = 1.0f / 1000000000.0f; float v1 = mV[mIndex]; if ((v - v1) > 180) { v -= 360; } else if ((v1 - v) > 180) { v += 360; } /* Manage the circular buffer, we write the data twice spaced * by COUNT values, so that we don't have to copy the array * when it's full */ mIndex++; if (mIndex >= COUNT * 2) { mIndex = COUNT; } mV[mIndex] = v; mT[mIndex] = time; mV[mIndex - COUNT] = v; mT[mIndex - COUNT] = time; float A, B, C, D, E; float a, b; int i; A = B = C = D = E = 0; for (i = 0; i < COUNT - 1; i++) { final int j = mIndex - 1 - i; final float Z = mV[j]; final float T = (mT[j] / 2 + mT[j + 1] / 2 - time) * ns; float dT = (mT[j] - mT[j + 1]) * ns; dT *= dT; A += Z * dT; B += T * (T * dT); C += (T * dT); D += Z * (T * dT); E += dT; } b = (A * B + C * D) / (E * B + C * C); a = (E * b - A) / C; float f = b + PREDICTION_TIME * a; // Normalize f *= (1.0f / 360.0f); if (((f >= 0) ? f : -f) >= 0.5f) { f = f - (float) Math.ceil(f + 0.5f) + 1.0f; } if (f < 0) { f += 1.0f; } f *= 360.0f; return f; } } }