blob: 6ac72e0d57c81095e2cd5bfebcd433e1402157e0 [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;
55
Patrick Scott18dd5f02009-07-02 11:31:12 -040056 private final LinkedList<Vibration> mVibrations;
57 private Vibration mCurrentVibration;
58
Joe Onorato95e4f702009-03-24 19:29:09 -070059 private boolean mAttentionLightOn;
60 private boolean mPulsing;
61
Patrick Scott18dd5f02009-07-02 11:31:12 -040062 private class Vibration implements IBinder.DeathRecipient {
63 private final IBinder mToken;
64 private final long mTimeout;
65 private final long mStartTime;
66 private final long[] mPattern;
67 private final int mRepeat;
68
69 Vibration(IBinder token, long millis) {
70 this(token, millis, null, 0);
71 }
72
73 Vibration(IBinder token, long[] pattern, int repeat) {
74 this(token, 0, pattern, repeat);
75 }
76
77 private Vibration(IBinder token, long millis, long[] pattern,
78 int repeat) {
79 mToken = token;
80 mTimeout = millis;
81 mStartTime = SystemClock.uptimeMillis();
82 mPattern = pattern;
83 mRepeat = repeat;
84 }
85
86 public void binderDied() {
87 synchronized (mVibrations) {
88 mVibrations.remove(this);
89 if (this == mCurrentVibration) {
90 doCancelVibrateLocked();
91 startNextVibrationLocked();
92 }
93 }
94 }
95
96 public boolean hasLongerTimeout(long millis) {
97 if (mTimeout == 0) {
98 // This is a pattern, return false to play the simple
99 // vibration.
100 return false;
101 }
102 if ((mStartTime + mTimeout)
103 < (SystemClock.uptimeMillis() + millis)) {
104 // If this vibration will end before the time passed in, let
105 // the new vibration play.
106 return false;
107 }
108 return true;
109 }
110 }
111
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 HardwareService(Context context) {
113 // Reset the hardware to a default state, in case this is a runtime
114 // restart instead of a fresh boot.
115 vibratorOff();
116
The Android Open Source Project10592532009-03-18 17:39:46 -0700117 mNativePointer = init_native();
118
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 mContext = context;
120 PowerManager pm = (PowerManager)context.getSystemService(
121 Context.POWER_SERVICE);
122 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
123 mWakeLock.setReferenceCounted(true);
124
Patrick Scott18dd5f02009-07-02 11:31:12 -0400125 mVibrations = new LinkedList<Vibration>();
126
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700127 mBatteryStats = BatteryStatsService.getService();
128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 IntentFilter filter = new IntentFilter();
130 filter.addAction(Intent.ACTION_SCREEN_OFF);
131 context.registerReceiver(mIntentReceiver, filter);
132 }
133
The Android Open Source Project10592532009-03-18 17:39:46 -0700134 protected void finalize() throws Throwable {
135 finalize_native(mNativePointer);
136 super.finalize();
137 }
138
Patrick Scott18dd5f02009-07-02 11:31:12 -0400139 public void vibrate(long milliseconds, IBinder token) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700140 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
141 != PackageManager.PERMISSION_GRANTED) {
142 throw new SecurityException("Requires VIBRATE permission");
143 }
Patrick Scott24f10762009-08-19 09:03:56 -0400144 // We're running in the system server so we cannot crash. Check for a
145 // timeout of 0 or negative. This will ensure that a vibration has
146 // either a timeout of > 0 or a non-null pattern.
147 if (milliseconds <= 0 || (mCurrentVibration != null
148 && mCurrentVibration.hasLongerTimeout(milliseconds))) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400149 // Ignore this vibration since the current vibration will play for
150 // longer than milliseconds.
151 return;
152 }
153 Vibration vib = new Vibration(token, milliseconds);
154 synchronized (mVibrations) {
155 removeVibrationLocked(token);
156 doCancelVibrateLocked();
157 mCurrentVibration = vib;
158 startVibrationLocked(vib);
159 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 }
161
162 private boolean isAll0(long[] pattern) {
163 int N = pattern.length;
164 for (int i = 0; i < N; i++) {
165 if (pattern[i] != 0) {
166 return false;
167 }
168 }
169 return true;
170 }
171
172 public void vibratePattern(long[] pattern, int repeat, IBinder token) {
173 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
174 != PackageManager.PERMISSION_GRANTED) {
175 throw new SecurityException("Requires VIBRATE permission");
176 }
177 // so wakelock calls will succeed
178 long identity = Binder.clearCallingIdentity();
179 try {
180 if (false) {
181 String s = "";
182 int N = pattern.length;
183 for (int i=0; i<N; i++) {
184 s += " " + pattern[i];
185 }
186 Log.i(TAG, "vibrating with pattern: " + s);
187 }
188
189 // we're running in the server so we can't fail
190 if (pattern == null || pattern.length == 0
191 || isAll0(pattern)
192 || repeat >= pattern.length || token == null) {
193 return;
194 }
195
Patrick Scott18dd5f02009-07-02 11:31:12 -0400196 Vibration vib = new Vibration(token, pattern, repeat);
197 try {
198 token.linkToDeath(vib, 0);
199 } catch (RemoteException e) {
200 return;
201 }
202
203 synchronized (mVibrations) {
204 removeVibrationLocked(token);
205 doCancelVibrateLocked();
206 if (repeat >= 0) {
207 mVibrations.addFirst(vib);
208 startNextVibrationLocked();
209 } else {
210 // A negative repeat means that this pattern is not meant
211 // to repeat. Treat it like a simple vibration.
212 mCurrentVibration = vib;
213 startVibrationLocked(vib);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 }
216 }
217 finally {
218 Binder.restoreCallingIdentity(identity);
219 }
220 }
221
Patrick Scott18dd5f02009-07-02 11:31:12 -0400222 public void cancelVibrate(IBinder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 mContext.enforceCallingOrSelfPermission(
224 android.Manifest.permission.VIBRATE,
225 "cancelVibrate");
226
227 // so wakelock calls will succeed
228 long identity = Binder.clearCallingIdentity();
229 try {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400230 synchronized (mVibrations) {
231 final Vibration vib = removeVibrationLocked(token);
232 if (vib == mCurrentVibration) {
233 doCancelVibrateLocked();
234 startNextVibrationLocked();
235 }
236 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 }
238 finally {
239 Binder.restoreCallingIdentity(identity);
240 }
241 }
242
243 public boolean getFlashlightEnabled() {
244 return Hardware.getFlashlightEnabled();
245 }
246
247 public void setFlashlightEnabled(boolean on) {
248 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.FLASHLIGHT)
249 != PackageManager.PERMISSION_GRANTED &&
250 mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
251 != PackageManager.PERMISSION_GRANTED) {
252 throw new SecurityException("Requires FLASHLIGHT or HARDWARE_TEST permission");
253 }
254 Hardware.setFlashlightEnabled(on);
255 }
256
257 public void enableCameraFlash(int milliseconds) {
258 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.CAMERA)
259 != PackageManager.PERMISSION_GRANTED &&
260 mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
261 != PackageManager.PERMISSION_GRANTED) {
262 throw new SecurityException("Requires CAMERA or HARDWARE_TEST permission");
263 }
264 Hardware.enableCameraFlash(milliseconds);
265 }
266
The Android Open Source Project10592532009-03-18 17:39:46 -0700267 public void setBacklights(int brightness) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
269 != PackageManager.PERMISSION_GRANTED) {
270 throw new SecurityException("Requires HARDWARE_TEST permission");
271 }
272 // Don't let applications turn the screen all the way off
273 brightness = Math.max(brightness, Power.BRIGHTNESS_DIM);
The Android Open Source Project10592532009-03-18 17:39:46 -0700274 setLightBrightness_UNCHECKED(LIGHT_ID_BACKLIGHT, brightness);
275 setLightBrightness_UNCHECKED(LIGHT_ID_KEYBOARD, brightness);
276 setLightBrightness_UNCHECKED(LIGHT_ID_BUTTONS, brightness);
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700277 long identity = Binder.clearCallingIdentity();
278 try {
279 mBatteryStats.noteScreenBrightness(brightness);
280 } catch (RemoteException e) {
281 Log.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e);
282 } finally {
283 Binder.restoreCallingIdentity(identity);
284 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 }
286
The Android Open Source Project10592532009-03-18 17:39:46 -0700287 void setLightOff_UNCHECKED(int light) {
288 setLight_native(mNativePointer, light, 0, LIGHT_FLASH_NONE, 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 }
290
The Android Open Source Project10592532009-03-18 17:39:46 -0700291 void setLightBrightness_UNCHECKED(int light, int brightness) {
292 int b = brightness & 0x000000ff;
293 b = 0xff000000 | (b << 16) | (b << 8) | b;
294 setLight_native(mNativePointer, light, b, LIGHT_FLASH_NONE, 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 }
296
The Android Open Source Project10592532009-03-18 17:39:46 -0700297 void setLightColor_UNCHECKED(int light, int color) {
298 setLight_native(mNativePointer, light, color, LIGHT_FLASH_NONE, 0, 0);
299 }
300
301 void setLightFlashing_UNCHECKED(int light, int color, int mode, int onMS, int offMS) {
302 setLight_native(mNativePointer, light, color, mode, onMS, offMS);
303 }
304
305 public void setAttentionLight(boolean on) {
306 // Not worthy of a permission. We shouldn't have a flashlight permission.
Joe Onorato95e4f702009-03-24 19:29:09 -0700307 synchronized (this) {
308 mAttentionLightOn = on;
309 mPulsing = false;
310 setLight_native(mNativePointer, LIGHT_ID_ATTENTION, on ? 0xffffffff : 0,
311 LIGHT_FLASH_NONE, 0, 0);
312 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 }
314
Joe Onorato95e4f702009-03-24 19:29:09 -0700315 public void pulseBreathingLight() {
316 synchronized (this) {
317 // HACK: Added at the last minute of cupcake -- design this better;
318 // Don't reuse the attention light -- make another one.
319 if (false) {
320 Log.d(TAG, "pulseBreathingLight mAttentionLightOn=" + mAttentionLightOn
321 + " mPulsing=" + mPulsing);
322 }
323 if (!mAttentionLightOn && !mPulsing) {
324 mPulsing = true;
325 setLight_native(mNativePointer, LIGHT_ID_ATTENTION, 0xff101010,
326 LIGHT_FLASH_NONE, 0, 0);
327 mH.sendMessageDelayed(Message.obtain(mH, 1), 3000);
328 }
329 }
330 }
331
332 private Handler mH = new Handler() {
333 @Override
334 public void handleMessage(Message msg) {
335 synchronized (this) {
336 if (false) {
337 Log.d(TAG, "pulse cleanup handler firing mPulsing=" + mPulsing);
338 }
339 if (mPulsing) {
340 mPulsing = false;
341 setLight_native(mNativePointer, LIGHT_ID_ATTENTION,
342 mAttentionLightOn ? 0xffffffff : 0,
343 LIGHT_FLASH_NONE, 0, 0);
344 }
345 }
346 }
347 };
348
Patrick Scott18dd5f02009-07-02 11:31:12 -0400349 private final Runnable mVibrationRunnable = new Runnable() {
350 public void run() {
351 synchronized (mVibrations) {
352 doCancelVibrateLocked();
353 startNextVibrationLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400355 }
356 };
357
358 // Lock held on mVibrations
359 private void doCancelVibrateLocked() {
360 if (mThread != null) {
361 synchronized (mThread) {
362 mThread.mDone = true;
363 mThread.notify();
364 }
365 mThread = null;
366 }
367 vibratorOff();
368 mH.removeCallbacks(mVibrationRunnable);
369 }
370
371 // Lock held on mVibrations
372 private void startNextVibrationLocked() {
373 if (mVibrations.size() <= 0) {
374 return;
375 }
376 mCurrentVibration = mVibrations.getFirst();
377 startVibrationLocked(mCurrentVibration);
378 }
379
380 // Lock held on mVibrations
381 private void startVibrationLocked(final Vibration vib) {
382 if (vib.mTimeout != 0) {
383 vibratorOn(vib.mTimeout);
384 mH.postDelayed(mVibrationRunnable, vib.mTimeout);
385 } else {
386 // mThread better be null here. doCancelVibrate should always be
387 // called before startNextVibrationLocked or startVibrationLocked.
388 mThread = new VibrateThread(vib);
389 mThread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 }
391 }
392
Patrick Scott18dd5f02009-07-02 11:31:12 -0400393 // Lock held on mVibrations
394 private Vibration removeVibrationLocked(IBinder token) {
395 ListIterator<Vibration> iter = mVibrations.listIterator(0);
396 while (iter.hasNext()) {
397 Vibration vib = iter.next();
398 if (vib.mToken == token) {
399 iter.remove();
400 return vib;
401 }
402 }
403 // We might be looking for a simple vibration which is only stored in
404 // mCurrentVibration.
405 if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
406 return mCurrentVibration;
407 }
408 return null;
409 }
410
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 private class VibrateThread extends Thread {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400412 final Vibration mVibration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 boolean mDone;
414
Patrick Scott18dd5f02009-07-02 11:31:12 -0400415 VibrateThread(Vibration vib) {
416 mVibration = vib;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 mWakeLock.acquire();
418 }
419
420 private void delay(long duration) {
421 if (duration > 0) {
422 long bedtime = SystemClock.uptimeMillis();
423 do {
424 try {
425 this.wait(duration);
426 }
427 catch (InterruptedException e) {
428 }
429 if (mDone) {
430 break;
431 }
432 duration = duration
433 - SystemClock.uptimeMillis() - bedtime;
434 } while (duration > 0);
435 }
436 }
437
438 public void run() {
439 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
440 synchronized (this) {
441 int index = 0;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400442 long[] pattern = mVibration.mPattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 int len = pattern.length;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400444 int repeat = mVibration.mRepeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 long duration = 0;
446
447 while (!mDone) {
448 // add off-time duration to any accumulated on-time duration
449 if (index < len) {
450 duration += pattern[index++];
451 }
452
453 // sleep until it is time to start the vibrator
454 delay(duration);
455 if (mDone) {
456 break;
457 }
458
459 if (index < len) {
460 // read on-time duration and start the vibrator
461 // duration is saved for delay() at top of loop
462 duration = pattern[index++];
463 if (duration > 0) {
464 HardwareService.this.vibratorOn(duration);
465 }
466 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400467 if (repeat < 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 break;
469 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400470 index = repeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 duration = 0;
472 }
473 }
474 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 mWakeLock.release();
476 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400477 synchronized (mVibrations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 if (mThread == this) {
479 mThread = null;
480 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400481 if (!mDone) {
482 // If this vibration finished naturally, start the next
483 // vibration.
484 mVibrations.remove(mVibration);
485 startNextVibrationLocked();
486 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 }
488 }
489 };
490
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
492 public void onReceive(Context context, Intent intent) {
493 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400494 synchronized (mVibrations) {
495 doCancelVibrateLocked();
496 mVibrations.clear();
497 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 }
499 }
500 };
The Android Open Source Project10592532009-03-18 17:39:46 -0700501
502 private static native int init_native();
503 private static native void finalize_native(int ptr);
504
505 private static native void setLight_native(int ptr, int light, int color, int mode,
506 int onMS, int offMS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700508 private final Context mContext;
509 private final PowerManager.WakeLock mWakeLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700511 private final IBatteryStats mBatteryStats;
512
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 volatile VibrateThread mThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514
The Android Open Source Project10592532009-03-18 17:39:46 -0700515 private int mNativePointer;
516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 native static void vibratorOn(long milliseconds);
518 native static void vibratorOff();
519}