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