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.app.AppOpsManager;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.content.pm.PackageManager;
     25 import android.database.ContentObserver;
     26 import android.hardware.input.InputManager;
     27 import android.os.BatteryStats;
     28 import android.os.Handler;
     29 import android.os.IVibratorService;
     30 import android.os.PowerManager;
     31 import android.os.Process;
     32 import android.os.RemoteException;
     33 import android.os.IBinder;
     34 import android.os.Binder;
     35 import android.os.ServiceManager;
     36 import android.os.SystemClock;
     37 import android.os.UserHandle;
     38 import android.os.Vibrator;
     39 import android.os.WorkSource;
     40 import android.provider.Settings;
     41 import android.provider.Settings.SettingNotFoundException;
     42 import android.util.Slog;
     43 import android.view.InputDevice;
     44 
     45 import com.android.internal.app.IAppOpsService;
     46 import com.android.internal.app.IBatteryStats;
     47 
     48 import java.util.ArrayList;
     49 import java.util.LinkedList;
     50 import java.util.ListIterator;
     51 
     52 public class VibratorService extends IVibratorService.Stub
     53         implements InputManager.InputDeviceListener {
     54     private static final String TAG = "VibratorService";
     55 
     56     private final LinkedList<Vibration> mVibrations;
     57     private Vibration mCurrentVibration;
     58     private final WorkSource mTmpWorkSource = new WorkSource();
     59     private final Handler mH = new Handler();
     60 
     61     private final Context mContext;
     62     private final PowerManager.WakeLock mWakeLock;
     63     private final IAppOpsService mAppOpsService;
     64     private final IBatteryStats mBatteryStatsService;
     65     private InputManager mIm;
     66 
     67     volatile VibrateThread mThread;
     68 
     69     // mInputDeviceVibrators lock should be acquired after mVibrations lock, if both are
     70     // to be acquired
     71     private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
     72     private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
     73     private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
     74 
     75     private int mCurVibUid = -1;
     76 
     77     native static boolean vibratorExists();
     78     native static void vibratorOn(long milliseconds);
     79     native static void vibratorOff();
     80 
     81     private class Vibration implements IBinder.DeathRecipient {
     82         private final IBinder mToken;
     83         private final long    mTimeout;
     84         private final long    mStartTime;
     85         private final long[]  mPattern;
     86         private final int     mRepeat;
     87         private final int     mUid;
     88         private final String  mPackageName;
     89 
     90         Vibration(IBinder token, long millis, int uid, String packageName) {
     91             this(token, millis, null, 0, uid, packageName);
     92         }
     93 
     94         Vibration(IBinder token, long[] pattern, int repeat, int uid, String packageName) {
     95             this(token, 0, pattern, repeat, uid, packageName);
     96         }
     97 
     98         private Vibration(IBinder token, long millis, long[] pattern,
     99                 int repeat, int uid, String packageName) {
    100             mToken = token;
    101             mTimeout = millis;
    102             mStartTime = SystemClock.uptimeMillis();
    103             mPattern = pattern;
    104             mRepeat = repeat;
    105             mUid = uid;
    106             mPackageName = packageName;
    107         }
    108 
    109         public void binderDied() {
    110             synchronized (mVibrations) {
    111                 mVibrations.remove(this);
    112                 if (this == mCurrentVibration) {
    113                     doCancelVibrateLocked();
    114                     startNextVibrationLocked();
    115                 }
    116             }
    117         }
    118 
    119         public boolean hasLongerTimeout(long millis) {
    120             if (mTimeout == 0) {
    121                 // This is a pattern, return false to play the simple
    122                 // vibration.
    123                 return false;
    124             }
    125             if ((mStartTime + mTimeout)
    126                     < (SystemClock.uptimeMillis() + millis)) {
    127                 // If this vibration will end before the time passed in, let
    128                 // the new vibration play.
    129                 return false;
    130             }
    131             return true;
    132         }
    133     }
    134 
    135     VibratorService(Context context) {
    136         // Reset the hardware to a default state, in case this is a runtime
    137         // restart instead of a fresh boot.
    138         vibratorOff();
    139 
    140         mContext = context;
    141         PowerManager pm = (PowerManager)context.getSystemService(
    142                 Context.POWER_SERVICE);
    143         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
    144         mWakeLock.setReferenceCounted(true);
    145 
    146         mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
    147         mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
    148                 BatteryStats.SERVICE_NAME));
    149 
    150         mVibrations = new LinkedList<Vibration>();
    151 
    152         IntentFilter filter = new IntentFilter();
    153         filter.addAction(Intent.ACTION_SCREEN_OFF);
    154         context.registerReceiver(mIntentReceiver, filter);
    155     }
    156 
    157     public void systemReady() {
    158         mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE);
    159 
    160         mContext.getContentResolver().registerContentObserver(
    161                 Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), true,
    162                 new ContentObserver(mH) {
    163                     @Override
    164                     public void onChange(boolean selfChange) {
    165                         updateInputDeviceVibrators();
    166                     }
    167                 }, UserHandle.USER_ALL);
    168 
    169         mContext.registerReceiver(new BroadcastReceiver() {
    170             @Override
    171             public void onReceive(Context context, Intent intent) {
    172                 updateInputDeviceVibrators();
    173             }
    174         }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
    175 
    176         updateInputDeviceVibrators();
    177     }
    178 
    179     public boolean hasVibrator() {
    180         return doVibratorExists();
    181     }
    182 
    183     private void verifyIncomingUid(int uid) {
    184         if (uid == Binder.getCallingUid()) {
    185             return;
    186         }
    187         if (Binder.getCallingPid() == Process.myPid()) {
    188             return;
    189         }
    190         mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
    191                 Binder.getCallingPid(), Binder.getCallingUid(), null);
    192     }
    193 
    194     public void vibrate(int uid, String packageName, long milliseconds, IBinder token) {
    195         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
    196                 != PackageManager.PERMISSION_GRANTED) {
    197             throw new SecurityException("Requires VIBRATE permission");
    198         }
    199         verifyIncomingUid(uid);
    200         // We're running in the system server so we cannot crash. Check for a
    201         // timeout of 0 or negative. This will ensure that a vibration has
    202         // either a timeout of > 0 or a non-null pattern.
    203         if (milliseconds <= 0 || (mCurrentVibration != null
    204                 && mCurrentVibration.hasLongerTimeout(milliseconds))) {
    205             // Ignore this vibration since the current vibration will play for
    206             // longer than milliseconds.
    207             return;
    208         }
    209 
    210         Vibration vib = new Vibration(token, milliseconds, uid, packageName);
    211 
    212         final long ident = Binder.clearCallingIdentity();
    213         try {
    214             synchronized (mVibrations) {
    215                 removeVibrationLocked(token);
    216                 doCancelVibrateLocked();
    217                 mCurrentVibration = vib;
    218                 startVibrationLocked(vib);
    219             }
    220         } finally {
    221             Binder.restoreCallingIdentity(ident);
    222         }
    223     }
    224 
    225     private boolean isAll0(long[] pattern) {
    226         int N = pattern.length;
    227         for (int i = 0; i < N; i++) {
    228             if (pattern[i] != 0) {
    229                 return false;
    230             }
    231         }
    232         return true;
    233     }
    234 
    235     public void vibratePattern(int uid, String packageName, long[] pattern, int repeat,
    236             IBinder token) {
    237         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
    238                 != PackageManager.PERMISSION_GRANTED) {
    239             throw new SecurityException("Requires VIBRATE permission");
    240         }
    241         verifyIncomingUid(uid);
    242         // so wakelock calls will succeed
    243         long identity = Binder.clearCallingIdentity();
    244         try {
    245             if (false) {
    246                 String s = "";
    247                 int N = pattern.length;
    248                 for (int i=0; i<N; i++) {
    249                     s += " " + pattern[i];
    250                 }
    251                 Slog.i(TAG, "vibrating with pattern: " + s);
    252             }
    253 
    254             // we're running in the server so we can't fail
    255             if (pattern == null || pattern.length == 0
    256                     || isAll0(pattern)
    257                     || repeat >= pattern.length || token == null) {
    258                 return;
    259             }
    260 
    261             Vibration vib = new Vibration(token, pattern, repeat, uid, packageName);
    262             try {
    263                 token.linkToDeath(vib, 0);
    264             } catch (RemoteException e) {
    265                 return;
    266             }
    267 
    268             synchronized (mVibrations) {
    269                 removeVibrationLocked(token);
    270                 doCancelVibrateLocked();
    271                 if (repeat >= 0) {
    272                     mVibrations.addFirst(vib);
    273                     startNextVibrationLocked();
    274                 } else {
    275                     // A negative repeat means that this pattern is not meant
    276                     // to repeat. Treat it like a simple vibration.
    277                     mCurrentVibration = vib;
    278                     startVibrationLocked(vib);
    279                 }
    280             }
    281         }
    282         finally {
    283             Binder.restoreCallingIdentity(identity);
    284         }
    285     }
    286 
    287     public void cancelVibrate(IBinder token) {
    288         mContext.enforceCallingOrSelfPermission(
    289                 android.Manifest.permission.VIBRATE,
    290                 "cancelVibrate");
    291 
    292         // so wakelock calls will succeed
    293         long identity = Binder.clearCallingIdentity();
    294         try {
    295             synchronized (mVibrations) {
    296                 final Vibration vib = removeVibrationLocked(token);
    297                 if (vib == mCurrentVibration) {
    298                     doCancelVibrateLocked();
    299                     startNextVibrationLocked();
    300                 }
    301             }
    302         }
    303         finally {
    304             Binder.restoreCallingIdentity(identity);
    305         }
    306     }
    307 
    308     private final Runnable mVibrationRunnable = new Runnable() {
    309         public void run() {
    310             synchronized (mVibrations) {
    311                 doCancelVibrateLocked();
    312                 startNextVibrationLocked();
    313             }
    314         }
    315     };
    316 
    317     // Lock held on mVibrations
    318     private void doCancelVibrateLocked() {
    319         if (mThread != null) {
    320             synchronized (mThread) {
    321                 mThread.mDone = true;
    322                 mThread.notify();
    323             }
    324             mThread = null;
    325         }
    326         doVibratorOff();
    327         mH.removeCallbacks(mVibrationRunnable);
    328         reportFinishVibrationLocked();
    329     }
    330 
    331     // Lock held on mVibrations
    332     private void startNextVibrationLocked() {
    333         if (mVibrations.size() <= 0) {
    334             reportFinishVibrationLocked();
    335             mCurrentVibration = null;
    336             return;
    337         }
    338         mCurrentVibration = mVibrations.getFirst();
    339         startVibrationLocked(mCurrentVibration);
    340     }
    341 
    342     // Lock held on mVibrations
    343     private void startVibrationLocked(final Vibration vib) {
    344         try {
    345             int mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
    346                     AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
    347             if (mode != AppOpsManager.MODE_ALLOWED) {
    348                 if (mode == AppOpsManager.MODE_ERRORED) {
    349                     Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
    350                 }
    351                 mH.post(mVibrationRunnable);
    352                 return;
    353             }
    354         } catch (RemoteException e) {
    355         }
    356         if (vib.mTimeout != 0) {
    357             doVibratorOn(vib.mTimeout, vib.mUid);
    358             mH.postDelayed(mVibrationRunnable, vib.mTimeout);
    359         } else {
    360             // mThread better be null here. doCancelVibrate should always be
    361             // called before startNextVibrationLocked or startVibrationLocked.
    362             mThread = new VibrateThread(vib);
    363             mThread.start();
    364         }
    365     }
    366 
    367     private void reportFinishVibrationLocked() {
    368         if (mCurrentVibration != null) {
    369             try {
    370                 mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
    371                         AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
    372                         mCurrentVibration.mPackageName);
    373             } catch (RemoteException e) {
    374             }
    375             mCurrentVibration = null;
    376         }
    377     }
    378 
    379     // Lock held on mVibrations
    380     private Vibration removeVibrationLocked(IBinder token) {
    381         ListIterator<Vibration> iter = mVibrations.listIterator(0);
    382         while (iter.hasNext()) {
    383             Vibration vib = iter.next();
    384             if (vib.mToken == token) {
    385                 iter.remove();
    386                 unlinkVibration(vib);
    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             unlinkVibration(mCurrentVibration);
    394             return mCurrentVibration;
    395         }
    396         return null;
    397     }
    398 
    399     private void unlinkVibration(Vibration vib) {
    400         if (vib.mPattern != null) {
    401             // If Vibration object has a pattern,
    402             // the Vibration object has also been linkedToDeath.
    403             vib.mToken.unlinkToDeath(vib, 0);
    404         }
    405     }
    406 
    407     private void updateInputDeviceVibrators() {
    408         synchronized (mVibrations) {
    409             doCancelVibrateLocked();
    410 
    411             synchronized (mInputDeviceVibrators) {
    412                 mVibrateInputDevicesSetting = false;
    413                 try {
    414                     mVibrateInputDevicesSetting = Settings.System.getIntForUser(
    415                             mContext.getContentResolver(),
    416                             Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
    417                 } catch (SettingNotFoundException snfe) {
    418                 }
    419 
    420                 if (mVibrateInputDevicesSetting) {
    421                     if (!mInputDeviceListenerRegistered) {
    422                         mInputDeviceListenerRegistered = true;
    423                         mIm.registerInputDeviceListener(this, mH);
    424                     }
    425                 } else {
    426                     if (mInputDeviceListenerRegistered) {
    427                         mInputDeviceListenerRegistered = false;
    428                         mIm.unregisterInputDeviceListener(this);
    429                     }
    430                 }
    431 
    432                 mInputDeviceVibrators.clear();
    433                 if (mVibrateInputDevicesSetting) {
    434                     int[] ids = mIm.getInputDeviceIds();
    435                     for (int i = 0; i < ids.length; i++) {
    436                         InputDevice device = mIm.getInputDevice(ids[i]);
    437                         Vibrator vibrator = device.getVibrator();
    438                         if (vibrator.hasVibrator()) {
    439                             mInputDeviceVibrators.add(vibrator);
    440                         }
    441                     }
    442                 }
    443             }
    444 
    445             startNextVibrationLocked();
    446         }
    447     }
    448 
    449     @Override
    450     public void onInputDeviceAdded(int deviceId) {
    451         updateInputDeviceVibrators();
    452     }
    453 
    454     @Override
    455     public void onInputDeviceChanged(int deviceId) {
    456         updateInputDeviceVibrators();
    457     }
    458 
    459     @Override
    460     public void onInputDeviceRemoved(int deviceId) {
    461         updateInputDeviceVibrators();
    462     }
    463 
    464     private boolean doVibratorExists() {
    465         // For now, we choose to ignore the presence of input devices that have vibrators
    466         // when reporting whether the device has a vibrator.  Applications often use this
    467         // information to decide whether to enable certain features so they expect the
    468         // result of hasVibrator() to be constant.  For now, just report whether
    469         // the device has a built-in vibrator.
    470         //synchronized (mInputDeviceVibrators) {
    471         //    return !mInputDeviceVibrators.isEmpty() || vibratorExists();
    472         //}
    473         return vibratorExists();
    474     }
    475 
    476     private void doVibratorOn(long millis, int uid) {
    477         synchronized (mInputDeviceVibrators) {
    478             try {
    479                 mBatteryStatsService.noteVibratorOn(uid, millis);
    480                 mCurVibUid = uid;
    481             } catch (RemoteException e) {
    482             }
    483             final int vibratorCount = mInputDeviceVibrators.size();
    484             if (vibratorCount != 0) {
    485                 for (int i = 0; i < vibratorCount; i++) {
    486                     mInputDeviceVibrators.get(i).vibrate(millis);
    487                 }
    488             } else {
    489                 vibratorOn(millis);
    490             }
    491         }
    492     }
    493 
    494     private void doVibratorOff() {
    495         synchronized (mInputDeviceVibrators) {
    496             if (mCurVibUid >= 0) {
    497                 try {
    498                     mBatteryStatsService.noteVibratorOff(mCurVibUid);
    499                 } catch (RemoteException e) {
    500                 }
    501                 mCurVibUid = -1;
    502             }
    503             final int vibratorCount = mInputDeviceVibrators.size();
    504             if (vibratorCount != 0) {
    505                 for (int i = 0; i < vibratorCount; i++) {
    506                     mInputDeviceVibrators.get(i).cancel();
    507                 }
    508             } else {
    509                 vibratorOff();
    510             }
    511         }
    512     }
    513 
    514     private class VibrateThread extends Thread {
    515         final Vibration mVibration;
    516         boolean mDone;
    517 
    518         VibrateThread(Vibration vib) {
    519             mVibration = vib;
    520             mTmpWorkSource.set(vib.mUid);
    521             mWakeLock.setWorkSource(mTmpWorkSource);
    522             mWakeLock.acquire();
    523         }
    524 
    525         private void delay(long duration) {
    526             if (duration > 0) {
    527                 long bedtime = duration + SystemClock.uptimeMillis();
    528                 do {
    529                     try {
    530                         this.wait(duration);
    531                     }
    532                     catch (InterruptedException e) {
    533                     }
    534                     if (mDone) {
    535                         break;
    536                     }
    537                     duration = bedtime - SystemClock.uptimeMillis();
    538                 } while (duration > 0);
    539             }
    540         }
    541 
    542         public void run() {
    543             Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
    544             synchronized (this) {
    545                 final long[] pattern = mVibration.mPattern;
    546                 final int len = pattern.length;
    547                 final int repeat = mVibration.mRepeat;
    548                 final int uid = mVibration.mUid;
    549                 int index = 0;
    550                 long duration = 0;
    551 
    552                 while (!mDone) {
    553                     // add off-time duration to any accumulated on-time duration
    554                     if (index < len) {
    555                         duration += pattern[index++];
    556                     }
    557 
    558                     // sleep until it is time to start the vibrator
    559                     delay(duration);
    560                     if (mDone) {
    561                         break;
    562                     }
    563 
    564                     if (index < len) {
    565                         // read on-time duration and start the vibrator
    566                         // duration is saved for delay() at top of loop
    567                         duration = pattern[index++];
    568                         if (duration > 0) {
    569                             VibratorService.this.doVibratorOn(duration, uid);
    570                         }
    571                     } else {
    572                         if (repeat < 0) {
    573                             break;
    574                         } else {
    575                             index = repeat;
    576                             duration = 0;
    577                         }
    578                     }
    579                 }
    580                 mWakeLock.release();
    581             }
    582             synchronized (mVibrations) {
    583                 if (mThread == this) {
    584                     mThread = null;
    585                 }
    586                 if (!mDone) {
    587                     // If this vibration finished naturally, start the next
    588                     // vibration.
    589                     mVibrations.remove(mVibration);
    590                     unlinkVibration(mVibration);
    591                     startNextVibrationLocked();
    592                 }
    593             }
    594         }
    595     };
    596 
    597     BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
    598         public void onReceive(Context context, Intent intent) {
    599             if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
    600                 synchronized (mVibrations) {
    601                     doCancelVibrateLocked();
    602 
    603                     int size = mVibrations.size();
    604                     for(int i = 0; i < size; i++) {
    605                         unlinkVibration(mVibrations.get(i));
    606                     }
    607 
    608                     mVibrations.clear();
    609                 }
    610             }
    611         }
    612     };
    613 }
    614