369 lines
15 KiB
Java
369 lines
15 KiB
Java
/*
|
|
* Copyright (C) 2021 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.FlaggedApi;
|
|
import android.annotation.IntDef;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.SystemService;
|
|
import android.annotation.TestApi;
|
|
import android.content.Context;
|
|
|
|
import com.android.internal.util.Preconditions;
|
|
|
|
import java.io.Closeable;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.lang.ref.Reference;
|
|
import java.util.Objects;
|
|
|
|
|
|
/** The PerformanceHintManager allows apps to send performance hint to system. */
|
|
@SystemService(Context.PERFORMANCE_HINT_SERVICE)
|
|
public final class PerformanceHintManager {
|
|
private final long mNativeManagerPtr;
|
|
|
|
/** @hide */
|
|
public static PerformanceHintManager create() throws ServiceManager.ServiceNotFoundException {
|
|
long nativeManagerPtr = nativeAcquireManager();
|
|
if (nativeManagerPtr == 0) {
|
|
throw new ServiceManager.ServiceNotFoundException(Context.PERFORMANCE_HINT_SERVICE);
|
|
}
|
|
return new PerformanceHintManager(nativeManagerPtr);
|
|
}
|
|
|
|
private PerformanceHintManager(long nativeManagerPtr) {
|
|
mNativeManagerPtr = nativeManagerPtr;
|
|
}
|
|
|
|
/**
|
|
* Get preferred update rate information for this device.
|
|
*
|
|
* @return the preferred update rate supported by device software
|
|
*/
|
|
public long getPreferredUpdateRateNanos() {
|
|
return nativeGetPreferredUpdateRateNanos(mNativeManagerPtr);
|
|
}
|
|
|
|
/**
|
|
* Creates a {@link Session} for the given set of threads and sets their initial target work
|
|
* duration.
|
|
*
|
|
* @param tids The list of threads to be associated with this session. They must be part of
|
|
* this process' thread group
|
|
* @param initialTargetWorkDurationNanos The desired duration in nanoseconds for the new
|
|
* session
|
|
* @return the new session if it is supported on this device, null if hint session is not
|
|
* supported on this device or the tid doesn't belong to the application
|
|
* @throws IllegalArgumentException if the thread id list is empty, or
|
|
* initialTargetWorkDurationNanos is non-positive
|
|
*/
|
|
@Nullable
|
|
public Session createHintSession(@NonNull int[] tids, long initialTargetWorkDurationNanos) {
|
|
Objects.requireNonNull(tids, "tids cannot be null");
|
|
if (tids.length == 0) {
|
|
throw new IllegalArgumentException("thread id list can't be empty.");
|
|
}
|
|
Preconditions.checkArgumentPositive(initialTargetWorkDurationNanos,
|
|
"the hint target duration should be positive.");
|
|
long nativeSessionPtr = nativeCreateSession(mNativeManagerPtr, tids,
|
|
initialTargetWorkDurationNanos);
|
|
if (nativeSessionPtr == 0) return null;
|
|
return new Session(nativeSessionPtr);
|
|
}
|
|
|
|
/**
|
|
* A Session represents a group of threads with an inter-related workload such that hints for
|
|
* their performance should be considered as a unit. The threads in a given session should be
|
|
* long-lived and not created or destroyed dynamically.
|
|
*
|
|
* The work duration API can be used with periodic workloads to dynamically adjust thread
|
|
* performance and keep the work on schedule while optimizing the available power budget.
|
|
* When using the work duration API, the starting target duration should be specified
|
|
* while creating the session, but can later be adjusted with
|
|
* {@link #updateTargetWorkDuration(long)}. While using the work duration API, the client is be
|
|
* expected to call {@link #reportActualWorkDuration(long)} each cycle to report the actual
|
|
* time taken to complete to the system.
|
|
*
|
|
* Any call in this class will change its internal data, so you must do your own thread
|
|
* safety to protect from racing.
|
|
*
|
|
* All timings should be in {@link SystemClock#uptimeNanos()}.
|
|
*/
|
|
public static class Session implements Closeable {
|
|
private long mNativeSessionPtr;
|
|
|
|
/** @hide */
|
|
public Session(long nativeSessionPtr) {
|
|
mNativeSessionPtr = nativeSessionPtr;
|
|
}
|
|
|
|
/**
|
|
* This hint indicates a sudden increase in CPU workload intensity. It means
|
|
* that this hint session needs extra CPU resources immediately to meet the
|
|
* target duration for the current work cycle.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final int CPU_LOAD_UP = 0;
|
|
/**
|
|
* This hint indicates a decrease in CPU workload intensity. It means that
|
|
* this hint session can reduce CPU resources and still meet the target duration.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final int CPU_LOAD_DOWN = 1;
|
|
/**
|
|
* This hint indicates an upcoming CPU workload that is completely changed and
|
|
* unknown. It means that the hint session should reset CPU resources to a known
|
|
* baseline to prepare for an arbitrary load, and must wake up if inactive.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final int CPU_LOAD_RESET = 2;
|
|
/**
|
|
* This hint indicates that the most recent CPU workload is resuming after a
|
|
* period of inactivity. It means that the hint session should allocate similar
|
|
* CPU resources to what was used previously, and must wake up if inactive.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public static final int CPU_LOAD_RESUME = 3;
|
|
|
|
/**
|
|
* This hint indicates an increase in GPU workload intensity. It means that
|
|
* this hint session needs extra GPU resources to meet the target duration.
|
|
* This hint must be sent before reporting the actual duration to the session.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
|
|
public static final int GPU_LOAD_UP = 5;
|
|
|
|
/**
|
|
* This hint indicates a decrease in GPU workload intensity. It means that
|
|
* this hint session can reduce GPU resources and still meet the target duration.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
|
|
public static final int GPU_LOAD_DOWN = 6;
|
|
|
|
/**
|
|
* This hint indicates an upcoming GPU workload that is completely changed and
|
|
* unknown. It means that the hint session should reset GPU resources to a known
|
|
* baseline to prepare for an arbitrary load, and must wake up if inactive.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
|
|
public static final int GPU_LOAD_RESET = 7;
|
|
|
|
/** @hide */
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef(prefix = {"CPU_LOAD_", "GPU_LOAD_"}, value = {
|
|
CPU_LOAD_UP,
|
|
CPU_LOAD_DOWN,
|
|
CPU_LOAD_RESET,
|
|
CPU_LOAD_RESUME,
|
|
GPU_LOAD_UP,
|
|
GPU_LOAD_DOWN,
|
|
GPU_LOAD_RESET
|
|
})
|
|
public @interface Hint {}
|
|
|
|
/** @hide */
|
|
@Override
|
|
protected void finalize() throws Throwable {
|
|
try {
|
|
close();
|
|
} finally {
|
|
super.finalize();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates this session's target total duration for each cycle of work.
|
|
*
|
|
* @param targetDurationNanos the new desired duration in nanoseconds
|
|
*/
|
|
public void updateTargetWorkDuration(long targetDurationNanos) {
|
|
Preconditions.checkArgumentPositive(targetDurationNanos, "the hint target duration"
|
|
+ " should be positive.");
|
|
nativeUpdateTargetWorkDuration(mNativeSessionPtr, targetDurationNanos);
|
|
}
|
|
|
|
/**
|
|
* Reports the actual duration for the last cycle of work.
|
|
*
|
|
* The system will attempt to adjust the core placement of the threads within the thread
|
|
* group and/or the frequency of the core on which they are run to bring the actual duration
|
|
* close to the target duration.
|
|
*
|
|
* @param actualDurationNanos how long the thread group took to complete its last task in
|
|
* nanoseconds
|
|
*/
|
|
public void reportActualWorkDuration(long actualDurationNanos) {
|
|
Preconditions.checkArgumentPositive(actualDurationNanos, "the actual duration should"
|
|
+ " be positive.");
|
|
nativeReportActualWorkDuration(mNativeSessionPtr, actualDurationNanos);
|
|
}
|
|
|
|
/**
|
|
* Ends the current hint session.
|
|
*
|
|
* Once called, you should not call anything else on this object.
|
|
*/
|
|
public void close() {
|
|
if (mNativeSessionPtr != 0) {
|
|
nativeCloseSession(mNativeSessionPtr);
|
|
mNativeSessionPtr = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sends performance hints to inform the hint session of changes in the workload.
|
|
*
|
|
* @param hint The hint to send to the session
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public void sendHint(@Hint int hint) {
|
|
Preconditions.checkArgumentNonNegative(hint, "the hint ID should be at least"
|
|
+ " zero.");
|
|
try {
|
|
nativeSendHint(mNativeSessionPtr, hint);
|
|
} finally {
|
|
Reference.reachabilityFence(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This tells the session that these threads can be
|
|
* safely scheduled to prefer power efficiency over performance.
|
|
*
|
|
* @param enabled The flag that sets whether this session uses power-efficient scheduling.
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_ADPF_PREFER_POWER_EFFICIENCY)
|
|
public void setPreferPowerEfficiency(boolean enabled) {
|
|
nativeSetPreferPowerEfficiency(mNativeSessionPtr, enabled);
|
|
}
|
|
|
|
/**
|
|
* Set a list of threads to the performance hint session. This operation will replace
|
|
* the current list of threads with the given list of threads.
|
|
* Note that this is not an oneway method.
|
|
*
|
|
* @param tids The list of threads to be associated with this session. They must be
|
|
* part of this app's thread group
|
|
*
|
|
* @throws IllegalStateException if the hint session is not in the foreground
|
|
* @throws IllegalArgumentException if the thread id list is empty
|
|
* @throws SecurityException if any thread id doesn't belong to the application
|
|
*/
|
|
public void setThreads(@NonNull int[] tids) {
|
|
if (mNativeSessionPtr == 0) {
|
|
return;
|
|
}
|
|
Objects.requireNonNull(tids, "tids cannot be null");
|
|
if (tids.length == 0) {
|
|
throw new IllegalArgumentException("Thread id list can't be empty.");
|
|
}
|
|
nativeSetThreads(mNativeSessionPtr, tids);
|
|
}
|
|
|
|
/**
|
|
* Returns the list of thread ids.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
public @Nullable int[] getThreadIds() {
|
|
return nativeGetThreadIds(mNativeSessionPtr);
|
|
}
|
|
|
|
/**
|
|
* Reports the work duration for the last cycle of work.
|
|
*
|
|
* The system will attempt to adjust the core placement of the threads within the thread
|
|
* group and/or the frequency of the core on which they are run to bring the actual duration
|
|
* close to the target duration.
|
|
*
|
|
* @param workDuration the work duration of each component.
|
|
* @throws IllegalArgumentException if
|
|
* the work period start timestamp or the total duration are less than or equal to zero,
|
|
* if either the actual CPU duration or actual GPU duration is less than zero,
|
|
* or if both the CPU and GPU durations are zero.
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_ADPF_GPU_REPORT_ACTUAL_WORK_DURATION)
|
|
public void reportActualWorkDuration(@NonNull WorkDuration workDuration) {
|
|
if (workDuration.mWorkPeriodStartTimestampNanos <= 0) {
|
|
throw new IllegalArgumentException(
|
|
"the work period start timestamp should be greater than zero.");
|
|
}
|
|
if (workDuration.mActualTotalDurationNanos <= 0) {
|
|
throw new IllegalArgumentException(
|
|
"the actual total duration should be greater than zero.");
|
|
}
|
|
if (workDuration.mActualCpuDurationNanos < 0) {
|
|
throw new IllegalArgumentException(
|
|
"the actual CPU duration should be greater than or equal to zero.");
|
|
}
|
|
if (workDuration.mActualGpuDurationNanos < 0) {
|
|
throw new IllegalArgumentException(
|
|
"the actual GPU duration should be greater than or equal to zero.");
|
|
}
|
|
if (workDuration.mActualCpuDurationNanos + workDuration.mActualGpuDurationNanos <= 0) {
|
|
throw new IllegalArgumentException(
|
|
"either the actual CPU duration or the actual GPU duration should be greater"
|
|
+ "than zero.");
|
|
}
|
|
nativeReportActualWorkDuration(mNativeSessionPtr,
|
|
workDuration.mWorkPeriodStartTimestampNanos,
|
|
workDuration.mActualTotalDurationNanos,
|
|
workDuration.mActualCpuDurationNanos, workDuration.mActualGpuDurationNanos);
|
|
}
|
|
}
|
|
|
|
private static native long nativeAcquireManager();
|
|
private static native long nativeGetPreferredUpdateRateNanos(long nativeManagerPtr);
|
|
private static native long nativeCreateSession(long nativeManagerPtr,
|
|
int[] tids, long initialTargetWorkDurationNanos);
|
|
private static native int[] nativeGetThreadIds(long nativeSessionPtr);
|
|
private static native void nativeUpdateTargetWorkDuration(long nativeSessionPtr,
|
|
long targetDurationNanos);
|
|
private static native void nativeReportActualWorkDuration(long nativeSessionPtr,
|
|
long actualDurationNanos);
|
|
private static native void nativeCloseSession(long nativeSessionPtr);
|
|
private static native void nativeSendHint(long nativeSessionPtr, int hint);
|
|
private static native void nativeSetThreads(long nativeSessionPtr, int[] tids);
|
|
private static native void nativeSetPreferPowerEfficiency(long nativeSessionPtr,
|
|
boolean enabled);
|
|
private static native void nativeReportActualWorkDuration(long nativeSessionPtr,
|
|
long workPeriodStartTimestampNanos, long actualTotalDurationNanos,
|
|
long actualCpuDurationNanos, long actualGpuDurationNanos);
|
|
}
|