1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server; 18 19import android.content.BroadcastReceiver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.IntentFilter; 23import android.content.pm.PackageManager; 24import android.os.Handler; 25import android.os.IVibratorService; 26import android.os.PowerManager; 27import android.os.Process; 28import android.os.RemoteException; 29import android.os.IBinder; 30import android.os.Binder; 31import android.os.SystemClock; 32import android.os.WorkSource; 33import android.util.Slog; 34 35import java.util.LinkedList; 36import java.util.ListIterator; 37 38public class VibratorService extends IVibratorService.Stub { 39 private static final String TAG = "VibratorService"; 40 41 private final LinkedList<Vibration> mVibrations; 42 private Vibration mCurrentVibration; 43 private final WorkSource mTmpWorkSource = new WorkSource(); 44 45 private class Vibration implements IBinder.DeathRecipient { 46 private final IBinder mToken; 47 private final long mTimeout; 48 private final long mStartTime; 49 private final long[] mPattern; 50 private final int mRepeat; 51 private final int mUid; 52 53 Vibration(IBinder token, long millis, int uid) { 54 this(token, millis, null, 0, uid); 55 } 56 57 Vibration(IBinder token, long[] pattern, int repeat, int uid) { 58 this(token, 0, pattern, repeat, uid); 59 } 60 61 private Vibration(IBinder token, long millis, long[] pattern, 62 int repeat, int uid) { 63 mToken = token; 64 mTimeout = millis; 65 mStartTime = SystemClock.uptimeMillis(); 66 mPattern = pattern; 67 mRepeat = repeat; 68 mUid = uid; 69 } 70 71 public void binderDied() { 72 synchronized (mVibrations) { 73 mVibrations.remove(this); 74 if (this == mCurrentVibration) { 75 doCancelVibrateLocked(); 76 startNextVibrationLocked(); 77 } 78 } 79 } 80 81 public boolean hasLongerTimeout(long millis) { 82 if (mTimeout == 0) { 83 // This is a pattern, return false to play the simple 84 // vibration. 85 return false; 86 } 87 if ((mStartTime + mTimeout) 88 < (SystemClock.uptimeMillis() + millis)) { 89 // If this vibration will end before the time passed in, let 90 // the new vibration play. 91 return false; 92 } 93 return true; 94 } 95 } 96 97 VibratorService(Context context) { 98 // Reset the hardware to a default state, in case this is a runtime 99 // restart instead of a fresh boot. 100 vibratorOff(); 101 102 mContext = context; 103 PowerManager pm = (PowerManager)context.getSystemService( 104 Context.POWER_SERVICE); 105 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); 106 mWakeLock.setReferenceCounted(true); 107 108 mVibrations = new LinkedList<Vibration>(); 109 110 IntentFilter filter = new IntentFilter(); 111 filter.addAction(Intent.ACTION_SCREEN_OFF); 112 context.registerReceiver(mIntentReceiver, filter); 113 } 114 115 public void vibrate(long milliseconds, IBinder token) { 116 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) 117 != PackageManager.PERMISSION_GRANTED) { 118 throw new SecurityException("Requires VIBRATE permission"); 119 } 120 int uid = Binder.getCallingUid(); 121 // We're running in the system server so we cannot crash. Check for a 122 // timeout of 0 or negative. This will ensure that a vibration has 123 // either a timeout of > 0 or a non-null pattern. 124 if (milliseconds <= 0 || (mCurrentVibration != null 125 && mCurrentVibration.hasLongerTimeout(milliseconds))) { 126 // Ignore this vibration since the current vibration will play for 127 // longer than milliseconds. 128 return; 129 } 130 Vibration vib = new Vibration(token, milliseconds, uid); 131 synchronized (mVibrations) { 132 removeVibrationLocked(token); 133 doCancelVibrateLocked(); 134 mCurrentVibration = vib; 135 startVibrationLocked(vib); 136 } 137 } 138 139 private boolean isAll0(long[] pattern) { 140 int N = pattern.length; 141 for (int i = 0; i < N; i++) { 142 if (pattern[i] != 0) { 143 return false; 144 } 145 } 146 return true; 147 } 148 149 public void vibratePattern(long[] pattern, int repeat, IBinder token) { 150 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) 151 != PackageManager.PERMISSION_GRANTED) { 152 throw new SecurityException("Requires VIBRATE permission"); 153 } 154 int uid = Binder.getCallingUid(); 155 // so wakelock calls will succeed 156 long identity = Binder.clearCallingIdentity(); 157 try { 158 if (false) { 159 String s = ""; 160 int N = pattern.length; 161 for (int i=0; i<N; i++) { 162 s += " " + pattern[i]; 163 } 164 Slog.i(TAG, "vibrating with pattern: " + s); 165 } 166 167 // we're running in the server so we can't fail 168 if (pattern == null || pattern.length == 0 169 || isAll0(pattern) 170 || repeat >= pattern.length || token == null) { 171 return; 172 } 173 174 Vibration vib = new Vibration(token, pattern, repeat, uid); 175 try { 176 token.linkToDeath(vib, 0); 177 } catch (RemoteException e) { 178 return; 179 } 180 181 synchronized (mVibrations) { 182 removeVibrationLocked(token); 183 doCancelVibrateLocked(); 184 if (repeat >= 0) { 185 mVibrations.addFirst(vib); 186 startNextVibrationLocked(); 187 } else { 188 // A negative repeat means that this pattern is not meant 189 // to repeat. Treat it like a simple vibration. 190 mCurrentVibration = vib; 191 startVibrationLocked(vib); 192 } 193 } 194 } 195 finally { 196 Binder.restoreCallingIdentity(identity); 197 } 198 } 199 200 public void cancelVibrate(IBinder token) { 201 mContext.enforceCallingOrSelfPermission( 202 android.Manifest.permission.VIBRATE, 203 "cancelVibrate"); 204 205 // so wakelock calls will succeed 206 long identity = Binder.clearCallingIdentity(); 207 try { 208 synchronized (mVibrations) { 209 final Vibration vib = removeVibrationLocked(token); 210 if (vib == mCurrentVibration) { 211 doCancelVibrateLocked(); 212 startNextVibrationLocked(); 213 } 214 } 215 } 216 finally { 217 Binder.restoreCallingIdentity(identity); 218 } 219 } 220 221 private final Runnable mVibrationRunnable = new Runnable() { 222 public void run() { 223 synchronized (mVibrations) { 224 doCancelVibrateLocked(); 225 startNextVibrationLocked(); 226 } 227 } 228 }; 229 230 // Lock held on mVibrations 231 private void doCancelVibrateLocked() { 232 if (mThread != null) { 233 synchronized (mThread) { 234 mThread.mDone = true; 235 mThread.notify(); 236 } 237 mThread = null; 238 } 239 vibratorOff(); 240 mH.removeCallbacks(mVibrationRunnable); 241 } 242 243 // Lock held on mVibrations 244 private void startNextVibrationLocked() { 245 if (mVibrations.size() <= 0) { 246 return; 247 } 248 mCurrentVibration = mVibrations.getFirst(); 249 startVibrationLocked(mCurrentVibration); 250 } 251 252 // Lock held on mVibrations 253 private void startVibrationLocked(final Vibration vib) { 254 if (vib.mTimeout != 0) { 255 vibratorOn(vib.mTimeout); 256 mH.postDelayed(mVibrationRunnable, vib.mTimeout); 257 } else { 258 // mThread better be null here. doCancelVibrate should always be 259 // called before startNextVibrationLocked or startVibrationLocked. 260 mThread = new VibrateThread(vib); 261 mThread.start(); 262 } 263 } 264 265 // Lock held on mVibrations 266 private Vibration removeVibrationLocked(IBinder token) { 267 ListIterator<Vibration> iter = mVibrations.listIterator(0); 268 while (iter.hasNext()) { 269 Vibration vib = iter.next(); 270 if (vib.mToken == token) { 271 iter.remove(); 272 return vib; 273 } 274 } 275 // We might be looking for a simple vibration which is only stored in 276 // mCurrentVibration. 277 if (mCurrentVibration != null && mCurrentVibration.mToken == token) { 278 return mCurrentVibration; 279 } 280 return null; 281 } 282 283 private class VibrateThread extends Thread { 284 final Vibration mVibration; 285 boolean mDone; 286 287 VibrateThread(Vibration vib) { 288 mVibration = vib; 289 mTmpWorkSource.set(vib.mUid); 290 mWakeLock.setWorkSource(mTmpWorkSource); 291 mWakeLock.acquire(); 292 } 293 294 private void delay(long duration) { 295 if (duration > 0) { 296 long bedtime = SystemClock.uptimeMillis(); 297 do { 298 try { 299 this.wait(duration); 300 } 301 catch (InterruptedException e) { 302 } 303 if (mDone) { 304 break; 305 } 306 duration = duration 307 - SystemClock.uptimeMillis() - bedtime; 308 } while (duration > 0); 309 } 310 } 311 312 public void run() { 313 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); 314 synchronized (this) { 315 int index = 0; 316 long[] pattern = mVibration.mPattern; 317 int len = pattern.length; 318 int repeat = mVibration.mRepeat; 319 long duration = 0; 320 321 while (!mDone) { 322 // add off-time duration to any accumulated on-time duration 323 if (index < len) { 324 duration += pattern[index++]; 325 } 326 327 // sleep until it is time to start the vibrator 328 delay(duration); 329 if (mDone) { 330 break; 331 } 332 333 if (index < len) { 334 // read on-time duration and start the vibrator 335 // duration is saved for delay() at top of loop 336 duration = pattern[index++]; 337 if (duration > 0) { 338 VibratorService.this.vibratorOn(duration); 339 } 340 } else { 341 if (repeat < 0) { 342 break; 343 } else { 344 index = repeat; 345 duration = 0; 346 } 347 } 348 } 349 mWakeLock.release(); 350 } 351 synchronized (mVibrations) { 352 if (mThread == this) { 353 mThread = null; 354 } 355 if (!mDone) { 356 // If this vibration finished naturally, start the next 357 // vibration. 358 mVibrations.remove(mVibration); 359 startNextVibrationLocked(); 360 } 361 } 362 } 363 }; 364 365 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 366 public void onReceive(Context context, Intent intent) { 367 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { 368 synchronized (mVibrations) { 369 doCancelVibrateLocked(); 370 mVibrations.clear(); 371 } 372 } 373 } 374 }; 375 376 private Handler mH = new Handler(); 377 378 private final Context mContext; 379 private final PowerManager.WakeLock mWakeLock; 380 381 volatile VibrateThread mThread; 382 383 native static void vibratorOn(long milliseconds); 384 native static void vibratorOff(); 385} 386