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.app.ActivityThread;
28 import android.hardware.display.DisplayManager;
29 import android.hardware.display.DisplayManagerGlobal;
30 import android.os.Handler;
31 import android.util.SparseArray;
32 import android.view.DisplayInfo;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 
39 /**
40 * A class that tracks the display resolutions.
41 * @hide
42 */
43 public class DisplayResolutionTracker {
44     private static final String TAG = DisplayResolutionTracker.class.getSimpleName();
45 
46     public static final int RESOLUTION_UNKNOWN =
47             UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__UNKNOWN_RESOLUTION;
48     public static final int RESOLUTION_SD =
49             UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__SD;
50     public static final int RESOLUTION_HD =
51             UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__HD;
52     public static final int RESOLUTION_FHD =
53             UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__FHD;
54     public static final int RESOLUTION_QHD =
55             UIINTERACTION_FRAME_INFO_REPORTED__DISPLAY_RESOLUTION__QHD;
56 
57     /** @hide */
58     @IntDef({
59         RESOLUTION_UNKNOWN,
60         RESOLUTION_SD,
61         RESOLUTION_HD,
62         RESOLUTION_FHD,
63         RESOLUTION_QHD,
64     })
65     @Retention(RetentionPolicy.SOURCE)
66     public @interface Resolution {
67     }
68 
69     private final DisplayInterface mManager;
70     private final SparseArray<Integer> mResolutions = new SparseArray<>();
71     private final Object mLock = new Object();
72 
DisplayResolutionTracker(@ullable Handler handler)73     public DisplayResolutionTracker(@Nullable Handler handler) {
74         this(DisplayInterface.getDefault(handler));
75     }
76 
77     @VisibleForTesting
DisplayResolutionTracker(DisplayInterface manager)78     public DisplayResolutionTracker(DisplayInterface manager) {
79         mManager = manager;
80         mManager.registerDisplayListener(new DisplayManager.DisplayListener() {
81             @Override
82             public void onDisplayAdded(int displayId) {
83                 updateDisplay(displayId);
84             }
85 
86             @Override
87             public void onDisplayChanged(int displayId) {
88                 updateDisplay(displayId);
89             }
90 
91             @Override
92             public void onDisplayRemoved(int displayId) {
93                 // Not in the event mask below, won't be called.
94             }
95         });
96     }
97 
updateDisplay(int displayId)98     private void updateDisplay(int displayId) {
99         DisplayInfo info = mManager.getDisplayInfo(displayId);
100         if (info == null) {
101             return;
102         }
103         @Resolution int resolution = getResolution(info);
104 
105         synchronized (mLock) {
106             mResolutions.put(displayId, resolution);
107         }
108     }
109 
110     /**
111      * Returns the (cached) resolution of the display with the given ID.
112      */
113     @Resolution
getResolution(int displayId)114     public int getResolution(int displayId) {
115         return mResolutions.get(displayId, RESOLUTION_UNKNOWN);
116     }
117 
118     /**
119      * Returns the resolution of the given {@link DisplayInfo}.
120      */
121     @VisibleForTesting
122     @Resolution
getResolution(DisplayInfo info)123     public static int getResolution(DisplayInfo info) {
124         int smaller = Math.min(info.logicalWidth, info.logicalHeight);
125         int larger = Math.max(info.logicalWidth, info.logicalHeight);
126         if (smaller < 720 || larger < 1280) {
127             return RESOLUTION_SD;
128         } else if (smaller < 1080 || larger < 1920) {
129             return RESOLUTION_HD;
130         } else if (smaller < 1440 || larger < 2560) {
131             return RESOLUTION_FHD;
132         } else {
133             return RESOLUTION_QHD;
134         }
135     }
136 
137     /**
138      * Wrapper around the final {@link DisplayManagerGlobal} class.
139      * @hide
140      */
141     @VisibleForTesting
142     public interface DisplayInterface {
143         /** Reurns an implementation wrapping {@link DisplayManagerGlobal}. */
getDefault(@ullable Handler handler)144         static DisplayInterface getDefault(@Nullable Handler handler) {
145             DisplayManagerGlobal manager = DisplayManagerGlobal.getInstance();
146             return new DisplayInterface() {
147                 @Override
148                 public void registerDisplayListener(DisplayManager.DisplayListener listener) {
149                     manager.registerDisplayListener(listener, handler,
150                             DisplayManager.EVENT_FLAG_DISPLAY_ADDED
151                                     | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED,
152                             ActivityThread.currentPackageName());
153                 }
154 
155                 @Override
156                 public DisplayInfo getDisplayInfo(int displayId) {
157                     return manager.getDisplayInfo(displayId);
158                 }
159             };
160         }
161 
162         /** {@see DisplayManagerGlobal#registerDisplayListener} */
registerDisplayListener(DisplayManager.DisplayListener listener)163         void registerDisplayListener(DisplayManager.DisplayListener listener);
164         /** {@see DisplayManagerGlobal#getDisplayInfo} */
getDisplayInfo(int displayId)165         DisplayInfo getDisplayInfo(int displayId);
166     }
167 }
168