|
| 1 | +package it.pgp.currenttoggles.utils.deviceadmin; |
| 2 | + |
| 3 | +import android.content.BroadcastReceiver; |
| 4 | +import android.content.Context; |
| 5 | +import android.content.Intent; |
| 6 | +import android.content.IntentFilter; |
| 7 | +import android.net.Uri; |
| 8 | +import android.os.Handler; |
| 9 | +import android.os.Message; |
| 10 | +import android.util.Log; |
| 11 | + |
| 12 | + |
| 13 | +import java.util.ArrayList; |
| 14 | +import java.util.HashMap; |
| 15 | +import java.util.Set; |
| 16 | + |
| 17 | +public final class LocalBroadcastManager { |
| 18 | + private static final class ReceiverRecord { |
| 19 | + final IntentFilter filter; |
| 20 | + final BroadcastReceiver receiver; |
| 21 | + boolean broadcasting; |
| 22 | + boolean dead; |
| 23 | + |
| 24 | + ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) { |
| 25 | + filter = _filter; |
| 26 | + receiver = _receiver; |
| 27 | + } |
| 28 | + |
| 29 | + @Override |
| 30 | + public String toString() { |
| 31 | + StringBuilder builder = new StringBuilder(128); |
| 32 | + builder.append("Receiver{"); |
| 33 | + builder.append(receiver); |
| 34 | + builder.append(" filter="); |
| 35 | + builder.append(filter); |
| 36 | + if (dead) builder.append(" DEAD"); |
| 37 | + builder.append("}"); |
| 38 | + return builder.toString(); |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + private static final class BroadcastRecord { |
| 43 | + final Intent intent; |
| 44 | + final ArrayList<ReceiverRecord> receivers; |
| 45 | + |
| 46 | + BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) { |
| 47 | + intent = _intent; |
| 48 | + receivers = _receivers; |
| 49 | + } |
| 50 | + } |
| 51 | + |
| 52 | + private static final String TAG = "LocalBroadcastManager"; |
| 53 | + private static final boolean DEBUG = false; |
| 54 | + |
| 55 | + private final Context mAppContext; |
| 56 | + |
| 57 | + private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers = new HashMap<>(); |
| 58 | + private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>(); |
| 59 | + |
| 60 | + private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>(); |
| 61 | + |
| 62 | + static final int MSG_EXEC_PENDING_BROADCASTS = 1; |
| 63 | + |
| 64 | + private final Handler mHandler; |
| 65 | + |
| 66 | + private static final Object mLock = new Object(); |
| 67 | + private static LocalBroadcastManager mInstance; |
| 68 | + |
| 69 | + public static LocalBroadcastManager getInstance(Context context) { |
| 70 | + synchronized(mLock) { |
| 71 | + if(mInstance == null) mInstance = new LocalBroadcastManager(context.getApplicationContext()); |
| 72 | + return mInstance; |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + private LocalBroadcastManager(Context context) { |
| 77 | + mAppContext = context; |
| 78 | + mHandler = new Handler(context.getMainLooper()) { |
| 79 | + @Override |
| 80 | + public void handleMessage(Message msg) { |
| 81 | + switch(msg.what) { |
| 82 | + case MSG_EXEC_PENDING_BROADCASTS: |
| 83 | + executePendingBroadcasts(); |
| 84 | + break; |
| 85 | + default: |
| 86 | + super.handleMessage(msg); |
| 87 | + } |
| 88 | + } |
| 89 | + }; |
| 90 | + } |
| 91 | + |
| 92 | + public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { |
| 93 | + synchronized (mReceivers) { |
| 94 | + ReceiverRecord entry = new ReceiverRecord(filter, receiver); |
| 95 | + ArrayList<ReceiverRecord> filters = mReceivers.get(receiver); |
| 96 | + if(filters == null) { |
| 97 | + filters = new ArrayList<>(1); |
| 98 | + mReceivers.put(receiver, filters); |
| 99 | + } |
| 100 | + filters.add(entry); |
| 101 | + for(int i=0; i<filter.countActions(); i++) { |
| 102 | + String action = filter.getAction(i); |
| 103 | + ArrayList<ReceiverRecord> entries = mActions.get(action); |
| 104 | + if(entries == null) { |
| 105 | + entries = new ArrayList<>(1); |
| 106 | + mActions.put(action, entries); |
| 107 | + } |
| 108 | + entries.add(entry); |
| 109 | + } |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + public void unregisterReceiver(BroadcastReceiver receiver) { |
| 114 | + synchronized(mReceivers) { |
| 115 | + ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver); |
| 116 | + if(filters == null) return; |
| 117 | + for(int i=filters.size()-1; i>=0; i--) { |
| 118 | + final ReceiverRecord filter = filters.get(i); |
| 119 | + filter.dead = true; |
| 120 | + for(int j=0; j<filter.filter.countActions(); j++) { |
| 121 | + final String action = filter.filter.getAction(j); |
| 122 | + final ArrayList<ReceiverRecord> receivers = mActions.get(action); |
| 123 | + if(receivers != null) { |
| 124 | + for(int k=receivers.size()-1; k>=0; k--) { |
| 125 | + ReceiverRecord rec = receivers.get(k); |
| 126 | + if(rec.receiver == receiver) { |
| 127 | + rec.dead = true; |
| 128 | + receivers.remove(k); |
| 129 | + } |
| 130 | + } |
| 131 | + if(receivers.size() <= 0) mActions.remove(action); |
| 132 | + } |
| 133 | + } |
| 134 | + } |
| 135 | + } |
| 136 | + } |
| 137 | + |
| 138 | + public boolean sendBroadcast(Intent intent) { |
| 139 | + synchronized (mReceivers) { |
| 140 | + final String action = intent.getAction(); |
| 141 | + final String type = intent.resolveTypeIfNeeded(mAppContext.getContentResolver()); |
| 142 | + final Uri data = intent.getData(); |
| 143 | + final String scheme = intent.getScheme(); |
| 144 | + final Set<String> categories = intent.getCategories(); |
| 145 | + |
| 146 | + boolean debug = DEBUG || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); |
| 147 | + if(debug) Log.v(TAG, "Resolving type " + type + " scheme " + scheme + " of intent " + intent); |
| 148 | + |
| 149 | + ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction()); |
| 150 | + if(entries != null) { |
| 151 | + if(debug) Log.v(TAG, "Action list: " + entries); |
| 152 | + |
| 153 | + ArrayList<ReceiverRecord> receivers = null; |
| 154 | + for(int i=0; i<entries.size(); i++) { |
| 155 | + ReceiverRecord receiver = entries.get(i); |
| 156 | + if(debug) Log.v(TAG, "Matching against filter " + receiver.filter); |
| 157 | + |
| 158 | + if(receiver.broadcasting) { |
| 159 | + if(debug) Log.v(TAG, " Filter's target already added"); |
| 160 | + continue; |
| 161 | + } |
| 162 | + |
| 163 | + int match = receiver.filter.match(action, type, scheme, data, categories, "LocalBroadcastManager"); |
| 164 | + if(match >= 0) { |
| 165 | + if(debug) Log.v(TAG, " Filter matched! match=0x" + Integer.toHexString(match)); |
| 166 | + if(receivers == null) receivers = new ArrayList<>(); |
| 167 | + receivers.add(receiver); |
| 168 | + receiver.broadcasting = true; |
| 169 | + } |
| 170 | + else { |
| 171 | + if(debug) { |
| 172 | + String reason; |
| 173 | + switch(match) { |
| 174 | + case IntentFilter.NO_MATCH_ACTION: reason = "action"; break; |
| 175 | + case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break; |
| 176 | + case IntentFilter.NO_MATCH_DATA: reason = "data"; break; |
| 177 | + case IntentFilter.NO_MATCH_TYPE: reason = "type"; break; |
| 178 | + default: reason = "unknown reason"; break; |
| 179 | + } |
| 180 | + Log.v(TAG, " Filter did not match: " + reason); |
| 181 | + } |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + if(receivers != null) { |
| 186 | + for(int i=0; i<receivers.size(); i++) receivers.get(i).broadcasting = false; |
| 187 | + mPendingBroadcasts.add(new BroadcastRecord(intent, receivers)); |
| 188 | + if(!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) |
| 189 | + mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS); |
| 190 | + return true; |
| 191 | + } |
| 192 | + } |
| 193 | + } |
| 194 | + return false; |
| 195 | + } |
| 196 | + |
| 197 | + public void sendBroadcastSync(Intent intent) { |
| 198 | + if(sendBroadcast(intent)) executePendingBroadcasts(); |
| 199 | + } |
| 200 | + |
| 201 | + private void executePendingBroadcasts() { |
| 202 | + for(;;) { |
| 203 | + final BroadcastRecord[] brs; |
| 204 | + synchronized(mReceivers) { |
| 205 | + final int N = mPendingBroadcasts.size(); |
| 206 | + if(N <= 0) return; |
| 207 | + brs = new BroadcastRecord[N]; |
| 208 | + mPendingBroadcasts.toArray(brs); |
| 209 | + mPendingBroadcasts.clear(); |
| 210 | + } |
| 211 | + for(int i=0; i<brs.length; i++) { |
| 212 | + final BroadcastRecord br = brs[i]; |
| 213 | + final int nbr = br.receivers.size(); |
| 214 | + for(int j=0; j<nbr; j++) { |
| 215 | + ReceiverRecord rec = br.receivers.get(j); |
| 216 | + if(!rec.dead) rec.receiver.onReceive(mAppContext, br.intent); |
| 217 | + } |
| 218 | + } |
| 219 | + } |
| 220 | + } |
| 221 | +} |
| 222 | + |
0 commit comments