blob: 3e3cf065bedbae11d9f0b16ed9cb753c4b004c92 [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 public void setAttentionLight(boolean on, int color) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700287 // Not worthy of a permission. We shouldn't have a flashlight permission.
Joe Onorato95e4f702009-03-24 19:29:09 -0700288 synchronized (this) {
289 mAttentionLightOn = on;
290 mPulsing = false;
Eric Olsenf42f15c2009-10-29 16:42:03 -0700291 setLight_native(mNativePointer, LIGHT_ID_ATTENTION, color,
292 LIGHT_FLASH_HARDWARE, on ? 3 : 0, 0);
Joe Onorato95e4f702009-03-24 19:29:09 -0700293 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 }
295
Joe Onorato95e4f702009-03-24 19:29:09 -0700296 public void pulseBreathingLight() {
297 synchronized (this) {
298 // HACK: Added at the last minute of cupcake -- design this better;
299 // Don't reuse the attention light -- make another one.
300 if (false) {
301 Log.d(TAG, "pulseBreathingLight mAttentionLightOn=" + mAttentionLightOn
302 + " mPulsing=" + mPulsing);
303 }
304 if (!mAttentionLightOn && !mPulsing) {
305 mPulsing = true;
Eric Olsenf42f15c2009-10-29 16:42:03 -0700306 setLight_native(mNativePointer, LIGHT_ID_ATTENTION, 0x00ffffff,
307 LIGHT_FLASH_HARDWARE, 7, 0);
Joe Onorato95e4f702009-03-24 19:29:09 -0700308 mH.sendMessageDelayed(Message.obtain(mH, 1), 3000);
309 }
310 }
311 }
312
313 private Handler mH = new Handler() {
314 @Override
315 public void handleMessage(Message msg) {
316 synchronized (this) {
317 if (false) {
318 Log.d(TAG, "pulse cleanup handler firing mPulsing=" + mPulsing);
319 }
320 if (mPulsing) {
321 mPulsing = false;
322 setLight_native(mNativePointer, LIGHT_ID_ATTENTION,
323 mAttentionLightOn ? 0xffffffff : 0,
324 LIGHT_FLASH_NONE, 0, 0);
325 }
326 }
327 }
328 };
329
Patrick Scott18dd5f02009-07-02 11:31:12 -0400330 private final Runnable mVibrationRunnable = new Runnable() {
331 public void run() {
332 synchronized (mVibrations) {
333 doCancelVibrateLocked();
334 startNextVibrationLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400336 }
337 };
338
339 // Lock held on mVibrations
340 private void doCancelVibrateLocked() {
341 if (mThread != null) {
342 synchronized (mThread) {
343 mThread.mDone = true;
344 mThread.notify();
345 }
346 mThread = null;
347 }
348 vibratorOff();
349 mH.removeCallbacks(mVibrationRunnable);
350 }
351
352 // Lock held on mVibrations
353 private void startNextVibrationLocked() {
354 if (mVibrations.size() <= 0) {
355 return;
356 }
357 mCurrentVibration = mVibrations.getFirst();
358 startVibrationLocked(mCurrentVibration);
359 }
360
361 // Lock held on mVibrations
362 private void startVibrationLocked(final Vibration vib) {
363 if (vib.mTimeout != 0) {
364 vibratorOn(vib.mTimeout);
365 mH.postDelayed(mVibrationRunnable, vib.mTimeout);
366 } else {
367 // mThread better be null here. doCancelVibrate should always be
368 // called before startNextVibrationLocked or startVibrationLocked.
369 mThread = new VibrateThread(vib);
370 mThread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 }
372 }
373
Patrick Scott18dd5f02009-07-02 11:31:12 -0400374 // Lock held on mVibrations
375 private Vibration removeVibrationLocked(IBinder token) {
376 ListIterator<Vibration> iter = mVibrations.listIterator(0);
377 while (iter.hasNext()) {
378 Vibration vib = iter.next();
379 if (vib.mToken == token) {
380 iter.remove();
381 return vib;
382 }
383 }
384 // We might be looking for a simple vibration which is only stored in
385 // mCurrentVibration.
386 if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
387 return mCurrentVibration;
388 }
389 return null;
390 }
391
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 private class VibrateThread extends Thread {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400393 final Vibration mVibration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 boolean mDone;
Eric Olsenf42f15c2009-10-29 16:42:03 -0700395
Patrick Scott18dd5f02009-07-02 11:31:12 -0400396 VibrateThread(Vibration vib) {
397 mVibration = vib;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 mWakeLock.acquire();
399 }
400
401 private void delay(long duration) {
402 if (duration > 0) {
403 long bedtime = SystemClock.uptimeMillis();
404 do {
405 try {
406 this.wait(duration);
407 }
408 catch (InterruptedException e) {
409 }
410 if (mDone) {
411 break;
412 }
413 duration = duration
414 - SystemClock.uptimeMillis() - bedtime;
415 } while (duration > 0);
416 }
417 }
418
419 public void run() {
420 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
421 synchronized (this) {
422 int index = 0;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400423 long[] pattern = mVibration.mPattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 int len = pattern.length;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400425 int repeat = mVibration.mRepeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 long duration = 0;
427
428 while (!mDone) {
Eric Olsenf42f15c2009-10-29 16:42:03 -0700429 // add off-time duration to any accumulated on-time duration
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 if (index < len) {
431 duration += pattern[index++];
432 }
433
434 // sleep until it is time to start the vibrator
435 delay(duration);
436 if (mDone) {
437 break;
438 }
439
440 if (index < len) {
441 // read on-time duration and start the vibrator
442 // duration is saved for delay() at top of loop
443 duration = pattern[index++];
444 if (duration > 0) {
445 HardwareService.this.vibratorOn(duration);
446 }
447 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400448 if (repeat < 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 break;
450 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400451 index = repeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 duration = 0;
453 }
454 }
455 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 mWakeLock.release();
457 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400458 synchronized (mVibrations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 if (mThread == this) {
460 mThread = null;
461 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400462 if (!mDone) {
463 // If this vibration finished naturally, start the next
464 // vibration.
465 mVibrations.remove(mVibration);
466 startNextVibrationLocked();
467 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 }
469 }
470 };
471
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
473 public void onReceive(Context context, Intent intent) {
474 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400475 synchronized (mVibrations) {
476 doCancelVibrateLocked();
477 mVibrations.clear();
478 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 }
480 }
481 };
Eric Olsenf42f15c2009-10-29 16:42:03 -0700482
The Android Open Source Project10592532009-03-18 17:39:46 -0700483 private static native int init_native();
484 private static native void finalize_native(int ptr);
485
486 private static native void setLight_native(int ptr, int light, int color, int mode,
487 int onMS, int offMS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700489 private final Context mContext;
490 private final PowerManager.WakeLock mWakeLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700492 private final IBatteryStats mBatteryStats;
Eric Olsenf42f15c2009-10-29 16:42:03 -0700493
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 volatile VibrateThread mThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495
The Android Open Source Project10592532009-03-18 17:39:46 -0700496 private int mNativePointer;
497
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 native static void vibratorOn(long milliseconds);
499 native static void vibratorOff();
500}