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