Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ff122ff0 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Enable system vibrations on screen off for other feedbacks" into tm-dev

parents 1b512780 8d2117d1
Loading
Loading
Loading
Loading
+39 −0
Original line number Original line Diff line number Diff line
@@ -34,6 +34,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.media.AudioManager;
@@ -42,6 +43,7 @@ import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.PowerSaveState;
import android.os.Process;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationAttributes;
@@ -106,6 +108,19 @@ final class VibrationSettings {
     */
     */
    private static final int VIBRATE_ON_DISABLED_USAGE_ALLOWED = USAGE_ACCESSIBILITY;
    private static final int VIBRATE_ON_DISABLED_USAGE_ALLOWED = USAGE_ACCESSIBILITY;


    /**
     * Set of usages allowed for vibrations from system packages when the screen goes off.
     *
     * <p>Some examples are touch and hardware feedback, and physical emulation. When the system is
     * playing one of these usages during the screen off event then the vibration will not be
     * cancelled by the service.
     */
    private static final Set<Integer> SYSTEM_VIBRATION_SCREEN_OFF_USAGE_ALLOWLIST = new HashSet<>(
            Arrays.asList(
                    USAGE_TOUCH,
                    USAGE_PHYSICAL_EMULATION,
                    USAGE_HARDWARE_FEEDBACK));

    /** Listener for changes on vibration settings. */
    /** Listener for changes on vibration settings. */
    interface OnVibratorSettingsChanged {
    interface OnVibratorSettingsChanged {
        /** Callback triggered when any of the vibrator settings change. */
        /** Callback triggered when any of the vibrator settings change. */
@@ -114,6 +129,7 @@ final class VibrationSettings {


    private final Object mLock = new Object();
    private final Object mLock = new Object();
    private final Context mContext;
    private final Context mContext;
    private final String mSystemUiPackage;
    private final SettingsObserver mSettingObserver;
    private final SettingsObserver mSettingObserver;
    @VisibleForTesting
    @VisibleForTesting
    final UidObserver mUidObserver;
    final UidObserver mUidObserver;
@@ -151,6 +167,9 @@ final class VibrationSettings {
        mUidObserver = new UidObserver();
        mUidObserver = new UidObserver();
        mUserReceiver = new UserObserver();
        mUserReceiver = new UserObserver();


        mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
                .getSystemUiServiceComponent().getPackageName();

        VibrationEffect clickEffect = createEffectFromResource(
        VibrationEffect clickEffect = createEffectFromResource(
                com.android.internal.R.array.config_virtualKeyVibePattern);
                com.android.internal.R.array.config_virtualKeyVibePattern);
        VibrationEffect doubleClickEffect = createEffectFromResource(
        VibrationEffect doubleClickEffect = createEffectFromResource(
@@ -343,6 +362,26 @@ final class VibrationSettings {
        return null;
        return null;
    }
    }


    /**
     * Check if given vibration should be cancelled by the service when the screen goes off.
     *
     * <p>When the system is entering a non-interactive state, we want to cancel vibrations in case
     * a misbehaving app has abandoned them. However, it may happen that the system is currently
     * playing haptic feedback as part of the transition. So we don't cancel system vibrations of
     * usages like touch and hardware feedback, and physical emulation.
     *
     * @return true if the vibration should be cancelled when the screen goes off, false otherwise.
     */
    public boolean shouldCancelVibrationOnScreenOff(int uid, String opPkg,
            @VibrationAttributes.Usage int usage) {
        if (!SYSTEM_VIBRATION_SCREEN_OFF_USAGE_ALLOWLIST.contains(usage)) {
            // Usages not allowed even for system vibrations should always be cancelled.
            return true;
        }
        // Only allow vibrations from System packages to continue vibrating when the screen goes off
        return uid != Process.SYSTEM_UID && uid != 0 && !mSystemUiPackage.equals(opPkg);
    }

    /**
    /**
     * Return {@code true} if the device should vibrate for current ringer mode.
     * Return {@code true} if the device should vibrate for current ringer mode.
     *
     *
+10 −18
Original line number Original line Diff line number Diff line
@@ -28,7 +28,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.IVibrator;
import android.os.BatteryStats;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Binder;
@@ -62,7 +61,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemService;


import libcore.util.NativeAllocationRegistry;
import libcore.util.NativeAllocationRegistry;
@@ -120,7 +118,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {


    private final Object mLock = new Object();
    private final Object mLock = new Object();
    private final Context mContext;
    private final Context mContext;
    private final String mSystemUiPackage;
    private final PowerManager.WakeLock mWakeLock;
    private final PowerManager.WakeLock mWakeLock;
    private final IBatteryStats mBatteryStatsService;
    private final IBatteryStats mBatteryStatsService;
    private final Handler mHandler;
    private final Handler mHandler;
@@ -153,17 +150,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        public void onReceive(Context context, Intent intent) {
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                synchronized (mLock) {
                synchronized (mLock) {
                    // When the system is entering a non-interactive state, we want
                    // When the system is entering a non-interactive state, we want to cancel
                    // to cancel vibrations in case a misbehaving app has abandoned
                    // vibrations in case a misbehaving app has abandoned them.
                    // them.  However it may happen that the system is currently playing
                    if (shouldCancelOnScreenOffLocked(mNextVibration)) {
                    // haptic feedback as part of the transition.  So we don't cancel
                    // system vibrations.
                    if (mNextVibration != null
                            && !isSystemHapticFeedback(mNextVibration.getVibration())) {
                        clearNextVibrationLocked(Vibration.Status.CANCELLED);
                        clearNextVibrationLocked(Vibration.Status.CANCELLED);
                    }
                    }
                    if (mCurrentVibration != null
                    if (shouldCancelOnScreenOffLocked(mCurrentVibration)) {
                            && !isSystemHapticFeedback(mCurrentVibration.getVibration())) {
                        mCurrentVibration.cancel();
                        mCurrentVibration.cancel();
                    }
                    }
                }
                }
@@ -203,9 +195,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                com.android.internal.R.integer.config_previousVibrationsDumpLimit);
                com.android.internal.R.integer.config_previousVibrationsDumpLimit);
        mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit);
        mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit);


        mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
                .getSystemUiServiceComponent().getPackageName();

        mBatteryStatsService = injector.getBatteryStatsService();
        mBatteryStatsService = injector.getBatteryStatsService();


        mAppOps = mContext.getSystemService(AppOpsManager.class);
        mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -1074,11 +1063,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                == PackageManager.PERMISSION_GRANTED;
                == PackageManager.PERMISSION_GRANTED;
    }
    }


    private boolean isSystemHapticFeedback(Vibration vib) {
    @GuardedBy("mLock")
        if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) {
    private boolean shouldCancelOnScreenOffLocked(@Nullable VibrationThread vibrationThread) {
        if (vibrationThread == null) {
            return false;
            return false;
        }
        }
        return vib.uid == Process.SYSTEM_UID || vib.uid == 0 || mSystemUiPackage.equals(vib.opPkg);
        Vibration vib = vibrationThread.getVibration();
        return mVibrationSettings.shouldCancelVibrationOnScreenOff(
                vib.uid, vib.opPkg, vib.attrs.getUsage());
    }
    }


    @GuardedBy("mLock")
    @GuardedBy("mLock")
+18 −0
Original line number Original line Diff line number Diff line
@@ -18,7 +18,10 @@ package com.android.server.vibrator;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;


import android.content.ComponentName;
import android.content.pm.PackageManagerInternal;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.IVibrator;
import android.os.Handler;
import android.os.Handler;
import android.os.VibrationEffect;
import android.os.VibrationEffect;
@@ -33,8 +36,14 @@ import android.platform.test.annotations.Presubmit;


import androidx.test.InstrumentationRegistry;
import androidx.test.InstrumentationRegistry;


import com.android.server.LocalServices;

import org.junit.Before;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;


import java.util.Arrays;
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.IntStream;
@@ -59,10 +68,19 @@ public class DeviceVibrationEffectAdapterTest {
            new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
            new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
                    TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
                    TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);


    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();

    @Mock private PackageManagerInternal mPackageManagerInternalMock;

    private DeviceVibrationEffectAdapter mAdapter;
    private DeviceVibrationEffectAdapter mAdapter;


    @Before
    @Before
    public void setUp() throws Exception {
    public void setUp() throws Exception {
        when(mPackageManagerInternalMock.getSystemUiServiceComponent())
                .thenReturn(new ComponentName("", ""));
        LocalServices.removeServiceForTest(PackageManagerInternal.class);
        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);

        VibrationSettings vibrationSettings = new VibrationSettings(
        VibrationSettings vibrationSettings = new VibrationSettings(
                InstrumentationRegistry.getContext(), new Handler(new TestLooper().getLooper()));
                InstrumentationRegistry.getContext(), new Handler(new TestLooper().getLooper()));
        mAdapter = new DeviceVibrationEffectAdapter(vibrationSettings);
        mAdapter = new DeviceVibrationEffectAdapter(vibrationSettings);
+7 −0
Original line number Original line Diff line number Diff line
@@ -31,8 +31,10 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentResolver;
import android.content.ContextWrapper;
import android.content.ContextWrapper;
import android.content.pm.PackageManagerInternal;
import android.os.Handler;
import android.os.Handler;
import android.os.IExternalVibratorService;
import android.os.IExternalVibratorService;
import android.os.PowerManagerInternal;
import android.os.PowerManagerInternal;
@@ -76,6 +78,7 @@ public class VibrationScalerTest {
    @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
    @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();


    @Mock private PowerManagerInternal mPowerManagerInternalMock;
    @Mock private PowerManagerInternal mPowerManagerInternalMock;
    @Mock private PackageManagerInternal mPackageManagerInternalMock;
    @Mock private VibrationConfig mVibrationConfigMock;
    @Mock private VibrationConfig mVibrationConfigMock;


    private TestLooper mTestLooper;
    private TestLooper mTestLooper;
@@ -90,7 +93,11 @@ public class VibrationScalerTest {


        ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
        ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
        when(mPackageManagerInternalMock.getSystemUiServiceComponent())
                .thenReturn(new ComponentName("", ""));


        LocalServices.removeServiceForTest(PackageManagerInternal.class);
        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
        LocalServices.removeServiceForTest(PowerManagerInternal.class);
        LocalServices.removeServiceForTest(PowerManagerInternal.class);
        LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
        LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);


+63 −11
Original line number Original line Diff line number Diff line
@@ -47,13 +47,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import android.app.ActivityManager;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentResolver;
import android.content.ContextWrapper;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.PackageManagerInternal;
import android.media.AudioManager;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Handler;
import android.os.PowerManagerInternal;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.PowerSaveState;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.VibrationEffect;
@@ -87,6 +90,7 @@ import org.mockito.junit.MockitoRule;
public class VibrationSettingsTest {
public class VibrationSettingsTest {


    private static final int UID = 1;
    private static final int UID = 1;
    private static final String SYSUI_PACKAGE_NAME = "sysui";
    private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
    private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
    private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
    private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
            .setBatterySaverEnabled(true).build();
            .setBatterySaverEnabled(true).build();
@@ -104,17 +108,13 @@ public class VibrationSettingsTest {
            USAGE_TOUCH,
            USAGE_TOUCH,
    };
    };


    @Rule
    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
    public MockitoRule mMockitoRule = MockitoJUnit.rule();
    @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
    @Rule
    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();


    @Mock
    @Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
    private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
    @Mock private PowerManagerInternal mPowerManagerInternalMock;
    @Mock
    @Mock private PackageManagerInternal mPackageManagerInternalMock;
    private PowerManagerInternal mPowerManagerInternalMock;
    @Mock private VibrationConfig mVibrationConfigMock;
    @Mock
    private VibrationConfig mVibrationConfigMock;


    private TestLooper mTestLooper;
    private TestLooper mTestLooper;
    private ContextWrapper mContextSpy;
    private ContextWrapper mContextSpy;
@@ -129,14 +129,17 @@ public class VibrationSettingsTest {


        ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
        ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);

        doAnswer(invocation -> {
        doAnswer(invocation -> {
            mRegisteredPowerModeListener = invocation.getArgument(0);
            mRegisteredPowerModeListener = invocation.getArgument(0);
            return null;
            return null;
        }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
        }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
        when(mPackageManagerInternalMock.getSystemUiServiceComponent())
                .thenReturn(new ComponentName(SYSUI_PACKAGE_NAME, ""));


        LocalServices.removeServiceForTest(PowerManagerInternal.class);
        LocalServices.removeServiceForTest(PowerManagerInternal.class);
        LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
        LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
        LocalServices.removeServiceForTest(PackageManagerInternal.class);
        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);


        setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM);
        setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM);
        mAudioManager = mContextSpy.getSystemService(AudioManager.class);
        mAudioManager = mContextSpy.getSystemService(AudioManager.class);
@@ -471,6 +474,55 @@ public class VibrationSettingsTest {
        assertFalse(mVibrationSettings.shouldVibrateInputDevices());
        assertFalse(mVibrationSettings.shouldVibrateInputDevices());
    }
    }


    @Test
    public void shouldCancelVibrationOnScreenOff_withNonSystemPackageAndUid_returnsAlwaysTrue() {
        for (int usage : ALL_USAGES) {
            assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(UID, "some.app", usage));
        }
    }

    @Test
    public void shouldCancelVibrationOnScreenOff_withUidZero_returnsFalseForTouchAndHardware() {
        for (int usage : ALL_USAGES) {
            if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
                    || usage == USAGE_PHYSICAL_EMULATION) {
                assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
                        /* uid= */ 0, "", usage));
            } else {
                assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
                        /* uid= */ 0, "", usage));
            }
        }
    }

    @Test
    public void shouldCancelVibrationOnScreenOff_withSystemUid_returnsFalseForTouchAndHardware() {
        for (int usage : ALL_USAGES) {
            if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
                    || usage == USAGE_PHYSICAL_EMULATION) {
                assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
                        Process.SYSTEM_UID, "", usage));
            } else {
                assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
                        Process.SYSTEM_UID, "", usage));
            }
        }
    }

    @Test
    public void shouldCancelVibrationOnScreenOff_withSysUi_returnsFalseForTouchAndHardware() {
        for (int usage : ALL_USAGES) {
            if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK
                    || usage == USAGE_PHYSICAL_EMULATION) {
                assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff(
                        UID, SYSUI_PACKAGE_NAME, usage));
            } else {
                assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(
                        UID, SYSUI_PACKAGE_NAME, usage));
            }
        }
    }

    @Test
    @Test
    public void getDefaultIntensity_returnsIntensityFromVibratorConfig() {
    public void getDefaultIntensity_returnsIntensityFromVibratorConfig() {
        setDefaultIntensity(VIBRATION_INTENSITY_HIGH);
        setDefaultIntensity(VIBRATION_INTENSITY_HIGH);
Loading