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