前景提示
控制view的触摸振动(系统层全局控制)使用
Settings.System.putInt(getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, isVibrate ? 1 : 0);
我们都知道Android的view分为可点击和不可点击,当触摸点击这些view时手机会有短促的振动,因为view视图都是继承view.java
/frameworks/base/core/java/android/view/View.java找到其点击方法performClick() ;
public boolean performClick() {
// We still need to call this method to handle the cases where performClick() was called
// externally, instead of through performClickInternal()
notifyAutofillManagerOnClick();
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);//此处是触摸音控制
performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);//此处是触摸振动控制
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
public boolean performHapticFeedback(int feedbackConstant, int flags) {
if (mAttachInfo == null) {
return false;
}
//noinspection SimplifiableIfStatement
//判断是否可以振动
if ((flags & HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING) == 0
&& !isHapticFeedbackEnabled()) {
return false;
}
//在回调中控制振动效果AttachInfo.mRootCallbacks
return mAttachInfo.mRootCallbacks.performHapticFeedback(feedbackConstant,
(flags & HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0);
}
attachinfo中包含了view和window传递所需要的参数,其是在ViewRootImpl中new attachinfo()初始化,
mRootCallbacks的借口回调实现方法
@Override
public boolean performHapticFeedback(int effectId, boolean always) {
try {
return mWindowSession.performHapticFeedback(effectId, always);
} catch (RemoteException e) {
return false;
}
}
mWindowSession = WindowManagerGlobal.getWindowSession()
frameworks/base/core/java/android/view/WindowManagerGlobal.java
@UnsupportedAppUsage
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
if (sWindowManagerService != null) {
ValueAnimator.setDurationScale(
sWindowManagerService.getCurrentAnimatorScale());
sUseBLASTAdapter = sWindowManagerService.useBLAST();
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
}
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
// Emulate the legacy behavior. The global instance of InputMethodManager
// was instantiated here.
// TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
});
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
mWindowSession来自IWindowSession.aidl;实现的服务类Session在WindowManagerService实例化,通过SystemServer查看WindowManagerService注册,Session中又使用mService.mPolicy.performHapticFeedback回调使用WindowManagerPolicy借口中方法,而WindowManagerPolicy接口管理的实现类PhoneWindowManager,所以最终的振动操作逻辑在PhoneWindowManager;
/frameworks/base/services/java/com/android/server/SystemServer.java
wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
@Override
public IWindowSession openSession(IWindowSessionCallback callback) {
return new Session(this, callback);
}
frameworks/base/services/core/java/com/android/server/wm/Session.java
@Override
public boolean performHapticFeedback(int effectId, boolean always) {
long ident = Binder.clearCallingIdentity();
try {
return mService.mPolicy.performHapticFeedback(mUid, mPackageName,
effectId, always, null);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
@Override
public boolean performHapticFeedback(int uid, String packageName, int effectId,
boolean always, String reason) {
if (!mVibrator.hasVibrator()) {
return false;
}
final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
if (hapticsDisabled && !always) {
return false;
}
VibrationEffect effect = getVibrationEffect(effectId);
if (effect == null) {
return false;
}
mVibrator.vibrate(uid, packageName, effect, reason, VIBRATION_ATTRIBUTES);
return true;
}
Vibrator的服务管理实现类是SystemVibrator
frameworks/base/core/java/android/os/SystemVibrator.java
@Override
public void vibrate(int uid, String opPkg, VibrationEffect effect,
String reason, AudioAttributes attributes) {
if (mService == null) {
Log.w(TAG, "Failed to vibrate; no vibrator service.");
return;
}
try {
if (attributes == null) {
attributes = new AudioAttributes.Builder().build();
}
VibrationAttributes atr = new VibrationAttributes.Builder(attributes, effect).build();
mService.vibrate(uid, opPkg, effect, atr, reason, mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
}
mService的服务类VibratorService
frameworks/base/services/core/java/com/android/server/VibratorService.java
@Override // Binder call
public void vibrate(int uid, String opPkg, VibrationEffect effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
。。。。。。
startVibrationLocked(vib);
。。。。。。
}
@GuardedBy("mLock")
private void startVibrationLocked(final Vibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
try {
final int intensity = getCurrentIntensityLocked(vib);
if (!shouldVibrate(vib, intensity)) {
return;
}
applyVibrationIntensityScalingLocked(vib, intensity);
startVibrationInnerLocked(vib);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
@GuardedBy("mLock")
private void startVibrationInnerLocked(Vibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
try {
mCurrentVibration = vib;
if (vib.effect instanceof VibrationEffect.OneShot) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.attrs);
mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
} else if (vib.effect instanceof VibrationEffect.Waveform) {
// mThread better be null here. doCancelVibrate should always be
// called before startNextVibrationLocked or startVibrationLocked.
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
mThread = new VibrateThread(waveform, vib.uid, vib.attrs);
mThread.start();
} else if (vib.effect instanceof VibrationEffect.Prebaked) {//****此处是触摸振动的条件判断入口***
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
long timeout = doVibratorPrebakedEffectLocked(vib);
if (timeout > 0) {
mH.postDelayed(mVibrationEndRunnable, timeout);
}
} else if (vib.effect instanceof VibrationEffect.Composed) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
doVibratorComposedEffectLocked(vib);
// FIXME: We rely on the completion callback here, but I don't think we require that
// devices which support composition also support the completion callback. If we
// ever get a device that supports the former but not the latter, then we have no
// real way of knowing how long a given effect should last.
mH.postDelayed(mVibrationEndRunnable, 10000);
} else {
Slog.e(TAG, "Unknown vibration type, ignoring");
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
@GuardedBy("mLock")
private long doVibratorPrebakedEffectLocked(Vibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorPrebakedEffectLocked");
try {
final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
final boolean usingInputDeviceVibrators;
synchronized (mInputDeviceVibrators) {
usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
}
// Input devices don't support prebaked effect, so skip trying it with them.
if (!usingInputDeviceVibrators) {
//***此处通过jni调用vibratorPerformEffect底层的振动逻辑代码,并返回振动时间值**
long duration = vibratorPerformEffect(prebaked.getId(),
prebaked.getEffectStrength(), vib,
hasCapability(IVibrator.CAP_PERFORM_CALLBACK));
long timeout = duration;
if (hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) {
timeout *= ASYNC_TIMEOUT_MULTIPLIER;
}
if (timeout > 0) {
// noteVibratorOnLocked(vib.uid, duration);
return timeout;
}
}
if (!prebaked.shouldFallback()) {
return 0;
}
VibrationEffect effect = getFallbackEffect(prebaked.getId());
if (effect == null) {
Slog.w(TAG, "Failed to play prebaked effect, no fallback");
return 0;
}
Vibration fallbackVib = new Vibration(vib.token, effect, vib.attrs, vib.uid,
vib.opPkg, vib.reason + " (fallback)");
final int intensity = getCurrentIntensityLocked(fallbackVib);
linkVibration(fallbackVib);
applyVibrationIntensityScalingLocked(fallbackVib, intensity);
startVibrationInnerLocked(fallbackVib);
return 0;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
jni类frameworks/base/services/core/jni/com_android_server_VibratorService.cpp