diff --git a/services/usb/java/com/android/server/usb/PowerBoostSetter.java b/services/usb/java/com/android/server/usb/PowerBoostSetter.java new file mode 100644 index 0000000000000..eb7c196aacc1d --- /dev/null +++ b/services/usb/java/com/android/server/usb/PowerBoostSetter.java @@ -0,0 +1,59 @@ +/* + * 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); + } + } +} diff --git a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java index eebcccb68b134..2ae328b0d8e94 100644 --- a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java +++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java @@ -98,6 +98,11 @@ public final class UsbDirectMidiDevice implements Closeable { 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() { @Override public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) { @@ -251,6 +256,8 @@ public final class UsbDirectMidiDevice implements Closeable { for (int port = 0; port < numOutputs; port++) { mMidiInputPortReceivers[port] = new InputReceiverProxy(); } + + mPowerBoostSetter = new PowerBoostSetter(); } private int calculateDefaultMidiProtocol() { @@ -418,6 +425,15 @@ public final class UsbDirectMidiDevice implements Closeable { } outputReceivers[portFinal].send(convertedArray, 0, 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) { @@ -721,4 +737,10 @@ public final class UsbDirectMidiDevice implements Closeable { 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; + } } diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java index 67955e155f191..d9ad703b82230 100644 --- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java +++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java @@ -77,6 +77,8 @@ public final class UsbMidiDevice implements Closeable { // only accessed from JNI code private int mPipeFD = -1; + private PowerBoostSetter mPowerBoostSetter = null; + private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() { @Override public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) { @@ -167,6 +169,8 @@ public final class UsbMidiDevice implements Closeable { for (int port = 0; port < numOutputs; port++) { mMidiInputPortReceivers[port] = new InputReceiverProxy(); } + + mPowerBoostSetter = new PowerBoostSetter(); } private boolean openLocked() { @@ -240,6 +244,11 @@ public final class UsbMidiDevice implements Closeable { int count = mInputStreams[index].read(buffer); outputReceivers[index].send(buffer, 0, count, timestamp); + + // If messages are more than size 1, boost power. + if (mPowerBoostSetter != null && count > 1) { + mPowerBoostSetter.boostPower(); + } } } }