/* * Copyright (C) 2008 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 com.android.server; import android.content.Context; import android.hardware.ISensorService; import android.os.Binder; import android.os.Bundle; import android.os.RemoteException; import android.os.IBinder; import android.util.Config; import android.util.Slog; import android.util.PrintWriterPrinter; import android.util.Printer; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import com.android.internal.app.IBatteryStats; import com.android.server.am.BatteryStatsService; /** * Class that manages the device's sensors. It register clients and activate * the needed sensors. The sensor events themselves are not broadcasted from * this service, instead, a file descriptor is provided to each client they * can read events from. */ class SensorService extends ISensorService.Stub { static final String TAG = SensorService.class.getSimpleName(); private static final boolean DEBUG = false; private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; private static final int SENSOR_DISABLE = -1; private int mCurrentDelay = 0; /** * Battery statistics to be updated when sensors are enabled and disabled. */ final IBatteryStats mBatteryStats = BatteryStatsService.getService(); private final class Listener implements IBinder.DeathRecipient { final IBinder mToken; final int mUid; int mSensors = 0; int mDelay = 0x7FFFFFFF; Listener(IBinder token, int uid) { mToken = token; mUid = uid; } void addSensor(int sensor, int delay) { mSensors |= (1<<sensor); if (delay < mDelay) mDelay = delay; } void removeSensor(int sensor) { mSensors &= ~(1<<sensor); } boolean hasSensor(int sensor) { return ((mSensors & (1<<sensor)) != 0); } public void binderDied() { if (localLOGV) Slog.d(TAG, "sensor listener died"); synchronized(mListeners) { mListeners.remove(this); mToken.unlinkToDeath(this, 0); // go through the lists of sensors used by the listener that // died and deactivate them. for (int sensor=0 ; sensor<32 && mSensors!=0 ; sensor++) { if (hasSensor(sensor)) { removeSensor(sensor); deactivateIfUnusedLocked(sensor); try { mBatteryStats.noteStopSensor(mUid, sensor); } catch (RemoteException e) { // oops. not a big deal. } } } if (mListeners.size() == 0) { _sensors_control_wake(); _sensors_control_close(); } else { // TODO: we should recalculate the delay, since removing // a listener may increase the overall rate. } mListeners.notify(); } } } @SuppressWarnings("unused") public SensorService(Context context) { if (localLOGV) Slog.d(TAG, "SensorService startup"); _sensors_control_init(); } public Bundle getDataChannel() throws RemoteException { // synchronize so we do not require sensor HAL to be thread-safe. synchronized(mListeners) { return _sensors_control_open(); } } public boolean enableSensor(IBinder binder, String name, int sensor, int enable) throws RemoteException { if (localLOGV) Slog.d(TAG, "enableSensor " + name + "(#" + sensor + ") " + enable); if (binder == null) { Slog.e(TAG, "listener is null (sensor=" + name + ", id=" + sensor + ")"); return false; } if (enable < 0 && (enable != SENSOR_DISABLE)) { Slog.e(TAG, "invalid enable parameter (enable=" + enable + ", sensor=" + name + ", id=" + sensor + ")"); return false; } boolean res; int uid = Binder.getCallingUid(); synchronized(mListeners) { res = enableSensorInternalLocked(binder, uid, name, sensor, enable); if (res == true) { // Inform battery statistics service of status change long identity = Binder.clearCallingIdentity(); if (enable == SENSOR_DISABLE) { mBatteryStats.noteStopSensor(uid, sensor); } else { mBatteryStats.noteStartSensor(uid, sensor); } Binder.restoreCallingIdentity(identity); } } return res; } private boolean enableSensorInternalLocked(IBinder binder, int uid, String name, int sensor, int enable) throws RemoteException { // check if we have this listener Listener l = null; for (Listener listener : mListeners) { if (binder == listener.mToken) { l = listener; break; } } if (enable != SENSOR_DISABLE) { // Activate the requested sensor if (_sensors_control_activate(sensor, true) == false) { Slog.w(TAG, "could not enable sensor " + sensor); return false; } if (l == null) { /* * we don't have a listener for this binder yet, so * create a new one and add it to the list. */ l = new Listener(binder, uid); binder.linkToDeath(l, 0); mListeners.add(l); mListeners.notify(); } // take note that this sensor is now used by this client l.addSensor(sensor, enable); } else { if (l == null) { /* * This client isn't in the list, this usually happens * when enabling the sensor failed, but the client * didn't handle the error and later tries to shut that * sensor off. */ Slog.w(TAG, "listener with binder " + binder + ", doesn't exist (sensor=" + name + ", id=" + sensor + ")"); return false; } // remove this sensor from this client l.removeSensor(sensor); // see if we need to deactivate this sensors= deactivateIfUnusedLocked(sensor); // if the listener doesn't have any more sensors active // we can get rid of it if (l.mSensors == 0) { // we won't need this death notification anymore binder.unlinkToDeath(l, 0); // remove the listener from the list mListeners.remove(l); // and if the list is empty, turn off the whole sensor h/w if (mListeners.size() == 0) { _sensors_control_wake(); _sensors_control_close(); } mListeners.notify(); } } // calculate and set the new delay int minDelay = 0x7FFFFFFF; for (Listener listener : mListeners) { if (listener.mDelay < minDelay) minDelay = listener.mDelay; } if (minDelay != 0x7FFFFFFF) { mCurrentDelay = minDelay; _sensors_control_set_delay(minDelay); } return true; } private void deactivateIfUnusedLocked(int sensor) { int size = mListeners.size(); for (int i=0 ; i<size ; i++) { if (mListeners.get(i).hasSensor(sensor)) { // this sensor is still in use, don't turn it off return; } } if (_sensors_control_activate(sensor, false) == false) { Slog.w(TAG, "could not disable sensor " + sensor); } } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mListeners) { Printer pr = new PrintWriterPrinter(pw); int c = 0; pr.println(mListeners.size() + " listener(s), delay=" + mCurrentDelay + " ms"); for (Listener l : mListeners) { pr.println("listener[" + c + "] " + "sensors=0x" + Integer.toString(l.mSensors, 16) + ", uid=" + l.mUid + ", delay=" + l.mDelay + " ms"); c++; } } } private ArrayList<Listener> mListeners = new ArrayList<Listener>(); private static native int _sensors_control_init(); private static native Bundle _sensors_control_open(); private static native int _sensors_control_close(); private static native boolean _sensors_control_activate(int sensor, boolean activate); private static native int _sensors_control_set_delay(int ms); private static native int _sensors_control_wake(); }