Merge "Create CombinedVibrationEffect.aidl"

This commit is contained in:
TreeHugger Robot
2020-10-22 14:06:09 +00:00
committed by Android (Google) Code Review
8 changed files with 345 additions and 4 deletions

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
parcelable CombinedVibrationEffect;

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
import android.annotation.NonNull;
import java.util.Objects;
/**
* A CombinedVibrationEffect describes a haptic effect to be performed by one or more {@link
* Vibrator Vibrators}.
*
* These effects may be any number of things, from single shot vibrations to complex waveforms.
*
* @hide
* @see VibrationEffect
*/
public abstract class CombinedVibrationEffect implements Parcelable {
private static final int PARCEL_TOKEN_MONO = 1;
/** @hide to prevent subclassing from outside of the framework */
public CombinedVibrationEffect() {
}
/**
* Create a synced vibration effect.
*
* A synced vibration effect should be performed by multiple vibrators at the same time.
*
* @param effect The {@link VibrationEffect} to perform
* @return The desired combined effect.
*/
@NonNull
public static CombinedVibrationEffect createSynced(@NonNull VibrationEffect effect) {
CombinedVibrationEffect combined = new Mono(effect);
combined.validate();
return combined;
}
@Override
public int describeContents() {
return 0;
}
/** @hide */
public abstract void validate();
/**
* Represents a single {@link VibrationEffect} that should be executed in all vibrators in sync.
*
* @hide
*/
public static final class Mono extends CombinedVibrationEffect {
private final VibrationEffect mEffect;
public Mono(Parcel in) {
mEffect = VibrationEffect.CREATOR.createFromParcel(in);
}
public Mono(@NonNull VibrationEffect effect) {
mEffect = effect;
}
public VibrationEffect getEffect() {
return mEffect;
}
/** @hide */
@Override
public void validate() {
mEffect.validate();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof CombinedVibrationEffect.Mono)) {
return false;
}
CombinedVibrationEffect.Mono other = (CombinedVibrationEffect.Mono) o;
return other.mEffect.equals(other.mEffect);
}
@Override
public int hashCode() {
return Objects.hash(mEffect);
}
@Override
public String toString() {
return "Mono{mEffect=" + mEffect + '}';
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(PARCEL_TOKEN_MONO);
mEffect.writeToParcel(out, flags);
}
@NonNull
public static final Parcelable.Creator<Mono> CREATOR =
new Parcelable.Creator<Mono>() {
@Override
public Mono createFromParcel(@NonNull Parcel in) {
// Skip the type token
in.readInt();
return new Mono(in);
}
@Override
@NonNull
public Mono[] newArray(int size) {
return new Mono[size];
}
};
}
@NonNull
public static final Parcelable.Creator<CombinedVibrationEffect> CREATOR =
new Parcelable.Creator<CombinedVibrationEffect>() {
@Override
public CombinedVibrationEffect createFromParcel(Parcel in) {
int token = in.readInt();
if (token == PARCEL_TOKEN_MONO) {
return new CombinedVibrationEffect.Mono(in);
} else {
throw new IllegalStateException(
"Unexpected combined vibration event type token in parcel.");
}
}
@Override
public CombinedVibrationEffect[] newArray(int size) {
return new CombinedVibrationEffect[size];
}
};
}

View File

@@ -16,9 +16,13 @@
package android.os;
import android.os.CombinedVibrationEffect;
import android.os.VibrationAttributes;
/** {@hide} */
interface IVibratorManagerService {
int[] getVibratorIds();
void vibrate(int uid, String opPkg, in CombinedVibrationEffect effect,
in VibrationAttributes attributes, String reason, IBinder token);
void cancelVibrate(IBinder token);
}

View File

