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.util.IntArray; 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 @VisibleForTesting 35 static final int GLOBAL_ID = -1; 36 37 private boolean mLoggingEnabled; 38 39 private final Listener mListener; 40 41 @Nullable 42 private final VotesStatsReporter mVotesStatsReporter; 43 44 private final Object mStorageLock = new Object(); 45 // A map from the display ID to the collection of votes and their priority. The latter takes 46 // the form of another map from the priority to the vote itself so that each priority is 47 // guaranteed to have exactly one vote, which is also easily and efficiently replaceable. 48 @GuardedBy("mStorageLock") 49 private final SparseArray<SparseArray<Vote>> mVotesByDisplay = new SparseArray<>(); 50 VotesStorage(@onNull Listener listener, @Nullable VotesStatsReporter votesStatsReporter)51 VotesStorage(@NonNull Listener listener, @Nullable VotesStatsReporter votesStatsReporter) { 52 mListener = listener; 53 mVotesStatsReporter = votesStatsReporter; 54 } 55 /** sets logging enabled/disabled for this class */ setLoggingEnabled(boolean loggingEnabled)56 void setLoggingEnabled(boolean loggingEnabled) { 57 mLoggingEnabled = loggingEnabled; 58 } 59 /** 60 * gets all votes for specific display, note that global display votes are also added to result 61 */ 62 @NonNull getVotes(int displayId)63 SparseArray<Vote> getVotes(int displayId) { 64 SparseArray<Vote> votesLocal; 65 SparseArray<Vote> globalVotesLocal; 66 synchronized (mStorageLock) { 67 SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId); 68 votesLocal = displayVotes != null ? displayVotes.clone() : new SparseArray<>(); 69 SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID); 70 globalVotesLocal = globalVotes != null ? globalVotes.clone() : new SparseArray<>(); 71 } 72 for (int i = 0; i < globalVotesLocal.size(); i++) { 73 int priority = globalVotesLocal.keyAt(i); 74 if (!votesLocal.contains(priority)) { 75 votesLocal.put(priority, globalVotesLocal.valueAt(i)); 76 } 77 } 78 return votesLocal; 79 } 80 81 /** updates vote storage for all displays */ updateGlobalVote(@ote.Priority int priority, @Nullable Vote vote)82 void updateGlobalVote(@Vote.Priority int priority, @Nullable Vote vote) { 83 updateVote(GLOBAL_ID, priority, vote); 84 } 85 86 /** updates vote storage */ updateVote(int displayId, @Vote.Priority int priority, @Nullable Vote vote)87 void updateVote(int displayId, @Vote.Priority int priority, @Nullable Vote vote) { 88 if (mLoggingEnabled) { 89 Slog.i(TAG, "updateVoteLocked(displayId=" + displayId 90 + ", priority=" + Vote.priorityToString(priority) 91 + ", vote=" + vote + ")"); 92 } 93 if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) { 94 Slog.w(TAG, "Received a vote with an invalid priority, ignoring:" 95 + " priority=" + Vote.priorityToString(priority) 96 + ", vote=" + vote); 97 return; 98 } 99 boolean changed = false; 100 SparseArray<Vote> votes; 101 synchronized (mStorageLock) { 102 if (mVotesByDisplay.contains(displayId)) { 103 votes = mVotesByDisplay.get(displayId); 104 } else { 105 votes = new SparseArray<>(); 106 mVotesByDisplay.put(displayId, votes); 107 } 108 var currentVote = votes.get(priority); 109 if (vote != null && !vote.equals(currentVote)) { 110 votes.put(priority, vote); 111 changed = true; 112 } else if (vote == null && currentVote != null) { 113 votes.remove(priority); 114 changed = true; 115 } 116 } 117 if (mLoggingEnabled) { 118 Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes); 119 } 120 if (changed) { 121 if (mVotesStatsReporter != null) { 122 mVotesStatsReporter.reportVoteChanged(displayId, priority, vote); 123 } 124 mListener.onChanged(); 125 } 126 } 127 128 /** removes all votes with certain priority from vote storage */ removeAllVotesForPriority(@ote.Priority int priority)129 void removeAllVotesForPriority(@Vote.Priority int priority) { 130 if (mLoggingEnabled) { 131 Slog.i(TAG, "removeAllVotesForPriority(priority=" 132 + Vote.priorityToString(priority) + ")"); 133 } 134 if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) { 135 Slog.w(TAG, "Received an invalid priority, ignoring:" 136 + " priority=" + Vote.priorityToString(priority)); 137 return; 138 } 139 IntArray removedVotesDisplayIds = new IntArray(); 140 synchronized (mStorageLock) { 141 int size = mVotesByDisplay.size(); 142 for (int i = 0; i < size; i++) { 143 SparseArray<Vote> votes = mVotesByDisplay.valueAt(i); 144 if (votes.get(priority) != null) { 145 votes.remove(priority); 146 removedVotesDisplayIds.add(mVotesByDisplay.keyAt(i)); 147 } 148 } 149 } 150 if (mLoggingEnabled) { 151 Slog.i(TAG, "Removed votes with priority=" + priority 152 + " for displays=" + removedVotesDisplayIds); 153 } 154 int removedVotesSize = removedVotesDisplayIds.size(); 155 if (removedVotesSize > 0) { 156 if (mVotesStatsReporter != null) { 157 for (int i = 0; i < removedVotesSize; i++) { 158 mVotesStatsReporter.reportVoteChanged( 159 removedVotesDisplayIds.get(i), priority, null); 160 } 161 } 162 mListener.onChanged(); 163 } 164 } 165 166 /** dump class values, for debugging */ dump(@onNull PrintWriter pw)167 void dump(@NonNull PrintWriter pw) { 168 SparseArray<SparseArray<Vote>> votesByDisplayLocal = new SparseArray<>(); 169 synchronized (mStorageLock) { 170 for (int i = 0; i < mVotesByDisplay.size(); i++) { 171 votesByDisplayLocal.put(mVotesByDisplay.keyAt(i), 172 mVotesByDisplay.valueAt(i).clone()); 173 } 174 } 175 pw.println(" mVotesByDisplay:"); 176 for (int i = 0; i < votesByDisplayLocal.size(); i++) { 177 SparseArray<Vote> votes = votesByDisplayLocal.valueAt(i); 178 if (votes.size() == 0) { 179 continue; 180 } 181 pw.println(" " + votesByDisplayLocal.keyAt(i) + ":"); 182 for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) { 183 Vote vote = votes.get(p); 184 if (vote == null) { 185 continue; 186 } 187 pw.println(" " + Vote.priorityToString(p) + " -> " + vote); 188 } 189 } 190 } 191 192 @VisibleForTesting injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay)193 void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) { 194 synchronized (mStorageLock) { 195 mVotesByDisplay.clear(); 196 for (int i = 0; i < votesByDisplay.size(); i++) { 197 mVotesByDisplay.put(votesByDisplay.keyAt(i), votesByDisplay.valueAt(i)); 198 } 199 } 200 } 201 202 interface Listener { onChanged()203 void onChanged(); 204 } 205 } 206