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