1 /*
2  * Copyright (C) 2015 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;
18 
19 import android.annotation.Nullable;
20 import android.os.IBinder;
21 import android.os.IInterface;
22 import android.os.RemoteException;
23 
24 import com.android.internal.annotations.GuardedBy;
25 
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.Map;
30 
31 /**
32  * Helper class to hold client's binder interface.
33  *
34  * @param <T> type of the value that is wrapped by this class
35  */
36 public class BinderInterfaceContainer<T extends IInterface> {
37 
38     /**
39      * Wrapper class for objects that want to be notified whenever they are unliked from
40      * the container ({@link BinderInterfaceContainer}).
41      *
42      * @param <T> type of the value that is wrapped by this class
43      */
44     public static class BinderInterface<T extends IInterface> implements IBinder.DeathRecipient {
45         public final T binderInterface;
46         private final BinderInterfaceContainer<T> mContainer;
47 
BinderInterface(BinderInterfaceContainer<T> container, T binderInterface)48         public BinderInterface(BinderInterfaceContainer<T> container, T binderInterface) {
49             mContainer = container;
50             this.binderInterface = binderInterface;
51         }
52 
53         @Override
binderDied()54         public void binderDied() {
55             binderInterface.asBinder().unlinkToDeath(this, 0);
56             mContainer.handleBinderDeath(this);
57         }
58     }
59 
60     /**
61      * Interface to be implemented by object that want to be notified whenever a binder is unliked
62      * (dies).
63      */
64     public interface BinderEventHandler<T extends IInterface> {
onBinderDeath(BinderInterface<T> bInterface)65         void onBinderDeath(BinderInterface<T> bInterface);
66     }
67 
68     private final Object mLock = new Object();
69 
70     private final BinderEventHandler<T> mEventHandler;
71 
72     @GuardedBy("mLock")
73     private final Map<IBinder, BinderInterface<T>> mBinders = new HashMap<>();
74 
75     /**
76      * Constructs a new <code>BinderInterfaceContainer</code> passing an event handler to be used to
77      * notify listeners when a registered binder dies (unlinked).
78      */
BinderInterfaceContainer(@ullable BinderEventHandler<T> eventHandler)79     public BinderInterfaceContainer(@Nullable BinderEventHandler<T> eventHandler) {
80         mEventHandler = eventHandler;
81     }
82 
BinderInterfaceContainer()83     public BinderInterfaceContainer() {
84         mEventHandler = null;
85     }
86 
87     /**
88      * Add the instance of {@link IInterface} representing the binder interface to this container.
89      *
90      * Internally, this object will be wrapped in an {@link BinderInterface} when added.
91      */
addBinder(T binderInterface)92     public void addBinder(T binderInterface) {
93         IBinder binder = binderInterface.asBinder();
94         synchronized (mLock) {
95             BinderInterface<T> bInterface = mBinders.get(binder);
96             if (bInterface != null) {
97                 return;
98             }
99             bInterface = new BinderInterface<T>(this, binderInterface);
100             try {
101                 binder.linkToDeath(bInterface, 0);
102             } catch (RemoteException e) {
103                 throw new IllegalArgumentException(e);
104             }
105             mBinders.put(binder, bInterface);
106         }
107     }
108 
109     /**
110      * Removes the {@link BinderInterface} object associated with the passed parameter (if there is
111      * any).
112      */
removeBinder(T binderInterface)113     public void removeBinder(T binderInterface) {
114         IBinder binder = binderInterface.asBinder();
115         synchronized (mLock) {
116             BinderInterface<T> bInterface = mBinders.get(binder);
117             if (bInterface == null) {
118                 return;
119             }
120             binder.unlinkToDeath(bInterface, 0);
121             mBinders.remove(binder);
122         }
123     }
124 
125     /**
126      * Returns the {@link BinderInterface} object associated with the passed parameter.
127      */
getBinderInterface(T binderInterface)128     public BinderInterface<T> getBinderInterface(T binderInterface) {
129         IBinder binder = binderInterface.asBinder();
130         synchronized (mLock) {
131             return mBinders.get(binder);
132         }
133     }
134 
135     /**
136      * Adds a new {@link BinderInterface} in this container.
137      */
addBinderInterface(BinderInterface<T> bInterface)138     public void addBinderInterface(BinderInterface<T> bInterface) {
139         IBinder binder = bInterface.binderInterface.asBinder();
140         synchronized (mLock) {
141             try {
142                 binder.linkToDeath(bInterface, 0);
143             } catch (RemoteException e) {
144                 throw new IllegalArgumentException(e);
145             }
146             mBinders.put(binder, bInterface);
147         }
148     }
149 
150     /**
151      * Returns an unmodified collection containing all registered {@link BinderInterface} objects
152      * with this container.
153      */
getInterfaces()154     public Collection<BinderInterface<T>> getInterfaces() {
155         synchronized (mLock) {
156             return Collections.unmodifiableCollection(mBinders.values());
157         }
158     }
159 
160     /**
161      * Returns the number of registered {@link BinderInterface} objects in this container.
162      */
size()163     public int size() {
164         synchronized (mLock) {
165             return mBinders.size();
166         }
167     }
168 
169     /**
170      * Clears all registered {@link BinderInterface} objects.
171      */
clear()172     public void clear() {
173         synchronized (mLock) {
174             Collection<BinderInterface<T>> interfaces = getInterfaces();
175             for (BinderInterface<T> bInterface : interfaces) {
176                 IBinder binder = bInterface.binderInterface.asBinder();
177                 binder.unlinkToDeath(bInterface, 0);
178             }
179         }
180         mBinders.clear();
181     }
182 
handleBinderDeath(BinderInterface<T> bInterface)183     private void handleBinderDeath(BinderInterface<T> bInterface) {
184         if (mEventHandler != null) {
185             mEventHandler.onBinderDeath(bInterface);
186         }
187         removeBinder(bInterface.binderInterface);
188     }
189 }
190