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 android.car.vms; 18 19 import android.annotation.NonNull; 20 import android.util.ArrayMap; 21 import android.util.ArraySet; 22 import android.util.SparseBooleanArray; 23 24 import com.android.internal.annotations.GuardedBy; 25 26 import java.util.Collections; 27 import java.util.Map; 28 import java.util.Objects; 29 import java.util.Set; 30 import java.util.function.Consumer; 31 import java.util.stream.Collectors; 32 import java.util.stream.Stream; 33 34 /** 35 * Internal utility for computing subscription updates. 36 * 37 * @hide 38 */ 39 public final class VmsSubscriptionHelper { 40 private final Consumer<Set<VmsAssociatedLayer>> mUpdateHandler; 41 42 private final Object mLock = new Object(); 43 44 @GuardedBy("mLock") 45 private final Set<VmsLayer> mLayerSubscriptions = new ArraySet<>(); 46 47 @GuardedBy("mLock") 48 private final Map<VmsLayer, SparseBooleanArray> mPublisherSubscriptions = new ArrayMap<>(); 49 50 @GuardedBy("mLock") 51 private boolean mPendingUpdate; 52 53 /** 54 * Constructor for subscription helper. 55 * 56 * @param updateHandler Consumer of subscription updates. 57 */ VmsSubscriptionHelper(@onNull Consumer<Set<VmsAssociatedLayer>> updateHandler)58 public VmsSubscriptionHelper(@NonNull Consumer<Set<VmsAssociatedLayer>> updateHandler) { 59 mUpdateHandler = Objects.requireNonNull(updateHandler, "updateHandler cannot be null"); 60 } 61 62 /** 63 * Adds a subscription to a layer. 64 */ subscribe(@onNull VmsLayer layer)65 public void subscribe(@NonNull VmsLayer layer) { 66 Objects.requireNonNull(layer, "layer cannot be null"); 67 synchronized (mLock) { 68 if (mLayerSubscriptions.add(layer)) { 69 mPendingUpdate = true; 70 } 71 publishSubscriptionUpdate(); 72 } 73 } 74 75 /** 76 * Adds a subscription to a specific provider of a layer. 77 */ subscribe(@onNull VmsLayer layer, int providerId)78 public void subscribe(@NonNull VmsLayer layer, int providerId) { 79 Objects.requireNonNull(layer, "layer cannot be null"); 80 synchronized (mLock) { 81 SparseBooleanArray providerIds = mPublisherSubscriptions.computeIfAbsent(layer, 82 ignored -> new SparseBooleanArray()); 83 if (!providerIds.get(providerId)) { 84 providerIds.put(providerId, true); 85 mPendingUpdate = true; 86 } 87 publishSubscriptionUpdate(); 88 } 89 } 90 91 /** 92 * Removes a subscription to a layer. 93 */ unsubscribe(@onNull VmsLayer layer)94 public void unsubscribe(@NonNull VmsLayer layer) { 95 Objects.requireNonNull(layer, "layer cannot be null"); 96 synchronized (mLock) { 97 if (mLayerSubscriptions.remove(layer)) { 98 mPendingUpdate = true; 99 } 100 publishSubscriptionUpdate(); 101 } 102 } 103 104 /** 105 * Removes a subscription to the specific provider of a layer. 106 */ unsubscribe(@onNull VmsLayer layer, int providerId)107 public void unsubscribe(@NonNull VmsLayer layer, int providerId) { 108 Objects.requireNonNull(layer, "layer cannot be null"); 109 synchronized (mLock) { 110 SparseBooleanArray providerIds = mPublisherSubscriptions.get(layer); 111 if (providerIds != null && providerIds.get(providerId)) { 112 providerIds.delete(providerId); 113 if (providerIds.size() == 0) { 114 mPublisherSubscriptions.remove(layer); 115 } 116 mPendingUpdate = true; 117 } 118 publishSubscriptionUpdate(); 119 } 120 } 121 122 /** 123 * Gets the current set of subscriptions. 124 */ 125 @NonNull getSubscriptions()126 public Set<VmsAssociatedLayer> getSubscriptions() { 127 return Stream.concat( 128 mLayerSubscriptions.stream().map( 129 layer -> new VmsAssociatedLayer(layer, Collections.emptySet())), 130 mPublisherSubscriptions.entrySet().stream() 131 .filter(entry -> !mLayerSubscriptions.contains(entry.getKey())) 132 .map(VmsSubscriptionHelper::toAssociatedLayer)) 133 .collect(Collectors.toSet()); 134 } 135 publishSubscriptionUpdate()136 private void publishSubscriptionUpdate() { 137 synchronized (mLock) { 138 if (mPendingUpdate) { 139 mUpdateHandler.accept(getSubscriptions()); 140 } 141 mPendingUpdate = false; 142 } 143 } 144 toAssociatedLayer( Map.Entry<VmsLayer, SparseBooleanArray> entry)145 private static VmsAssociatedLayer toAssociatedLayer( 146 Map.Entry<VmsLayer, SparseBooleanArray> entry) { 147 SparseBooleanArray providerIdArray = entry.getValue(); 148 Set<Integer> providerIds = new ArraySet<>(providerIdArray.size()); 149 for (int i = 0; i < providerIdArray.size(); i++) { 150 providerIds.add(providerIdArray.keyAt(i)); 151 } 152 return new VmsAssociatedLayer(entry.getKey(), providerIds); 153 } 154 } 155