1 /* 2 * Copyright (C) 2023 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.server.display.mode; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.os.Trace; 22 import android.util.Slog; 23 import android.util.SparseArray; 24 25 import com.android.internal.annotations.GuardedBy; 26 import com.android.internal.annotations.VisibleForTesting; 27 28 import java.io.PrintWriter; 29 30 class VotesStorage { 31 private static final String TAG = "VotesStorage"; 32 // Special ID used to indicate that given vote is to be applied globally, rather than to a 33 // specific display. 34 private static final int GLOBAL_ID = -1; 35 36 private boolean mLoggingEnabled; 37 38 private final Listener mListener; 39 40 private final Object mStorageLock = new Object(); 41 // A map from the display ID to the collection of votes and their priority. The latter takes 42 // the form of another map from the priority to the vote itself so that each priority is 43 // guaranteed to have exactly one vote, which is also easily and efficiently replaceable. 44 @GuardedBy("mStorageLock") 45 private final SparseArray<SparseArray<Vote>> mVotesByDisplay = new SparseArray<>(); 46 VotesStorage(@onNull Listener listener)47 VotesStorage(@NonNull Listener listener) { 48 mListener = listener; 49 } 50 /** sets logging enabled/disabled for this class */ setLoggingEnabled(boolean loggingEnabled)51 void setLoggingEnabled(boolean loggingEnabled) { 52 mLoggingEnabled = loggingEnabled; 53 } 54 /** 55 * gets all votes for specific display, note that global display votes are also added to result 56 */ 57 @NonNull getVotes(int displayId)58 SparseArray<Vote> getVotes(int displayId) { 59 SparseArray<Vote> votesLocal; 60 SparseArray<Vote> globalVotesLocal; 61 synchronized (mStorageLock) { 62 SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId); 63 votesLocal = displayVotes != null ? displayVotes.clone() : new SparseArray<>(); 64 SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID); 65 globalVotesLocal = globalVotes != null ? globalVotes.clone() : new SparseArray<>(); 66 } 67 for (int i = 0; i < globalVotesLocal.size(); i++) { 68 int priority = globalVotesLocal.keyAt(i); 69 if (!votesLocal.contains(priority)) { 70 votesLocal.put(priority, globalVotesLocal.valueAt(i)); 71 } 72 } 73 return votesLocal; 74 } 75 76 /** updates vote storage for all displays */ updateGlobalVote(int priority, @Nullable Vote vote)77 void updateGlobalVote(int priority, @Nullable Vote vote) { 78 updateVote(GLOBAL_ID, priority, vote); 79 } 80 81 /** updates vote storage */ updateVote(int displayId, int priority, @Nullable Vote vote)82 void updateVote(int displayId, int priority, @Nullable Vote vote) { 83 if (mLoggingEnabled) { 84 Slog.i(TAG, "updateVoteLocked(displayId=" + displayId 85 + ", priority=" + Vote.priorityToString(priority) 86 + ", vote=" + vote + ")"); 87 } 88 if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) { 89 Slog.w(TAG, "Received a vote with an invalid priority, ignoring:" 90 + " priority=" + Vote.priorityToString(priority) 91 + ", vote=" + vote); 92 return; 93 } 94 SparseArray<Vote> votes; 95 synchronized (mStorageLock) { 96 if (mVotesByDisplay.contains(displayId)) { 97 votes = mVotesByDisplay.get(displayId); 98 } else { 99 votes = new SparseArray<>(); 100 mVotesByDisplay.put(displayId, votes); 101 } 102 if (vote != null) { 103 votes.put(priority, vote); 104 } else { 105 votes.remove(priority); 106 } 107 } 108 Trace.traceCounter(Trace.TRACE_TAG_POWER, 109 TAG + "." + displayId + ":" + Vote.priorityToString(priority), 110 getMaxPhysicalRefreshRate(vote)); 111 if (mLoggingEnabled) { 112 Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes); 113 } 114 mListener.onChanged(); 115 } 116 117 /** dump class values, for debugging */ dump(@onNull PrintWriter pw)118 void dump(@NonNull PrintWriter pw) { 119 SparseArray<SparseArray<Vote>> votesByDisplayLocal = new SparseArray<>(); 120 synchronized (mStorageLock) { 121 for (int i = 0; i < mVotesByDisplay.size(); i++) { 122 votesByDisplayLocal.put(mVotesByDisplay.keyAt(i), 123 mVotesByDisplay.valueAt(i).clone()); 124 } 125 } 126 pw.println(" mVotesByDisplay:"); 127 for (int i = 0; i < votesByDisplayLocal.size(); i++) { 128 SparseArray<Vote> votes = votesByDisplayLocal.valueAt(i); 129 if (votes.size() == 0) { 130 continue; 131 } 132 pw.println(" " + votesByDisplayLocal.keyAt(i) + ":"); 133 for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) { 134 Vote vote = votes.get(p); 135 if (vote == null) { 136 continue; 137 } 138 pw.println(" " + Vote.priorityToString(p) + " -> " + vote); 139 } 140 } 141 } 142 143 @VisibleForTesting injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay)144 void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) { 145 synchronized (mStorageLock) { 146 mVotesByDisplay.clear(); 147 for (int i = 0; i < votesByDisplay.size(); i++) { 148 mVotesByDisplay.put(votesByDisplay.keyAt(i), votesByDisplay.valueAt(i)); 149 } 150 } 151 } 152 getMaxPhysicalRefreshRate(@ullable Vote vote)153 private int getMaxPhysicalRefreshRate(@Nullable Vote vote) { 154 if (vote == null) { 155 return -1; 156 } else if (vote.refreshRateRanges.physical.max == Float.POSITIVE_INFINITY) { 157 return 1000; // for visualisation, otherwise e.g. -1 -> 60 will be unnoticeable 158 } 159 return (int) vote.refreshRateRanges.physical.max; 160 } 161 162 interface Listener { onChanged()163 void onChanged(); 164 } 165 } 166