1 /* 2 * Copyright (C) 2017 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; 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.Log; 25 import com.android.internal.annotations.GuardedBy; 26 27 import java.util.ArrayList; 28 import java.util.Collection; 29 import java.util.Collections; 30 import java.util.HashMap; 31 import java.util.HashSet; 32 import java.util.List; 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 * <p> 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 * @hide 45 */ 46 47 public class VmsLayersAvailability { 48 49 private static final boolean DBG = true; 50 private static final String TAG = "VmsLayersAvailability"; 51 52 private final Object mLock = new Object(); 53 @GuardedBy("mLock") 54 private final Map<VmsLayer, Set<Set<VmsLayer>>> mPotentialLayersAndDependencies = 55 new HashMap<>(); 56 @GuardedBy("mLock") 57 private Set<VmsAssociatedLayer> mAvailableAssociatedLayers = Collections.EMPTY_SET; 58 @GuardedBy("mLock") 59 private Set<VmsAssociatedLayer> mUnavailableAssociatedLayers = Collections.EMPTY_SET; 60 @GuardedBy("mLock") 61 private Map<VmsLayer, Set<Integer>> mPotentialLayersAndPublishers = new HashMap<>(); 62 @GuardedBy("mLock") 63 private int mSeq = 0; 64 65 /** 66 * Setting the current layers offerings as reported by publishers. 67 */ setPublishersOffering(Collection<VmsLayersOffering> publishersLayersOfferings)68 public void setPublishersOffering(Collection<VmsLayersOffering> publishersLayersOfferings) { 69 synchronized (mLock) { 70 reset(); 71 72 for (VmsLayersOffering offering : publishersLayersOfferings) { 73 for (VmsLayerDependency dependency : offering.getDependencies()) { 74 VmsLayer layer = dependency.getLayer(); 75 76 // Associate publishers with layers. 77 Set<Integer> curPotentialLayerAndPublishers = 78 mPotentialLayersAndPublishers.get(layer); 79 if (curPotentialLayerAndPublishers == null) { 80 curPotentialLayerAndPublishers = new HashSet<>(); 81 mPotentialLayersAndPublishers.put(layer, curPotentialLayerAndPublishers); 82 } 83 curPotentialLayerAndPublishers.add(offering.getPublisherId()); 84 85 // Add dependencies for availability calculation. 86 Set<Set<VmsLayer>> curDependencies = 87 mPotentialLayersAndDependencies.get(layer); 88 if (curDependencies == null) { 89 curDependencies = new HashSet<>(); 90 mPotentialLayersAndDependencies.put(layer, curDependencies); 91 } 92 curDependencies.add(dependency.getDependencies()); 93 } 94 } 95 calculateLayers(); 96 } 97 } 98 99 /** 100 * Returns a collection of all the layers which may be published. 101 */ getAvailableLayers()102 public VmsAvailableLayers getAvailableLayers() { 103 synchronized (mLock) { 104 return new VmsAvailableLayers(mAvailableAssociatedLayers, mSeq); 105 } 106 } 107 reset()108 private void reset() { 109 synchronized (mLock) { 110 mPotentialLayersAndDependencies.clear(); 111 mPotentialLayersAndPublishers.clear(); 112 mAvailableAssociatedLayers = Collections.EMPTY_SET; 113 mUnavailableAssociatedLayers = Collections.EMPTY_SET; 114 if (mSeq + 1 < mSeq) { 115 throw new IllegalStateException("Sequence is about to loop"); 116 } 117 mSeq += 1; 118 } 119 } 120 calculateLayers()121 private void calculateLayers() { 122 synchronized (mLock) { 123 Set<VmsLayer> availableLayersSet = new HashSet<>(); 124 Set<VmsLayer> cyclicAvoidanceAuxiliarySet = new HashSet<>(); 125 126 for (VmsLayer layer : mPotentialLayersAndDependencies.keySet()) { 127 addLayerToAvailabilityCalculationLocked(layer, 128 availableLayersSet, 129 cyclicAvoidanceAuxiliarySet); 130 } 131 132 mAvailableAssociatedLayers = Collections.unmodifiableSet( 133 availableLayersSet 134 .stream() 135 .map(l -> new VmsAssociatedLayer(l, mPotentialLayersAndPublishers.get(l))) 136 .collect(Collectors.toSet())); 137 138 mUnavailableAssociatedLayers = Collections.unmodifiableSet( 139 mPotentialLayersAndDependencies.keySet() 140 .stream() 141 .filter(l -> !availableLayersSet.contains(l)) 142 .map(l -> new VmsAssociatedLayer(l, mPotentialLayersAndPublishers.get(l))) 143 .collect(Collectors.toSet())); 144 } 145 } 146 147 @GuardedBy("mLock") addLayerToAvailabilityCalculationLocked(VmsLayer layer, Set<VmsLayer> currentAvailableLayers, Set<VmsLayer> cyclicAvoidanceSet)148 private void addLayerToAvailabilityCalculationLocked(VmsLayer layer, 149 Set<VmsLayer> currentAvailableLayers, 150 Set<VmsLayer> cyclicAvoidanceSet) { 151 if (DBG) { 152 Log.d(TAG, "addLayerToAvailabilityCalculationLocked: checking layer: " + layer); 153 } 154 // If we already know that this layer is supported then we are done. 155 if (currentAvailableLayers.contains(layer)) { 156 return; 157 } 158 // If there is no offering for this layer we're done. 159 if (!mPotentialLayersAndDependencies.containsKey(layer)) { 160 return; 161 } 162 // Avoid cyclic dependency. 163 if (cyclicAvoidanceSet.contains(layer)) { 164 Log.e(TAG, "Detected a cyclic dependency: " + cyclicAvoidanceSet + " -> " + layer); 165 return; 166 } 167 // A layer may have multiple dependency sets. The layer is available if any dependency 168 // set is satisfied 169 for (Set<VmsLayer> dependencies : mPotentialLayersAndDependencies.get(layer)) { 170 // If layer does not have any dependencies then add to supported. 171 if (dependencies == null || dependencies.isEmpty()) { 172 currentAvailableLayers.add(layer); 173 return; 174 } 175 // Add the layer to cyclic avoidance set 176 cyclicAvoidanceSet.add(layer); 177 178 boolean isSupported = true; 179 for (VmsLayer dependency : dependencies) { 180 addLayerToAvailabilityCalculationLocked(dependency, 181 currentAvailableLayers, 182 cyclicAvoidanceSet); 183 184 if (!currentAvailableLayers.contains(dependency)) { 185 isSupported = false; 186 break; 187 } 188 } 189 cyclicAvoidanceSet.remove(layer); 190 191 if (isSupported) { 192 currentAvailableLayers.add(layer); 193 return; 194 } 195 } 196 return; 197 } 198 } 199