1package com.xtremelabs.robolectric.shadows;
2
3import java.util.HashMap;
4
5import android.os.IBinder;
6import android.os.IInterface;
7import android.os.RemoteCallbackList;
8import android.os.RemoteException;
9
10import com.xtremelabs.robolectric.internal.Implementation;
11import com.xtremelabs.robolectric.internal.Implements;
12
13@Implements(RemoteCallbackList.class)
14public 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
24        Callback(E callback, Object cookie) {
25            this.callback = callback;
26            this.cookie = cookie;
27        }
28
29        public void binderDied() {
30            synchronized (callbacks) {
31                callbacks.remove(callback.asBinder());
32            }
33            onCallbackDied(callback, cookie);
34        }
35    }
36
37    @Implementation
38    public boolean register(E callback) {
39        return register(callback, null);
40    }
41
42    @Implementation
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
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
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
84    public void onCallbackDied(E callback) {}
85
86    @Implementation
87    public void onCallbackDied(E callback, Object cookie) {
88        onCallbackDied(callback);
89    }
90
91    @Implementation
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
114    public E getBroadcastItem(int index) {
115        return ((Callback) activeBroadcast[index]).callback;
116    }
117
118    @Implementation
119    public Object getBroadcastCookie(int index) {
120        return ((Callback) activeBroadcast[index]).cookie;
121    }
122
123    @Implementation
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}