Home | History | Annotate | Download | only in server
      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 
     17 package com.android.server;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.content.pm.PackageManager;
     24 import android.os.Handler;
     25 import android.os.IVibratorService;
     26 import android.os.PowerManager;
     27 import android.os.Process;
     28 import android.os.RemoteException;
     29 import android.os.IBinder;
     30 import android.os.Binder;
     31 import android.os.SystemClock;
     32 import android.os.WorkSource;
     33 import android.util.Slog;
     34 
     35 import java.util.LinkedList;
     36 import java.util.ListIterator;
     37 
     38 public 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 boolean hasVibrator() {
    116         return vibratorExists();
    117     }
    118 
    119     public void vibrate(long milliseconds, IBinder token) {
    120         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
    121                 != PackageManager.PERMISSION_GRANTED) {
    122             throw new SecurityException("Requires VIBRATE permission");
    123         }
    124         int uid = Binder.getCallingUid();
    125         // We're running in the system server so we cannot crash. Check for a
    126         // timeout of 0 or negative. This will ensure that a vibration has
    127         // either a timeout of > 0 or a non-null pattern.
    128         if (milliseconds <= 0 || (mCurrentVibration != null
    129                 && mCurrentVibration.hasLongerTimeout(milliseconds))) {
    130             // Ignore this vibration since the current vibration will play for
    131             // longer than milliseconds.
    132             return;
    133         }
    134         Vibration vib = new Vibration(token, milliseconds, uid);
    135         synchronized (mVibrations) {
    136             removeVibrationLocked(token);
    137             doCancelVibrateLocked();
    138             mCurrentVibration = vib;
    139             startVibrationLocked(vib);
    140         }
    141     }
    142 
    143     private boolean isAll0(long[] pattern) {
    144         int N = pattern.length;
    145         for (int i = 0; i < N; i++) {
    146             if (pattern[i] != 0) {
    147                 return false;
    148             }
    149         }
    150         return true;
    151     }
    152 
    153     public void vibratePattern(long[] pattern, int repeat, IBinder token) {
    154         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
    155                 != PackageManager.PERMISSION_GRANTED) {
    156             throw new SecurityException("Requires VIBRATE permission");
    157         }
    158         int uid = Binder.getCallingUid();
    159         // so wakelock calls will succeed
    160         long identity = Binder.clearCallingIdentity();
    161         try {
    162             if (false) {
    163                 String s = "";
    164                 int N = pattern.length;
    165                 for (int i=0; i<N; i++) {
    166                     s += " " + pattern[i];
    167                 }
    168                 Slog.i(TAG, "vibrating with pattern: " + s);
    169             }
    170 
    171             // we're running in the server so we can't fail
    172             if (pattern == null || pattern.length == 0
    173                     || isAll0(pattern)
    174                     || repeat >= pattern.length || token == null) {
    175                 return;
    176             }
    177 
    178             Vibration vib = new Vibration(token, pattern, repeat, uid);
    179             try {
    180                 token.linkToDeath(vib, 0);
    181             } catch (RemoteException e) {
    182                 return;
    183             }
    184 
    185             synchronized (mVibrations) {
    186                 removeVibrationLocked(token);
    187                 doCancelVibrateLocked();
    188                 if (repeat >= 0) {
    189                     mVibrations.addFirst(vib);
    190                     startNextVibrationLocked();
    191                 } else {
    192                     // A negative repeat means that this pattern is not meant
    193                     // to repeat. Treat it like a simple vibration.
    194                     mCurrentVibration = vib;
    195                     startVibrationLocked(vib);
    196                 }
    197             }
    198         }
    199         finally {
    200             Binder.restoreCallingIdentity(identity);
    201         }
    202     }
    203 
    204     public void cancelVibrate(IBinder token) {
    205         mContext.enforceCallingOrSelfPermission(
    206                 android.Manifest.permission.VIBRATE,
    207                 "cancelVibrate");
    208 
    209         // so wakelock calls will succeed
    210         long identity = Binder.clearCallingIdentity();
    211         try {
    212             synchronized (mVibrations) {
    213                 final Vibration vib = removeVibrationLocked(token);
    214                 if (vib == mCurrentVibration) {
    215                     doCancelVibrateLocked();
    216                     startNextVibrationLocked();
    217                 }
    218             }
    219         }
    220         finally {
    221             Binder.restoreCallingIdentity(identity);
    222         }
    223     }
    224 
    225     private final Runnable mVibrationRunnable = new Runnable() {
    226         public void run() {
    227             synchronized (mVibrations) {
    228                 doCancelVibrateLocked();
    229                 startNextVibrationLocked();
    230             }
    231         }
    232     };
    233 
    234     // Lock held on mVibrations
    235     private void doCancelVibrateLocked() {
    236         if (mThread != null) {
    237             synchronized (mThread) {
    238                 mThread.mDone = true;
    239                 mThread.notify();
    240             }
    241             mThread = null;
    242         }
    243         vibratorOff();
    244         mH.removeCallbacks(mVibrationRunnable);
    245     }
    246 
    247     // Lock held on mVibrations
    248     private void startNextVibrationLocked() {
    249         if (mVibrations.size() <= 0) {
    250             mCurrentVibration = null;
    251             return;
    252         }
    253         mCurrentVibration = mVibrations.getFirst();
    254         startVibrationLocked(mCurrentVibration);
    255     }
    256 
    257     // Lock held on mVibrations
    258     private void startVibrationLocked(final Vibration vib) {
    259         if (vib.mTimeout != 0) {
    260             vibratorOn(vib.mTimeout);
    261             mH.postDelayed(mVibrationRunnable, vib.mTimeout);
    262         } else {
    263             // mThread better be null here. doCancelVibrate should always be
    264             // called before startNextVibrationLocked or startVibrationLocked.
    265             mThread = new VibrateThread(vib);
    266             mThread.start();
    267         }
    268     }
    269 
    270     // Lock held on mVibrations
    271     private Vibration removeVibrationLocked(IBinder token) {
    272         ListIterator<Vibration> iter = mVibrations.listIterator(0);
    273         while (iter.hasNext()) {
    274             Vibration vib = iter.next();
    275             if (vib.mToken == token) {
    276                 iter.remove();
    277                 unlinkVibration(vib);
    278                 return vib;
    279             }
    280         }
    281         // We might be looking for a simple vibration which is only stored in
    282         // mCurrentVibration.
    283         if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
    284             unlinkVibration(mCurrentVibration);
    285             return mCurrentVibration;
    286         }
    287         return null;
    288     }
    289 
    290     private void unlinkVibration(Vibration vib) {
    291         if (vib.mPattern != null) {
    292             // If Vibration object has a pattern,
    293             // the Vibration object has also been linkedToDeath.
    294             vib.mToken.unlinkToDeath(vib, 0);
    295         }
    296     }
    297 
    298     private class VibrateThread extends Thread {
    299         final Vibration mVibration;
    300         boolean mDone;
    301 
    302         VibrateThread(Vibration vib) {
    303             mVibration = vib;
    304             mTmpWorkSource.set(vib.mUid);
    305             mWakeLock.setWorkSource(mTmpWorkSource);
    306             mWakeLock.acquire();
    307         }
    308 
    309         private void delay(long duration) {
    310             if (duration > 0) {
    311                 long bedtime = SystemClock.uptimeMillis();
    312                 do {
    313                     try {
    314                         this.wait(duration);
    315                     }
    316                     catch (InterruptedException e) {
    317                     }
    318                     if (mDone) {
    319                         break;
    320                     }
    321                     duration = duration
    322                             - SystemClock.uptimeMillis() - bedtime;
    323                 } while (duration > 0);
    324             }
    325         }
    326 
    327         public void run() {
    328             Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
    329             synchronized (this) {
    330                 int index = 0;
    331                 long[] pattern = mVibration.mPattern;
    332                 int len = pattern.length;
    333                 int repeat = mVibration.mRepeat;
    334                 long duration = 0;
    335 
    336                 while (!mDone) {
    337                     // add off-time duration to any accumulated on-time duration
    338                     if (index < len) {
    339                         duration += pattern[index++];
    340                     }
    341 
    342                     // sleep until it is time to start the vibrator
    343                     delay(duration);
    344                     if (mDone) {
    345                         break;
    346                     }
    347 
    348                     if (index < len) {
    349                         // read on-time duration and start the vibrator
    350                         // duration is saved for delay() at top of loop
    351                         duration = pattern[index++];
    352                         if (duration > 0) {
    353                             VibratorService.this.vibratorOn(duration);
    354                         }
    355                     } else {
    356                         if (repeat < 0) {
    357                             break;
    358                         } else {
    359                             index = repeat;
    360                             duration = 0;
    361                         }
    362                     }
    363                 }
    364                 mWakeLock.release();
    365             }
    366             synchronized (mVibrations) {
    367                 if (mThread == this) {
    368                     mThread = null;
    369                 }
    370                 if (!mDone) {
    371                     // If this vibration finished naturally, start the next
    372                     // vibration.
    373                     mVibrations.remove(mVibration);
    374                     unlinkVibration(mVibration);
    375                     startNextVibrationLocked();
    376                 }
    377             }
    378         }
    379     };
    380 
    381     BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
    382         public void onReceive(Context context, Intent intent) {
    383             if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
    384                 synchronized (mVibrations) {
    385                     doCancelVibrateLocked();
    386 
    387                     int size = mVibrations.size();
    388                     for(int i = 0; i < size; i++) {
    389                         unlinkVibration(mVibrations.get(i));
    390                     }
    391 
    392                     mVibrations.clear();
    393                 }
    394             }
    395         }
    396     };
    397 
    398     private Handler mH = new Handler();
    399 
    400     private final Context mContext;
    401     private final PowerManager.WakeLock mWakeLock;
    402 
    403     volatile VibrateThread mThread;
    404 
    405     native static boolean vibratorExists();
    406     native static void vibratorOn(long milliseconds);
    407     native static void vibratorOff();
    408 }
    409