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