Loading services/core/java/com/android/server/power/hint/HintManagerService.java +128 −7 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static com.android.server.power.hint.Flags.cpuHeadroomAffinityCheck; import static com.android.server.power.hint.Flags.powerhintThreadCleanup; import static com.android.server.power.hint.Flags.resetOnForkEnabled; import android.Manifest; import android.adpf.ISessionManager; import android.annotation.NonNull; import android.annotation.Nullable; Loading Loading @@ -59,6 +60,9 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SessionCreationConfig; import android.os.SystemProperties; import android.os.UserHandle; import android.system.Os; import android.system.OsConstants; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; Loading @@ -79,7 +83,10 @@ import com.android.server.SystemService; import com.android.server.power.hint.HintManagerService.AppHintSession.SessionModes; import com.android.server.utils.Slogf; import java.io.BufferedReader; import java.io.FileDescriptor; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Field; import java.util.ArrayList; Loading @@ -95,6 +102,8 @@ import java.util.Set; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; /** An hint service implementation that runs in System Server process. */ public final class HintManagerService extends SystemService { Loading @@ -103,10 +112,10 @@ public final class HintManagerService extends SystemService { private static final int EVENT_CLEAN_UP_UID = 3; @VisibleForTesting static final int CLEAN_UP_UID_DELAY_MILLIS = 1000; // The minimum interval between the headroom calls as rate limiting. private static final int DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS = 1000; private static final int DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS = 1000; // example: cpu 2255 34 2290 22625563 6290 127 456 private static final Pattern PROC_STAT_CPU_TIME_TOTAL_PATTERN = Pattern.compile("cpu\\s+(?<user>[0-9]+)\\s(?<nice>[0-9]+).+"); @VisibleForTesting final long mHintSessionPreferredRate; Loading Loading @@ -192,10 +201,26 @@ public final class HintManagerService extends SystemService { private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager"; private static final String PROPERTY_USE_HAL_HEADROOMS = "persist.hms.use_hal_headrooms"; private static final String PROPERTY_CHECK_HEADROOM_TID = "persist.hms.check_headroom_tid"; private static final String PROPERTY_CHECK_HEADROOM_AFFINITY = "persist.hms.check_affinity"; private static final String PROPERTY_CHECK_HEADROOM_AFFINITY = "persist.hms.check_headroom_affinity"; private static final String PROPERTY_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS = "persist.hms.check_headroom_proc_stat_min_millis"; private Boolean mFMQUsesIntegratedEventFlag = false; private final Object mCpuHeadroomLock = new Object(); @VisibleForTesting final float mJiffyMillis; private final int mCheckHeadroomProcStatMinMillis; @GuardedBy("mCpuHeadroomLock") private long mLastCpuUserModeTimeCheckedMillis = 0; @GuardedBy("mCpuHeadroomLock") private long mLastCpuUserModeJiffies = 0; @GuardedBy("mCpuHeadroomLock") private final Map<Integer, Long> mUidToLastUserModeJiffies; @VisibleForTesting private String mProcStatFilePathOverride = null; @VisibleForTesting private boolean mEnforceCpuHeadroomUserModeCpuTimeCheck = false; private ISessionManager mSessionManager; Loading Loading @@ -310,8 +335,16 @@ public final class HintManagerService extends SystemService { new GpuHeadroomParamsInternal().calculationWindowMillis; if (mSupportInfo.headroom.isCpuSupported) { mCpuHeadroomCache = new HeadroomCache<>(2, mSupportInfo.headroom.cpuMinIntervalMillis); mUidToLastUserModeJiffies = new ArrayMap<>(); long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK); mJiffyMillis = 1000.0f / jiffyHz; mCheckHeadroomProcStatMinMillis = SystemProperties.getInt( PROPERTY_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS, 50); } else { mCpuHeadroomCache = null; mUidToLastUserModeJiffies = null; mJiffyMillis = 0.0f; mCheckHeadroomProcStatMinMillis = 0; } if (mSupportInfo.headroom.isGpuSupported) { mGpuHeadroomCache = new HeadroomCache<>(2, mSupportInfo.headroom.gpuMinIntervalMillis); Loading Loading @@ -370,6 +403,12 @@ public final class HintManagerService extends SystemService { return supportInfo; } @VisibleForTesting void setProcStatPathOverride(String override) { mProcStatFilePathOverride = override; mEnforceCpuHeadroomUserModeCpuTimeCheck = true; } private ServiceThread createCleanUpThread() { final ServiceThread handlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_LOWEST, true /*allowIo*/); Loading Loading @@ -851,6 +890,11 @@ public final class HintManagerService extends SystemService { mChannelMap.remove(uid); } } synchronized (mCpuHeadroomLock) { if (mSupportInfo.headroom.isCpuSupported && mUidToLastUserModeJiffies != null) { mUidToLastUserModeJiffies.remove(uid); } } }); } Loading Loading @@ -1230,7 +1274,7 @@ public final class HintManagerService extends SystemService { // Only call into AM if the tid is either isolated or invalid if (isolatedPids == null) { // To avoid deadlock, do not call into AMS if the call is from system. if (uid == Process.SYSTEM_UID) { if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { return tid; } isolatedPids = mAmInternal.getIsolatedProcesses(uid); Loading Loading @@ -1485,14 +1529,17 @@ public final class HintManagerService extends SystemService { throw new UnsupportedOperationException(); } checkCpuHeadroomParams(params); final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); final CpuHeadroomParams halParams = new CpuHeadroomParams(); halParams.tids = new int[]{Binder.getCallingPid()}; halParams.tids = new int[]{pid}; halParams.calculationType = params.calculationType; halParams.calculationWindowMillis = params.calculationWindowMillis; if (params.usesDeviceHeadroom) { halParams.tids = new int[]{}; } else if (params.tids != null && params.tids.length > 0) { if (SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_TID, true)) { if (UserHandle.getAppId(uid) != Process.SYSTEM_UID && SystemProperties.getBoolean( PROPERTY_CHECK_HEADROOM_TID, true)) { final int tgid = Process.getThreadGroupLeader(Binder.getCallingPid()); for (int tid : params.tids) { if (Process.getThreadGroupLeader(tid) != tgid) { Loading @@ -1515,6 +1562,20 @@ public final class HintManagerService extends SystemService { if (res != null) return res; } } final boolean shouldCheckUserModeCpuTime = mEnforceCpuHeadroomUserModeCpuTimeCheck || (UserHandle.getAppId(uid) != Process.SYSTEM_UID && mContext.checkCallingPermission( Manifest.permission.DEVICE_POWER) == PackageManager.PERMISSION_DENIED); if (shouldCheckUserModeCpuTime) { synchronized (mCpuHeadroomLock) { if (!checkPerUidUserModeCpuTimeElapsedLocked(uid)) { return null; } } } // return from HAL directly try { final CpuHeadroomResult result = mPowerHal.getCpuHeadroom(halParams); Loading @@ -1528,6 +1589,11 @@ public final class HintManagerService extends SystemService { mCpuHeadroomCache.add(halParams, result); } } if (shouldCheckUserModeCpuTime) { synchronized (mCpuHeadroomLock) { mUidToLastUserModeJiffies.put(uid, mLastCpuUserModeJiffies); } } return result; } catch (RemoteException e) { Slog.e(TAG, "Failed to get CPU headroom from Power HAL", e); Loading Loading @@ -1556,6 +1622,40 @@ public final class HintManagerService extends SystemService { } } // check if there has been sufficient user mode cpu time elapsed since last call // from the same uid @GuardedBy("mCpuHeadroomLock") private boolean checkPerUidUserModeCpuTimeElapsedLocked(int uid) { // skip checking proc stat if it's within mCheckHeadroomProcStatMinMillis if (System.currentTimeMillis() - mLastCpuUserModeTimeCheckedMillis > mCheckHeadroomProcStatMinMillis) { try { mLastCpuUserModeJiffies = getUserModeJiffies(); } catch (Exception e) { Slog.e(TAG, "Failed to get user mode CPU time", e); return false; } mLastCpuUserModeTimeCheckedMillis = System.currentTimeMillis(); } if (mUidToLastUserModeJiffies.containsKey(uid)) { long uidLastUserModeJiffies = mUidToLastUserModeJiffies.get(uid); if ((mLastCpuUserModeJiffies - uidLastUserModeJiffies) * mJiffyMillis < mSupportInfo.headroom.cpuMinIntervalMillis) { Slog.w(TAG, "UID " + uid + " is requesting CPU headroom too soon"); Slog.d(TAG, "UID " + uid + " last request at " + uidLastUserModeJiffies * mJiffyMillis + "ms with device currently at " + mLastCpuUserModeJiffies * mJiffyMillis + "ms, the interval: " + (mLastCpuUserModeJiffies - uidLastUserModeJiffies) * mJiffyMillis + "ms is less than require minimum interval " + mSupportInfo.headroom.cpuMinIntervalMillis + "ms"); return false; } } return true; } private void checkCpuHeadroomParams(CpuHeadroomParamsInternal params) { boolean calculationTypeMatched = false; try { Loading Loading @@ -1731,6 +1831,27 @@ public final class HintManagerService extends SystemService { } } private long getUserModeJiffies() throws IOException { String filePath = mProcStatFilePathOverride == null ? "/proc/stat" : mProcStatFilePathOverride; try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { String line; while ((line = reader.readLine()) != null) { Matcher matcher = PROC_STAT_CPU_TIME_TOTAL_PATTERN.matcher(line.trim()); if (matcher.find()) { long userJiffies = Long.parseLong(matcher.group("user")); long niceJiffies = Long.parseLong(matcher.group("nice")); Slog.d(TAG, "user: " + userJiffies + " nice: " + niceJiffies + " total " + (userJiffies + niceJiffies)); reader.close(); return userJiffies + niceJiffies; } } } throw new IllegalStateException("Can't find cpu line in " + filePath); } private boolean checkGraphicsPipelineValid(SessionCreationConfig creationConfig, int uid) { if (creationConfig.modesToEnable == null) { return true; Loading services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java +75 −10 Original line number Diff line number Diff line Loading @@ -78,12 +78,15 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; import android.util.Log; import androidx.test.InstrumentationRegistry; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.power.hint.HintManagerService.AppHintSession; import com.android.server.power.hint.HintManagerService.Injector; import com.android.server.power.hint.HintManagerService.NativeWrapper; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; Loading @@ -93,6 +96,8 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; Loading @@ -111,6 +116,7 @@ import java.util.concurrent.locks.LockSupport; */ public class HintManagerServiceTest { private static final String TAG = "HintManagerServiceTest"; private List<File> mFilesCreated = new ArrayList<>(); private static WorkDuration makeWorkDuration( long timestamp, long duration, long workPeriodStartTime, Loading Loading @@ -192,9 +198,9 @@ public class HintManagerServiceTest { mSupportInfo.sessionTags = -1; mSupportInfo.headroom = new SupportInfo.HeadroomSupportInfo(); mSupportInfo.headroom.isCpuSupported = true; mSupportInfo.headroom.cpuMinIntervalMillis = 2000; mSupportInfo.headroom.cpuMinIntervalMillis = 1000; mSupportInfo.headroom.isGpuSupported = true; mSupportInfo.headroom.gpuMinIntervalMillis = 2000; mSupportInfo.headroom.gpuMinIntervalMillis = 1000; mSupportInfo.compositionData = new SupportInfo.CompositionDataSupportInfo(); return mSupportInfo; } Loading Loading @@ -243,6 +249,13 @@ public class HintManagerServiceTest { LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock); } @After public void tearDown() { for (File file : mFilesCreated) { file.delete(); } } /** * Mocks the creation calls, but without support for new createHintSessionWithConfig method */ Loading Loading @@ -1327,6 +1340,58 @@ public class HintManagerServiceTest { }); } @Test public void testCpuHeadroomCpuProcStatPath() throws Exception { File dir = InstrumentationRegistry.getTargetContext().getFilesDir(); dir.mkdir(); String procStatFileStr = "mock_proc_stat"; File file = new File(dir, procStatFileStr); mFilesCreated.add(file); try (FileOutputStream output = new FileOutputStream(file)) { output.write("cpu 2000 3000 4000 0 0 0 0 0 0 0".getBytes()); } HintManagerService service = createService(); service.setProcStatPathOverride(file.getPath()); CpuHeadroomParamsInternal params1 = new CpuHeadroomParamsInternal(); CpuHeadroomParams halParams1 = new CpuHeadroomParams(); halParams1.calculationType = CpuHeadroomParams.CalculationType.MIN; halParams1.tids = new int[]{Process.myPid()}; float headroom1 = 0.1f; CpuHeadroomResult halRet1 = CpuHeadroomResult.globalHeadroom(headroom1); when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(halRet1); clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1)); // expire the cache but cpu proc hasn't changed so we expect no value return Thread.sleep(1100); clearInvocations(mIPowerMock); assertEquals(null, service.getBinderServiceInstance().getCpuHeadroom(params1)); verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1)); // update user jiffies with 500 equivalent jiffies, which is not sufficient cpu time Thread.sleep(1100); try (FileOutputStream output = new FileOutputStream(file)) { output.write(("cpu " + (2000 + (int) (500 / service.mJiffyMillis)) + " 3000 4000 0 0 0 0 0 0 0").getBytes()); } clearInvocations(mIPowerMock); assertEquals(null, service.getBinderServiceInstance().getCpuHeadroom(params1)); verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1)); // update nice jiffies with 600 equivalent jiffies, now it exceeds 1000ms requirement Thread.sleep(1100); try (FileOutputStream output = new FileOutputStream(file)) { output.write(("cpu " + (2000 + (int) (500 / service.mJiffyMillis)) + " " + +(3000 + (int) (600 / service.mJiffyMillis)) + " 4000 0 0 0 0 0 0 0").getBytes()); } clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1)); } @Test @EnableFlags({Flags.FLAG_CPU_HEADROOM_AFFINITY_CHECK}) Loading Loading @@ -1397,8 +1462,8 @@ public class HintManagerServiceTest { verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); // after 1 more second it should be served with cache still Thread.sleep(1000); // after 500ms more it should be served with cache Thread.sleep(500); clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); Loading @@ -1410,8 +1475,8 @@ public class HintManagerServiceTest { verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval Thread.sleep(1100); // after 1+ seconds it should be served from HAL as it exceeds 1000 millis interval Thread.sleep(600); clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); Loading Loading @@ -1519,8 +1584,8 @@ public class HintManagerServiceTest { verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1)); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); // after 1 more second it should be served with cache still Thread.sleep(1000); // after 500ms it should be served with cache Thread.sleep(500); clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); Loading @@ -1528,8 +1593,8 @@ public class HintManagerServiceTest { verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1)); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval Thread.sleep(1100); // after 1+ seconds it should be served from HAL as it exceeds 1000 millis interval Thread.sleep(600); clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); Loading Loading
services/core/java/com/android/server/power/hint/HintManagerService.java +128 −7 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static com.android.server.power.hint.Flags.cpuHeadroomAffinityCheck; import static com.android.server.power.hint.Flags.powerhintThreadCleanup; import static com.android.server.power.hint.Flags.resetOnForkEnabled; import android.Manifest; import android.adpf.ISessionManager; import android.annotation.NonNull; import android.annotation.Nullable; Loading Loading @@ -59,6 +60,9 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SessionCreationConfig; import android.os.SystemProperties; import android.os.UserHandle; import android.system.Os; import android.system.OsConstants; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; Loading @@ -79,7 +83,10 @@ import com.android.server.SystemService; import com.android.server.power.hint.HintManagerService.AppHintSession.SessionModes; import com.android.server.utils.Slogf; import java.io.BufferedReader; import java.io.FileDescriptor; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Field; import java.util.ArrayList; Loading @@ -95,6 +102,8 @@ import java.util.Set; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; /** An hint service implementation that runs in System Server process. */ public final class HintManagerService extends SystemService { Loading @@ -103,10 +112,10 @@ public final class HintManagerService extends SystemService { private static final int EVENT_CLEAN_UP_UID = 3; @VisibleForTesting static final int CLEAN_UP_UID_DELAY_MILLIS = 1000; // The minimum interval between the headroom calls as rate limiting. private static final int DEFAULT_GPU_HEADROOM_INTERVAL_MILLIS = 1000; private static final int DEFAULT_CPU_HEADROOM_INTERVAL_MILLIS = 1000; // example: cpu 2255 34 2290 22625563 6290 127 456 private static final Pattern PROC_STAT_CPU_TIME_TOTAL_PATTERN = Pattern.compile("cpu\\s+(?<user>[0-9]+)\\s(?<nice>[0-9]+).+"); @VisibleForTesting final long mHintSessionPreferredRate; Loading Loading @@ -192,10 +201,26 @@ public final class HintManagerService extends SystemService { private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager"; private static final String PROPERTY_USE_HAL_HEADROOMS = "persist.hms.use_hal_headrooms"; private static final String PROPERTY_CHECK_HEADROOM_TID = "persist.hms.check_headroom_tid"; private static final String PROPERTY_CHECK_HEADROOM_AFFINITY = "persist.hms.check_affinity"; private static final String PROPERTY_CHECK_HEADROOM_AFFINITY = "persist.hms.check_headroom_affinity"; private static final String PROPERTY_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS = "persist.hms.check_headroom_proc_stat_min_millis"; private Boolean mFMQUsesIntegratedEventFlag = false; private final Object mCpuHeadroomLock = new Object(); @VisibleForTesting final float mJiffyMillis; private final int mCheckHeadroomProcStatMinMillis; @GuardedBy("mCpuHeadroomLock") private long mLastCpuUserModeTimeCheckedMillis = 0; @GuardedBy("mCpuHeadroomLock") private long mLastCpuUserModeJiffies = 0; @GuardedBy("mCpuHeadroomLock") private final Map<Integer, Long> mUidToLastUserModeJiffies; @VisibleForTesting private String mProcStatFilePathOverride = null; @VisibleForTesting private boolean mEnforceCpuHeadroomUserModeCpuTimeCheck = false; private ISessionManager mSessionManager; Loading Loading @@ -310,8 +335,16 @@ public final class HintManagerService extends SystemService { new GpuHeadroomParamsInternal().calculationWindowMillis; if (mSupportInfo.headroom.isCpuSupported) { mCpuHeadroomCache = new HeadroomCache<>(2, mSupportInfo.headroom.cpuMinIntervalMillis); mUidToLastUserModeJiffies = new ArrayMap<>(); long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK); mJiffyMillis = 1000.0f / jiffyHz; mCheckHeadroomProcStatMinMillis = SystemProperties.getInt( PROPERTY_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS, 50); } else { mCpuHeadroomCache = null; mUidToLastUserModeJiffies = null; mJiffyMillis = 0.0f; mCheckHeadroomProcStatMinMillis = 0; } if (mSupportInfo.headroom.isGpuSupported) { mGpuHeadroomCache = new HeadroomCache<>(2, mSupportInfo.headroom.gpuMinIntervalMillis); Loading Loading @@ -370,6 +403,12 @@ public final class HintManagerService extends SystemService { return supportInfo; } @VisibleForTesting void setProcStatPathOverride(String override) { mProcStatFilePathOverride = override; mEnforceCpuHeadroomUserModeCpuTimeCheck = true; } private ServiceThread createCleanUpThread() { final ServiceThread handlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_LOWEST, true /*allowIo*/); Loading Loading @@ -851,6 +890,11 @@ public final class HintManagerService extends SystemService { mChannelMap.remove(uid); } } synchronized (mCpuHeadroomLock) { if (mSupportInfo.headroom.isCpuSupported && mUidToLastUserModeJiffies != null) { mUidToLastUserModeJiffies.remove(uid); } } }); } Loading Loading @@ -1230,7 +1274,7 @@ public final class HintManagerService extends SystemService { // Only call into AM if the tid is either isolated or invalid if (isolatedPids == null) { // To avoid deadlock, do not call into AMS if the call is from system. if (uid == Process.SYSTEM_UID) { if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) { return tid; } isolatedPids = mAmInternal.getIsolatedProcesses(uid); Loading Loading @@ -1485,14 +1529,17 @@ public final class HintManagerService extends SystemService { throw new UnsupportedOperationException(); } checkCpuHeadroomParams(params); final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); final CpuHeadroomParams halParams = new CpuHeadroomParams(); halParams.tids = new int[]{Binder.getCallingPid()}; halParams.tids = new int[]{pid}; halParams.calculationType = params.calculationType; halParams.calculationWindowMillis = params.calculationWindowMillis; if (params.usesDeviceHeadroom) { halParams.tids = new int[]{}; } else if (params.tids != null && params.tids.length > 0) { if (SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_TID, true)) { if (UserHandle.getAppId(uid) != Process.SYSTEM_UID && SystemProperties.getBoolean( PROPERTY_CHECK_HEADROOM_TID, true)) { final int tgid = Process.getThreadGroupLeader(Binder.getCallingPid()); for (int tid : params.tids) { if (Process.getThreadGroupLeader(tid) != tgid) { Loading @@ -1515,6 +1562,20 @@ public final class HintManagerService extends SystemService { if (res != null) return res; } } final boolean shouldCheckUserModeCpuTime = mEnforceCpuHeadroomUserModeCpuTimeCheck || (UserHandle.getAppId(uid) != Process.SYSTEM_UID && mContext.checkCallingPermission( Manifest.permission.DEVICE_POWER) == PackageManager.PERMISSION_DENIED); if (shouldCheckUserModeCpuTime) { synchronized (mCpuHeadroomLock) { if (!checkPerUidUserModeCpuTimeElapsedLocked(uid)) { return null; } } } // return from HAL directly try { final CpuHeadroomResult result = mPowerHal.getCpuHeadroom(halParams); Loading @@ -1528,6 +1589,11 @@ public final class HintManagerService extends SystemService { mCpuHeadroomCache.add(halParams, result); } } if (shouldCheckUserModeCpuTime) { synchronized (mCpuHeadroomLock) { mUidToLastUserModeJiffies.put(uid, mLastCpuUserModeJiffies); } } return result; } catch (RemoteException e) { Slog.e(TAG, "Failed to get CPU headroom from Power HAL", e); Loading Loading @@ -1556,6 +1622,40 @@ public final class HintManagerService extends SystemService { } } // check if there has been sufficient user mode cpu time elapsed since last call // from the same uid @GuardedBy("mCpuHeadroomLock") private boolean checkPerUidUserModeCpuTimeElapsedLocked(int uid) { // skip checking proc stat if it's within mCheckHeadroomProcStatMinMillis if (System.currentTimeMillis() - mLastCpuUserModeTimeCheckedMillis > mCheckHeadroomProcStatMinMillis) { try { mLastCpuUserModeJiffies = getUserModeJiffies(); } catch (Exception e) { Slog.e(TAG, "Failed to get user mode CPU time", e); return false; } mLastCpuUserModeTimeCheckedMillis = System.currentTimeMillis(); } if (mUidToLastUserModeJiffies.containsKey(uid)) { long uidLastUserModeJiffies = mUidToLastUserModeJiffies.get(uid); if ((mLastCpuUserModeJiffies - uidLastUserModeJiffies) * mJiffyMillis < mSupportInfo.headroom.cpuMinIntervalMillis) { Slog.w(TAG, "UID " + uid + " is requesting CPU headroom too soon"); Slog.d(TAG, "UID " + uid + " last request at " + uidLastUserModeJiffies * mJiffyMillis + "ms with device currently at " + mLastCpuUserModeJiffies * mJiffyMillis + "ms, the interval: " + (mLastCpuUserModeJiffies - uidLastUserModeJiffies) * mJiffyMillis + "ms is less than require minimum interval " + mSupportInfo.headroom.cpuMinIntervalMillis + "ms"); return false; } } return true; } private void checkCpuHeadroomParams(CpuHeadroomParamsInternal params) { boolean calculationTypeMatched = false; try { Loading Loading @@ -1731,6 +1831,27 @@ public final class HintManagerService extends SystemService { } } private long getUserModeJiffies() throws IOException { String filePath = mProcStatFilePathOverride == null ? "/proc/stat" : mProcStatFilePathOverride; try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) { String line; while ((line = reader.readLine()) != null) { Matcher matcher = PROC_STAT_CPU_TIME_TOTAL_PATTERN.matcher(line.trim()); if (matcher.find()) { long userJiffies = Long.parseLong(matcher.group("user")); long niceJiffies = Long.parseLong(matcher.group("nice")); Slog.d(TAG, "user: " + userJiffies + " nice: " + niceJiffies + " total " + (userJiffies + niceJiffies)); reader.close(); return userJiffies + niceJiffies; } } } throw new IllegalStateException("Can't find cpu line in " + filePath); } private boolean checkGraphicsPipelineValid(SessionCreationConfig creationConfig, int uid) { if (creationConfig.modesToEnable == null) { return true; Loading
services/tests/performancehinttests/src/com/android/server/power/hint/HintManagerServiceTest.java +75 −10 Original line number Diff line number Diff line Loading @@ -78,12 +78,15 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; import android.util.Log; import androidx.test.InstrumentationRegistry; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.power.hint.HintManagerService.AppHintSession; import com.android.server.power.hint.HintManagerService.Injector; import com.android.server.power.hint.HintManagerService.NativeWrapper; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; Loading @@ -93,6 +96,8 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; Loading @@ -111,6 +116,7 @@ import java.util.concurrent.locks.LockSupport; */ public class HintManagerServiceTest { private static final String TAG = "HintManagerServiceTest"; private List<File> mFilesCreated = new ArrayList<>(); private static WorkDuration makeWorkDuration( long timestamp, long duration, long workPeriodStartTime, Loading Loading @@ -192,9 +198,9 @@ public class HintManagerServiceTest { mSupportInfo.sessionTags = -1; mSupportInfo.headroom = new SupportInfo.HeadroomSupportInfo(); mSupportInfo.headroom.isCpuSupported = true; mSupportInfo.headroom.cpuMinIntervalMillis = 2000; mSupportInfo.headroom.cpuMinIntervalMillis = 1000; mSupportInfo.headroom.isGpuSupported = true; mSupportInfo.headroom.gpuMinIntervalMillis = 2000; mSupportInfo.headroom.gpuMinIntervalMillis = 1000; mSupportInfo.compositionData = new SupportInfo.CompositionDataSupportInfo(); return mSupportInfo; } Loading Loading @@ -243,6 +249,13 @@ public class HintManagerServiceTest { LocalServices.addService(ActivityManagerInternal.class, mAmInternalMock); } @After public void tearDown() { for (File file : mFilesCreated) { file.delete(); } } /** * Mocks the creation calls, but without support for new createHintSessionWithConfig method */ Loading Loading @@ -1327,6 +1340,58 @@ public class HintManagerServiceTest { }); } @Test public void testCpuHeadroomCpuProcStatPath() throws Exception { File dir = InstrumentationRegistry.getTargetContext().getFilesDir(); dir.mkdir(); String procStatFileStr = "mock_proc_stat"; File file = new File(dir, procStatFileStr); mFilesCreated.add(file); try (FileOutputStream output = new FileOutputStream(file)) { output.write("cpu 2000 3000 4000 0 0 0 0 0 0 0".getBytes()); } HintManagerService service = createService(); service.setProcStatPathOverride(file.getPath()); CpuHeadroomParamsInternal params1 = new CpuHeadroomParamsInternal(); CpuHeadroomParams halParams1 = new CpuHeadroomParams(); halParams1.calculationType = CpuHeadroomParams.CalculationType.MIN; halParams1.tids = new int[]{Process.myPid()}; float headroom1 = 0.1f; CpuHeadroomResult halRet1 = CpuHeadroomResult.globalHeadroom(headroom1); when(mIPowerMock.getCpuHeadroom(eq(halParams1))).thenReturn(halRet1); clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1)); // expire the cache but cpu proc hasn't changed so we expect no value return Thread.sleep(1100); clearInvocations(mIPowerMock); assertEquals(null, service.getBinderServiceInstance().getCpuHeadroom(params1)); verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1)); // update user jiffies with 500 equivalent jiffies, which is not sufficient cpu time Thread.sleep(1100); try (FileOutputStream output = new FileOutputStream(file)) { output.write(("cpu " + (2000 + (int) (500 / service.mJiffyMillis)) + " 3000 4000 0 0 0 0 0 0 0").getBytes()); } clearInvocations(mIPowerMock); assertEquals(null, service.getBinderServiceInstance().getCpuHeadroom(params1)); verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1)); // update nice jiffies with 600 equivalent jiffies, now it exceeds 1000ms requirement Thread.sleep(1100); try (FileOutputStream output = new FileOutputStream(file)) { output.write(("cpu " + (2000 + (int) (500 / service.mJiffyMillis)) + " " + +(3000 + (int) (600 / service.mJiffyMillis)) + " 4000 0 0 0 0 0 0 0").getBytes()); } clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams1)); } @Test @EnableFlags({Flags.FLAG_CPU_HEADROOM_AFFINITY_CHECK}) Loading Loading @@ -1397,8 +1462,8 @@ public class HintManagerServiceTest { verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); // after 1 more second it should be served with cache still Thread.sleep(1000); // after 500ms more it should be served with cache Thread.sleep(500); clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); Loading @@ -1410,8 +1475,8 @@ public class HintManagerServiceTest { verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3)); verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4)); // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval Thread.sleep(1100); // after 1+ seconds it should be served from HAL as it exceeds 1000 millis interval Thread.sleep(600); clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getCpuHeadroom(params1)); assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2)); Loading Loading @@ -1519,8 +1584,8 @@ public class HintManagerServiceTest { verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1)); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); // after 1 more second it should be served with cache still Thread.sleep(1000); // after 500ms it should be served with cache Thread.sleep(500); clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); Loading @@ -1528,8 +1593,8 @@ public class HintManagerServiceTest { verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1)); verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2)); // after 2+ seconds it should be served from HAL as it exceeds 2000 millis interval Thread.sleep(1100); // after 1+ seconds it should be served from HAL as it exceeds 1000 millis interval Thread.sleep(600); clearInvocations(mIPowerMock); assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1)); assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2)); Loading