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

Commit e5f330f8 authored by Robert Wu's avatar Robert Wu
Browse files

MIDI: Power Boost when MIDI hardware sends data

When USB MIDI hardware sends bytes to Android, Android should set
power boost as if an interaction event just happened.

This change reduces the number of audio underruns.

Bug: 122678281
Test: Connect Akai MIDI USB Keyboard to MIDI Synth Ex
Test: Connect another Android phone as host to MIDI Synth Ex
Test: Collect perfetto traces and verify a difference
Change-Id: Ic95283813beb16e55d941f85f876124afa2f2794
parent 863cf2a7
Loading
Loading
Loading
Loading
+59 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2022 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.usb;

import android.os.PowerManagerInternal;
import android.util.Log;

import com.android.server.LocalServices;

import java.time.Instant;

/**
 * Sends power boost events to the power manager.
 */
public class PowerBoostSetter {
    private static final String TAG = "PowerBoostSetter";
    // Set power boost timeout to 15 seconds
    private static final int POWER_BOOST_TIMEOUT_MS = 15 * 1000;

    PowerManagerInternal mPowerManagerInternal = null;
    Instant mPreviousTimeout = null;

    PowerBoostSetter() {
        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
    }

    /**
     * Boosts the CPU clock frequency as if the screen is touched
     */
    public void boostPower() {
        if (mPowerManagerInternal == null) {
            mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
        }

        if (mPowerManagerInternal == null) {
            Log.w(TAG, "PowerManagerInternal null");
        } else if ((mPreviousTimeout == null) || Instant.now().isAfter(
                mPreviousTimeout.plusMillis(POWER_BOOST_TIMEOUT_MS / 2))) {
            // Only boost if the previous timeout is at least halfway done
            mPreviousTimeout = Instant.now();
            mPowerManagerInternal.setPowerBoost(PowerManagerInternal.BOOST_INTERACTION,
                    POWER_BOOST_TIMEOUT_MS);
        }
    }
}
+22 −0
Original line number Original line Diff line number Diff line
@@ -98,6 +98,11 @@ public final class UsbDirectMidiDevice implements Closeable {


    private UsbMidiPacketConverter mUsbMidiPacketConverter;
    private UsbMidiPacketConverter mUsbMidiPacketConverter;


    private PowerBoostSetter mPowerBoostSetter = null;

    private static final byte MESSAGE_TYPE_MIDI_1_CHANNEL_VOICE = 0x02;
    private static final byte MESSAGE_TYPE_MIDI_2_CHANNEL_VOICE = 0x04;

    private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
    private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
        @Override
        @Override
        public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
        public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
@@ -251,6 +256,8 @@ public final class UsbDirectMidiDevice implements Closeable {
        for (int port = 0; port < numOutputs; port++) {
        for (int port = 0; port < numOutputs; port++) {
            mMidiInputPortReceivers[port] = new InputReceiverProxy();
            mMidiInputPortReceivers[port] = new InputReceiverProxy();
        }
        }

        mPowerBoostSetter = new PowerBoostSetter();
    }
    }


    private int calculateDefaultMidiProtocol() {
    private int calculateDefaultMidiProtocol() {
@@ -418,6 +425,15 @@ public final class UsbDirectMidiDevice implements Closeable {
                                    }
                                    }
                                    outputReceivers[portFinal].send(convertedArray, 0,
                                    outputReceivers[portFinal].send(convertedArray, 0,
                                            convertedArray.length, timestamp);
                                            convertedArray.length, timestamp);

                                    // Boost power if there seems to be a voice message.
                                    // For legacy devices, boost when message is more than size 1.
                                    // For UMP devices, boost for channel voice messages.
                                    if ((mPowerBoostSetter != null && convertedArray.length > 1)
                                            && (!mIsUniversalMidiDevice
                                            || isChannelVoiceMessage(convertedArray))) {
                                        mPowerBoostSetter.boostPower();
                                    }
                                }
                                }
                            }
                            }
                        } catch (IOException e) {
                        } catch (IOException e) {
@@ -721,4 +737,10 @@ public final class UsbDirectMidiDevice implements Closeable {


        dump.end(token);
        dump.end(token);
    }
    }

    private boolean isChannelVoiceMessage(byte[] umpMessage) {
        byte messageType = (byte) ((umpMessage[0] >> 4) & 0x0f);
        return messageType == MESSAGE_TYPE_MIDI_1_CHANNEL_VOICE
                || messageType == MESSAGE_TYPE_MIDI_2_CHANNEL_VOICE;
    }
}
}
+9 −0
Original line number Original line Diff line number Diff line
@@ -77,6 +77,8 @@ public final class UsbMidiDevice implements Closeable {
    // only accessed from JNI code
    // only accessed from JNI code
    private int mPipeFD = -1;
    private int mPipeFD = -1;


    private PowerBoostSetter mPowerBoostSetter = null;

    private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
    private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
        @Override
        @Override
        public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
        public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
@@ -167,6 +169,8 @@ public final class UsbMidiDevice implements Closeable {
        for (int port = 0; port < numOutputs; port++) {
        for (int port = 0; port < numOutputs; port++) {
            mMidiInputPortReceivers[port] = new InputReceiverProxy();
            mMidiInputPortReceivers[port] = new InputReceiverProxy();
        }
        }

        mPowerBoostSetter = new PowerBoostSetter();
    }
    }


    private boolean openLocked() {
    private boolean openLocked() {
@@ -240,6 +244,11 @@ public final class UsbMidiDevice implements Closeable {


                                        int count = mInputStreams[index].read(buffer);
                                        int count = mInputStreams[index].read(buffer);
                                        outputReceivers[index].send(buffer, 0, count, timestamp);
                                        outputReceivers[index].send(buffer, 0, count, timestamp);

                                        // If messages are more than size 1, boost power.
                                        if (mPowerBoostSetter != null && count > 1) {
                                            mPowerBoostSetter.boostPower();
                                        }
                                    }
                                    }
                                }
                                }
                            }
                            }