Loading services/core/java/com/android/server/display/DisplayFrameworkStatsLogger.java 0 → 100644 +64 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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.display; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.util.SparseIntArray; import com.android.internal.util.FrameworkStatsLog; public final class DisplayFrameworkStatsLogger { /** Logs DisplayEventCallbackOccurred push atom */ public void logDisplayEvent(@DisplayManagerGlobal.DisplayEvent int event, SparseIntArray notifiedUids) { FrameworkStatsLog.write( FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED, toProtoEventType(event), notifiedUids.copyKeys()); } /** * Maps DisplayEvent to atom. Default case "unknown" is required when defining an atom. * Currently private display events {@link DisplayManager.PrivateEventType} are marked as * unknown. */ private int toProtoEventType(@DisplayManagerGlobal.DisplayEvent int event) { return switch (event) { case DisplayManagerGlobal.EVENT_DISPLAY_ADDED -> FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_ADDED; case DisplayManagerGlobal.EVENT_DISPLAY_REMOVED -> FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_REMOVED; case DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED -> FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_CHANGED; case DisplayManagerGlobal.EVENT_DISPLAY_REFRESH_RATE_CHANGED -> FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_REFRESH_RATE_CHANGED; case DisplayManagerGlobal.EVENT_DISPLAY_STATE_CHANGED -> FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_STATE_CHANGED; case DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED -> FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_BRIGHTNESS_CHANGED; default -> FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_UNKNOWN; }; } } services/core/java/com/android/server/display/DisplayManagerService.java +20 −6 Original line number Original line Diff line number Diff line Loading @@ -307,6 +307,8 @@ public final class DisplayManagerService extends SystemService { private ActivityManagerInternal mActivityManagerInternal; private ActivityManagerInternal mActivityManagerInternal; private final UidImportanceListener mUidImportanceListener = new UidImportanceListener(); private final UidImportanceListener mUidImportanceListener = new UidImportanceListener(); private final DisplayFrameworkStatsLogger mStatsLogger = new DisplayFrameworkStatsLogger(); @Nullable @Nullable private IMediaProjectionManager mProjectionService; private IMediaProjectionManager mProjectionService; private DeviceStateManagerInternal mDeviceStateManager; private DeviceStateManagerInternal mDeviceStateManager; Loading Loading @@ -3715,11 +3717,23 @@ public final class DisplayManagerService extends SystemService { } } } } // Map that maps a uid to the number of times it was notified SparseIntArray notifiedUids = new SparseIntArray(); // After releasing the lock, send the notifications out. // After releasing the lock, send the notifications out. for (int i = 0; i < mTempCallbacks.size(); i++) { for (int i = 0; i < mTempCallbacks.size(); i++) { CallbackRecord callbackRecord = mTempCallbacks.get(i); CallbackRecord callbackRecord = mTempCallbacks.get(i); callbackRecord.notifyDisplayEventAsync(displayId, event); boolean notified = callbackRecord.notifyDisplayEventAsync(displayId, event); if (notified) { int uid = callbackRecord.mUid; notifiedUids.put(uid, notifiedUids.get(uid, 0) + 1); } } } if (mFlags.isDisplayEventsLoggingEnabled()) { mStatsLogger.logDisplayEvent(event, notifiedUids); } mTempCallbacks.clear(); mTempCallbacks.clear(); } } Loading Loading @@ -4497,9 +4511,9 @@ public final class DisplayManagerService extends SystemService { } } /** /** * @return {@code false} if RemoteException happens; otherwise {@code true} for * @return {@code true} if the notification was processed (sent, queued). * success. This returns true even if the event was deferred because the remote client is * Returns {@code false} if the notification was not sent e.g. because client is * cached or frozen. * not registered for this event. */ */ public boolean notifyDisplayEventAsync(int displayId, @DisplayEvent int event) { public boolean notifyDisplayEventAsync(int displayId, @DisplayEvent int event) { if (!shouldSendDisplayEvent(event)) { if (!shouldSendDisplayEvent(event)) { Loading @@ -4515,7 +4529,7 @@ public final class DisplayManagerService extends SystemService { + ",uid" + mUid); + ",uid" + mUid); } } // The client is not interested in this event, so do nothing. // The client is not interested in this event, so do nothing. return true; return false; } } synchronized (mCallback) { synchronized (mCallback) { Loading @@ -4535,7 +4549,7 @@ public final class DisplayManagerService extends SystemService { if (!shouldReceiveRefreshRateWithChangeUpdate(event)) { if (!shouldReceiveRefreshRateWithChangeUpdate(event)) { // The client is not visible to the user and is not a system service, so do nothing. // The client is not visible to the user and is not a system service, so do nothing. return true; return false; } } try { try { Loading services/core/java/com/android/server/display/feature/Android.bp +7 −0 Original line number Original line Diff line number Diff line Loading @@ -6,3 +6,10 @@ aconfig_declarations { "*.aconfig", "*.aconfig", ], ], } } java_aconfig_library { name: "display_flags_lib_host", aconfig_declarations: "display_flags", host_supported: true, defaults: ["framework-minus-apex-aconfig-java-defaults"], } services/tests/displayservicetests/src/com/android/server/display/DisplayFrameworkStatsLoggerTest.java 0 → 100644 +118 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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.display; import static org.mockito.Mockito.verify; import android.hardware.display.DisplayManagerGlobal; import android.util.SparseIntArray; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.util.FrameworkStatsLog; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * Tests for {@link com.android.server.display.DisplayFrameworkStatsLogger}. * * <p>Build with: atest DisplayFrameworkStatsLoggerTest */ @SmallTest @RunWith(AndroidJUnit4.class) public class DisplayFrameworkStatsLoggerTest { @InjectMocks private DisplayFrameworkStatsLogger mLogger; @Mock private FrameworkStatsLog mFrameworkStatsLogMock; @Before public void setUp() { MockitoAnnotations.initMocks(this); } @Test public void testLogDisplayEvent_displayAdded_writesToStatsLog() { final int event = DisplayManagerGlobal.EVENT_DISPLAY_ADDED; final SparseIntArray uidMap = new SparseIntArray() { { put(1001, 1); put(1002, 3); } }; final int expectedProtoType = FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_ADDED; mLogger.logDisplayEvent(event, uidMap); verify(mFrameworkStatsLogMock) .write( FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED, expectedProtoType, uidMap.copyKeys()); } @Test public void testLogDisplayEvent_brightnessChanged_writesToStatsLog() { final int event = DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED; final SparseIntArray uidMap = new SparseIntArray() { { put(1005, 1); } }; final int expectedProtoType = FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_BRIGHTNESS_CHANGED; mLogger.logDisplayEvent(event, uidMap); verify(mFrameworkStatsLogMock) .write( FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED, expectedProtoType, uidMap.copyKeys()); } @Test public void testLogDisplayEvent_unknownEvent_writesUnknownTypeToStatsLog() { final int event = -1; final SparseIntArray uidMap = new SparseIntArray() { { put(9999, 6); } }; final int expectedProtoType = FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_UNKNOWN; mLogger.logDisplayEvent(event, uidMap); verify(mFrameworkStatsLogMock) .write( FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED, expectedProtoType, uidMap.copyKeys()); } } Loading
services/core/java/com/android/server/display/DisplayFrameworkStatsLogger.java 0 → 100644 +64 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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.display; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.util.SparseIntArray; import com.android.internal.util.FrameworkStatsLog; public final class DisplayFrameworkStatsLogger { /** Logs DisplayEventCallbackOccurred push atom */ public void logDisplayEvent(@DisplayManagerGlobal.DisplayEvent int event, SparseIntArray notifiedUids) { FrameworkStatsLog.write( FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED, toProtoEventType(event), notifiedUids.copyKeys()); } /** * Maps DisplayEvent to atom. Default case "unknown" is required when defining an atom. * Currently private display events {@link DisplayManager.PrivateEventType} are marked as * unknown. */ private int toProtoEventType(@DisplayManagerGlobal.DisplayEvent int event) { return switch (event) { case DisplayManagerGlobal.EVENT_DISPLAY_ADDED -> FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_ADDED; case DisplayManagerGlobal.EVENT_DISPLAY_REMOVED -> FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_REMOVED; case DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED -> FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_CHANGED; case DisplayManagerGlobal.EVENT_DISPLAY_REFRESH_RATE_CHANGED -> FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_REFRESH_RATE_CHANGED; case DisplayManagerGlobal.EVENT_DISPLAY_STATE_CHANGED -> FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_STATE_CHANGED; case DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED -> FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_BRIGHTNESS_CHANGED; default -> FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_UNKNOWN; }; } }
services/core/java/com/android/server/display/DisplayManagerService.java +20 −6 Original line number Original line Diff line number Diff line Loading @@ -307,6 +307,8 @@ public final class DisplayManagerService extends SystemService { private ActivityManagerInternal mActivityManagerInternal; private ActivityManagerInternal mActivityManagerInternal; private final UidImportanceListener mUidImportanceListener = new UidImportanceListener(); private final UidImportanceListener mUidImportanceListener = new UidImportanceListener(); private final DisplayFrameworkStatsLogger mStatsLogger = new DisplayFrameworkStatsLogger(); @Nullable @Nullable private IMediaProjectionManager mProjectionService; private IMediaProjectionManager mProjectionService; private DeviceStateManagerInternal mDeviceStateManager; private DeviceStateManagerInternal mDeviceStateManager; Loading Loading @@ -3715,11 +3717,23 @@ public final class DisplayManagerService extends SystemService { } } } } // Map that maps a uid to the number of times it was notified SparseIntArray notifiedUids = new SparseIntArray(); // After releasing the lock, send the notifications out. // After releasing the lock, send the notifications out. for (int i = 0; i < mTempCallbacks.size(); i++) { for (int i = 0; i < mTempCallbacks.size(); i++) { CallbackRecord callbackRecord = mTempCallbacks.get(i); CallbackRecord callbackRecord = mTempCallbacks.get(i); callbackRecord.notifyDisplayEventAsync(displayId, event); boolean notified = callbackRecord.notifyDisplayEventAsync(displayId, event); if (notified) { int uid = callbackRecord.mUid; notifiedUids.put(uid, notifiedUids.get(uid, 0) + 1); } } } if (mFlags.isDisplayEventsLoggingEnabled()) { mStatsLogger.logDisplayEvent(event, notifiedUids); } mTempCallbacks.clear(); mTempCallbacks.clear(); } } Loading Loading @@ -4497,9 +4511,9 @@ public final class DisplayManagerService extends SystemService { } } /** /** * @return {@code false} if RemoteException happens; otherwise {@code true} for * @return {@code true} if the notification was processed (sent, queued). * success. This returns true even if the event was deferred because the remote client is * Returns {@code false} if the notification was not sent e.g. because client is * cached or frozen. * not registered for this event. */ */ public boolean notifyDisplayEventAsync(int displayId, @DisplayEvent int event) { public boolean notifyDisplayEventAsync(int displayId, @DisplayEvent int event) { if (!shouldSendDisplayEvent(event)) { if (!shouldSendDisplayEvent(event)) { Loading @@ -4515,7 +4529,7 @@ public final class DisplayManagerService extends SystemService { + ",uid" + mUid); + ",uid" + mUid); } } // The client is not interested in this event, so do nothing. // The client is not interested in this event, so do nothing. return true; return false; } } synchronized (mCallback) { synchronized (mCallback) { Loading @@ -4535,7 +4549,7 @@ public final class DisplayManagerService extends SystemService { if (!shouldReceiveRefreshRateWithChangeUpdate(event)) { if (!shouldReceiveRefreshRateWithChangeUpdate(event)) { // The client is not visible to the user and is not a system service, so do nothing. // The client is not visible to the user and is not a system service, so do nothing. return true; return false; } } try { try { Loading
services/core/java/com/android/server/display/feature/Android.bp +7 −0 Original line number Original line Diff line number Diff line Loading @@ -6,3 +6,10 @@ aconfig_declarations { "*.aconfig", "*.aconfig", ], ], } } java_aconfig_library { name: "display_flags_lib_host", aconfig_declarations: "display_flags", host_supported: true, defaults: ["framework-minus-apex-aconfig-java-defaults"], }
services/tests/displayservicetests/src/com/android/server/display/DisplayFrameworkStatsLoggerTest.java 0 → 100644 +118 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2025 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.display; import static org.mockito.Mockito.verify; import android.hardware.display.DisplayManagerGlobal; import android.util.SparseIntArray; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.util.FrameworkStatsLog; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * Tests for {@link com.android.server.display.DisplayFrameworkStatsLogger}. * * <p>Build with: atest DisplayFrameworkStatsLoggerTest */ @SmallTest @RunWith(AndroidJUnit4.class) public class DisplayFrameworkStatsLoggerTest { @InjectMocks private DisplayFrameworkStatsLogger mLogger; @Mock private FrameworkStatsLog mFrameworkStatsLogMock; @Before public void setUp() { MockitoAnnotations.initMocks(this); } @Test public void testLogDisplayEvent_displayAdded_writesToStatsLog() { final int event = DisplayManagerGlobal.EVENT_DISPLAY_ADDED; final SparseIntArray uidMap = new SparseIntArray() { { put(1001, 1); put(1002, 3); } }; final int expectedProtoType = FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_ADDED; mLogger.logDisplayEvent(event, uidMap); verify(mFrameworkStatsLogMock) .write( FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED, expectedProtoType, uidMap.copyKeys()); } @Test public void testLogDisplayEvent_brightnessChanged_writesToStatsLog() { final int event = DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED; final SparseIntArray uidMap = new SparseIntArray() { { put(1005, 1); } }; final int expectedProtoType = FrameworkStatsLog .DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_DISPLAY_BRIGHTNESS_CHANGED; mLogger.logDisplayEvent(event, uidMap); verify(mFrameworkStatsLogMock) .write( FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED, expectedProtoType, uidMap.copyKeys()); } @Test public void testLogDisplayEvent_unknownEvent_writesUnknownTypeToStatsLog() { final int event = -1; final SparseIntArray uidMap = new SparseIntArray() { { put(9999, 6); } }; final int expectedProtoType = FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED__EVENT_TYPE__TYPE_UNKNOWN; mLogger.logDisplayEvent(event, uidMap); verify(mFrameworkStatsLogMock) .write( FrameworkStatsLog.DISPLAY_EVENT_CALLBACK_OCCURRED, expectedProtoType, uidMap.copyKeys()); } }