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

Commit 0694afac authored by Lais Andrade's avatar Lais Andrade
Browse files

Extract Vibration class to vibrator package.

This class currently holds a VibrationEffect, but will be changed to
hold a CombinedVibrationEffect and used to represent a vibration call to
VibratorManagerService.

Bug: 167946816
Bug: 131311651
Test: atest FrameworksServiceTests:VibratorServiceTest
Change-Id: I65985b6a079458b610a9a2728c3df157f9cee953
parent 43cfbbe8
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -159,7 +159,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                pw.println("    Prints this help text.");
                pw.println("");
                pw.println("  list");
                pw.println("    Prints the id of device vibrators. This do not include any ");
                pw.println("    Prints the id of device vibrators. This does not include any ");
                pw.println("    connected input device.");
                pw.println("");
            }
+139 −353

File changed.

Preview size limit exceeded, changes collapsed.

+287 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.server.vibrator;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.SystemClock;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.util.proto.ProtoOutputStream;

import com.android.server.ComposedProto;
import com.android.server.OneShotProto;
import com.android.server.PrebakedProto;
import com.android.server.VibrationAttributesProto;
import com.android.server.VibrationEffectProto;
import com.android.server.VibrationProto;
import com.android.server.WaveformProto;

import java.text.SimpleDateFormat;
import java.util.Date;

/** Represents a vibration request to the vibrator service. */
// TODO(b/159207608): Make this package-private once vibrator services are moved to this package
public class Vibration {
    private static final String TAG = "Vibration";
    private static final SimpleDateFormat DEBUG_DATE_FORMAT =
            new SimpleDateFormat("MM-dd HH:mm:ss.SSS");

    public enum Status {
        RUNNING,
        FINISHED,
        FORWARDED_TO_INPUT_DEVICES,
        CANCELLED,
        ERROR_APP_OPS,
        IGNORED,
        IGNORED_APP_OPS,
        IGNORED_BACKGROUND,
        IGNORED_RINGTONE,
        IGNORED_UNKNOWN_VIBRATION,
        IGNORED_UNSUPPORTED,
        IGNORED_FOR_ALARM,
        IGNORED_FOR_EXTERNAL,
        IGNORED_FOR_ONGOING,
        IGNORED_FOR_POWER,
        IGNORED_FOR_SETTINGS,
    }

    /** Start time in CLOCK_BOOTTIME base. */
    public final long startTime;
    public final VibrationAttributes attrs;
    public final long id;
    public final int uid;
    public final String opPkg;
    public final String reason;
    public final IBinder token;

    /** The actual effect to be played. */
    @Nullable
    private VibrationEffect mEffect;

    /**
     * The original effect that was requested. Typically these two things differ because the effect
     * was scaled based on the users vibration intensity settings.
     */
    @Nullable
    private VibrationEffect mOriginalEffect;

    /**
     * Start/end times in unix epoch time. Only to be used for debugging purposes and to correlate
     * with other system events, any duration calculations should be done use {@link #startTime} so
     * as not to be affected by discontinuities created by RTC adjustments.
     */
    private final long mStartTimeDebug;
    private long mEndTimeDebug;
    private Status mStatus;

    public Vibration(IBinder token, int id, VibrationEffect effect,
            VibrationAttributes attrs, int uid, String opPkg, String reason) {
        this.token = token;
        this.mEffect = effect;
        this.id = id;
        this.startTime = SystemClock.elapsedRealtime();
        this.attrs = attrs;
        this.uid = uid;
        this.opPkg = opPkg;
        this.reason = reason;
        mStartTimeDebug = System.currentTimeMillis();
        mStatus = Status.RUNNING;
    }

    /**
     * Set the {@link Status} of this vibration and the current system time as this
     * vibration end time, for debugging purposes.
     *
     * <p>This method will only accept given value if the current status is {@link
     * Status#RUNNING}.
     */
    public void end(Status status) {
        if (hasEnded()) {
            // Vibration already ended, keep first ending status set and ignore this one.
            return;
        }
        mStatus = status;
        mEndTimeDebug = System.currentTimeMillis();
    }

    /**
     * Replace this vibration effect if given {@code scaledEffect} is different, preserving the
     * original one for debug purposes.
     */
    public void updateEffect(@NonNull VibrationEffect newEffect) {
        if (newEffect.equals(mEffect)) {
            return;
        }
        mOriginalEffect = mEffect;
        mEffect = newEffect;
    }

    /** Return true is current status is different from {@link Status#RUNNING}. */
    public boolean hasEnded() {
        return mStatus != Status.RUNNING;
    }

    /** Return the effect that should be played by this vibration. */
    @Nullable
    public VibrationEffect getEffect() {
        return mEffect;
    }

    /** Return {@link Vibration.DebugInfo} with read-only debug information about this vibration. */
    public Vibration.DebugInfo getDebugInfo() {
        return new Vibration.DebugInfo(
                mStartTimeDebug, mEndTimeDebug, mEffect, mOriginalEffect, /* scale= */ 0, attrs,
                uid, opPkg, reason, mStatus);
    }

    /** Debug information about vibrations. */
    public static final class DebugInfo {
        private final long mStartTimeDebug;
        private final long mEndTimeDebug;
        private final VibrationEffect mEffect;
        private final VibrationEffect mOriginalEffect;
        private final float mScale;
        private final VibrationAttributes mAttrs;
        private final int mUid;
        private final String mOpPkg;
        private final String mReason;
        private final Status mStatus;

