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 }