Merge "Changes the vibrator_manager shell commands to block until the end of the vibration by default, and adds a -B option to give the old behaviour of just initiating the vibration."

This commit is contained in:
Simon Bowden
2021-08-18 11:00:46 +00:00
committed by Android (Google) Code Review
2 changed files with 84 additions and 25 deletions

View File

@@ -33,6 +33,7 @@ import android.util.proto.ProtoOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.function.Function;
/** Represents a vibration request to the vibrator service. */
@@ -58,6 +59,7 @@ final class Vibration {
IGNORED_FOR_ONGOING,
IGNORED_FOR_POWER,
IGNORED_FOR_SETTINGS,
IGNORED_SUPERSEDED,
}
/** Start time in CLOCK_BOOTTIME base. */
@@ -90,6 +92,9 @@ final class Vibration {
private long mEndTimeDebug;
private Status mStatus;
/** A {@link CountDownLatch} to enable waiting for completion. */
private final CountDownLatch mCompletionLatch = new CountDownLatch(1);
Vibration(IBinder token, int id, CombinedVibration effect,
VibrationAttributes attrs, int uid, String opPkg, String reason) {
this.token = token;
@@ -118,6 +123,12 @@ final class Vibration {
}
mStatus = status;
mEndTimeDebug = System.currentTimeMillis();
mCompletionLatch.countDown();
}
/** Waits indefinitely until another thread calls {@link #end(Status)} on this vibration. */
public void waitForEnd() throws InterruptedException {
mCompletionLatch.await();
}
/**

View File

@@ -144,9 +144,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// 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.
if (mNextVibration != null
&& !isSystemHapticFeedback(mNextVibration.getVibration())) {
clearNextVibrationLocked(Vibration.Status.CANCELLED);
}
if (mCurrentVibration != null
&& !isSystemHapticFeedback(mCurrentVibration.getVibration())) {
mNextVibration = null;
mCurrentVibration.cancel();
}
}
@@ -336,17 +339,27 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Override // Binder call
public void vibrate(int uid, String opPkg, @NonNull CombinedVibration effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
vibrateInternal(uid, opPkg, effect, attrs, reason, token);
}
/**
* An internal-only version of vibrate that allows the caller access to the {@link Vibration}.
* The Vibration is only returned if it is ongoing after this method returns.
*/
@Nullable
private Vibration vibrateInternal(int uid, String opPkg, @NonNull CombinedVibration effect,
@Nullable VibrationAttributes attrs, String reason, IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
try {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.VIBRATE, "vibrate");
if (token == null) {
Slog.e(TAG, "token must not be null");
return;
return null;
}
enforceUpdateAppOpsStatsPermission(uid);
if (!isEffectValid(effect)) {
return;
return null;
}
attrs = fixupVibrationAttributes(attrs);
Vibration vib = new Vibration(token, mNextVibrationId.getAndIncrement(), effect, attrs,
@@ -357,13 +370,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vib);
if (ignoreStatus != null) {
endVibrationLocked(vib, ignoreStatus);
return;
return vib;
}
ignoreStatus = shouldIgnoreVibrationForCurrentLocked(vib);
if (ignoreStatus != null) {
endVibrationLocked(vib, ignoreStatus);
return;
return vib;
}
final long ident = Binder.clearCallingIdentity();
@@ -375,6 +388,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (status != Vibration.Status.RUNNING) {
endVibrationLocked(vib, status);
}
return vib;
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -401,7 +415,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (mNextVibration != null
&& shouldCancelVibration(mNextVibration.getVibration(),
usageFilter, token)) {
mNextVibration = null;
clearNextVibrationLocked(Vibration.Status.CANCELLED);
}
if (mCurrentVibration != null
&& shouldCancelVibration(mCurrentVibration.getVibration(),
@@ -453,7 +467,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback cb, ResultReceiver resultReceiver) {
new VibratorManagerShellCommand(this).exec(this, in, out, err, args, cb, resultReceiver);
new VibratorManagerShellCommand(cb.getShellCallbackBinder())
.exec(this, in, out, err, args, cb, resultReceiver);
}
@VisibleForTesting
@@ -521,7 +536,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (mCurrentVibration == null) {
return startVibrationThreadLocked(vibThread);
}
// If there's already a vibration queued (waiting for the previous one to finish
// cancelling), end it cleanly and replace it with the new one.
clearNextVibrationLocked(Vibration.Status.IGNORED_SUPERSEDED);
mNextVibration = vibThread;
return Vibration.Status.RUNNING;
} finally {
@@ -1300,6 +1317,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
/** Clears mNextVibration if set, ending it cleanly */
private void clearNextVibrationLocked(Vibration.Status endStatus) {
if (mNextVibration != null) {
endVibrationLocked(mNextVibration.getVibration(), endStatus);
mNextVibration = null;
}
}
/** Implementation of {@link IExternalVibratorService} to be triggered on external control. */
@VisibleForTesting
final class ExternalVibratorService extends IExternalVibratorService.Stub {
@@ -1347,7 +1372,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// If we're not under external control right now, then cancel any normal
// vibration that may be playing and ready the vibrator for external control.
if (mCurrentVibration != null) {
mNextVibration = null;
clearNextVibrationLocked(Vibration.Status.IGNORED_FOR_EXTERNAL);
mCurrentVibration.cancelImmediately();
cancelingVibration = mCurrentVibration;
}
@@ -1454,6 +1479,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private final class CommonOptions {
public boolean force = false;
public String description = "Shell command";
public boolean background = false;
CommonOptions() {
String nextArg;
@@ -1463,6 +1489,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
getNextArgRequired(); // consume "-f"
force = true;
break;
case "-B":
getNextArgRequired(); // consume "-B"
background = true;
break;
case "-d":
getNextArgRequired(); // consume "-d"
description = getNextArgRequired();
@@ -1475,10 +1505,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
private final IBinder mToken;
private final IBinder mShellCallbacksToken;
private VibratorManagerShellCommand(IBinder token) {
mToken = token;
private VibratorManagerShellCommand(IBinder shellCallbacksToken) {
mShellCallbacksToken = shellCallbacksToken;
}
@Override
@@ -1520,12 +1550,28 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
private int runMono() {
CommonOptions commonOptions = new CommonOptions();
CombinedVibration effect = CombinedVibration.createParallel(nextEffect());
/**
* Runs a CombinedVibration using the configured common options and attributes.
*/
private void runVibrate(CommonOptions commonOptions, CombinedVibration combined) {
VibrationAttributes attrs = createVibrationAttributes(commonOptions);
vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, effect, attrs,
commonOptions.description, mToken);
// If running in the background, bind to death of the server binder rather than the
// client, and the cancel command likewise uses the server binder reference to
// only cancel background vibrations.
IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
: mShellCallbacksToken;
Vibration vib = vibrateInternal(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combined,
attrs, commonOptions.description, deathBinder);
if (vib != null && !commonOptions.background) {
try {
vib.waitForEnd();
} catch (InterruptedException e) {
}
}
}
private int runMono() {
runVibrate(new CommonOptions(), CombinedVibration.createParallel(nextEffect()));
return 0;
}
@@ -1537,9 +1583,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
int vibratorId = Integer.parseInt(getNextArgRequired());
combination.addVibrator(vibratorId, nextEffect());
}
VibrationAttributes attrs = createVibrationAttributes(commonOptions);
vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs,
commonOptions.description, mToken);
runVibrate(commonOptions, combination.combine());
return 0;
}
@@ -1551,14 +1595,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
int vibratorId = Integer.parseInt(getNextArgRequired());
combination.addNext(vibratorId, nextEffect());
}
VibrationAttributes attrs = createVibrationAttributes(commonOptions);
vibrate(Binder.getCallingUid(), SHELL_PACKAGE_NAME, combination.combine(), attrs,
commonOptions.description, mToken);
runVibrate(commonOptions, combination.combine());
return 0;
}
private int runCancel() {
cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, mToken);
// Cancel is only needed if the vibration was run in the background, otherwise it's
// terminated by the shell command ending. In these cases, the token was that of the
// service rather than the client.
cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, VibratorManagerService.this);
return 0;
}
@@ -1741,7 +1786,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.println(" wait time in milliseconds.");
pw.println(" If -a is provided, the command accepts a second argument for ");
pw.println(" amplitude, in a scale of 1-255.");
pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] ");
pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] ");
pw.println("(<duration> [<amplitude>] [<frequency>])...");
pw.println(" Vibrates for durations and amplitudes in list; ignored when ");
pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength ");
@@ -1777,6 +1822,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
pw.println("Common Options:");
pw.println(" -f");
pw.println(" Force. Ignore Do Not Disturb setting.");
pw.println(" -B");
pw.println(" Run in the background; without this option the shell cmd will");
pw.println(" block until the vibration has completed.");
pw.println(" -d <description>");
pw.println(" Add description to the vibration.");
pw.println("");