1 package org.robolectric.shadows; 2 3 import android.os.IBinder; 4 import android.os.IInterface; 5 import android.os.RemoteCallbackList; 6 import android.os.RemoteException; 7 import java.util.HashMap; 8 import org.robolectric.annotation.Implementation; 9 import org.robolectric.annotation.Implements; 10 11 @Implements(RemoteCallbackList.class) 12 public class ShadowRemoteCallbackList<E extends IInterface> { 13 private final HashMap<IBinder, Callback> callbacks = new HashMap<>(); 14 private Object[] activeBroadcast; 15 private int broadcastCount = -1; 16 private boolean killed = false; 17 18 private final class Callback implements IBinder.DeathRecipient { 19 final E callback; 20 final Object cookie; 21 22 Callback(E callback, Object cookie) { 23 this.callback = callback; 24 this.cookie = cookie; 25 } 26 27 @Override public void binderDied() { 28 synchronized (callbacks) { 29 callbacks.remove(callback.asBinder()); 30 } 31 onCallbackDied(callback, cookie); 32 } 33 } 34 35 @Implementation 36 public boolean register(E callback) { 37 return register(callback, null); 38 } 39 40 @Implementation 41 public boolean register(E callback, Object cookie) { 42 synchronized (callbacks) { 43 if (killed) { 44 return false; 45 } 46 IBinder binder = callback.asBinder(); 47 try { 48 Callback cb = new Callback(callback, cookie); 49 binder.linkToDeath(cb, 0); 50 callbacks.put(binder, cb); 51 return true; 52 } catch (RemoteException e) { 53 return false; 54 } 55 } 56 } 57 58 @Implementation 59 public boolean unregister(E callback) { 60 synchronized (callbacks) { 61 Callback cb = callbacks.remove(callback.asBinder()); 62 if (cb != null) { 63 cb.callback.asBinder().unlinkToDeath(cb, 0); 64 return true; 65 } 66 return false; 67 } 68 } 69 70 @Implementation 71 public void kill() { 72 synchronized (callbacks) { 73 for (Callback cb : callbacks.values()) { 74 cb.callback.asBinder().unlinkToDeath(cb, 0); 75 } 76 callbacks.clear(); 77 killed = true; 78 } 79 } 80 81 @Implementation 82 public void onCallbackDied(E callback) {} 83 84 @Implementation 85 public void onCallbackDied(E callback, Object cookie) { 86 onCallbackDied(callback); 87 } 88 89 @Implementation 90 public int beginBroadcast() { 91 synchronized (callbacks) { 92 if (broadcastCount > 0) { 93 throw new IllegalStateException("beginBroadcast() called while already in a broadcast"); 94 } 95 final int N = broadcastCount = callbacks.size(); 96 if (N <= 0) { 97 return 0; 98 } 99 Object[] active = activeBroadcast; 100 if (active == null || active.length < N) { 101 activeBroadcast = active = new Object[N]; 102 } 103 int i = 0; 104 for (Callback cb : callbacks.values()) { 105 active[i++] = cb; 106 } 107 return i; 108 } 109 } 110 111 @Implementation 112 public E getBroadcastItem(int index) { 113 return ((Callback) activeBroadcast[index]).callback; 114 } 115 116 @Implementation 117 public Object getBroadcastCookie(int index) { 118 return ((Callback) activeBroadcast[index]).cookie; 119 } 120 121 @Implementation 122 public void finishBroadcast() { 123 if (broadcastCount < 0) { 124 throw new IllegalStateException("finishBroadcast() called outside of a broadcast"); 125 } 126 Object[] active = activeBroadcast; 127 if (active != null) { 128 final int N = broadcastCount; 129 for (int i = 0; i < N; i++) { 130 active[i] = null; 131 } 132 } 133 broadcastCount = -1; 134 } 135 }