Home | History | Annotate | Download | only in shadows
      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 }