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