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