blob: 5f5ac3085e904b9b91a5921d721f0da845cab7ef [file] [log] [blame]
/*
* Copyright (C) 2017 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.car;
import android.car.annotation.FutureFeature;
import android.car.vms.VmsLayer;
import android.car.vms.VmsLayerDependency;
import android.car.vms.VmsLayersOffering;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Manages VMS availability for layers.
*
* Each VMS publisher sets its layers offering which are a list of layers the publisher claims
* it might publish. VmsLayersAvailability calculates from all the offering what are the
* available layers.
*/
@FutureFeature
public class VmsLayersAvailability {
private static final boolean DBG = true;
private static final String TAG = "VmsLayersAvailability";
private final Object mLock = new Object();
@GuardedBy("mLock")
private final Map<VmsLayer, Set<Set<VmsLayer>>> mPotentialLayersAndDependencies =
new HashMap<>();
@GuardedBy("mLock")
private final Set<VmsLayer> mCyclicAvoidanceSet = new HashSet<>();
@GuardedBy("mLock")
private Set<VmsLayer> mAvailableLayers = Collections.EMPTY_SET;
@GuardedBy("mLock")
private Set<VmsLayer> mUnavailableLayers = Collections.EMPTY_SET;
/**
* Setting the current layers offerings as reported by publishers.
*/
public void setPublishersOffering(Collection<VmsLayersOffering> publishersLayersOfferings) {
synchronized (mLock) {
reset();
for (VmsLayersOffering offering : publishersLayersOfferings) {
for (VmsLayerDependency dependency : offering.getDependencies()) {
VmsLayer layer = dependency.getLayer();
Set<Set<VmsLayer>> curDependencies =
mPotentialLayersAndDependencies.get(layer);
if (curDependencies == null) {
curDependencies = new HashSet<>();
mPotentialLayersAndDependencies.put(layer, curDependencies);
}
curDependencies.add(dependency.getDependencies());
}
}
calculateLayers();
}
}
/**
* Returns a collection of all the layers which may be published.
*/
public Collection<VmsLayer> getAvailableLayers() {
synchronized (mLock) {
return mAvailableLayers;
}
}
/**
* Returns a collection of all the layers which publishers could have published if the
* dependencies were satisfied.
*/
public Collection<VmsLayer> getUnavailableLayers() {
synchronized (mLock) {
return mUnavailableLayers;
}
}
private void reset() {
synchronized (mLock) {
mCyclicAvoidanceSet.clear();
mPotentialLayersAndDependencies.clear();
mAvailableLayers = Collections.EMPTY_SET;
mUnavailableLayers = Collections.EMPTY_SET;
}
}
private void calculateLayers() {
synchronized (mLock) {
final Set<VmsLayer> availableLayers = new HashSet<>();
availableLayers.addAll(
mPotentialLayersAndDependencies.keySet()
.stream()
.filter(layer -> isLayerSupportedLocked(layer, availableLayers))
.collect(Collectors.toSet()));
mAvailableLayers = Collections.unmodifiableSet(availableLayers);
mUnavailableLayers = Collections.unmodifiableSet(
mPotentialLayersAndDependencies.keySet()
.stream()
.filter(layer -> !availableLayers.contains(layer))
.collect(Collectors.toSet()));
}
}
private boolean isLayerSupportedLocked(VmsLayer layer, Set<VmsLayer> currentAvailableLayers) {
if (DBG) {
Log.d(TAG, "isLayerSupported: checking layer: " + layer);
}
// If we already know that this layer is supported then we are done.
if (currentAvailableLayers.contains(layer)) {
return true;
}
// If there is no offering for this layer we're done.
if (!mPotentialLayersAndDependencies.containsKey(layer)) {
return false;
}
// Avoid cyclic dependency.
if (mCyclicAvoidanceSet.contains(layer)) {
Log.e(TAG, "Detected a cyclic dependency: " + mCyclicAvoidanceSet + " -> " + layer);
return false;
}
for (Set<VmsLayer> dependencies : mPotentialLayersAndDependencies.get(layer)) {
// If layer does not have any dependencies then add to supported.
if (dependencies == null || dependencies.isEmpty()) {
currentAvailableLayers.add(layer);
return true;
}
// Add the layer to cyclic avoidance set
mCyclicAvoidanceSet.add(layer);
boolean isSupported = true;
for (VmsLayer dependency : dependencies) {
if (!isLayerSupportedLocked(dependency, currentAvailableLayers)) {
isSupported = false;
break;
}
}
mCyclicAvoidanceSet.remove(layer);
if (isSupported) {
currentAvailableLayers.add(layer);
return true;
}
}
return false;
}
}