ADPF: PerformanceHintManager API implementation
Test: Manual test, run bouncy ball Test: atest FrameworksCoreTests:android.os.PerformanceHintManagerTest Bug: 158791282 Change-Id: Ia0bf2b06675b8cf57cdf1668ee90e98026416439 Signed-off-by: Wei Wang <wvw@google.com>
This commit is contained in:
@@ -10644,6 +10644,7 @@ package android.content {
|
||||
field public static final String NOTIFICATION_SERVICE = "notification";
|
||||
field public static final String NSD_SERVICE = "servicediscovery";
|
||||
field public static final String PEOPLE_SERVICE = "people";
|
||||
field public static final String PERFORMANCE_HINT_SERVICE = "performance_hint";
|
||||
field public static final String POWER_SERVICE = "power";
|
||||
field public static final String PRINT_SERVICE = "print";
|
||||
field public static final int RECEIVER_VISIBLE_TO_INSTANT_APPS = 1; // 0x1
|
||||
@@ -31559,6 +31560,17 @@ package android.os {
|
||||
field public static final int PATTERN_SUFFIX = 4; // 0x4
|
||||
}
|
||||
|
||||
public final class PerformanceHintManager {
|
||||
method @Nullable public android.os.PerformanceHintManager.Session createHintSession(@NonNull int[], long);
|
||||
method public long getPreferredUpdateRateNanos();
|
||||
}
|
||||
|
||||
public static class PerformanceHintManager.Session implements java.io.Closeable {
|
||||
method public void close();
|
||||
method public void reportActualWorkDuration(long);
|
||||
method public void updateTargetWorkDuration(long);
|
||||
}
|
||||
|
||||
public final class PersistableBundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
|
||||
ctor public PersistableBundle();
|
||||
ctor public PersistableBundle(int);
|
||||
|
||||
@@ -158,12 +158,14 @@ import android.os.IBatteryPropertiesRegistrar;
|
||||
import android.os.IBinder;
|
||||
import android.os.IDumpstate;
|
||||
import android.os.IHardwarePropertiesManager;
|
||||
import android.os.IHintManager;
|
||||
import android.os.IPowerManager;
|
||||
import android.os.IRecoverySystem;
|
||||
import android.os.ISystemUpdateManager;
|
||||
import android.os.IThermalService;
|
||||
import android.os.IUserManager;
|
||||
import android.os.IncidentManager;
|
||||
import android.os.PerformanceHintManager;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RecoverySystem;
|
||||
import android.os.ServiceManager;
|
||||
@@ -592,6 +594,17 @@ public final class SystemServiceRegistry {
|
||||
ctx.mMainThread.getHandler());
|
||||
}});
|
||||
|
||||
registerService(Context.PERFORMANCE_HINT_SERVICE, PerformanceHintManager.class,
|
||||
new CachedServiceFetcher<PerformanceHintManager>() {
|
||||
@Override
|
||||
public PerformanceHintManager createService(ContextImpl ctx)
|
||||
throws ServiceNotFoundException {
|
||||
IBinder hintBinder = ServiceManager.getServiceOrThrow(
|
||||
Context.PERFORMANCE_HINT_SERVICE);
|
||||
IHintManager hintService = IHintManager.Stub.asInterface(hintBinder);
|
||||
return new PerformanceHintManager(hintService);
|
||||
}});
|
||||
|
||||
registerService(Context.RECOVERY_SERVICE, RecoverySystem.class,
|
||||
new CachedServiceFetcher<RecoverySystem>() {
|
||||
@Override
|
||||
|
||||
@@ -5224,6 +5224,14 @@ public abstract class Context {
|
||||
*/
|
||||
public static final String THERMAL_SERVICE = "thermalservice";
|
||||
|
||||
/**
|
||||
* Use with {@link #getSystemService(String)} to retrieve a
|
||||
* {@link android.os.PerformanceHintManager} for accessing the performance hinting service.
|
||||
*
|
||||
* @see #getSystemService(String)
|
||||
*/
|
||||
public static final String PERFORMANCE_HINT_SERVICE = "performance_hint";
|
||||
|
||||
/**
|
||||
* Use with {@link #getSystemService(String)} to retrieve a
|
||||
* {@link android.content.pm.ShortcutManager} for accessing the launcher shortcut service.
|
||||
|
||||
33
core/java/android/os/IHintManager.aidl
Normal file
33
core/java/android/os/IHintManager.aidl
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 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.os.IHintSession;
|
||||
|
||||
/** {@hide} */
|
||||
interface IHintManager {
|
||||
/**
|
||||
* Creates a {@link Session} for the given set of threads and associates to a binder token.
|
||||
*/
|
||||
IHintSession createHintSession(in IBinder token, in int[] tids, long durationNanos);
|
||||
|
||||
/**
|
||||
* Get preferred rate limit in nano second.
|
||||
*/
|
||||
long getHintSessionPreferredRate();
|
||||
}
|
||||
25
core/java/android/os/IHintSession.aidl
Normal file
25
core/java/android/os/IHintSession.aidl
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 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;
|
||||
|
||||
/** {@hide} */
|
||||
oneway interface IHintSession {
|
||||
void updateTargetWorkDuration(long targetDurationNanos);
|
||||
void reportActualWorkDuration(in long[] actualDurationNanos, in long[] timeStampNanos);
|
||||
void close();
|
||||
}
|
||||
226
core/java/android/os/PerformanceHintManager.java
Normal file
226
core/java/android/os/PerformanceHintManager.java
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemService;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/** The PerformanceHintManager allows apps to send performance hint to system. */
|
||||
@SystemService(Context.PERFORMANCE_HINT_SERVICE)
|
||||
public final class PerformanceHintManager {
|
||||
private static final String TAG = "PerformanceHintManager";
|
||||
private final IHintManager mService;
|
||||
// HAL preferred update rate
|
||||
private final long mPreferredRate;
|
||||
|
||||
/** @hide */
|
||||
public PerformanceHintManager(IHintManager service) {
|
||||
mService = service;
|
||||
try {
|
||||
mPreferredRate = mService.getHintSessionPreferredRate();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Nullable
|
||||
public Session createHintSession(@NonNull int[] tids, long initialTargetWorkDurationNanos) {
|
||||
try {
|
||||
IBinder token = new Binder();
|
||||
IHintSession session = mService.createHintSession(token, tids,
|
||||
initialTargetWorkDurationNanos);
|
||||
if (session == null) return null;
|
||||
return new Session(session, sNanoClock, mPreferredRate,
|
||||
initialTargetWorkDurationNanos);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get preferred update rate information for this device.
|
||||
*
|
||||
* @return the preferred update rate supported by device software.
|
||||
*/
|
||||
public long getPreferredUpdateRateNanos() {
|
||||
return mPreferredRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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-life and not created or destroyed dynamically.
|
||||
*
|
||||
* <p>Each session is expected to have a periodic workload with a target duration for each
|
||||
* cycle. The cycle duration is likely greater than the target work duration to allow other
|
||||
* parts of the pipeline to run within the available budget. For example, a renderer thread may
|
||||
* work at 60hz in order to produce frames at the display's frame but have a target work
|
||||
* duration of only 6ms.</p>
|
||||
*
|
||||
* <p>Any call in this class will change its internal data, so you must do your own thread
|
||||
* safety to protect from racing.</p>
|
||||
*
|
||||
* <p>Note that the target work duration can be {@link #updateTargetWorkDuration(long) updated}
|
||||
* if workloads change.</p>
|
||||
*
|
||||
* <p>After each cycle of work, the client is expected to
|
||||
* {@link #reportActualWorkDuration(long) report} the actual time taken to complete.</p>
|
||||
*
|
||||
* <p>All timings should be in {@link SystemClock#elapsedRealtimeNanos()}.</p>
|
||||
*/
|
||||
public static class Session implements Closeable {
|
||||
private final IHintSession mSession;
|
||||
private final NanoClock mElapsedRealtimeClock;
|
||||
// Target duration for choosing update rate
|
||||
private long mTargetDurationInNanos;
|
||||
// HAL preferred update rate
|
||||
private long mPreferredRate;
|
||||
// Last update timestamp
|
||||
private long mLastUpdateTimeStamp = -1L;
|
||||
// Cached samples
|
||||
private final ArrayList<Long> mActualDurationNanos;
|
||||
private final ArrayList<Long> mTimeStampNanos;
|
||||
|
||||
/** @hide */
|
||||
public Session(IHintSession session, NanoClock elapsedRealtimeClock, long preferredRate,
|
||||
long durationNanos) {
|
||||
mSession = session;
|
||||
mElapsedRealtimeClock = elapsedRealtimeClock;
|
||||
mTargetDurationInNanos = durationNanos;
|
||||
mPreferredRate = preferredRate;
|
||||
mActualDurationNanos = new ArrayList<Long>();
|
||||
mTimeStampNanos = new ArrayList<Long>();
|
||||
mLastUpdateTimeStamp = mElapsedRealtimeClock.nanos();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this session's target 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.");
|
||||
try {
|
||||
mSession.updateTargetWorkDuration(targetDurationNanos);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mTargetDurationInNanos = targetDurationNanos;
|
||||
/**
|
||||
* Most of the workload is target_duration dependent, so now clear the cached samples
|
||||
* as they are most likely obsolete.
|
||||
*/
|
||||
mActualDurationNanos.clear();
|
||||
mTimeStampNanos.clear();
|
||||
mLastUpdateTimeStamp = mElapsedRealtimeClock.nanos();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the actual duration for the last cycle of work.
|
||||
*
|
||||
* <p>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.</p>
|
||||
*
|
||||
* @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.");
|
||||
final long now = mElapsedRealtimeClock.nanos();
|
||||
mActualDurationNanos.add(actualDurationNanos);
|
||||
mTimeStampNanos.add(now);
|
||||
|
||||
/**
|
||||
* Use current sample to determine the rate limit. We can pick a shorter rate limit
|
||||
* if any sample underperformed, however, it could be the lower level system is slow
|
||||
* to react. So here we explicitly choose the rate limit with the latest sample.
|
||||
*/
|
||||
long rateLimit =
|
||||
actualDurationNanos > mTargetDurationInNanos ? mPreferredRate
|
||||
: 10 * mPreferredRate;
|
||||
|
||||
if (now - mLastUpdateTimeStamp <= rateLimit) {
|
||||
return;
|
||||
}
|
||||
Preconditions.checkState(mActualDurationNanos.size() == mTimeStampNanos.size());
|
||||
final int size = mActualDurationNanos.size();
|
||||
long[] actualDurationArray = new long[size];
|
||||
long[] timeStampArray = new long[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
actualDurationArray[i] = mActualDurationNanos.get(i);
|
||||
timeStampArray[i] = mTimeStampNanos.get(i);
|
||||
}
|
||||
try {
|
||||
mSession.reportActualWorkDuration(actualDurationArray, timeStampArray);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mActualDurationNanos.clear();
|
||||
mTimeStampNanos.clear();
|
||||
mLastUpdateTimeStamp = now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the current hint session.
|
||||
*
|
||||
* <p>Once called, you should not call anything else on this object.</p>
|
||||
*/
|
||||
public void close() {
|
||||
try {
|
||||
mSession.close();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface is to make the FakeClock for testing.
|
||||
* @hide
|
||||
*/
|
||||
public interface NanoClock {
|
||||
/** Gets the current nanosecond instant of the clock. */
|
||||
long nanos();
|
||||
}
|
||||
|
||||
private static final NanoClock sNanoClock = new NanoClock() {
|
||||
public long nanos() {
|
||||
return SystemClock.elapsedRealtimeNanos();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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 static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeNotNull;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.os.PerformanceHintManager.Session;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PerformanceHintManagerTest {
|
||||
private static final long RATE_1000 = 1000L;
|
||||
private static final long TARGET_166 = 166L;
|
||||
private static final long DEFAULT_TARGET_NS = 16666666L;
|
||||
private PerformanceHintManager mPerformanceHintManager;
|
||||
|
||||
@Mock
|
||||
private IHintSession mIHintSessionMock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mPerformanceHintManager =
|
||||
InstrumentationRegistry.getInstrumentation().getContext().getSystemService(
|
||||
PerformanceHintManager.class);
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
private Session createSession() {
|
||||
return mPerformanceHintManager.createHintSession(
|
||||
new int[]{Process.myPid()}, DEFAULT_TARGET_NS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateHintSession() {
|
||||
Session a = createSession();
|
||||
Session b = createSession();
|
||||
if (a == null) {
|
||||
assertNull(b);
|
||||
} else {
|
||||
assertNotEquals(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPreferredUpdateRateNanos() {
|
||||
if (createSession() != null) {
|
||||
assertTrue(mPerformanceHintManager.getPreferredUpdateRateNanos() > 0);
|
||||
} else {
|
||||
assertEquals(-1, mPerformanceHintManager.getPreferredUpdateRateNanos());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateTargetWorkDuration() {
|
||||
Session s = createSession();
|
||||
assumeNotNull(s);
|
||||
s.updateTargetWorkDuration(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateTargetWorkDurationWithNegativeDuration() {
|
||||
Session s = createSession();
|
||||
assumeNotNull(s);
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
s.updateTargetWorkDuration(-1);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReportActualWorkDuration() {
|
||||
Session s = createSession();
|
||||
assumeNotNull(s);
|
||||
s.updateTargetWorkDuration(100);
|
||||
s.reportActualWorkDuration(1);
|
||||
s.reportActualWorkDuration(100);
|
||||
s.reportActualWorkDuration(1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReportActualWorkDurationWithIllegalArgument() {
|
||||
Session s = createSession();
|
||||
assumeNotNull(s);
|
||||
s.updateTargetWorkDuration(100);
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
s.reportActualWorkDuration(-1);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRateLimitWithDurationFastEnough() throws Exception {
|
||||
FakeClock fakeClock = new FakeClock();
|
||||
Session s = new Session(mIHintSessionMock, fakeClock, RATE_1000, TARGET_166);
|
||||
|
||||
reset(mIHintSessionMock);
|
||||
fakeClock.setNow(0);
|
||||
s.updateTargetWorkDuration(TARGET_166);
|
||||
|
||||
s.reportActualWorkDuration(TARGET_166 - 1);
|
||||
s.reportActualWorkDuration(TARGET_166);
|
||||
// we should not see update as the rate should be 10X for over-perform case.
|
||||
verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any());
|
||||
fakeClock.incrementClock(10 * RATE_1000);
|
||||
s.reportActualWorkDuration(TARGET_166);
|
||||
verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any());
|
||||
fakeClock.incrementClock(1);
|
||||
s.reportActualWorkDuration(TARGET_166);
|
||||
// we should see update after rate limit
|
||||
verify(mIHintSessionMock, times(1)).reportActualWorkDuration(
|
||||
eq(new long[] {TARGET_166 - 1, TARGET_166, TARGET_166, TARGET_166}),
|
||||
eq(new long[] {0, 0, 10 * RATE_1000, 10 * RATE_1000 + 1}));
|
||||
|
||||
reset(mIHintSessionMock);
|
||||
s.reportActualWorkDuration(TARGET_166);
|
||||
s.reportActualWorkDuration(TARGET_166 - 1);
|
||||
s.reportActualWorkDuration(TARGET_166 - 2);
|
||||
// we should not see update as the rate should be 10X for over-perform case.
|
||||
verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any());
|
||||
fakeClock.incrementClock(10 * RATE_1000 + 1);
|
||||
s.reportActualWorkDuration(TARGET_166);
|
||||
s.reportActualWorkDuration(TARGET_166 - 1);
|
||||
// we should see update now
|
||||
verify(mIHintSessionMock, times(1)).reportActualWorkDuration(
|
||||
eq(new long[] {TARGET_166, TARGET_166 - 1, TARGET_166 - 2, TARGET_166}),
|
||||
eq(new long[] {10 * RATE_1000 + 1, 10 * RATE_1000 + 1, 10 * RATE_1000 + 1,
|
||||
(10 * RATE_1000 + 1) * 2}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRateLimitWithDurationTooSlow() throws Exception {
|
||||
FakeClock fakeClock = new FakeClock();
|
||||
Session s = new Session(mIHintSessionMock, fakeClock, RATE_1000, TARGET_166);
|
||||
|
||||
reset(mIHintSessionMock);
|
||||
fakeClock.setNow(0);
|
||||
s.updateTargetWorkDuration(TARGET_166);
|
||||
|
||||
verify(mIHintSessionMock, times(1)).updateTargetWorkDuration(eq(TARGET_166));
|
||||
// shouldn't update before rate limit
|
||||
s.reportActualWorkDuration(TARGET_166 + 1);
|
||||
verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any());
|
||||
|
||||
// shouldn't update when the time is exactly at rate limit
|
||||
fakeClock.incrementClock(RATE_1000);
|
||||
s.reportActualWorkDuration(TARGET_166 + 1);
|
||||
verify(mIHintSessionMock, never()).reportActualWorkDuration(any(), any());
|
||||
|
||||
// should be ready for sending hint
|
||||
fakeClock.incrementClock(1);
|
||||
s.reportActualWorkDuration(TARGET_166 + 1);
|
||||
verify(mIHintSessionMock, times(1)).reportActualWorkDuration(
|
||||
eq(new long[] {TARGET_166 + 1, TARGET_166 + 1, TARGET_166 + 1}),
|
||||
eq(new long[] {0 , RATE_1000, RATE_1000 + 1}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseHintSession() {
|
||||
Session s = createSession();
|
||||
assumeNotNull(s);
|
||||
s.close();
|
||||
}
|
||||
|
||||
private static class FakeClock implements PerformanceHintManager.NanoClock {
|
||||
private long mCurrentTime = 0L;
|
||||
|
||||
@Override
|
||||
public long nanos() {
|
||||
return mCurrentTime;
|
||||
}
|
||||
|
||||
public void setNow(long nanos) {
|
||||
mCurrentTime = nanos;
|
||||
}
|
||||
|
||||
public void incrementClock(long nanos) {
|
||||
mCurrentTime += nanos;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user