blob: 0446a9edbae93f0acb330f34c1411c77f759c101 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Dianne Hackbornbed30e12009-03-31 14:46:20 -070019import com.android.internal.app.IBatteryStats;
20import com.android.server.am.BatteryStatsService;
21
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.PackageManager;
Joe Onorato95e4f702009-03-24 19:29:09 -070027import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.os.Hardware;
29import android.os.IHardwareService;
Joe Onorato95e4f702009-03-24 19:29:09 -070030import android.os.Message;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.os.Power;
32import android.os.PowerManager;
33import android.os.Process;
34import android.os.RemoteException;
35import android.os.IBinder;
36import android.os.Binder;
37import android.os.SystemClock;
38import android.util.Log;
39
Patrick Scott18dd5f02009-07-02 11:31:12 -040040import java.util.LinkedList;
41import java.util.ListIterator;
42
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043public class HardwareService extends IHardwareService.Stub {
44 private static final String TAG = "HardwareService";
45
The Android Open Source Project10592532009-03-18 17:39:46 -070046 static final int LIGHT_ID_BACKLIGHT = 0;
47 static final int LIGHT_ID_KEYBOARD = 1;
48 static final int LIGHT_ID_BUTTONS = 2;
49 static final int LIGHT_ID_BATTERY = 3;
50 static final int LIGHT_ID_NOTIFICATIONS = 4;
51 static final int LIGHT_ID_ATTENTION = 5;
52
53 static final int LIGHT_FLASH_NONE = 0;
54 static final int LIGHT_FLASH_TIMED = 1;
Eric Olsenf42f15c2009-10-29 16:42:03 -070055 static final int LIGHT_FLASH_HARDWARE = 2;
The Android Open Source Project10592532009-03-18 17:39:46 -070056
Patrick Scott18dd5f02009-07-02 11:31:12 -040057 private final LinkedList<Vibration> mVibrations;
58 private Vibration mCurrentVibration;
59
Joe Onorato95e4f702009-03-24 19:29:09 -070060 private boolean mAttentionLightOn;
61 private boolean mPulsing;
62
Patrick Scott18dd5f02009-07-02 11:31:12 -040063 private class Vibration implements IBinder.DeathRecipient {
64 private final IBinder mToken;
65 private final long mTimeout;
66 private final long mStartTime;
67 private final long[] mPattern;
68 private final int mRepeat;
69
70 Vibration(IBinder token, long millis) {
71 this(token, millis, null, 0);
72 }
73
74 Vibration(IBinder token, long[] pattern, int repeat) {
75 this(token, 0, pattern, repeat);
76 }
77
78 private Vibration(IBinder token, long millis, long[] pattern,
79 int repeat) {
80 mToken = token;
81 mTimeout = millis;
82 mStartTime = SystemClock.uptimeMillis();
83 mPattern = pattern;
84 mRepeat = repeat;
85 }
86
87 public void binderDied() {
88 synchronized (mVibrations) {
89 mVibrations.remove(this);
90 if (this == mCurrentVibration) {
91 doCancelVibrateLocked();
92 startNextVibrationLocked();
93 }
94 }
95 }
96
97 public boolean hasLongerTimeout(long millis) {
98 if (mTimeout == 0) {
99 // This is a pattern, return false to play the simple
100 // vibration.
101 return false;
102 }
103 if ((mStartTime + mTimeout)
104 < (SystemClock.uptimeMillis() + millis)) {
105 // If this vibration will end before the time passed in, let
106 // the new vibration play.
107 return false;
108 }
109 return true;
110 }
111 }
112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 HardwareService(Context context) {
114 // Reset the hardware to a default state, in case this is a runtime
115 // restart instead of a fresh boot.
116 vibratorOff();
117
The Android Open Source Project10592532009-03-18 17:39:46 -0700118 mNativePointer = init_native();
119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 mContext = context;
121 PowerManager pm = (PowerManager)context.getSystemService(
122 Context.POWER_SERVICE);
123 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
124 mWakeLock.setReferenceCounted(true);
125
Patrick Scott18dd5f02009-07-02 11:31:12 -0400126 mVibrations = new LinkedList<Vibration>();
127
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700128 mBatteryStats = BatteryStatsService.getService();
Eric Olsenf42f15c2009-10-29 16:42:03 -0700129
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 IntentFilter filter = new IntentFilter();
131 filter.addAction(Intent.ACTION_SCREEN_OFF);
132 context.registerReceiver(mIntentReceiver, filter);
133 }
134
The Android Open Source Project10592532009-03-18 17:39:46 -0700135 protected void finalize() throws Throwable {
136 finalize_native(mNativePointer);
137 super.finalize();
138 }
139
Patrick Scott18dd5f02009-07-02 11:31:12 -0400140 public void vibrate(long milliseconds, IBinder token) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700141 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
142 != PackageManager.PERMISSION_GRANTED) {
143 throw new SecurityException("Requires VIBRATE permission");
144 }
Patrick Scott24f10762009-08-19 09:03:56 -0400145 // We're running in the system server so we cannot crash. Check for a
146 // timeout of 0 or negative. This will ensure that a vibration has
147 // either a timeout of > 0 or a non-null pattern.
148 if (milliseconds <= 0 || (mCurrentVibration != null
149 && mCurrentVibration.hasLongerTimeout(milliseconds))) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400150 // Ignore this vibration since the current vibration will play for
151 // longer than milliseconds.
152 return;
153 }
154 Vibration vib = new Vibration(token, milliseconds);
155 synchronized (mVibrations) {
156 removeVibrationLocked(token);
157 doCancelVibrateLocked();
158 mCurrentVibration = vib;
159 startVibrationLocked(vib);
160 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 }
162
163 private boolean isAll0(long[] pattern) {
164 int N = pattern.length;
165 for (int i = 0; i < N; i++) {
166 if (pattern[i] != 0) {
167 return false;
168 }
169 }
170 return true;
171 }
172
173 public void vibratePattern(long[] pattern, int repeat, IBinder token) {
174 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
175 != PackageManager.PERMISSION_GRANTED) {
176 throw new SecurityException("Requires VIBRATE permission");
177 }
178 // so wakelock calls will succeed
179 long identity = Binder.clearCallingIdentity();
180 try {
181 if (false) {
182 String s = "";
183 int N = pattern.length;
184 for (int i=0; i<N; i++) {
185 s += " " + pattern[i];
186 }
187 Log.i(TAG, "vibrating with pattern: " + s);
188 }
189
190 // we're running in the server so we can't fail
191 if (pattern == null || pattern.length == 0
192 || isAll0(pattern)
193 || repeat >= pattern.length || token == null) {
194 return;
195 }
196
Patrick Scott18dd5f02009-07-02 11:31:12 -0400197 Vibration vib = new Vibration(token, pattern, repeat);
198 try {
199 token.linkToDeath(vib, 0);
200 } catch (RemoteException e) {
201 return;
202 }
203
204 synchronized (mVibrations) {
205 removeVibrationLocked(token);
206 doCancelVibrateLocked();
207 if (repeat >= 0) {
208 mVibrations.addFirst(vib);
209 startNextVibrationLocked();
210 } else {
211 // A negative repeat means that this pattern is not meant
212 // to repeat. Treat it like a simple vibration.
213 mCurrentVibration = vib;
214 startVibrationLocked(vib);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 }
217 }
218 finally {
219 Binder.restoreCallingIdentity(identity);
220 }
221 }
222
Patrick Scott18dd5f02009-07-02 11:31:12 -0400223 public void cancelVibrate(IBinder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 mContext.enforceCallingOrSelfPermission(
225 android.Manifest.permission.VIBRATE,
226 "cancelVibrate");
227
228 // so wakelock calls will succeed
229 long identity = Binder.clearCallingIdentity();
230 try {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400231 synchronized (mVibrations) {
232 final Vibration vib = removeVibrationLocked(token);
233 if (vib == mCurrentVibration) {
234 doCancelVibrateLocked();
235 startNextVibrationLocked();
236 }
237 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 }
239 finally {
240 Binder.restoreCallingIdentity(identity);
241 }
242 }
Eric Olsenf42f15c2009-10-29 16:42:03 -0700243
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 public boolean getFlashlightEnabled() {
245 return Hardware.getFlashlightEnabled();
246 }
Eric Olsenf42f15c2009-10-29 16:42:03 -0700247
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 public void setFlashlightEnabled(boolean on) {
Eric Olsenf42f15c2009-10-29 16:42:03 -0700249 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.FLASHLIGHT)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 != PackageManager.PERMISSION_GRANTED &&
Eric Olsenf42f15c2009-10-29 16:42:03 -0700251 mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 != PackageManager.PERMISSION_GRANTED) {
253 throw new SecurityException("Requires FLASHLIGHT or HARDWARE_TEST permission");
254 }
255 Hardware.setFlashlightEnabled(on);
256 }
257
258 public void enableCameraFlash(int milliseconds) {
Eric Olsenf42f15c2009-10-29 16:42:03 -0700259 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.CAMERA)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 != PackageManager.PERMISSION_GRANTED &&
Eric Olsenf42f15c2009-10-29 16:42:03 -0700261 mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 != PackageManager.PERMISSION_GRANTED) {
263 throw new SecurityException("Requires CAMERA or HARDWARE_TEST permission");
264 }
265 Hardware.enableCameraFlash(milliseconds);
266 }
267
The Android Open Source Project10592532009-03-18 17:39:46 -0700268 void setLightOff_UNCHECKED(int light) {
269 setLight_native(mNativePointer, light, 0, LIGHT_FLASH_NONE, 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 }
271
The Android Open Source Project10592532009-03-18 17:39:46 -0700272 void setLightBrightness_UNCHECKED(int light, int brightness) {
273 int b = brightness & 0x000000ff;
274 b = 0xff000000 | (b << 16) | (b << 8) | b;
275 setLight_native(mNativePointer, light, b, LIGHT_FLASH_NONE, 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 }
277
The Android Open Source Project10592532009-03-18 17:39:46 -0700278 void setLightColor_UNCHECKED(int light, int color) {
279 setLight_native(mNativePointer, light, color, LIGHT_FLASH_NONE, 0, 0);
280 }
281
282 void setLightFlashing_UNCHECKED(int light, int color, int mode, int onMS, int offMS) {
283 setLight_native(mNativePointer, light, color, mode, onMS, offMS);
284 }
285
Eric Olsenf42f15c2009-10-29 16:42:03 -0700286 void setAutoBrightness_UNCHECKED(boolean on) {
287 if (mAutoBrightnessAvailable) {
288 setAutoBrightness_native(mNativePointer, on);
289 }
290 }
291
292 public void setAttentionLight(boolean on, int color) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700293 // Not worthy of a permission. We shouldn't have a flashlight permission.
Joe Onorato95e4f702009-03-24 19:29:09 -0700294 synchronized (this) {
295 mAttentionLightOn = on;
296 mPulsing = false;
Eric Olsenf42f15c2009-10-29 16:42:03 -0700297 setLight_native(mNativePointer, LIGHT_ID_ATTENTION, color,
298 LIGHT_FLASH_HARDWARE, on ? 3 : 0, 0);
Joe Onorato95e4f702009-03-24 19:29:09 -0700299 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 }
301
Joe Onorato95e4f702009-03-24 19:29:09 -0700302 public void pulseBreathingLight() {
303 synchronized (this) {
304 // HACK: Added at the last minute of cupcake -- design this better;
305 // Don't reuse the attention light -- make another one.
306 if (false) {
307 Log.d(TAG, "pulseBreathingLight mAttentionLightOn=" + mAttentionLightOn
308 + " mPulsing=" + mPulsing);
309 }
310 if (!mAttentionLightOn && !mPulsing) {
311 mPulsing = true;
Eric Olsenf42f15c2009-10-29 16:42:03 -0700312 setLight_native(mNativePointer, LIGHT_ID_ATTENTION, 0x00ffffff,
313 LIGHT_FLASH_HARDWARE, 7, 0);
Joe Onorato95e4f702009-03-24 19:29:09 -0700314 mH.sendMessageDelayed(Message.obtain(mH, 1), 3000);
315 }
316 }
317 }
318
319 private Handler mH = new Handler() {
320 @Override
321 public void handleMessage(Message msg) {
322 synchronized (this) {
323 if (false) {
324 Log.d(TAG, "pulse cleanup handler firing mPulsing=" + mPulsing);
325 }
326 if (mPulsing) {
327 mPulsing = false;
328 setLight_native(mNativePointer, LIGHT_ID_ATTENTION,
329 mAttentionLightOn ? 0xffffffff : 0,
330 LIGHT_FLASH_NONE, 0, 0);
331 }
332 }
333 }
334 };
335
Patrick Scott18dd5f02009-07-02 11:31:12 -0400336 private final Runnable mVibrationRunnable = new Runnable() {
337 public void run() {
338 synchronized (mVibrations) {
339 doCancelVibrateLocked();
340 startNextVibrationLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400342 }
343 };
344
345 // Lock held on mVibrations
346 private void doCancelVibrateLocked() {
347 if (mThread != null) {
348 synchronized (mThread) {
349 mThread.mDone = true;
350 mThread.notify();
351 }
352 mThread = null;
353 }
354 vibratorOff();
355 mH.removeCallbacks(mVibrationRunnable);
356 }
357
358 // Lock held on mVibrations
359 private void startNextVibrationLocked() {
360 if (mVibrations.size() <= 0) {
361 return;
362 }
363 mCurrentVibration = mVibrations.getFirst();
364 startVibrationLocked(mCurrentVibration);
365 }
366
367 // Lock held on mVibrations
368 private void startVibrationLocked(final Vibration vib) {
369 if (vib.mTimeout != 0) {
370 vibratorOn(vib.mTimeout);
371 mH.postDelayed(mVibrationRunnable, vib.mTimeout);
372 } else {
373 // mThread better be null here. doCancelVibrate should always be
374 // called before startNextVibrationLocked or startVibrationLocked.
375 mThread = new VibrateThread(vib);
376 mThread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 }
378 }
379
Patrick Scott18dd5f02009-07-02 11:31:12 -0400380 // Lock held on mVibrations
381 private Vibration removeVibrationLocked(IBinder token) {
382 ListIterator<Vibration> iter = mVibrations.listIterator(0);
383 while (iter.hasNext()) {
384 Vibration vib = iter.next();
385 if (vib.mToken == token) {
386 iter.remove();
387 return vib;
388 }
389 }
390 // We might be looking for a simple vibration which is only stored in
391 // mCurrentVibration.
392 if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
393 return mCurrentVibration;
394 }
395 return null;
396 }
397
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 private class VibrateThread extends Thread {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400399 final Vibration mVibration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 boolean mDone;
Eric Olsenf42f15c2009-10-29 16:42:03 -0700401
Patrick Scott18dd5f02009-07-02 11:31:12 -0400402 VibrateThread(Vibration vib) {
403 mVibration = vib;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 mWakeLock.acquire();
405 }
406
407 private void delay(long duration) {
408 if (duration > 0) {
409 long bedtime = SystemClock.uptimeMillis();
410 do {
411 try {
412 this.wait(duration);
413 }
414 catch (InterruptedException e) {
415 }
416 if (mDone) {
417 break;
418 }
419 duration = duration
420 - SystemClock.uptimeMillis() - bedtime;
421 } while (duration > 0);
422 }
423 }
424
425 public void run() {
426 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
427 synchronized (this) {
428 int index = 0;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400429 long[] pattern = mVibration.mPattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 int len = pattern.length;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400431 int repeat = mVibration.mRepeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 long duration = 0;
433
434 while (!mDone) {
Eric Olsenf42f15c2009-10-29 16:42:03 -0700435 // add off-time duration to any accumulated on-time duration
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 if (index < len) {
437 duration += pattern[index++];
438 }
439
440 // sleep until it is time to start the vibrator
441 delay(duration);
442 if (mDone) {
443 break;
444 }
445
446 if (index < len) {
447 // read on-time duration and start the vibrator
448 // duration is saved for delay() at top of loop
449 duration = pattern[index++];
450 if (duration > 0) {
451 HardwareService.this.vibratorOn(duration);
452 }
453 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400454 if (repeat < 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 break;
456 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400457 index = repeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 duration = 0;
459 }
460 }
461 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 mWakeLock.release();
463 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400464 synchronized (mVibrations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 if (mThread == this) {
466 mThread = null;
467 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400468 if (!mDone) {
469 // If this vibration finished naturally, start the next
470 // vibration.
471 mVibrations.remove(mVibration);
472 startNextVibrationLocked();
473 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 }
475 }
476 };
477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
479 public void onReceive(Context context, Intent intent) {
480 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400481 synchronized (mVibrations) {
482 doCancelVibrateLocked();
483 mVibrations.clear();
484 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 }
486 }
487 };
Eric Olsenf42f15c2009-10-29 16:42:03 -0700488
The Android Open Source Project10592532009-03-18 17:39:46 -0700489 private static native int init_native();
490 private static native void finalize_native(int ptr);
491
492 private static native void setLight_native(int ptr, int light, int color, int mode,
493 int onMS, int offMS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700495 private final Context mContext;
496 private final PowerManager.WakeLock mWakeLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700498 private final IBatteryStats mBatteryStats;
Eric Olsenf42f15c2009-10-29 16:42:03 -0700499
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 volatile VibrateThread mThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501
The Android Open Source Project10592532009-03-18 17:39:46 -0700502 private int mNativePointer;
503
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 native static void vibratorOn(long milliseconds);
505 native static void vibratorOff();
506}