Loading services/core/java/com/android/server/vibrator/VibrationScaler.java +23 −0 Original line number Diff line number Diff line Loading @@ -17,11 +17,13 @@ package com.android.server.vibrator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.vibrator.V1_0.EffectStrength; import android.os.IExternalVibratorService; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.VibrationEffectSegment; import android.util.Slog; Loading Loading @@ -56,6 +58,8 @@ final class VibrationScaler { private final VibrationSettings mSettingsController; private final int mDefaultVibrationAmplitude; private SparseArray<Float> mAdaptiveHapticsScales; VibrationScaler(Context context, VibrationSettings settingsController) { mSettingsController = settingsController; mDefaultVibrationAmplitude = context.getResources().getInteger( Loading Loading @@ -140,6 +144,15 @@ final class VibrationScaler { if (scaleLevel != null) { segment = segment.scale(scaleLevel.factor); } // If adaptive haptics scaling is available for this usage, apply it to the segment. if (Flags.adaptiveHapticsEnabled() && mAdaptiveHapticsScales != null && mAdaptiveHapticsScales.size() > 0 && mAdaptiveHapticsScales.contains(usageHint)) { float adaptiveScale = mAdaptiveHapticsScales.get(usageHint); segment = segment.scale(adaptiveScale); } segments.set(i, segment); } if (segments.equals(composedEffect.getSegments())) { Loading Loading @@ -173,6 +186,16 @@ final class VibrationScaler { return prebaked.applyEffectStrength(newEffectStrength); } /** * Updates the adaptive haptics scales. * @param scales the new vibration scales to apply. * * @hide */ public void updateAdaptiveHapticsScales(@Nullable SparseArray<Float> scales) { mAdaptiveHapticsScales = scales; } /** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */ private static int intensityToEffectStrength(int intensity) { switch (intensity) { Loading services/core/java/com/android/server/vibrator/VibratorControlService.java +108 −8 Original line number Diff line number Diff line Loading @@ -16,14 +16,26 @@ package com.android.server.vibrator; import static android.os.VibrationAttributes.USAGE_ALARM; import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST; import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK; import static android.os.VibrationAttributes.USAGE_MEDIA; import static android.os.VibrationAttributes.USAGE_NOTIFICATION; import static android.os.VibrationAttributes.USAGE_RINGTONE; import static android.os.VibrationAttributes.USAGE_TOUCH; import static android.os.VibrationAttributes.USAGE_UNKNOWN; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.frameworks.vibrator.IVibratorControlService; import android.frameworks.vibrator.IVibratorController; import android.frameworks.vibrator.ScaleParam; import android.frameworks.vibrator.VibrationParam; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import android.util.SparseArray; import java.util.Objects; Loading @@ -37,10 +49,13 @@ public final class VibratorControlService extends IVibratorControlService.Stub { private static final String TAG = "VibratorControlService"; private final VibratorControllerHolder mVibratorControllerHolder; private final VibrationScaler mVibrationScaler; private final Object mLock; public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, Object lock) { public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, VibrationScaler vibrationScaler, Object lock) { mVibratorControllerHolder = vibratorControllerHolder; mVibrationScaler = vibrationScaler; mLock = lock; } Loading Loading @@ -70,25 +85,62 @@ public final class VibratorControlService extends IVibratorControlService.Stub { + "controller doesn't match the registered one. " + this); return; } updateAdaptiveHapticsScales(/* params= */ null); mVibratorControllerHolder.setVibratorController(null); } } @Override public void setVibrationParams( @SuppressLint("ArrayReturn") VibrationParam[] params, IVibratorController token) throws RemoteException { // TODO(b/305939964): Add set vibration implementation. public void setVibrationParams(@SuppressLint("ArrayReturn") VibrationParam[] params, @NonNull IVibratorController token) throws RemoteException { Objects.requireNonNull(token); synchronized (mLock) { if (mVibratorControllerHolder.getVibratorController() == null) { Slog.w(TAG, "Received request to set VibrationParams for IVibratorController = " + token + ", but no controller was previously registered. Request " + "Ignored."); return; } if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(), token.asBinder())) { Slog.wtf(TAG, "Failed to set new VibrationParams. The provided " + "controller doesn't match the registered one. " + this); return; } updateAdaptiveHapticsScales(params); } } @Override public void clearVibrationParams(int types, IVibratorController token) throws RemoteException { // TODO(b/305939964): Add clear vibration implementation. public void clearVibrationParams(int types, @NonNull IVibratorController token) throws RemoteException { Objects.requireNonNull(token); synchronized (mLock) { if (mVibratorControllerHolder.getVibratorController() == null) { Slog.w(TAG, "Received request to clear VibrationParams for IVibratorController = " + token + ", but no controller was previously registered. Request " + "Ignored."); return; } if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(), token.asBinder())) { Slog.wtf(TAG, "Failed to clear VibrationParams. The provided " + "controller doesn't match the registered one. " + this); return; } //TODO(305942827): Update this method to only clear the specified vibration types. // Perhaps look into whether it makes more sense to have this clear all scales and // rely on setVibrationParams for clearing the scales for specific vibrations. updateAdaptiveHapticsScales(/* params= */ null); } } @Override public void onRequestVibrationParamsComplete( IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result) @NonNull IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result) throws RemoteException { // TODO(305942827): Cache the vibration params in VibrationScaler } Loading @@ -102,4 +154,52 @@ public final class VibratorControlService extends IVibratorControlService.Stub { public String getInterfaceHash() throws RemoteException { return this.HASH; } /** * Extracts the vibration scales and caches them in {@link VibrationScaler}. * * @param params the new vibration params to cache. */ private void updateAdaptiveHapticsScales(@Nullable VibrationParam[] params) { if (params == null || params.length == 0) { mVibrationScaler.updateAdaptiveHapticsScales(null); return; } SparseArray<Float> vibrationScales = new SparseArray<>(); for (int i = 0; i < params.length; i++) { ScaleParam scaleParam = params[i].getScale(); extractVibrationScales(scaleParam, vibrationScales); } mVibrationScaler.updateAdaptiveHapticsScales(vibrationScales); } /** * Extracts the vibration scales and map them to their corresponding * {@link android.os.VibrationAttributes} usages. */ private void extractVibrationScales(ScaleParam scaleParam, SparseArray<Float> vibrationScales) { if ((ScaleParam.TYPE_ALARM & scaleParam.typesMask) != 0) { vibrationScales.put(USAGE_ALARM, scaleParam.scale); } if ((ScaleParam.TYPE_NOTIFICATION & scaleParam.typesMask) != 0) { vibrationScales.put(USAGE_NOTIFICATION, scaleParam.scale); vibrationScales.put(USAGE_COMMUNICATION_REQUEST, scaleParam.scale); } if ((ScaleParam.TYPE_RINGTONE & scaleParam.typesMask) != 0) { vibrationScales.put(USAGE_RINGTONE, scaleParam.scale); } if ((ScaleParam.TYPE_MEDIA & scaleParam.typesMask) != 0) { vibrationScales.put(USAGE_MEDIA, scaleParam.scale); vibrationScales.put(USAGE_UNKNOWN, scaleParam.scale); } if ((ScaleParam.TYPE_INTERACTIVE & scaleParam.typesMask) != 0) { vibrationScales.put(USAGE_TOUCH, scaleParam.scale); vibrationScales.put(USAGE_HARDWARE_FEEDBACK, scaleParam.scale); } } } services/core/java/com/android/server/vibrator/VibratorManagerService.java +2 −1 Original line number Diff line number Diff line Loading @@ -273,7 +273,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) { injector.addService(VIBRATOR_CONTROL_SERVICE, new VibratorControlService(new VibratorControllerHolder(), mLock)); new VibratorControlService(new VibratorControllerHolder(), mVibrationScaler, mLock)); } } Loading services/tests/vibrator/Android.bp +2 −2 Original line number Diff line number Diff line Loading @@ -31,13 +31,13 @@ android_test { "frameworks-base-testutils", "frameworks-services-vibrator-testutils", "junit", "mockito-target-minus-junit4", "mockito-target-inline-minus-junit4", "platform-test-annotations", "service-permission.stubs.system_server", "services.core", "flag-junit", ], jni_libs: ["libdexmakerjvmtiagent"], platform_apis: true, certificate: "platform", dxflags: ["--multi-dex"], Loading services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java +31 −0 Original line number Diff line number Diff line Loading @@ -43,12 +43,17 @@ import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.test.TestLooper; import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationEffectSegment; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.util.SparseArray; import androidx.test.InstrumentationRegistry; Loading @@ -68,6 +73,9 @@ public class VibrationScalerTest { @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Mock private PowerManagerInternal mPowerManagerInternalMock; @Mock private PackageManagerInternal mPackageManagerInternalMock; Loading Loading @@ -256,6 +264,29 @@ public class VibrationScalerTest { assertEquals(0.5, scaled.getScale(), 1e-5); } @Test @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) public void scale_withAdaptiveHaptics_scalesVibrationsCorrectly() { setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH); setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH); SparseArray<Float> adaptiveHapticsScales = new SparseArray<>(); adaptiveHapticsScales.put(USAGE_RINGTONE, 0.5f); adaptiveHapticsScales.put(USAGE_NOTIFICATION, 0.5f); mVibrationScaler.updateAdaptiveHapticsScales(adaptiveHapticsScales); StepSegment scaled = getFirstSegment(mVibrationScaler.scale( VibrationEffect.createOneShot(128, 128), USAGE_RINGTONE)); // Ringtone scales down. assertTrue(scaled.getAmplitude() < 0.5); scaled = getFirstSegment(mVibrationScaler.scale( VibrationEffect.createWaveform(new long[]{128}, new int[]{128}, -1), USAGE_NOTIFICATION)); // Notification scales down. assertTrue(scaled.getAmplitude() < 0.5); } private void setDefaultIntensity(@VibrationAttributes.Usage int usage, @Vibrator.VibrationIntensity int intensity) { when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity); Loading Loading
services/core/java/com/android/server/vibrator/VibrationScaler.java +23 −0 Original line number Diff line number Diff line Loading @@ -17,11 +17,13 @@ package com.android.server.vibrator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.vibrator.V1_0.EffectStrength; import android.os.IExternalVibratorService; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.VibrationEffectSegment; import android.util.Slog; Loading Loading @@ -56,6 +58,8 @@ final class VibrationScaler { private final VibrationSettings mSettingsController; private final int mDefaultVibrationAmplitude; private SparseArray<Float> mAdaptiveHapticsScales; VibrationScaler(Context context, VibrationSettings settingsController) { mSettingsController = settingsController; mDefaultVibrationAmplitude = context.getResources().getInteger( Loading Loading @@ -140,6 +144,15 @@ final class VibrationScaler { if (scaleLevel != null) { segment = segment.scale(scaleLevel.factor); } // If adaptive haptics scaling is available for this usage, apply it to the segment. if (Flags.adaptiveHapticsEnabled() && mAdaptiveHapticsScales != null && mAdaptiveHapticsScales.size() > 0 && mAdaptiveHapticsScales.contains(usageHint)) { float adaptiveScale = mAdaptiveHapticsScales.get(usageHint); segment = segment.scale(adaptiveScale); } segments.set(i, segment); } if (segments.equals(composedEffect.getSegments())) { Loading Loading @@ -173,6 +186,16 @@ final class VibrationScaler { return prebaked.applyEffectStrength(newEffectStrength); } /** * Updates the adaptive haptics scales. * @param scales the new vibration scales to apply. * * @hide */ public void updateAdaptiveHapticsScales(@Nullable SparseArray<Float> scales) { mAdaptiveHapticsScales = scales; } /** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */ private static int intensityToEffectStrength(int intensity) { switch (intensity) { Loading
services/core/java/com/android/server/vibrator/VibratorControlService.java +108 −8 Original line number Diff line number Diff line Loading @@ -16,14 +16,26 @@ package com.android.server.vibrator; import static android.os.VibrationAttributes.USAGE_ALARM; import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST; import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK; import static android.os.VibrationAttributes.USAGE_MEDIA; import static android.os.VibrationAttributes.USAGE_NOTIFICATION; import static android.os.VibrationAttributes.USAGE_RINGTONE; import static android.os.VibrationAttributes.USAGE_TOUCH; import static android.os.VibrationAttributes.USAGE_UNKNOWN; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.frameworks.vibrator.IVibratorControlService; import android.frameworks.vibrator.IVibratorController; import android.frameworks.vibrator.ScaleParam; import android.frameworks.vibrator.VibrationParam; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import android.util.SparseArray; import java.util.Objects; Loading @@ -37,10 +49,13 @@ public final class VibratorControlService extends IVibratorControlService.Stub { private static final String TAG = "VibratorControlService"; private final VibratorControllerHolder mVibratorControllerHolder; private final VibrationScaler mVibrationScaler; private final Object mLock; public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, Object lock) { public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, VibrationScaler vibrationScaler, Object lock) { mVibratorControllerHolder = vibratorControllerHolder; mVibrationScaler = vibrationScaler; mLock = lock; } Loading Loading @@ -70,25 +85,62 @@ public final class VibratorControlService extends IVibratorControlService.Stub { + "controller doesn't match the registered one. " + this); return; } updateAdaptiveHapticsScales(/* params= */ null); mVibratorControllerHolder.setVibratorController(null); } } @Override public void setVibrationParams( @SuppressLint("ArrayReturn") VibrationParam[] params, IVibratorController token) throws RemoteException { // TODO(b/305939964): Add set vibration implementation. public void setVibrationParams(@SuppressLint("ArrayReturn") VibrationParam[] params, @NonNull IVibratorController token) throws RemoteException { Objects.requireNonNull(token); synchronized (mLock) { if (mVibratorControllerHolder.getVibratorController() == null) { Slog.w(TAG, "Received request to set VibrationParams for IVibratorController = " + token + ", but no controller was previously registered. Request " + "Ignored."); return; } if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(), token.asBinder())) { Slog.wtf(TAG, "Failed to set new VibrationParams. The provided " + "controller doesn't match the registered one. " + this); return; } updateAdaptiveHapticsScales(params); } } @Override public void clearVibrationParams(int types, IVibratorController token) throws RemoteException { // TODO(b/305939964): Add clear vibration implementation. public void clearVibrationParams(int types, @NonNull IVibratorController token) throws RemoteException { Objects.requireNonNull(token); synchronized (mLock) { if (mVibratorControllerHolder.getVibratorController() == null) { Slog.w(TAG, "Received request to clear VibrationParams for IVibratorController = " + token + ", but no controller was previously registered. Request " + "Ignored."); return; } if (!Objects.equals(mVibratorControllerHolder.getVibratorController().asBinder(), token.asBinder())) { Slog.wtf(TAG, "Failed to clear VibrationParams. The provided " + "controller doesn't match the registered one. " + this); return; } //TODO(305942827): Update this method to only clear the specified vibration types. // Perhaps look into whether it makes more sense to have this clear all scales and // rely on setVibrationParams for clearing the scales for specific vibrations. updateAdaptiveHapticsScales(/* params= */ null); } } @Override public void onRequestVibrationParamsComplete( IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result) @NonNull IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result) throws RemoteException { // TODO(305942827): Cache the vibration params in VibrationScaler } Loading @@ -102,4 +154,52 @@ public final class VibratorControlService extends IVibratorControlService.Stub { public String getInterfaceHash() throws RemoteException { return this.HASH; } /** * Extracts the vibration scales and caches them in {@link VibrationScaler}. * * @param params the new vibration params to cache. */ private void updateAdaptiveHapticsScales(@Nullable VibrationParam[] params) { if (params == null || params.length == 0) { mVibrationScaler.updateAdaptiveHapticsScales(null); return; } SparseArray<Float> vibrationScales = new SparseArray<>(); for (int i = 0; i < params.length; i++) { ScaleParam scaleParam = params[i].getScale(); extractVibrationScales(scaleParam, vibrationScales); } mVibrationScaler.updateAdaptiveHapticsScales(vibrationScales); } /** * Extracts the vibration scales and map them to their corresponding * {@link android.os.VibrationAttributes} usages. */ private void extractVibrationScales(ScaleParam scaleParam, SparseArray<Float> vibrationScales) { if ((ScaleParam.TYPE_ALARM & scaleParam.typesMask) != 0) { vibrationScales.put(USAGE_ALARM, scaleParam.scale); } if ((ScaleParam.TYPE_NOTIFICATION & scaleParam.typesMask) != 0) { vibrationScales.put(USAGE_NOTIFICATION, scaleParam.scale); vibrationScales.put(USAGE_COMMUNICATION_REQUEST, scaleParam.scale); } if ((ScaleParam.TYPE_RINGTONE & scaleParam.typesMask) != 0) { vibrationScales.put(USAGE_RINGTONE, scaleParam.scale); } if ((ScaleParam.TYPE_MEDIA & scaleParam.typesMask) != 0) { vibrationScales.put(USAGE_MEDIA, scaleParam.scale); vibrationScales.put(USAGE_UNKNOWN, scaleParam.scale); } if ((ScaleParam.TYPE_INTERACTIVE & scaleParam.typesMask) != 0) { vibrationScales.put(USAGE_TOUCH, scaleParam.scale); vibrationScales.put(USAGE_HARDWARE_FEEDBACK, scaleParam.scale); } } }
services/core/java/com/android/server/vibrator/VibratorManagerService.java +2 −1 Original line number Diff line number Diff line Loading @@ -273,7 +273,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) { injector.addService(VIBRATOR_CONTROL_SERVICE, new VibratorControlService(new VibratorControllerHolder(), mLock)); new VibratorControlService(new VibratorControllerHolder(), mVibrationScaler, mLock)); } } Loading
services/tests/vibrator/Android.bp +2 −2 Original line number Diff line number Diff line Loading @@ -31,13 +31,13 @@ android_test { "frameworks-base-testutils", "frameworks-services-vibrator-testutils", "junit", "mockito-target-minus-junit4", "mockito-target-inline-minus-junit4", "platform-test-annotations", "service-permission.stubs.system_server", "services.core", "flag-junit", ], jni_libs: ["libdexmakerjvmtiagent"], platform_apis: true, certificate: "platform", dxflags: ["--multi-dex"], Loading
services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java +31 −0 Original line number Diff line number Diff line Loading @@ -43,12 +43,17 @@ import android.os.VibrationAttributes; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.test.TestLooper; import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationConfig; import android.os.vibrator.VibrationEffectSegment; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.util.SparseArray; import androidx.test.InstrumentationRegistry; Loading @@ -68,6 +73,9 @@ public class VibrationScalerTest { @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @Mock private PowerManagerInternal mPowerManagerInternalMock; @Mock private PackageManagerInternal mPackageManagerInternalMock; Loading Loading @@ -256,6 +264,29 @@ public class VibrationScalerTest { assertEquals(0.5, scaled.getScale(), 1e-5); } @Test @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED) public void scale_withAdaptiveHaptics_scalesVibrationsCorrectly() { setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH); setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH); SparseArray<Float> adaptiveHapticsScales = new SparseArray<>(); adaptiveHapticsScales.put(USAGE_RINGTONE, 0.5f); adaptiveHapticsScales.put(USAGE_NOTIFICATION, 0.5f); mVibrationScaler.updateAdaptiveHapticsScales(adaptiveHapticsScales); StepSegment scaled = getFirstSegment(mVibrationScaler.scale( VibrationEffect.createOneShot(128, 128), USAGE_RINGTONE)); // Ringtone scales down. assertTrue(scaled.getAmplitude() < 0.5); scaled = getFirstSegment(mVibrationScaler.scale( VibrationEffect.createWaveform(new long[]{128}, new int[]{128}, -1), USAGE_NOTIFICATION)); // Notification scales down. assertTrue(scaled.getAmplitude() < 0.5); } private void setDefaultIntensity(@VibrationAttributes.Usage int usage, @Vibrator.VibrationIntensity int intensity) { when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity); Loading