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.builtin.util.Slogf;
20 import android.car.vms.VmsAssociatedLayer;
21 import android.car.vms.VmsAvailableLayers;
22 import android.car.vms.VmsLayer;
23 import android.car.vms.VmsLayerDependency;
24 import android.car.vms.VmsLayersOffering;
25 import android.util.ArrayMap;
26 import android.util.ArraySet;
27 import android.util.Log;
28 
29 import com.android.car.CarLog;
30 import com.android.internal.annotations.GuardedBy;
31 
32 import java.util.Collection;
33 import java.util.Collections;
34 import java.util.Map;
35 import java.util.Set;
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 String TAG = CarLog.tagFor(VmsLayerAvailability.class);
47     private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG);
48 
49     private final Object mLock = new Object();
50     @GuardedBy("mLock")
51     private final Map<VmsLayer, Set<Set<VmsLayer>>> mPotentialLayersAndDependencies =
52             new ArrayMap<>();
53     @GuardedBy("mLock")
54     private Set<VmsAssociatedLayer> mAvailableAssociatedLayers = Collections.emptySet();
55     @GuardedBy("mLock")
56     private Map<VmsLayer, Set<Integer>> mPotentialLayersAndPublishers = new ArrayMap<>();
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 ArraySet<>())
73                             .add(offering.getPublisherId());
74 
75                     // Add dependencies for availability calculation.
76                     mPotentialLayersAndDependencies.computeIfAbsent(layer, k -> new ArraySet<>())
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             ArraySet<VmsLayer> availableLayersSet = new ArraySet<>();
105             ArraySet<VmsLayer> cyclicAvoidanceAuxiliarySet = new ArraySet<>();
106 
107             for (VmsLayer layer : mPotentialLayersAndDependencies.keySet()) {
108                 addLayerToAvailabilityCalculationLocked(layer,
109                         availableLayersSet,
110                         cyclicAvoidanceAuxiliarySet);
111             }
112 
113             ArraySet<VmsAssociatedLayer> associatedLayerSet = new ArraySet<>();
114             for (int index = 0; index < availableLayersSet.size(); index++) {
115                 VmsLayer availableLayer = availableLayersSet.valueAt(index);
116                 VmsAssociatedLayer associatedLayer = new VmsAssociatedLayer(availableLayer,
117                         mPotentialLayersAndPublishers.get(availableLayer));
118                 associatedLayerSet.add(associatedLayer);
119             }
120             mAvailableAssociatedLayers = Collections.unmodifiableSet(associatedLayerSet);
121         }
122     }
123 
124     @GuardedBy("mLock")
addLayerToAvailabilityCalculationLocked(VmsLayer layer, Set<VmsLayer> currentAvailableLayers, Set<VmsLayer> cyclicAvoidanceSet)125     private void addLayerToAvailabilityCalculationLocked(VmsLayer layer,
126                                                          Set<VmsLayer> currentAvailableLayers,
127                                                          Set<VmsLayer> cyclicAvoidanceSet) {
128         if (DBG) {
129             Slogf.d(TAG, "addLayerToAvailabilityCalculationLocked: checking layer: " + layer);
130         }
131         // If we already know that this layer is supported then we are done.
132         if (currentAvailableLayers.contains(layer)) {
133             return;
134         }
135         // If there is no offering for this layer we're done.
136         if (!mPotentialLayersAndDependencies.containsKey(layer)) {
137             return;
138         }
139         // Avoid cyclic dependency.
140         if (cyclicAvoidanceSet.contains(layer)) {
141             Slogf.e(TAG, "Detected a cyclic dependency: " + cyclicAvoidanceSet + " -> " + layer);
142             return;
143         }
144         // A layer may have multiple dependency sets. The layer is available if any dependency
145         // set is satisfied
146         for (Set<VmsLayer> dependencies : mPotentialLayersAndDependencies.get(layer)) {
147             // If layer does not have any dependencies then add to supported.
148             if (dependencies == null || dependencies.isEmpty()) {
149                 currentAvailableLayers.add(layer);
150                 return;
151             }
152             // Add the layer to cyclic avoidance set
153             cyclicAvoidanceSet.add(layer);
154 
155             boolean isSupported = true;
156             for (VmsLayer dependency : dependencies) {
157                 addLayerToAvailabilityCalculationLocked(dependency,
158                         currentAvailableLayers,
159                         cyclicAvoidanceSet);
160 
161                 if (!currentAvailableLayers.contains(dependency)) {
162                     isSupported = false;
163                     break;
164                 }
165             }
166             cyclicAvoidanceSet.remove(layer);
167 
168             if (isSupported) {
169                 currentAvailableLayers.add(layer);
170                 return;
171             }
172         }
173     }
174 }
175