1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.jank;
18 
19 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__FHD;
20 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__HD;
21 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__QHD;
22 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__SD;
23 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__UNKNOWN_RESOLUTION;
24 
25 import android.annotation.IntDef;
26 import android.annotation.Nullable;
27 import android.hardware.display.DisplayManager;
28 import android.hardware.display.DisplayManagerGlobal;
29 import android.os.Handler;
30 import android.util.SparseArray;
31 import android.view.DisplayInfo;
32 
33 import com.android.internal.annotations.VisibleForTesting;
34 
35 import java.lang.annotation.Retention;
36 import java.lang.annotation.RetentionPolicy;
37 
38 /**
39 * A class that tracks the display resolutions.
40 * @hide
41 */
42 public class DisplayResolutionTracker {
43     private static final String TAG = DisplayResolutionTracker.class.getSimpleName();
44 
45     public static final int RESOLUTION_UNKNOWN =
46             UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__UNKNOWN_RESOLUTION;
47     public static final int RESOLUTION_SD =
48             UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__SD;
49     public static final int RESOLUTION_HD =
50             UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__HD;
51     public static final int RESOLUTION_FHD =
52             UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__FHD;
53     public static final int RESOLUTION_QHD =
54             UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__QHD;
55 
56     /** @hide */
57     @IntDef({
58         RESOLUTION_UNKNOWN,
59         RESOLUTION_SD,
60         RESOLUTION_HD,
61         RESOLUTION_FHD,
62         RESOLUTION_QHD,
63     })
64     @Retention(RetentionPolicy.SOURCE)
65     public @interface Resolution {
66     }
67 
68     private final DisplayInterface mManager;
69     private final SparseArray<Integer> mResolutions = new SparseArray<>();
70     private final Object mLock = new Object();
71 
DisplayResolutionTracker(@ullable Handler handler)72     public DisplayResolutionTracker(@Nullable Handler handler) {
73         this(DisplayInterface.getDefault(handler));
74     }
75 
76     @VisibleForTesting
DisplayResolutionTracker(DisplayInterface manager)77     public DisplayResolutionTracker(DisplayInterface manager) {
78         mManager = manager;
79         mManager.registerDisplayListener(new DisplayManager.DisplayListener() {
80             @Override
81             public void onDisplayAdded(int displayId) {
82                 updateDisplay(displayId);
83             }
84 
85             @Override
86             public void onDisplayChanged(int displayId) {
87                 updateDisplay(displayId);
88             }
89 
90             @Override
91             public void onDisplayRemoved(int displayId) {
92                 // Not in the event mask below, won't be called.
93             }
94         });
95     }
96 
updateDisplay(int displayId)97     private void updateDisplay(int displayId) {
98         DisplayInfo info = mManager.getDisplayInfo(displayId);
99         if (info == null) {
100             return;
101         }
102         @Resolution int resolution = getResolution(info);
103 
104         synchronized (mLock) {
105             mResolutions.put(displayId, resolution);
106         }
107     }
108 
109     /**
110      * Returns the (cached) resolution of the display with the given ID.
111      */
112     @Resolution
getResolution(int displayId)113     public int getResolution(int displayId) {
114         return mResolutions.get(displayId, RESOLUTION_UNKNOWN);
115     }
116 
117     /**
118      * Returns the resolution of the given {@link DisplayInfo}.
119      */
120     @VisibleForTesting
121     @Resolution
getResolution(DisplayInfo info)122     public static int getResolution(DisplayInfo info) {
123         int smaller = Math.min(info.logicalWidth, info.logicalHeight);
124         int larger = Math.max(info.logicalWidth, info.logicalHeight);
125         if (smaller < 720 || larger < 1280) {
126             return RESOLUTION_SD;
127         } else if (smaller < 1080 || larger < 1920) {
128             return RESOLUTION_HD;
129         } else if (smaller < 1440 || larger < 2560) {
130             return RESOLUTION_FHD;
131         } else {
132             return RESOLUTION_QHD;
133         }
134     }
135 
136     /**
137      * Wrapper around the final {@link DisplayManagerGlobal} class.
138      * @hide
139      */
140     @VisibleForTesting
141     public interface DisplayInterface {
142         /** Reurns an implementation wrapping {@link DisplayManagerGlobal}. */
getDefault(@ullable Handler handler)143         static DisplayInterface getDefault(@Nullable Handler handler) {
144             DisplayManagerGlobal manager = DisplayManagerGlobal.getInstance();
145             return new DisplayInterface() {
146                 @Override
147                 public void registerDisplayListener(DisplayManager.DisplayListener listener) {
148                     manager.registerDisplayListener(listener, handler,
149                             DisplayManager.EVENT_FLAG_DISPLAY_ADDED
150                                     | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED);
151                 }
152 
153                 @Override
154                 public DisplayInfo getDisplayInfo(int displayId) {
155                     return manager.getDisplayInfo(displayId);
156                 }
157             };
158         }
159 
160         /** {@see DisplayManagerGlobal#registerDisplayListener} */
registerDisplayListener(DisplayManager.DisplayListener listener)161         void registerDisplayListener(DisplayManager.DisplayListener listener);
162         /** {@see DisplayManagerGlobal#getDisplayInfo} */
getDisplayInfo(int displayId)163         DisplayInfo getDisplayInfo(int displayId);
164     }
165 }
166