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.IVmsClientCallback;
20 import android.car.vms.VmsAssociatedLayer;
21 import android.car.vms.VmsLayer;
22 import android.car.vms.VmsLayerDependency;
23 import android.car.vms.VmsLayersOffering;
24 import android.os.IBinder;
25 import android.util.ArraySet;
26 import android.util.SparseArray;
27 import android.util.SparseBooleanArray;
28 
29 import com.android.internal.annotations.GuardedBy;
30 
31 import java.io.PrintWriter;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.stream.Collectors;
39 
40 /**
41  * Class for tracking Vehicle Map Service client information, offerings, and subscriptions.
42  */
43 final class VmsClientInfo {
44     private final int mUid;
45     private final String mPackageName;
46     private final IVmsClientCallback mCallback;
47     private final boolean mLegacyClient;
48     private final IBinder.DeathRecipient mDeathRecipient;
49 
50     private final Object mLock = new Object();
51     @GuardedBy("mLock")
52     private final SparseBooleanArray mProviderIds = new SparseBooleanArray();
53     @GuardedBy("mLock")
54     private final SparseArray<Set<VmsLayerDependency>> mOfferings = new SparseArray<>();
55     @GuardedBy("mLock")
56     private final SparseArray<Set<VmsLayer>> mPotentialOfferings = new SparseArray<>();
57     @GuardedBy("mLock")
58     private Set<VmsLayer> mLayerSubscriptions = Collections.emptySet();
59     @GuardedBy("mLock")
60     private Map<VmsLayer, Set<Integer>> mLayerAndProviderSubscriptions = Collections.emptyMap();
61     @GuardedBy("mLock")
62     private boolean mMonitoringEnabled;
63 
VmsClientInfo(int uid, String packageName, IVmsClientCallback callback, boolean legacyClient, IBinder.DeathRecipient deathRecipient)64     VmsClientInfo(int uid, String packageName, IVmsClientCallback callback, boolean legacyClient,
65             IBinder.DeathRecipient deathRecipient) {
66         mUid = uid;
67         mPackageName = packageName;
68         mCallback = callback;
69         mLegacyClient = legacyClient;
70         mDeathRecipient = deathRecipient;
71     }
72 
getUid()73     int getUid() {
74         return mUid;
75     }
76 
getPackageName()77     String getPackageName() {
78         return mPackageName;
79     }
80 
getCallback()81     IVmsClientCallback getCallback() {
82         return mCallback;
83     }
84 
isLegacyClient()85     boolean isLegacyClient() {
86         return mLegacyClient;
87     }
88 
getDeathRecipient()89     IBinder.DeathRecipient getDeathRecipient() {
90         return mDeathRecipient;
91     }
92 
addProviderId(int providerId)93     void addProviderId(int providerId) {
94         synchronized (mLock) {
95             mProviderIds.put(providerId, true);
96         }
97     }
98 
hasProviderId(int providerId)99     boolean hasProviderId(int providerId) {
100         synchronized (mLock) {
101             return mProviderIds.get(providerId);
102         }
103     }
104 
setProviderOfferings(int providerId, Collection<VmsLayerDependency> offerings)105     boolean setProviderOfferings(int providerId, Collection<VmsLayerDependency> offerings) {
106         synchronized (mLock) {
107             Set<VmsLayerDependency> providerOfferings = mOfferings.get(providerId);
108 
109             // If the offerings are unchanged, do nothing
110             if (providerOfferings != null
111                     && providerOfferings.size() == offerings.size()
112                     && providerOfferings.containsAll(offerings)) {
113                 return false;
114             }
115 
116             // Otherwise, update the offerings and return true
117             mOfferings.put(providerId, new ArraySet<>(offerings));
118             mPotentialOfferings.put(providerId, offerings.stream()
119                     .map(VmsLayerDependency::getLayer)
120                     .collect(Collectors.toSet()));
121             return true;
122         }
123     }
124 
getAllOfferings()125     Collection<VmsLayersOffering> getAllOfferings() {
126         List<VmsLayersOffering> result = new ArrayList<>(mOfferings.size());
127         synchronized (mLock) {
128             for (int i = 0; i < mOfferings.size(); i++) {
129                 int providerId = mOfferings.keyAt(i);
130                 Set<VmsLayerDependency> providerOfferings = mOfferings.valueAt(i);
131                 result.add(new VmsLayersOffering(new ArraySet<>(providerOfferings), providerId));
132             }
133         }
134         return result;
135     }
136 
hasOffering(int providerId, VmsLayer layer)137     boolean hasOffering(int providerId, VmsLayer layer) {
138         synchronized (mLock) {
139             return mPotentialOfferings.get(providerId, Collections.emptySet()).contains(layer);
140         }
141     }
142 
setSubscriptions(List<VmsAssociatedLayer> layers)143     void setSubscriptions(List<VmsAssociatedLayer> layers) {
144         synchronized (mLock) {
145             mLayerSubscriptions = layers.stream()
146                     .filter(associatedLayer -> associatedLayer.getProviderIds().isEmpty())
147                     .map(VmsAssociatedLayer::getVmsLayer)
148                     .collect(Collectors.toSet());
149             mLayerAndProviderSubscriptions = layers.stream()
150                     .filter(associatedLayer -> !associatedLayer.getProviderIds().isEmpty())
151                     .collect(Collectors.toMap(
152                             VmsAssociatedLayer::getVmsLayer,
153                             associatedLayer -> new ArraySet<>(associatedLayer.getProviderIds())));
154         }
155     }
156 
getLayerSubscriptions()157     Set<VmsLayer> getLayerSubscriptions() {
158         synchronized (mLock) {
159             return new ArraySet<>(mLayerSubscriptions);
160         }
161     }
162 
getLayerAndProviderSubscriptions()163     Map<VmsLayer, Set<Integer>> getLayerAndProviderSubscriptions() {
164         synchronized (mLock) {
165             return deepCopy(mLayerAndProviderSubscriptions);
166         }
167     }
168 
setMonitoringEnabled(boolean enabled)169     void setMonitoringEnabled(boolean enabled) {
170         synchronized (mLock) {
171             mMonitoringEnabled = enabled;
172         }
173     }
174 
isSubscribed(int providerId, VmsLayer layer)175     boolean isSubscribed(int providerId, VmsLayer layer) {
176         synchronized (mLock) {
177             return mMonitoringEnabled
178                     || mLayerSubscriptions.contains(layer)
179                     || mLayerAndProviderSubscriptions.getOrDefault(layer, Collections.emptySet())
180                             .contains(providerId);
181         }
182     }
183 
dump(PrintWriter writer, String indent)184     void dump(PrintWriter writer, String indent) {
185         synchronized (mLock) {
186             String prefix = indent;
187             writer.println(prefix + "VmsClient [" + mPackageName + "]");
188 
189             prefix = indent + "  ";
190             writer.println(prefix + "UID: " + mUid);
191             writer.println(prefix + "Legacy Client: " + mLegacyClient);
192             writer.println(prefix + "Monitoring: " + mMonitoringEnabled);
193 
194             if (mProviderIds.size() > 0) {
195                 writer.println(prefix + "Offerings:");
196                 for (int i = 0; i < mProviderIds.size(); i++) {
197                     prefix = indent + "    ";
198                     int providerId = mProviderIds.keyAt(i);
199                     writer.println(prefix + "Provider [" + providerId + "]");
200 
201                     for (VmsLayerDependency layerOffering : mOfferings.get(
202                             providerId, Collections.emptySet())) {
203                         prefix = indent + "      ";
204                         writer.println(prefix + layerOffering.getLayer());
205                         if (!layerOffering.getDependencies().isEmpty()) {
206                             prefix = indent + "        ";
207                             writer.println(prefix + "Dependencies: "
208                                     + layerOffering.getDependencies());
209                         }
210                     }
211                 }
212             }
213 
214             if (!mLayerSubscriptions.isEmpty() || !mLayerAndProviderSubscriptions.isEmpty()) {
215                 prefix = indent + "  ";
216                 writer.println(prefix + "Subscriptions:");
217 
218                 prefix = indent + "    ";
219                 for (VmsLayer layer : mLayerSubscriptions) {
220                     writer.println(prefix + layer);
221                 }
222                 for (Map.Entry<VmsLayer, Set<Integer>> layerEntry :
223                         mLayerAndProviderSubscriptions.entrySet()) {
224                     writer.println(prefix + layerEntry.getKey() + ": " + layerEntry.getValue());
225                 }
226             }
227         }
228     }
229 
deepCopy(Map<K, Set<V>> original)230     private static <K, V> Map<K, Set<V>> deepCopy(Map<K, Set<V>> original) {
231         return original.entrySet().stream().collect(Collectors.toMap(
232                 Map.Entry::getKey,
233                 entry -> new ArraySet<>(entry.getValue())));
234     }
235 }
236