blob: 997c1562b57c7ae78152eff9c191e30e8a70480d [file] [log] [blame]
/*
* Copyright (C) 2023 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.mode;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
class VotesStorage {
private static final String TAG = "VotesStorage";
// Special ID used to indicate that given vote is to be applied globally, rather than to a
// specific display.
@VisibleForTesting
static final int GLOBAL_ID = -1;
private boolean mLoggingEnabled;
private final Listener mListener;
private final Object mStorageLock = new Object();
// A map from the display ID to the collection of votes and their priority. The latter takes
// the form of another map from the priority to the vote itself so that each priority is
// guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
@GuardedBy("mStorageLock")
private final SparseArray<SparseArray<Vote>> mVotesByDisplay = new SparseArray<>();
VotesStorage(@NonNull Listener listener) {
mListener = listener;
}
/** sets logging enabled/disabled for this class */
void setLoggingEnabled(boolean loggingEnabled) {
mLoggingEnabled = loggingEnabled;
}
/**
* gets all votes for specific display, note that global display votes are also added to result
*/
@NonNull
SparseArray<Vote> getVotes(int displayId) {
SparseArray<Vote> votesLocal;
SparseArray<Vote> globalVotesLocal;
synchronized (mStorageLock) {
SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
votesLocal = displayVotes != null ? displayVotes.clone() : new SparseArray<>();
SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
globalVotesLocal = globalVotes != null ? globalVotes.clone() : new SparseArray<>();
}
for (int i = 0; i < globalVotesLocal.size(); i++) {
int priority = globalVotesLocal.keyAt(i);
if (!votesLocal.contains(priority)) {
votesLocal.put(priority, globalVotesLocal.valueAt(i));
}
}
return votesLocal;
}
/** updates vote storage for all displays */
void updateGlobalVote(int priority, @Nullable Vote vote) {
updateVote(GLOBAL_ID, priority, vote);
}
/** updates vote storage */
void updateVote(int displayId, int priority, @Nullable Vote vote) {
if (mLoggingEnabled) {
Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
+ ", priority=" + Vote.priorityToString(priority)
+ ", vote=" + vote + ")");
}
if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
+ " priority=" + Vote.priorityToString(priority)
+ ", vote=" + vote);
return;
}
boolean changed = false;
SparseArray<Vote> votes;
synchronized (mStorageLock) {
if (mVotesByDisplay.contains(displayId)) {
votes = mVotesByDisplay.get(displayId);
} else {
votes = new SparseArray<>();
mVotesByDisplay.put(displayId, votes);
}
var currentVote = votes.get(priority);
if (vote != null && !vote.equals(currentVote)) {
votes.put(priority, vote);
changed = true;
} else if (vote == null && currentVote != null) {
votes.remove(priority);
changed = true;
}
}
if (mLoggingEnabled) {
Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes);
}
if (changed) {
mListener.onChanged();
}
}
/** dump class values, for debugging */
void dump(@NonNull PrintWriter pw) {
SparseArray<SparseArray<Vote>> votesByDisplayLocal = new SparseArray<>();
synchronized (mStorageLock) {
for (int i = 0; i < mVotesByDisplay.size(); i++) {
votesByDisplayLocal.put(mVotesByDisplay.keyAt(i),
mVotesByDisplay.valueAt(i).clone());
}
}
pw.println(" mVotesByDisplay:");
for (int i = 0; i < votesByDisplayLocal.size(); i++) {
SparseArray<Vote> votes = votesByDisplayLocal.valueAt(i);
if (votes.size() == 0) {
continue;
}
pw.println(" " + votesByDisplayLocal.keyAt(i) + ":");
for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
Vote vote = votes.get(p);
if (vote == null) {
continue;
}
pw.println(" " + Vote.priorityToString(p) + " -> " + vote);
}
}
}
@VisibleForTesting
void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) {
synchronized (mStorageLock) {
mVotesByDisplay.clear();
for (int i = 0; i < votesByDisplay.size(); i++) {
mVotesByDisplay.put(votesByDisplay.keyAt(i), votesByDisplay.valueAt(i));
}
}
}
interface Listener {
void onChanged();
}
}