Files
frameworks_base/services/java/com/android/server/VibratorService.java
Dianne Hackborn 91268cf21e More work on process tracking.
Re-arranged code to be more flexible, now track
state of services, dump ordered list of running
processes while memory was critical and low.

Also rename battery stats service from "batteryinfo" to "batterystats".

Change-Id: I0f4f0c8d443c49d255cb84d0fc917e8ec18b152e
2013-06-14 17:30:17 -07:00

612 lines
21 KiB
Java

/*
* 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.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.hardware.input.InputManager;
import android.os.BatteryStats;
import android.os.Handler;
import android.os.IVibratorService;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.Binder;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.Vibrator;
import android.os.WorkSource;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Slog;
import android.view.InputDevice;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.ListIterator;
public class VibratorService extends IVibratorService.Stub
implements InputManager.InputDeviceListener {
private static final String TAG = "VibratorService";
private final LinkedList<Vibration> mVibrations;
private Vibration mCurrentVibration;
private final WorkSource mTmpWorkSource = new WorkSource();
private final Handler mH = new Handler();
private final Context mContext;
private final PowerManager.WakeLock mWakeLock;
private final IAppOpsService mAppOpsService;
private final IBatteryStats mBatteryStatsService;
private InputManager mIm;
volatile VibrateThread mThread;
// mInputDeviceVibrators lock should be acquired after mVibrations lock, if both are
// to be acquired
private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
private int mCurVibUid = -1;
native static boolean vibratorExists();
native static void vibratorOn(long milliseconds);
native static void vibratorOff();
private class Vibration implements IBinder.DeathRecipient {
private final IBinder mToken;
private final long mTimeout;
private final long mStartTime;
private final long[] mPattern;
private final int mRepeat;
private final int mUid;
private final String mPackageName;
Vibration(IBinder token, long millis, int uid, String packageName) {
this(token, millis, null, 0, uid, packageName);
}
Vibration(IBinder token, long[] pattern, int repeat, int uid, String packageName) {
this(token, 0, pattern, repeat, uid, packageName);
}
private Vibration(IBinder token, long millis, long[] pattern,
int repeat, int uid, String packageName) {
mToken = token;
mTimeout = millis;
mStartTime = SystemClock.uptimeMillis();
mPattern = pattern;
mRepeat = repeat;
mUid = uid;
mPackageName = packageName;
}
public void binderDied() {
synchronized (mVibrations) {
mVibrations.remove(this);
if (this == mCurrentVibration) {
doCancelVibrateLocked();
startNextVibrationLocked();
}
}
}
public boolean hasLongerTimeout(long millis) {
if (mTimeout == 0) {
// This is a pattern, return false to play the simple
// vibration.
return false;
}
if ((mStartTime + mTimeout)
< (SystemClock.uptimeMillis() + millis)) {
// If this vibration will end before the time passed in, let
// the new vibration play.
return false;
}
return true;
}
}
VibratorService(Context context) {
// Reset the hardware to a default state, in case this is a runtime
// restart instead of a fresh boot.
vibratorOff();
mContext = context;
PowerManager pm = (PowerManager)context.getSystemService(
Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
mWakeLock.setReferenceCounted(true);
mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
BatteryStats.SERVICE_NAME));
mVibrations = new LinkedList<Vibration>();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
context.registerReceiver(mIntentReceiver, filter);
}
public void systemReady() {
mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE);
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), true,
new ContentObserver(mH) {
@Override
public void onChange(boolean selfChange) {
updateInputDeviceVibrators();
}
}, UserHandle.USER_ALL);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateInputDeviceVibrators();
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
updateInputDeviceVibrators();
}
public boolean hasVibrator() {
return doVibratorExists();
}
private void verifyIncomingUid(int uid) {
if (uid == Binder.getCallingUid()) {
return;
}
if (Binder.getCallingPid() == Process.myPid()) {
return;
}
mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
Binder.getCallingPid(), Binder.getCallingUid(), null);
}
public void vibrate(int uid, String packageName, long milliseconds, IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
}
verifyIncomingUid(uid);
// We're running in the system server so we cannot crash. Check for a
// timeout of 0 or negative. This will ensure that a vibration has
// either a timeout of > 0 or a non-null pattern.
if (milliseconds <= 0 || (mCurrentVibration != null
&& mCurrentVibration.hasLongerTimeout(milliseconds))) {
// Ignore this vibration since the current vibration will play for
// longer than milliseconds.
return;
}
Vibration vib = new Vibration(token, milliseconds, uid, packageName);
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mVibrations) {
removeVibrationLocked(token);
doCancelVibrateLocked();
mCurrentVibration = vib;
startVibrationLocked(vib);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private boolean isAll0(long[] pattern) {
int N = pattern.length;
for (int i = 0; i < N; i++) {
if (pattern[i] != 0) {
return false;
}
}
return true;
}
public void vibratePattern(int uid, String packageName, long[] pattern, int repeat,
IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
}
verifyIncomingUid(uid);
// so wakelock calls will succeed
long identity = Binder.clearCallingIdentity();
try {
if (false) {
String s = "";
int N = pattern.length;
for (int i=0; i<N; i++) {
s += " " + pattern[i];
}
Slog.i(TAG, "vibrating with pattern: " + s);
}
// we're running in the server so we can't fail
if (pattern == null || pattern.length == 0
|| isAll0(pattern)
|| repeat >= pattern.length || token == null) {
return;
}
Vibration vib = new Vibration(token, pattern, repeat, uid, packageName);
try {
token.linkToDeath(vib, 0);
} catch (RemoteException e) {
return;
}
synchronized (mVibrations) {
removeVibrationLocked(token);
doCancelVibrateLocked();
if (repeat >= 0) {
mVibrations.addFirst(vib);
startNextVibrationLocked();
} else {
// A negative repeat means that this pattern is not meant
// to repeat. Treat it like a simple vibration.
mCurrentVibration = vib;
startVibrationLocked(vib);
}
}
}
finally {
Binder.restoreCallingIdentity(identity);
}
}
public void cancelVibrate(IBinder token) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.VIBRATE,
"cancelVibrate");
// so wakelock calls will succeed
long identity = Binder.clearCallingIdentity();
try {
synchronized (mVibrations) {
final Vibration vib = removeVibrationLocked(token);
if (vib == mCurrentVibration) {
doCancelVibrateLocked();
startNextVibrationLocked();
}
}
}
finally {
Binder.restoreCallingIdentity(identity);
}
}
private final Runnable mVibrationRunnable = new Runnable() {
public void run() {
synchronized (mVibrations) {
doCancelVibrateLocked();
startNextVibrationLocked();
}
}
};
// Lock held on mVibrations
private void doCancelVibrateLocked() {
if (mThread != null) {
synchronized (mThread) {
mThread.mDone = true;
mThread.notify();
}
mThread = null;
}
doVibratorOff();
mH.removeCallbacks(mVibrationRunnable);
reportFinishVibrationLocked();
}
// Lock held on mVibrations
private void startNextVibrationLocked() {
if (mVibrations.size() <= 0) {
reportFinishVibrationLocked();
mCurrentVibration = null;
return;
}
mCurrentVibration = mVibrations.getFirst();
startVibrationLocked(mCurrentVibration);
}
// Lock held on mVibrations
private void startVibrationLocked(final Vibration vib) {
try {
int mode = mAppOpsService.startOperation(AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
if (mode == AppOpsManager.MODE_ERRORED) {
Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
}
mH.post(mVibrationRunnable);
return;
}
} catch (RemoteException e) {
}
if (vib.mTimeout != 0) {
doVibratorOn(vib.mTimeout, vib.mUid);
mH.postDelayed(mVibrationRunnable, vib.mTimeout);
} else {
// mThread better be null here. doCancelVibrate should always be
// called before startNextVibrationLocked or startVibrationLocked.
mThread = new VibrateThread(vib);
mThread.start();
}
}
private void reportFinishVibrationLocked() {
if (mCurrentVibration != null) {
try {
mAppOpsService.finishOperation(AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
mCurrentVibration.mPackageName);
} catch (RemoteException e) {
}
mCurrentVibration = null;
}
}
// Lock held on mVibrations
private Vibration removeVibrationLocked(IBinder token) {
ListIterator<Vibration> iter = mVibrations.listIterator(0);
while (iter.hasNext()) {
Vibration vib = iter.next();
if (vib.mToken == token) {
iter.remove();
unlinkVibration(vib);
return vib;
}
}
// We might be looking for a simple vibration which is only stored in
// mCurrentVibration.
if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
unlinkVibration(mCurrentVibration);
return mCurrentVibration;
}
return null;
}
private void unlinkVibration(Vibration vib) {
if (vib.mPattern != null) {
// If Vibration object has a pattern,
// the Vibration object has also been linkedToDeath.
vib.mToken.unlinkToDeath(vib, 0);
}
}
private void updateInputDeviceVibrators() {
synchronized (mVibrations) {
doCancelVibrateLocked();
synchronized (mInputDeviceVibrators) {
mVibrateInputDevicesSetting = false;
try {
mVibrateInputDevicesSetting = Settings.System.getIntForUser(
mContext.getContentResolver(),
Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
} catch (SettingNotFoundException snfe) {
}
if (mVibrateInputDevicesSetting) {
if (!mInputDeviceListenerRegistered) {
mInputDeviceListenerRegistered = true;
mIm.registerInputDeviceListener(this, mH);
}
} else {
if (mInputDeviceListenerRegistered) {
mInputDeviceListenerRegistered = false;
mIm.unregisterInputDeviceListener(this);
}
}
mInputDeviceVibrators.clear();
if (mVibrateInputDevicesSetting) {
int[] ids = mIm.getInputDeviceIds();
for (int i = 0; i < ids.length; i++) {
InputDevice device = mIm.getInputDevice(ids[i]);
Vibrator vibrator = device.getVibrator();
if (vibrator.hasVibrator()) {
mInputDeviceVibrators.add(vibrator);
}
}
}
}
startNextVibrationLocked();
}
}
@Override
public void onInputDeviceAdded(int deviceId) {
updateInputDeviceVibrators();
}
@Override
public void onInputDeviceChanged(int deviceId) {
updateInputDeviceVibrators();
}
@Override
public void onInputDeviceRemoved(int deviceId) {
updateInputDeviceVibrators();
}
private boolean doVibratorExists() {
// For now, we choose to ignore the presence of input devices that have vibrators
// when reporting whether the device has a vibrator. Applications often use this
// information to decide whether to enable certain features so they expect the
// result of hasVibrator() to be constant. For now, just report whether
// the device has a built-in vibrator.
//synchronized (mInputDeviceVibrators) {
// return !mInputDeviceVibrators.isEmpty() || vibratorExists();
//}
return vibratorExists();
}
private void doVibratorOn(long millis, int uid) {
synchronized (mInputDeviceVibrators) {
try {
mBatteryStatsService.noteVibratorOn(uid, millis);
mCurVibUid = uid;
} catch (RemoteException e) {
}
final int vibratorCount = mInputDeviceVibrators.size();
if (vibratorCount != 0) {
for (int i = 0; i < vibratorCount; i++) {
mInputDeviceVibrators.get(i).vibrate(millis);
}
} else {
vibratorOn(millis);
}
}
}
private void doVibratorOff() {
synchronized (mInputDeviceVibrators) {
if (mCurVibUid >= 0) {
try {
mBatteryStatsService.noteVibratorOff(mCurVibUid);
} catch (RemoteException e) {
}
mCurVibUid = -1;
}
final int vibratorCount = mInputDeviceVibrators.size();
if (vibratorCount != 0) {
for (int i = 0; i < vibratorCount; i++) {
mInputDeviceVibrators.get(i).cancel();
}
} else {
vibratorOff();
}
}
}
private class VibrateThread extends Thread {
final Vibration mVibration;
boolean mDone;
VibrateThread(Vibration vib) {
mVibration = vib;
mTmpWorkSource.set(vib.mUid);
mWakeLock.setWorkSource(mTmpWorkSource);
mWakeLock.acquire();
}
private void delay(long duration) {
if (duration > 0) {
long bedtime = duration + SystemClock.uptimeMillis();
do {
try {
this.wait(duration);
}
catch (InterruptedException e) {
}
if (mDone) {
break;
}
duration = bedtime - SystemClock.uptimeMillis();
} while (duration > 0);
}
}
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
synchronized (this) {
final long[] pattern = mVibration.mPattern;
final int len = pattern.length;
final int repeat = mVibration.mRepeat;
final int uid = mVibration.mUid;
int index = 0;
long duration = 0;
while (!mDone) {
// add off-time duration to any accumulated on-time duration
if (index < len) {
duration += pattern[index++];
}
// sleep until it is time to start the vibrator
delay(duration);
if (mDone) {
break;
}
if (index < len) {
// read on-time duration and start the vibrator
// duration is saved for delay() at top of loop
duration = pattern[index++];
if (duration > 0) {
VibratorService.this.doVibratorOn(duration, uid);
}
} else {
if (repeat < 0) {
break;
} else {
index = repeat;
duration = 0;
}
}
}
mWakeLock.release();
}
synchronized (mVibrations) {
if (mThread == this) {
mThread = null;
}
if (!mDone) {
// If this vibration finished naturally, start the next
// vibration.
mVibrations.remove(mVibration);
unlinkVibration(mVibration);
startNextVibrationLocked();
}
}
}
};
BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
synchronized (mVibrations) {
doCancelVibrateLocked();
int size = mVibrations.size();
for(int i = 0; i < size; i++) {
unlinkVibration(mVibrations.get(i));
}
mVibrations.clear();
}
}
}
};
}