• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 package com.android.car.internal.util;
17 
18 import android.annotation.Nullable;
19 import android.car.builtin.util.Slogf;
20 import android.os.IBinder;
21 import android.os.IInterface;
22 import android.os.RemoteException;
23 import android.util.ArrayMap;
24 
25 import com.android.internal.annotations.GuardedBy;
26 
27 import java.util.Collections;
28 import java.util.Objects;
29 import java.util.Set;
30 
31 /**
32  * A map-like container to hold client's binder interface. The item in the container will be removed
33  * automatically once the associated binder is unlinked (dies).
34  *
35  * @param <K> type of the key of the item
36  * @param <V> type wrapped in the value of the item
37  */
38 public final class BinderKeyValueContainer<K, V extends IInterface> {
39 
40     private static final String TAG = BinderKeyValueContainer.class.getSimpleName();
41 
42     // BinderInterfaceHolder#binderDied() is called on binder thread, and it might change this
43     // container, so guard this container with a lock to avoid racing condition between binder
44     // thread and the calling thread of this container.
45     private final Object mLock = new Object();
46 
47     @GuardedBy("mLock")
48     private final ArrayMap<K, BinderInterfaceHolder<K, V>> mBinderMap;
49 
50     @Nullable
51     private BinderDeathCallback<K> mBinderDeathCallback;
52 
53     /**
54      * Wrapper class for objects that want to be notified whenever they are unlinked from
55      * the container ({@link BinderKeyValueContainer}).
56      *
57      * @param <K> type of the key of the item
58      * @param <V> type of the value wrapped by this class
59      */
60     private static final class BinderInterfaceHolder<K, V extends IInterface> implements
61             IBinder.DeathRecipient {
62 
63         private final V mBinderInterface;
64         private final IBinder mBinder;
65         private final BinderKeyValueContainer<K, V> mMap;
66 
BinderInterfaceHolder(BinderKeyValueContainer<K, V> map, V binderInterface, IBinder binder)67         private BinderInterfaceHolder(BinderKeyValueContainer<K, V> map, V binderInterface,
68                 IBinder binder) {
69             mMap = map;
70             this.mBinderInterface = binderInterface;
71             this.mBinder = binder;
72         }
73 
74         @Override
binderDied()75         public void binderDied() {
76             mBinder.unlinkToDeath(this, 0);
77             mMap.removeByBinderInterfaceHolder(this);
78         }
79     }
80 
81     /**
82      * Interface to be implemented by object that wants to be notified whenever a binder is unlinked
83      * (dies).
84      *
85      * @param <K> type of the key of the container
86      */
87     public interface BinderDeathCallback<K> {
88         /** Callback to be invoked after a binder is unlinked and removed from the container. */
onBinderDied(K deadKey)89         void onBinderDied(K deadKey);
90     }
91 
BinderKeyValueContainer()92     public BinderKeyValueContainer() {
93         mBinderMap = new ArrayMap<>();
94     }
95 
96     /**
97      * Returns the {@link IInterface} object associated with the {@code key}, or {@code null} if
98      * there is no such key.
99      */
100     @Nullable
get(K key)101     public V get(K key) {
102         Objects.requireNonNull(key);
103         synchronized (mLock) {
104             BinderInterfaceHolder<K, V> holder = mBinderMap.get(key);
105             return holder == null ? null : holder.mBinderInterface;
106         }
107     }
108 
109     /**
110      * Adds the instance of {@link IInterface} representing the binder interface to this container.
111      * <p>
112      * Updates the value if the {@code key} exists already.
113      * <p>
114      * Internally, this {@code binderInterface} will be wrapped in a {@link BinderInterfaceHolder}
115      * when added.
116      */
put(K key, V binderInterface)117     public void put(K key, V binderInterface) {
118         IBinder binder = binderInterface.asBinder();
119         BinderInterfaceHolder<K, V> holder =
120                 new BinderInterfaceHolder<>(this, binderInterface, binder);
121         BinderInterfaceHolder<K, V> oldHolder;
122         try {
123             binder.linkToDeath(holder, 0);
124         } catch (RemoteException e) {
125             throw new IllegalArgumentException(e);
126         }
127         synchronized (mLock) {
128             oldHolder = mBinderMap.put(key, holder);
129         }
130         if (oldHolder != null) {
131             Slogf.i(TAG, "Replaced the existing callback %s", oldHolder.mBinderInterface);
132         }
133     }
134 
135     /**
136      * Removes an item in the container by its key, if there is any.
137      */
remove(K key)138     public void remove(K key) {
139         synchronized (mLock) {
140             BinderInterfaceHolder<K, V> holder = mBinderMap.get(key);
141             if (holder == null) {
142                 Slogf.i(TAG, "Failed to remove because there was no item with key %s", key);
143                 return;
144             }
145             holder.mBinder.unlinkToDeath(holder, 0);
146             mBinderMap.remove(key);
147         }
148     }
149 
150     /**
151      * Removes the item at the given index, if there is any.
152      */
removeAt(int index)153     public void removeAt(int index) {
154         synchronized (mLock) {
155             BinderInterfaceHolder<K, V> holder = mBinderMap.valueAt(index);
156             if (holder == null) {
157                 Slogf.i(TAG, "Failed to remove because there was no item at index %d", index);
158                 return;
159             }
160             holder.mBinder.unlinkToDeath(holder, 0);
161             mBinderMap.removeAt(index);
162         }
163     }
164 
165     /**
166      * Returns the number of registered {@link BinderInterfaceHolder} objects in this container.
167      */
size()168     public int size() {
169         synchronized (mLock) {
170             return mBinderMap.size();
171         }
172     }
173 
174     /**
175      * Returns the key at the given index in the container.
176      *
177      * @param index The desired index, must be between 0 and {@link #size()}-1.
178      * @throws ArrayIndexOutOfBoundsException if the index is invalid
179      */
keyAt(int index)180     public K keyAt(int index) {
181         synchronized (mLock) {
182             return mBinderMap.keyAt(index);
183         }
184     }
185 
186     /**
187      * Returns the {@link IInterface} at the given index in the container.
188      *
189      * @param index The desired index, must be between 0 and {@link #size()}-1.
190      * @throws ArrayIndexOutOfBoundsException if the index is invalid
191      */
valueAt(int index)192     public V valueAt(int index) {
193         synchronized (mLock) {
194             BinderInterfaceHolder<K, V> holder = mBinderMap.valueAt(index);
195             return holder.mBinderInterface;
196         }
197     }
198 
199     /**
200      * Returns whether the {@code key} is stored in the container.
201      */
containsKey(K key)202     public boolean containsKey(K key) {
203         synchronized (mLock) {
204             return mBinderMap.containsKey(key);
205         }
206     }
207 
208     /**
209      * Returns an unmodifiable copy of keys in the container, or an empty set if the container is
210      * empty.
211      */
keySet()212     public Set<K> keySet() {
213         synchronized (mLock) {
214             return Collections.unmodifiableSet(mBinderMap.keySet());
215         }
216     }
217 
218     /**
219      * Sets a death callback to be notified after a binder is unlinked and removed from the
220      * container.
221      */
setBinderDeathCallback(@ullable BinderDeathCallback<K> binderDeathCallback)222     public void setBinderDeathCallback(@Nullable BinderDeathCallback<K> binderDeathCallback) {
223         mBinderDeathCallback = binderDeathCallback;
224     }
225 
removeByBinderInterfaceHolder(BinderInterfaceHolder<K, V> holder)226     private void removeByBinderInterfaceHolder(BinderInterfaceHolder<K, V> holder) {
227         K deadKey = null;
228         synchronized (mLock) {
229             int index = mBinderMap.indexOfValue(holder);
230             if (index >= 0) {
231                 deadKey = mBinderMap.keyAt(index);
232                 mBinderMap.removeAt(index);
233                 Slogf.i(TAG, "Binder died, so remove %s", holder.mBinderInterface);
234             }
235         }
236         if (mBinderDeathCallback != null && deadKey != null) {
237             mBinderDeathCallback.onBinderDied(deadKey);
238         }
239     }
240 }
241