1 /* 2 * Copyright (C) 2020 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.car.vms; 18 19 import android.car.vms.VmsAssociatedLayer; 20 import android.car.vms.VmsAvailableLayers; 21 import android.car.vms.VmsLayer; 22 import android.car.vms.VmsLayerDependency; 23 import android.car.vms.VmsLayersOffering; 24 import android.util.Slog; 25 26 import com.android.car.CarLog; 27 import com.android.internal.annotations.GuardedBy; 28 29 import java.util.Collection; 30 import java.util.Collections; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.Map; 34 import java.util.Set; 35 import java.util.stream.Collectors; 36 37 /** 38 * Manages VMS availability for layers. 39 * 40 * Each VMS publisher sets its layers offering which are a list of layers the publisher claims 41 * it might publish. VmsLayersAvailability calculates from all the offering what are the 42 * available layers. 43 */ 44 45 class VmsLayerAvailability { 46 private static final boolean DBG = false; 47 private static final String TAG = CarLog.tagFor(VmsLayerAvailability.class); 48 49 private final Object mLock = new Object(); 50 @GuardedBy("mLock") 51 private final Map<VmsLayer, Set<Set<VmsLayer>>> mPotentialLayersAndDependencies = 52 new HashMap<>(); 53 @GuardedBy("mLock") 54 private Set<VmsAssociatedLayer> mAvailableAssociatedLayers = Collections.emptySet(); 55 @GuardedBy("mLock") 56 private Map<VmsLayer, Set<Integer>> mPotentialLayersAndPublishers = new HashMap<>(); 57 @GuardedBy("mLock") 58 private int mSeq = 0; 59 60 /** 61 * Setting the current layers offerings as reported by publishers. 62 */ setPublishersOffering(Collection<VmsLayersOffering> publishersLayersOfferings)63 void setPublishersOffering(Collection<VmsLayersOffering> publishersLayersOfferings) { 64 synchronized (mLock) { 65 reset(); 66 67 for (VmsLayersOffering offering : publishersLayersOfferings) { 68 for (VmsLayerDependency dependency : offering.getDependencies()) { 69 VmsLayer layer = dependency.getLayer(); 70 71 // Associate publishers with layers. 72 mPotentialLayersAndPublishers.computeIfAbsent(layer, k -> new HashSet<>()) 73 .add(offering.getPublisherId()); 74 75 // Add dependencies for availability calculation. 76 mPotentialLayersAndDependencies.computeIfAbsent(layer, k -> new HashSet<>()) 77 .add(dependency.getDependencies()); 78 } 79 } 80 calculateLayers(); 81 } 82 } 83 84 /** 85 * Returns a collection of all the layers which may be published. 86 */ getAvailableLayers()87 VmsAvailableLayers getAvailableLayers() { 88 synchronized (mLock) { 89 return new VmsAvailableLayers(mAvailableAssociatedLayers, mSeq); 90 } 91 } 92 reset()93 private void reset() { 94 synchronized (mLock) { 95 mPotentialLayersAndDependencies.clear(); 96 mPotentialLayersAndPublishers.clear(); 97 mAvailableAssociatedLayers = Collections.emptySet(); 98 mSeq += 1; 99 } 100 } 101 calculateLayers()102 private void calculateLayers() { 103 synchronized (mLock) { 104 Set<VmsLayer> availableLayersSet = new HashSet<>(); 105 Set<VmsLayer> cyclicAvoidanceAuxiliarySet = new HashSet<>(); 106 107 for (VmsLayer layer : mPotentialLayersAndDependencies.keySet()) { 108 addLayerToAvailabilityCalculationLocked(layer, 109 availableLayersSet, 110 cyclicAvoidanceAuxiliarySet); 111 } 112 113 mAvailableAssociatedLayers = Collections.unmodifiableSet( 114 availableLayersSet 115 .stream() 116 .map(l -> new VmsAssociatedLayer(l, 117 mPotentialLayersAndPublishers.get(l))) 118 .collect(Collectors.toSet())); 119 } 120 } 121 122 @GuardedBy("mLock") addLayerToAvailabilityCalculationLocked(VmsLayer layer, Set<VmsLayer> currentAvailableLayers, Set<VmsLayer> cyclicAvoidanceSet)123 private void addLayerToAvailabilityCalculationLocked(VmsLayer layer, 124 Set<VmsLayer> currentAvailableLayers, 125 Set<VmsLayer> cyclicAvoidanceSet) { 126 if (DBG) { 127 Slog.d(TAG, "addLayerToAvailabilityCalculationLocked: checking layer: " + layer); 128 } 129 // If we already know that this layer is supported then we are done. 130 if (currentAvailableLayers.contains(layer)) { 131 return; 132 } 133 // If there is no offering for this layer we're done. 134 if (!mPotentialLayersAndDependencies.containsKey(layer)) { 135 return; 136 } 137 // Avoid cyclic dependency. 138 if (cyclicAvoidanceSet.contains(layer)) { 139 Slog.e(TAG, "Detected a cyclic dependency: " + cyclicAvoidanceSet + " -> " + layer); 140 return; 141 } 142 // A layer may have multiple dependency sets. The layer is available if any dependency 143 // set is satisfied 144 for (Set<VmsLayer> dependencies : mPotentialLayersAndDependencies.get(layer)) { 145 // If layer does not have any dependencies then add to supported. 146 if (dependencies == null || dependencies.isEmpty()) { 147 currentAvailableLayers.add(layer); 148 return; 149 } 150 // Add the layer to cyclic avoidance set 151 cyclicAvoidanceSet.add(layer); 152 153 boolean isSupported = true; 154 for (VmsLayer dependency : dependencies) { 155 addLayerToAvailabilityCalculationLocked(dependency, 156 currentAvailableLayers, 157 cyclicAvoidanceSet); 158 159 if (!currentAvailableLayers.contains(dependency)) { 160 isSupported = false; 161 break; 162 } 163 } 164 cyclicAvoidanceSet.remove(layer); 165 166 if (isSupported) { 167 currentAvailableLayers.add(layer); 168 return; 169 } 170 } 171 } 172 } 173