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