        public DebugInfo(long startTimeDebug, long endTimeDebug, VibrationEffect effect,
                VibrationEffect originalEffect, float scale, VibrationAttributes attrs,
                int uid, String opPkg, String reason, Status status) {
            mStartTimeDebug = startTimeDebug;
            mEndTimeDebug = endTimeDebug;
            mEffect = effect;
            mOriginalEffect = originalEffect;
            mScale = scale;
            mAttrs = attrs;
            mUid = uid;
            mOpPkg = opPkg;
            mReason = reason;
            mStatus = status;
        }

        @Override
        public String toString() {
            return new StringBuilder()
                    .append("startTime: ")
                    .append(DEBUG_DATE_FORMAT.format(new Date(mStartTimeDebug)))
                    .append(", endTime: ")
                    .append(mEndTimeDebug == 0 ? null
                            : DEBUG_DATE_FORMAT.format(new Date(mEndTimeDebug)))
                    .append(", status: ")
                    .append(mStatus.name().toLowerCase())
                    .append(", effect: ")
                    .append(mEffect)
                    .append(", originalEffect: ")
                    .append(mOriginalEffect)
                    .append(", scale: ")
                    .append(String.format("%.2f", mScale))
                    .append(", attrs: ")
                    .append(mAttrs)
                    .append(", uid: ")
                    .append(mUid)
                    .append(", opPkg: ")
                    .append(mOpPkg)
                    .append(", reason: ")
                    .append(mReason)
                    .toString();
        }

        /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */
        public void dumpProto(ProtoOutputStream proto, long fieldId) {
            final long token = proto.start(fieldId);
            proto.write(VibrationProto.START_TIME, mStartTimeDebug);
            proto.write(VibrationProto.END_TIME, mEndTimeDebug);
            proto.write(VibrationProto.STATUS, mStatus.ordinal());

            final long attrsToken = proto.start(VibrationProto.ATTRIBUTES);
            proto.write(VibrationAttributesProto.USAGE, mAttrs.getUsage());
            proto.write(VibrationAttributesProto.AUDIO_USAGE, mAttrs.getAudioUsage());
            proto.write(VibrationAttributesProto.FLAGS, mAttrs.getFlags());
            proto.end(attrsToken);

            if (mEffect != null) {
                dumpEffect(proto, VibrationProto.EFFECT, mEffect);
            }
            if (mOriginalEffect != null) {
                dumpEffect(proto, VibrationProto.ORIGINAL_EFFECT, mOriginalEffect);
            }

            proto.end(token);
        }

        private void dumpEffect(ProtoOutputStream proto, long fieldId, VibrationEffect effect) {
            final long token = proto.start(fieldId);
            if (effect instanceof VibrationEffect.OneShot) {
                dumpEffect(proto, VibrationEffectProto.ONESHOT, (VibrationEffect.OneShot) effect);
            } else if (effect instanceof VibrationEffect.Waveform) {
                dumpEffect(proto, VibrationEffectProto.WAVEFORM, (VibrationEffect.Waveform) effect);
            } else if (effect instanceof VibrationEffect.Prebaked) {
                dumpEffect(proto, VibrationEffectProto.PREBAKED, (VibrationEffect.Prebaked) effect);
            } else if (effect instanceof VibrationEffect.Composed) {
                dumpEffect(proto, VibrationEffectProto.COMPOSED, (VibrationEffect.Composed) effect);
            }
            proto.end(token);
        }

        private void dumpEffect(ProtoOutputStream proto, long fieldId,
                VibrationEffect.OneShot effect) {
            final long token = proto.start(fieldId);
            proto.write(OneShotProto.DURATION, (int) effect.getDuration());
            proto.write(OneShotProto.AMPLITUDE, effect.getAmplitude());
            proto.end(token);
        }

        private void dumpEffect(ProtoOutputStream proto, long fieldId,
                VibrationEffect.Waveform effect) {
            final long token = proto.start(fieldId);
            for (long timing : effect.getTimings()) {
                proto.write(WaveformProto.TIMINGS, (int) timing);
            }
            for (int amplitude : effect.getAmplitudes()) {
                proto.write(WaveformProto.AMPLITUDES, amplitude);
            }
            proto.write(WaveformProto.REPEAT, effect.getRepeatIndex() >= 0);
            proto.end(token);
        }

        private void dumpEffect(ProtoOutputStream proto, long fieldId,
                VibrationEffect.Prebaked effect) {
            final long token = proto.start(fieldId);
            proto.write(PrebakedProto.EFFECT_ID, effect.getId());
            proto.write(PrebakedProto.EFFECT_STRENGTH, effect.getEffectStrength());
            proto.write(PrebakedProto.FALLBACK, effect.shouldFallback());
            proto.end(token);
        }

        private void dumpEffect(ProtoOutputStream proto, long fieldId,
                VibrationEffect.Composed effect) {
            final long token = proto.start(fieldId);
            for (VibrationEffect.Composition.PrimitiveEffect primitive :
                    effect.getPrimitiveEffects()) {
                proto.write(ComposedProto.EFFECT_IDS, primitive.id);
                proto.write(ComposedProto.EFFECT_SCALES, primitive.scale);
                proto.write(ComposedProto.DELAYS, primitive.delay);
            }
            proto.end(token);
        }
    }
}
+0 −1
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ public final class VibrationScaler {

    // Scale levels. Each level, except MUTE, is defined as the delta between the current setting
    // and the default intensity for that type of vibration (i.e. current - default).
    private static final int SCALE_MUTE = IExternalVibratorService.SCALE_MUTE; // -100
    private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2
    private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1
    private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0