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