1 /*
2  * Copyright (C) 2019 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.internal.os;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.reset;
23 import static org.mockito.Mockito.times;
24 import static org.mockito.Mockito.verify;
25 
26 import android.os.DeadObjectException;
27 import android.os.IBinder;
28 import android.os.IBinder.DeathRecipient;
29 import android.os.IInterface;
30 import android.os.Parcel;
31 import android.os.RemoteException;
32 import android.os.ResultReceiver;
33 import android.os.ShellCallback;
34 import android.platform.test.annotations.DisabledOnRavenwood;
35 import android.platform.test.ravenwood.RavenwoodRule;
36 
37 import androidx.test.filters.SmallTest;
38 import androidx.test.runner.AndroidJUnit4;
39 
40 import org.junit.Rule;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 
44 import java.io.FileDescriptor;
45 
46 @SmallTest
47 @RunWith(AndroidJUnit4.class)
48 public class BinderDeathDispatcherTest {
49     @Rule
50     public RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
51 
52     private static class MyTarget implements IInterface, IBinder {
53         public boolean isAlive = true;
54         public DeathRecipient mRecipient;
55 
56         @Override
getInterfaceDescriptor()57         public String getInterfaceDescriptor() throws RemoteException {
58             return null;
59         }
60 
61         @Override
pingBinder()62         public boolean pingBinder() {
63             return false;
64         }
65 
66         @Override
isBinderAlive()67         public boolean isBinderAlive() {
68             return isAlive;
69         }
70 
71         @Override
queryLocalInterface(String descriptor)72         public IInterface queryLocalInterface(String descriptor) {
73             return null;
74         }
75 
76         @Override
dump(FileDescriptor fd, String[] args)77         public void dump(FileDescriptor fd, String[] args) throws RemoteException {
78 
79         }
80 
81         @Override
dumpAsync(FileDescriptor fd, String[] args)82         public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
83 
84         }
85 
86         @Override
shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback shellCallback, ResultReceiver resultReceiver)87         public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
88                 String[] args, ShellCallback shellCallback, ResultReceiver resultReceiver)
89                 throws RemoteException {
90 
91         }
92 
93         @Override
transact(int code, Parcel data, Parcel reply, int flags)94         public boolean transact(int code, Parcel data, Parcel reply, int flags)
95                 throws RemoteException {
96             return false;
97         }
98 
99         @Override
linkToDeath(DeathRecipient recipient, int flags)100         public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {
101             // In any situation, a single binder object should only have at most one death
102             // recipient.
103             assertThat(mRecipient).isNull();
104 
105             if (!isAlive) {
106                 throw new DeadObjectException();
107             }
108 
109             mRecipient = recipient;
110         }
111 
112         @Override
unlinkToDeath(DeathRecipient recipient, int flags)113         public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
114             if (!isAlive) {
115                 return false;
116             }
117             assertThat(mRecipient).isSameInstanceAs(recipient);
118             mRecipient = null;
119             return true;
120         }
121 
122         @Override
asBinder()123         public IBinder asBinder() {
124             return this;
125         }
126 
die()127         public void die() {
128             isAlive = false;
129             if (mRecipient != null) {
130                 mRecipient.binderDied(this);
131             }
132             mRecipient = null;
133         }
134 
hasDeathRecipient()135         public boolean hasDeathRecipient() {
136             return mRecipient != null;
137         }
138     }
139 
140     @Test
testRegisterAndUnregister()141     public void testRegisterAndUnregister() {
142         BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
143 
144         MyTarget t1 = new MyTarget();
145         MyTarget t2 = new MyTarget();
146         MyTarget t3 = new MyTarget();
147 
148         DeathRecipient r1 = mock(DeathRecipient.class);
149         DeathRecipient r2 = mock(DeathRecipient.class);
150         DeathRecipient r3 = mock(DeathRecipient.class);
151         DeathRecipient r4 = mock(DeathRecipient.class);
152         DeathRecipient r5 = mock(DeathRecipient.class);
153 
154         // Start hooking up.
155 
156         // Link 3 recipients to t1 -- only one real recipient will be set.
157         assertThat(d.linkToDeath(t1, r1)).isEqualTo(1);
158         assertThat(d.getTargetsForTest().size()).isEqualTo(1);
159 
160         assertThat(d.linkToDeath(t1, r2)).isEqualTo(2);
161         assertThat(d.linkToDeath(t1, r3)).isEqualTo(3);
162         assertThat(d.getTargetsForTest().size()).isEqualTo(1);
163 
164         // Unlink two -- the real recipient is still set.
165         d.unlinkToDeath(t1, r1);
166         d.unlinkToDeath(t1, r2);
167 
168         assertThat(t1.hasDeathRecipient()).isTrue();
169         assertThat(d.getTargetsForTest().size()).isEqualTo(1);
170 
171         // Unlink the last one. The real recipient is also unlinked.
172         d.unlinkToDeath(t1, r3);
173         assertThat(t1.hasDeathRecipient()).isFalse();
174         assertThat(d.getTargetsForTest().size()).isEqualTo(0);
175 
176         // Set recipients to t1, t2 and t3. t3 has two.
177         assertThat(d.linkToDeath(t1, r1)).isEqualTo(1);
178         assertThat(d.linkToDeath(t2, r1)).isEqualTo(1);
179         assertThat(d.linkToDeath(t3, r1)).isEqualTo(1);
180         assertThat(d.linkToDeath(t3, r2)).isEqualTo(2);
181 
182 
183         // They should all have a real recipient.
184         assertThat(t1.hasDeathRecipient()).isTrue();
185         assertThat(t2.hasDeathRecipient()).isTrue();
186         assertThat(t3.hasDeathRecipient()).isTrue();
187 
188         assertThat(d.getTargetsForTest().size()).isEqualTo(3);
189 
190         // Unlink r1 from t3. t3 still has r2, so it should still have a real recipient.
191         d.unlinkToDeath(t3, r1);
192         assertThat(t1.hasDeathRecipient()).isTrue();
193         assertThat(t2.hasDeathRecipient()).isTrue();
194         assertThat(t3.hasDeathRecipient()).isTrue();
195         assertThat(d.getTargetsForTest().size()).isEqualTo(3);
196 
197         // Unlink r2 from t3. Now t3 has no real recipient.
198         d.unlinkToDeath(t3, r2);
199         assertThat(t3.hasDeathRecipient()).isFalse();
200         assertThat(d.getTargetsForTest().size()).isEqualTo(2);
201     }
202 
203     @Test
204     @DisabledOnRavenwood(reason = "b/324433654 -- depends on unsupported classes")
testRegisterAndKill()205     public void testRegisterAndKill() {
206         BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
207 
208         MyTarget t1 = new MyTarget();
209         MyTarget t2 = new MyTarget();
210         MyTarget t3 = new MyTarget();
211 
212         DeathRecipient r1 = mock(DeathRecipient.class);
213         DeathRecipient r2 = mock(DeathRecipient.class);
214         DeathRecipient r3 = mock(DeathRecipient.class);
215         DeathRecipient r4 = mock(DeathRecipient.class);
216         DeathRecipient r5 = mock(DeathRecipient.class);
217 
218         // Hook them up.
219 
220         d.linkToDeath(t1, r1);
221         d.linkToDeath(t1, r2);
222         d.linkToDeath(t1, r3);
223 
224         // r4 is linked then unlinked. It shouldn't be notified.
225         d.linkToDeath(t1, r4);
226         d.unlinkToDeath(t1, r4);
227 
228         d.linkToDeath(t2, r1);
229 
230         d.linkToDeath(t3, r3);
231         d.linkToDeath(t3, r5);
232 
233         assertThat(d.getTargetsForTest().size()).isEqualTo(3);
234 
235         // Kill the targets.
236 
237         t1.die();
238         verify(r1, times(1)).binderDied(t1);
239         verify(r2, times(1)).binderDied(t1);
240         verify(r3, times(1)).binderDied(t1);
241         verify(r4, times(0)).binderDied(any());
242         verify(r5, times(0)).binderDied(any());
243 
244         assertThat(d.getTargetsForTest().size()).isEqualTo(2);
245 
246         reset(r1, r2, r3, r4, r5);
247 
248         t2.die();
249         verify(r1, times(1)).binderDied(t2);
250         verify(r2, times(0)).binderDied(any());
251         verify(r3, times(0)).binderDied(any());
252         verify(r4, times(0)).binderDied(any());
253         verify(r5, times(0)).binderDied(any());
254 
255         assertThat(d.getTargetsForTest().size()).isEqualTo(1);
256 
257         reset(r1, r2, r3, r4, r5);
258 
259         t3.die();
260         verify(r1, times(0)).binderDied(any());
261         verify(r2, times(0)).binderDied(any());
262         verify(r3, times(1)).binderDied(t3);
263         verify(r4, times(0)).binderDied(any());
264         verify(r5, times(1)).binderDied(t3);
265 
266         assertThat(d.getTargetsForTest().size()).isEqualTo(0);
267 
268         // Try to register to a dead object -> should return -1.
269         assertThat(d.linkToDeath(t1, r1)).isEqualTo(-1);
270 
271         assertThat(d.getTargetsForTest().size()).isEqualTo(0);
272     }
273 
274     @Test
275     @DisabledOnRavenwood(reason = "b/324433654 -- depends on unsupported classes")
duplicateRegistrations()276     public void duplicateRegistrations() {
277         BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
278 
279         MyTarget t1 = new MyTarget();
280 
281         DeathRecipient r1 = mock(DeathRecipient.class);
282         DeathRecipient r2 = mock(DeathRecipient.class);
283 
284         for (int i = 0; i < 5; i++) {
285             assertThat(d.linkToDeath(t1, r1)).isEqualTo(1);
286         }
287         assertThat(d.linkToDeath(t1, r2)).isEqualTo(2);
288 
289         t1.die();
290         verify(r1, times(1)).binderDied(t1);
291         verify(r2, times(1)).binderDied(t1);
292 
293         d.unlinkToDeath(t1, r1);
294         d.unlinkToDeath(t1, r2);
295         assertThat(d.getTargetsForTest()).isEmpty();
296     }
297 }
298