blob: 01daae344197ff2f1088936e3a4aa0f23e565bac [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
Dan Murphy951764b2009-08-27 14:59:03 -050062 private boolean mAutoBrightnessAvailable;
63
Patrick Scott18dd5f02009-07-02 11:31:12 -040064 private class Vibration implements IBinder.DeathRecipient {
65 private final IBinder mToken;
66 private final long mTimeout;
67 private final long mStartTime;
68 private final long[] mPattern;
69 private final int mRepeat;
70
71 Vibration(IBinder token, long millis) {
72 this(token, millis, null, 0);
73 }
74
75 Vibration(IBinder token, long[] pattern, int repeat) {
76 this(token, 0, pattern, repeat);
77 }
78
79 private Vibration(IBinder token, long millis, long[] pattern,
80 int repeat) {
81 mToken = token;
82 mTimeout = millis;
83 mStartTime = SystemClock.uptimeMillis();
84 mPattern = pattern;
85 mRepeat = repeat;
86 }
87
88 public void binderDied() {
89 synchronized (mVibrations) {
90 mVibrations.remove(this);
91 if (this == mCurrentVibration) {
92 doCancelVibrateLocked();
93 startNextVibrationLocked();
94 }
95 }
96 }
97
98 public boolean hasLongerTimeout(long millis) {
99 if (mTimeout == 0) {
100 // This is a pattern, return false to play the simple
101 // vibration.
102 return false;
103 }
104 if ((mStartTime + mTimeout)
105 < (SystemClock.uptimeMillis() + millis)) {
106 // If this vibration will end before the time passed in, let
107 // the new vibration play.
108 return false;
109 }
110 return true;
111 }
112 }
113
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 HardwareService(Context context) {
115 // Reset the hardware to a default state, in case this is a runtime
116 // restart instead of a fresh boot.
117 vibratorOff();
118
The Android Open Source Project10592532009-03-18 17:39:46 -0700119 mNativePointer = init_native();
120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 mContext = context;
122 PowerManager pm = (PowerManager)context.getSystemService(
123 Context.POWER_SERVICE);
124 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
125 mWakeLock.setReferenceCounted(true);
126
Patrick Scott18dd5f02009-07-02 11:31:12 -0400127 mVibrations = new LinkedList<Vibration>();
128
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700129 mBatteryStats = BatteryStatsService.getService();
130
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 IntentFilter filter = new IntentFilter();
132 filter.addAction(Intent.ACTION_SCREEN_OFF);
133 context.registerReceiver(mIntentReceiver, filter);
Dan Murphy951764b2009-08-27 14:59:03 -0500134
135 mAutoBrightnessAvailable = context.getResources().getBoolean(
136 com.android.internal.R.bool.config_automatic_brightness_available);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 }
138
The Android Open Source Project10592532009-03-18 17:39:46 -0700139 protected void finalize() throws Throwable {
140 finalize_native(mNativePointer);
141 super.finalize();
142 }
143
Patrick Scott18dd5f02009-07-02 11:31:12 -0400144 public void vibrate(long milliseconds, IBinder token) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700145 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
146 != PackageManager.PERMISSION_GRANTED) {
147 throw new SecurityException("Requires VIBRATE permission");
148 }
Patrick Scott24f10762009-08-19 09:03:56 -0400149 // We're running in the system server so we cannot crash. Check for a
150 // timeout of 0 or negative. This will ensure that a vibration has
151 // either a timeout of > 0 or a non-null pattern.
152 if (milliseconds <= 0 || (mCurrentVibration != null
153 && mCurrentVibration.hasLongerTimeout(milliseconds))) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400154 // Ignore this vibration since the current vibration will play for
155 // longer than milliseconds.
156 return;
157 }
158 Vibration vib = new Vibration(token, milliseconds);
159 synchronized (mVibrations) {
160 removeVibrationLocked(token);
161 doCancelVibrateLocked();
162 mCurrentVibration = vib;
163 startVibrationLocked(vib);
164 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 }
166
167 private boolean isAll0(long[] pattern) {
168 int N = pattern.length;
169 for (int i = 0; i < N; i++) {
170 if (pattern[i] != 0) {
171 return false;
172 }
173 }
174 return true;
175 }
176
177 public void vibratePattern(long[] pattern, int repeat, IBinder token) {
178 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
179 != PackageManager.PERMISSION_GRANTED) {
180 throw new SecurityException("Requires VIBRATE permission");
181 }
182 // so wakelock calls will succeed
183 long identity = Binder.clearCallingIdentity();
184 try {
185 if (false) {
186 String s = "";
187 int N = pattern.length;
188 for (int i=0; i<N; i++) {
189 s += " " + pattern[i];
190 }
191 Log.i(TAG, "vibrating with pattern: " + s);
192 }
193
194 // we're running in the server so we can't fail
195 if (pattern == null || pattern.length == 0
196 || isAll0(pattern)
197 || repeat >= pattern.length || token == null) {
198 return;
199 }
200
Patrick Scott18dd5f02009-07-02 11:31:12 -0400201 Vibration vib = new Vibration(token, pattern, repeat);
202 try {
203 token.linkToDeath(vib, 0);
204 } catch (RemoteException e) {
205 return;
206 }
207
208 synchronized (mVibrations) {
209 removeVibrationLocked(token);
210 doCancelVibrateLocked();
211 if (repeat >= 0) {
212 mVibrations.addFirst(vib);
213 startNextVibrationLocked();
214 } else {
215 // A negative repeat means that this pattern is not meant
216 // to repeat. Treat it like a simple vibration.
217 mCurrentVibration = vib;
218 startVibrationLocked(vib);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 }
221 }
222 finally {
223 Binder.restoreCallingIdentity(identity);
224 }
225 }
226
Patrick Scott18dd5f02009-07-02 11:31:12 -0400227 public void cancelVibrate(IBinder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 mContext.enforceCallingOrSelfPermission(
229 android.Manifest.permission.VIBRATE,
230 "cancelVibrate");
231
232 // so wakelock calls will succeed
233 long identity = Binder.clearCallingIdentity();
234 try {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400235 synchronized (mVibrations) {
236 final Vibration vib = removeVibrationLocked(token);
237 if (vib == mCurrentVibration) {
238 doCancelVibrateLocked();
239 startNextVibrationLocked();
240 }
241 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 }
243 finally {
244 Binder.restoreCallingIdentity(identity);
245 }
246 }
247
248 public boolean getFlashlightEnabled() {
249 return Hardware.getFlashlightEnabled();
250 }
251
252 public void setFlashlightEnabled(boolean on) {
253 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.FLASHLIGHT)
254 != PackageManager.PERMISSION_GRANTED &&
255 mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
256 != PackageManager.PERMISSION_GRANTED) {
257 throw new SecurityException("Requires FLASHLIGHT or HARDWARE_TEST permission");
258 }
259 Hardware.setFlashlightEnabled(on);
260 }
261
262 public void enableCameraFlash(int milliseconds) {
263 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.CAMERA)
264 != PackageManager.PERMISSION_GRANTED &&
265 mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
266 != PackageManager.PERMISSION_GRANTED) {
267 throw new SecurityException("Requires CAMERA or HARDWARE_TEST permission");
268 }
269 Hardware.enableCameraFlash(milliseconds);
270 }
271
The Android Open Source Project10592532009-03-18 17:39:46 -0700272 public void setBacklights(int brightness) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
274 != PackageManager.PERMISSION_GRANTED) {
275 throw new SecurityException("Requires HARDWARE_TEST permission");
276 }
277 // Don't let applications turn the screen all the way off
278 brightness = Math.max(brightness, Power.BRIGHTNESS_DIM);
The Android Open Source Project10592532009-03-18 17:39:46 -0700279 setLightBrightness_UNCHECKED(LIGHT_ID_BACKLIGHT, brightness);
280 setLightBrightness_UNCHECKED(LIGHT_ID_KEYBOARD, brightness);
281 setLightBrightness_UNCHECKED(LIGHT_ID_BUTTONS, brightness);
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700282 long identity = Binder.clearCallingIdentity();
283 try {
284 mBatteryStats.noteScreenBrightness(brightness);
285 } catch (RemoteException e) {
286 Log.w(TAG, "RemoteException calling noteScreenBrightness on BatteryStatsService", e);
287 } finally {
288 Binder.restoreCallingIdentity(identity);
289 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 }
291
The Android Open Source Project10592532009-03-18 17:39:46 -0700292 void setLightOff_UNCHECKED(int light) {
293 setLight_native(mNativePointer, light, 0, LIGHT_FLASH_NONE, 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 }
295
The Android Open Source Project10592532009-03-18 17:39:46 -0700296 void setLightBrightness_UNCHECKED(int light, int brightness) {
297 int b = brightness & 0x000000ff;
298 b = 0xff000000 | (b << 16) | (b << 8) | b;
299 setLight_native(mNativePointer, light, b, LIGHT_FLASH_NONE, 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 }
301
The Android Open Source Project10592532009-03-18 17:39:46 -0700302 void setLightColor_UNCHECKED(int light, int color) {
303 setLight_native(mNativePointer, light, color, LIGHT_FLASH_NONE, 0, 0);
304 }
305
306 void setLightFlashing_UNCHECKED(int light, int color, int mode, int onMS, int offMS) {
307 setLight_native(mNativePointer, light, color, mode, onMS, offMS);
308 }
309
Dan Murphy951764b2009-08-27 14:59:03 -0500310 public void setAutoBrightness(boolean on) {
311 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
312 != PackageManager.PERMISSION_GRANTED) {
313 throw new SecurityException("Requires HARDWARE_TEST permission");
314 }
315 setAutoBrightness_UNCHECKED(on);
316 }
317
318 void setAutoBrightness_UNCHECKED(boolean on) {
319 if (mAutoBrightnessAvailable) {
320 setAutoBrightness_native(mNativePointer, on);
321 }
322 }
323
The Android Open Source Project10592532009-03-18 17:39:46 -0700324 public void setAttentionLight(boolean on) {
325 // Not worthy of a permission. We shouldn't have a flashlight permission.
Joe Onorato95e4f702009-03-24 19:29:09 -0700326 synchronized (this) {
327 mAttentionLightOn = on;
328 mPulsing = false;
329 setLight_native(mNativePointer, LIGHT_ID_ATTENTION, on ? 0xffffffff : 0,
330 LIGHT_FLASH_NONE, 0, 0);
331 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 }
333
Joe Onorato95e4f702009-03-24 19:29:09 -0700334 public void pulseBreathingLight() {
335 synchronized (this) {
336 // HACK: Added at the last minute of cupcake -- design this better;
337 // Don't reuse the attention light -- make another one.
338 if (false) {
339 Log.d(TAG, "pulseBreathingLight mAttentionLightOn=" + mAttentionLightOn
340 + " mPulsing=" + mPulsing);
341 }
342 if (!mAttentionLightOn && !mPulsing) {
343 mPulsing = true;
344 setLight_native(mNativePointer, LIGHT_ID_ATTENTION, 0xff101010,
345 LIGHT_FLASH_NONE, 0, 0);
346 mH.sendMessageDelayed(Message.obtain(mH, 1), 3000);
347 }
348 }
349 }
350
351 private Handler mH = new Handler() {
352 @Override
353 public void handleMessage(Message msg) {
354 synchronized (this) {
355 if (false) {
356 Log.d(TAG, "pulse cleanup handler firing mPulsing=" + mPulsing);
357 }
358 if (mPulsing) {
359 mPulsing = false;
360 setLight_native(mNativePointer, LIGHT_ID_ATTENTION,
361 mAttentionLightOn ? 0xffffffff : 0,
362 LIGHT_FLASH_NONE, 0, 0);
363 }
364 }
365 }
366 };
367
Patrick Scott18dd5f02009-07-02 11:31:12 -0400368 private final Runnable mVibrationRunnable = new Runnable() {
369 public void run() {
370 synchronized (mVibrations) {
371 doCancelVibrateLocked();
372 startNextVibrationLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400374 }
375 };
376
377 // Lock held on mVibrations
378 private void doCancelVibrateLocked() {
379 if (mThread != null) {
380 synchronized (mThread) {
381 mThread.mDone = true;
382 mThread.notify();
383 }
384 mThread = null;
385 }
386 vibratorOff();
387 mH.removeCallbacks(mVibrationRunnable);
388 }
389
390 // Lock held on mVibrations
391 private void startNextVibrationLocked() {
392 if (mVibrations.size() <= 0) {
393 return;
394 }
395 mCurrentVibration = mVibrations.getFirst();
396 startVibrationLocked(mCurrentVibration);
397 }
398
399 // Lock held on mVibrations
400 private void startVibrationLocked(final Vibration vib) {
401 if (vib.mTimeout != 0) {
402 vibratorOn(vib.mTimeout);
403 mH.postDelayed(mVibrationRunnable, vib.mTimeout);
404 } else {
405 // mThread better be null here. doCancelVibrate should always be
406 // called before startNextVibrationLocked or startVibrationLocked.
407 mThread = new VibrateThread(vib);
408 mThread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 }
410 }
411
Patrick Scott18dd5f02009-07-02 11:31:12 -0400412 // Lock held on mVibrations
413 private Vibration removeVibrationLocked(IBinder token) {
414 ListIterator<Vibration> iter = mVibrations.listIterator(0);
415 while (iter.hasNext()) {
416 Vibration vib = iter.next();
417 if (vib.mToken == token) {
418 iter.remove();
419 return vib;
420 }
421 }
422 // We might be looking for a simple vibration which is only stored in
423 // mCurrentVibration.
424 if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
425 return mCurrentVibration;
426 }
427 return null;
428 }
429
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 private class VibrateThread extends Thread {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400431 final Vibration mVibration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 boolean mDone;
433
Patrick Scott18dd5f02009-07-02 11:31:12 -0400434 VibrateThread(Vibration vib) {
435 mVibration = vib;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 mWakeLock.acquire();
437 }
438
439 private void delay(long duration) {
440 if (duration > 0) {
441 long bedtime = SystemClock.uptimeMillis();
442 do {
443 try {
444 this.wait(duration);
445 }
446 catch (InterruptedException e) {
447 }
448 if (mDone) {
449 break;
450 }
451 duration = duration
452 - SystemClock.uptimeMillis() - bedtime;
453 } while (duration > 0);
454 }
455 }
456
457 public void run() {
458 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
459 synchronized (this) {
460 int index = 0;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400461 long[] pattern = mVibration.mPattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 int len = pattern.length;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400463 int repeat = mVibration.mRepeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 long duration = 0;
465
466 while (!mDone) {
467 // add off-time duration to any accumulated on-time duration
468 if (index < len) {
469 duration += pattern[index++];
470 }
471
472 // sleep until it is time to start the vibrator
473 delay(duration);
474 if (mDone) {
475 break;
476 }
477
478 if (index < len) {
479 // read on-time duration and start the vibrator
480 // duration is saved for delay() at top of loop
481 duration = pattern[index++];
482 if (duration > 0) {
483 HardwareService.this.vibratorOn(duration);
484 }
485 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400486 if (repeat < 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 break;
488 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400489 index = repeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 duration = 0;
491 }
492 }
493 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 mWakeLock.release();
495 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400496 synchronized (mVibrations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 if (mThread == this) {
498 mThread = null;
499 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400500 if (!mDone) {
501 // If this vibration finished naturally, start the next
502 // vibration.
503 mVibrations.remove(mVibration);
504 startNextVibrationLocked();
505 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 }
507 }
508 };
509
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
511 public void onReceive(Context context, Intent intent) {
512 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400513 synchronized (mVibrations) {
514 doCancelVibrateLocked();
515 mVibrations.clear();
516 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 }
518 }
519 };
The Android Open Source Project10592532009-03-18 17:39:46 -0700520
521 private static native int init_native();
522 private static native void finalize_native(int ptr);
523
Dan Murphy951764b2009-08-27 14:59:03 -0500524 private static native void setAutoBrightness_native(int ptr, boolean automatic);
The Android Open Source Project10592532009-03-18 17:39:46 -0700525 private static native void setLight_native(int ptr, int light, int color, int mode,
526 int onMS, int offMS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700528 private final Context mContext;
529 private final PowerManager.WakeLock mWakeLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700531 private final IBatteryStats mBatteryStats;
532
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 volatile VibrateThread mThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534
The Android Open Source Project10592532009-03-18 17:39:46 -0700535 private int mNativePointer;
536
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 native static void vibratorOn(long milliseconds);
538 native static void vibratorOff();
539}