@@ -983,6 +983,8 @@ public abstract class VibrationEffect implements Parcelable {
Composition.checkPrimitive(effect.id);
Preconditions.checkArgumentInRange(
effect.scale, 0.0f, 1.0f, "scale");
Preconditions.checkArgumentNonNegative(effect.delay,
"Primitive delay must be zero or positive");
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
import static junit.framework.Assert.assertEquals;
import static org.testng.Assert.assertThrows;
import android.platform.test.annotations.Presubmit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@Presubmit
@RunWith(JUnit4.class)
public class CombinedVibrationEffectTest {
@Test
public void testValidateMono() {
CombinedVibrationEffect.createSynced(VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
assertThrows(IllegalArgumentException.class,
() -> CombinedVibrationEffect.createSynced(new VibrationEffect.OneShot(-1, -1)));
}
@Test
public void testSerializationMono() {
CombinedVibrationEffect original = CombinedVibrationEffect.createSynced(
VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
Parcel parcel = Parcel.obtain();
original.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
CombinedVibrationEffect restored = CombinedVibrationEffect.CREATOR.createFromParcel(parcel);
assertEquals(original, restored);
}
}

View File

@@ -28,6 +28,7 @@ import static org.junit.Assert.assertArrayEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
import android.content.ContentInterface;
import android.content.ContentResolver;
@@ -99,6 +100,73 @@ public class VibrationEffectTest {
assertEquals(expectedEffect, effect);
}
@Test
public void testValidateOneShot() {
VibrationEffect.createOneShot(1, 255).validate();
VibrationEffect.createOneShot(1, VibrationEffect.DEFAULT_AMPLITUDE).validate();
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.createOneShot(-1, 255).validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.createOneShot(0, 255).validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.createOneShot(1, -2).validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.createOneShot(1, 256).validate());
}
@Test
public void testValidatePrebaked() {
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK).validate();
VibrationEffect.createPredefined(VibrationEffect.RINGTONES[1]).validate();
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.createPredefined(-1).validate());
}
@Test
public void testValidateWaveform() {
VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1).validate();
VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, 0).validate();
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.createWaveform(new long[0], new int[0], -1).validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.createWaveform(TEST_TIMINGS, new int[0], -1).validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.createWaveform(
new long[]{0, 0, 0}, TEST_AMPLITUDES, -1).validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.createWaveform(
TEST_TIMINGS, new int[]{-1, -1, -2}, -1).validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.createWaveform(
TEST_TIMINGS, TEST_AMPLITUDES, TEST_TIMINGS.length).validate());
}
@Test
public void testValidateComposed() {
VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
.compose()
.validate();
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.startComposition().addPrimitive(-1).compose().validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, -1, 10)
.compose()
.validate());
assertThrows(IllegalArgumentException.class,
() -> VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, -1)
.compose()
.validate());
}
@Test
public void testScalePrebaked_ignoresScaleAndReturnsSameEffect() {
VibrationEffect initial = VibrationEffect.get(VibrationEffect.RINGTONES[1]);

View File

@@ -16,12 +16,15 @@
package com.android.server;
import android.annotation.Nullable;
import android.content.Context;
import android.os.CombinedVibrationEffect;
import android.os.IBinder;
import android.os.IVibratorManagerService;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.VibrationAttributes;
import com.android.internal.annotations.VisibleForTesting;
@@ -66,6 +69,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return Arrays.copyOf(mVibratorIds, mVibratorIds.length);
}
@Override // Binder call
public void vibrate(int uid, String opPkg, CombinedVibrationEffect effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
throw new UnsupportedOperationException("Not implemented");
}
@Override // Binder call
public void cancelVibrate(IBinder token) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback cb, ResultReceiver resultReceiver) {

View File

@@ -16,10 +16,16 @@
package com.android.server;
import static com.android.server.testutils.TestUtils.assertExpectException;
import static org.junit.Assert.assertArrayEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.CombinedVibrationEffect;
import android.os.Process;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -40,9 +46,16 @@ import org.mockito.junit.MockitoRule;
@Presubmit
public class VibratorManagerServiceTest {
@Rule public MockitoRule rule = MockitoJUnit.rule();
private static final int UID = Process.ROOT_UID;
private static final String PACKAGE_NAME = "package";
private static final VibrationAttributes ALARM_ATTRS =
new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_ALARM).build();
@Mock private VibratorManagerService.NativeWrapper mNativeWrapperMock;
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
private VibratorManagerService.NativeWrapper mNativeWrapperMock;
@Before
public void setUp() throws Exception {
@@ -72,7 +85,26 @@ public class VibratorManagerServiceTest {
@Test
public void getVibratorIds_withNonEmptyResultFromNative_returnsSameArray() {
when(mNativeWrapperMock.getVibratorIds()).thenReturn(new int[]{ 1, 2 });
assertArrayEquals(new int[]{ 1, 2 }, createService().getVibratorIds());
when(mNativeWrapperMock.getVibratorIds()).thenReturn(new int[]{1, 2});
assertArrayEquals(new int[]{1, 2}, createService().getVibratorIds());
}
@Test
public void vibrate_isUnsupported() {
VibratorManagerService service = createService();
CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
assertExpectException(UnsupportedOperationException.class,
"Not implemented",
() -> service.vibrate(UID, PACKAGE_NAME, effect, ALARM_ATTRS, "reason", service));
}
@Test
public void cancelVibrate_isUnsupported() {
VibratorManagerService service = createService();
CombinedVibrationEffect effect = CombinedVibrationEffect.createSynced(
VibrationEffect.get(VibrationEffect.EFFECT_CLICK));
assertExpectException(UnsupportedOperationException.class,
"Not implemented", () -> service.cancelVibrate(service));
}
}