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.annotation.FutureFeature;
20 import android.car.vms.VmsLayer;
21 import android.car.vms.VmsLayerDependency;
22 import android.car.vms.VmsLayersOffering;
23 import android.util.Log;
24 import com.android.internal.annotations.GuardedBy;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.stream.Collectors;
34 
35 /**
36  * Manages VMS availability for layers.
37  *
38  * Each VMS publisher sets its layers offering which are a list of layers the publisher claims
39  * it might publish. VmsLayersAvailability calculates from all the offering what are the
40  * available layers.
41  */
42 
43 @FutureFeature
44 public class VmsLayersAvailability {
45 
46     private static final boolean DBG = true;
47     private static final String TAG = "VmsLayersAvailability";
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 final Set<VmsLayer> mCyclicAvoidanceSet = new HashSet<>();
55     @GuardedBy("mLock")
56     private Set<VmsLayer> mAvailableLayers = Collections.EMPTY_SET;
57     @GuardedBy("mLock")
58     private Set<VmsLayer> mUnavailableLayers = Collections.EMPTY_SET;
59 
60     /**
61      * Setting the current layers offerings as reported by publishers.
62      */
setPublishersOffering(Collection<VmsLayersOffering> publishersLayersOfferings)63     public 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                     Set<Set<VmsLayer>> curDependencies =
71                         mPotentialLayersAndDependencies.get(layer);
72                     if (curDependencies == null) {
73                         curDependencies = new HashSet<>();
74                         mPotentialLayersAndDependencies.put(layer, curDependencies);
75                     }
76                     curDependencies.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     public Collection<VmsLayer> getAvailableLayers() {
87         synchronized (mLock) {
88             return mAvailableLayers;
89         }
90     }
91 
92     /**
93      * Returns a collection of all the layers which publishers could have published if the
94      * dependencies were satisfied.
95      */
getUnavailableLayers()96     public Collection<VmsLayer> getUnavailableLayers() {
97         synchronized (mLock) {
98             return mUnavailableLayers;
99         }
100     }
101 
reset()102     private void reset() {
103         synchronized (mLock) {
104             mCyclicAvoidanceSet.clear();
105             mPotentialLayersAndDependencies.clear();
106             mAvailableLayers = Collections.EMPTY_SET;
107             mUnavailableLayers = Collections.EMPTY_SET;
108         }
109     }
110 
calculateLayers()111     private void calculateLayers() {
112         synchronized (mLock) {
113             final Set<VmsLayer> availableLayers = new HashSet<>();
114 
115             availableLayers.addAll(
116                 mPotentialLayersAndDependencies.keySet()
117                     .stream()
118                     .filter(layer -> isLayerSupportedLocked(layer, availableLayers))
119                     .collect(Collectors.toSet()));
120 
121             mAvailableLayers = Collections.unmodifiableSet(availableLayers);
122             mUnavailableLayers = Collections.unmodifiableSet(
123                 mPotentialLayersAndDependencies.keySet()
124                     .stream()
125                     .filter(layer -> !availableLayers.contains(layer))
126                     .collect(Collectors.toSet()));
127         }
128     }
129 
isLayerSupportedLocked(VmsLayer layer, Set<VmsLayer> currentAvailableLayers)130     private boolean isLayerSupportedLocked(VmsLayer layer, Set<VmsLayer> currentAvailableLayers) {
131         if (DBG) {
132             Log.d(TAG, "isLayerSupported: checking layer: " + layer);
133         }
134         // If we already know that this layer is supported then we are done.
135         if (currentAvailableLayers.contains(layer)) {
136             return true;
137         }
138         // If there is no offering for this layer we're done.
139         if (!mPotentialLayersAndDependencies.containsKey(layer)) {
140             return false;
141         }
142         // Avoid cyclic dependency.
143         if (mCyclicAvoidanceSet.contains(layer)) {
144             Log.e(TAG, "Detected a cyclic dependency: " + mCyclicAvoidanceSet + " -> " + layer);
145             return false;
146         }
147         for (Set<VmsLayer> dependencies : mPotentialLayersAndDependencies.get(layer)) {
148             // If layer does not have any dependencies then add to supported.
149             if (dependencies == null || dependencies.isEmpty()) {
150                 currentAvailableLayers.add(layer);
151                 return true;
152             }
153             // Add the layer to cyclic avoidance set
154             mCyclicAvoidanceSet.add(layer);
155 
156             boolean isSupported = true;
157             for (VmsLayer dependency : dependencies) {
158                 if (!isLayerSupportedLocked(dependency, currentAvailableLayers)) {
159                     isSupported = false;
160                     break;
161                 }
162             }
163             mCyclicAvoidanceSet.remove(layer);
164 
165             if (isSupported) {
166                 currentAvailableLayers.add(layer);
167                 return true;
168             }
169         }
170         return false;
171     }
172 }
173