1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.support.v4.content; 18 19 import java.util.ArrayList; 20 import java.util.HashMap; 21 import java.util.Set; 22 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.net.Uri; 28 import android.os.Handler; 29 import android.os.Message; 30 import android.util.Log; 31 32 /** 33 * Helper to register for and send broadcasts of Intents to local objects 34 * within your process. This is has a number of advantages over sending 35 * global broadcasts with {@link android.content.Context#sendBroadcast}: 36 * <ul> 37 * <li> You know that the data you are broadcasting won't leave your app, so 38 * don't need to worry about leaking private data. 39 * <li> It is not possible for other applications to send these broadcasts to 40 * your app, so you don't need to worry about having security holes they can 41 * exploit. 42 * <li> It is more efficient than sending a global broadcast through the 43 * system. 44 * </ul> 45 */ 46 public class LocalBroadcastManager { 47 private static class ReceiverRecord { 48 final IntentFilter filter; 49 final BroadcastReceiver receiver; 50 boolean broadcasting; 51 ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver)52 ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) { 53 filter = _filter; 54 receiver = _receiver; 55 } 56 57 @Override toString()58 public String toString() { 59 StringBuilder builder = new StringBuilder(128); 60 builder.append("Receiver{"); 61 builder.append(receiver); 62 builder.append(" filter="); 63 builder.append(filter); 64 builder.append("}"); 65 return builder.toString(); 66 } 67 } 68 69 private static class BroadcastRecord { 70 final Intent intent; 71 final ArrayList<ReceiverRecord> receivers; 72 BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers)73 BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) { 74 intent = _intent; 75 receivers = _receivers; 76 } 77 } 78 79 private static final String TAG = "LocalBroadcastManager"; 80 private static final boolean DEBUG = false; 81 82 private final Context mAppContext; 83 84 private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers 85 = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>(); 86 private final HashMap<String, ArrayList<ReceiverRecord>> mActions 87 = new HashMap<String, ArrayList<ReceiverRecord>>(); 88 89 private final ArrayList<BroadcastRecord> mPendingBroadcasts 90 = new ArrayList<BroadcastRecord>(); 91 92 static final int MSG_EXEC_PENDING_BROADCASTS = 1; 93 94 private final Handler mHandler; 95 96 private static final Object mLock = new Object(); 97 private static LocalBroadcastManager mInstance; 98 getInstance(Context context)99 public static LocalBroadcastManager getInstance(Context context) { 100 synchronized (mLock) { 101 if (mInstance == null) { 102 mInstance = new LocalBroadcastManager(context.getApplicationContext()); 103 } 104 return mInstance; 105 } 106 } 107 LocalBroadcastManager(Context context)108 private LocalBroadcastManager(Context context) { 109 mAppContext = context; 110 mHandler = new Handler(context.getMainLooper()) { 111 112 @Override 113 public void handleMessage(Message msg) { 114 switch (msg.what) { 115 case MSG_EXEC_PENDING_BROADCASTS: 116 executePendingBroadcasts(); 117 break; 118 default: 119 super.handleMessage(msg); 120 } 121 } 122 }; 123 } 124 125 /** 126 * Register a receive for any local broadcasts that match the given IntentFilter. 127 * 128 * @param receiver The BroadcastReceiver to handle the broadcast. 129 * @param filter Selects the Intent broadcasts to be received. 130 * 131 * @see #unregisterReceiver 132 */ registerReceiver(BroadcastReceiver receiver, IntentFilter filter)133 public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { 134 synchronized (mReceivers) { 135 ReceiverRecord entry = new ReceiverRecord(filter, receiver); 136 ArrayList<IntentFilter> filters = mReceivers.get(receiver); 137 if (filters == null) { 138 filters = new ArrayList<IntentFilter>(1); 139 mReceivers.put(receiver, filters); 140 } 141 filters.add(filter); 142 for (int i=0; i<filter.countActions(); i++) { 143 String action = filter.getAction(i); 144 ArrayList<ReceiverRecord> entries = mActions.get(action); 145 if (entries == null) { 146 entries = new ArrayList<ReceiverRecord>(1); 147 mActions.put(action, entries); 148 } 149 entries.add(entry); 150 } 151 } 152 } 153 154 /** 155 * Unregister a previously registered BroadcastReceiver. <em>All</em> 156 * filters that have been registered for this BroadcastReceiver will be 157 * removed. 158 * 159 * @param receiver The BroadcastReceiver to unregister. 160 * 161 * @see #registerReceiver 162 */ unregisterReceiver(BroadcastReceiver receiver)163 public void unregisterReceiver(BroadcastReceiver receiver) { 164 synchronized (mReceivers) { 165 ArrayList<IntentFilter> filters = mReceivers.remove(receiver); 166 if (filters == null) { 167 return; 168 } 169 for (int i=0; i<filters.size(); i++) { 170 IntentFilter filter = filters.get(i); 171 for (int j=0; j<filter.countActions(); j++) { 172 String action = filter.getAction(j); 173 ArrayList<ReceiverRecord> receivers = mActions.get(action); 174 if (receivers != null) { 175 for (int k=0; k<receivers.size(); k++) { 176 if (receivers.get(k).receiver == receiver) { 177 receivers.remove(k); 178 k--; 179 } 180 } 181 if (receivers.size() <= 0) { 182 mActions.remove(action); 183 } 184 } 185 } 186 } 187 } 188 } 189 190 /** 191 * Broadcast the given intent to all interested BroadcastReceivers. This 192 * call is asynchronous; it returns immediately, and you will continue 193 * executing while the receivers are run. 194 * 195 * @param intent The Intent to broadcast; all receivers matching this 196 * Intent will receive the broadcast. 197 * 198 * @see #registerReceiver 199 */ sendBroadcast(Intent intent)200 public boolean sendBroadcast(Intent intent) { 201 synchronized (mReceivers) { 202 final String action = intent.getAction(); 203 final String type = intent.resolveTypeIfNeeded( 204 mAppContext.getContentResolver()); 205 final Uri data = intent.getData(); 206 final String scheme = intent.getScheme(); 207 final Set<String> categories = intent.getCategories(); 208 209 final boolean debug = DEBUG || 210 ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); 211 if (debug) Log.v( 212 TAG, "Resolving type " + type + " scheme " + scheme 213 + " of intent " + intent); 214 215 ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction()); 216 if (entries != null) { 217 if (debug) Log.v(TAG, "Action list: " + entries); 218 219 ArrayList<ReceiverRecord> receivers = null; 220 for (int i=0; i<entries.size(); i++) { 221 ReceiverRecord receiver = entries.get(i); 222 if (debug) Log.v(TAG, "Matching against filter " + receiver.filter); 223 224 if (receiver.broadcasting) { 225 if (debug) { 226 Log.v(TAG, " Filter's target already added"); 227 } 228 continue; 229 } 230 231 int match = receiver.filter.match(action, type, scheme, data, 232 categories, "LocalBroadcastManager"); 233 if (match >= 0) { 234 if (debug) Log.v(TAG, " Filter matched! match=0x" + 235 Integer.toHexString(match)); 236 if (receivers == null) { 237 receivers = new ArrayList<ReceiverRecord>(); 238 } 239 receivers.add(receiver); 240 receiver.broadcasting = true; 241 } else { 242 if (debug) { 243 String reason; 244 switch (match) { 245 case IntentFilter.NO_MATCH_ACTION: reason = "action"; break; 246 case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break; 247 case IntentFilter.NO_MATCH_DATA: reason = "data"; break; 248 case IntentFilter.NO_MATCH_TYPE: reason = "type"; break; 249 default: reason = "unknown reason"; break; 250 } 251 Log.v(TAG, " Filter did not match: " + reason); 252 } 253 } 254 } 255 256 if (receivers != null) { 257 for (int i=0; i<receivers.size(); i++) { 258 receivers.get(i).broadcasting = false; 259 } 260 mPendingBroadcasts.add(new BroadcastRecord(intent, receivers)); 261 if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) { 262 mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS); 263 } 264 return true; 265 } 266 } 267 } 268 return false; 269 } 270 271 /** 272 * Like {@link #sendBroadcast(Intent)}, but if there are any receivers for 273 * the Intent this function will block and immediately dispatch them before 274 * returning. 275 */ sendBroadcastSync(Intent intent)276 public void sendBroadcastSync(Intent intent) { 277 if (sendBroadcast(intent)) { 278 executePendingBroadcasts(); 279 } 280 } 281 executePendingBroadcasts()282 private void executePendingBroadcasts() { 283 while (true) { 284 BroadcastRecord[] brs = null; 285 synchronized (mReceivers) { 286 final int N = mPendingBroadcasts.size(); 287 if (N <= 0) { 288 return; 289 } 290 brs = new BroadcastRecord[N]; 291 mPendingBroadcasts.toArray(brs); 292 mPendingBroadcasts.clear(); 293 } 294 for (int i=0; i<brs.length; i++) { 295 BroadcastRecord br = brs[i]; 296 for (int j=0; j<br.receivers.size(); j++) { 297 br.receivers.get(j).receiver.onReceive(mAppContext, br.intent); 298 } 299 } 300 } 301 } 302 } 303