• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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 final class LocalBroadcastManager {
47     private static final class ReceiverRecord {
48         final IntentFilter filter;
49         final BroadcastReceiver receiver;
50         boolean broadcasting;
51         boolean dead;
52 
ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver)53         ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
54             filter = _filter;
55             receiver = _receiver;
56         }
57 
58         @Override
toString()59         public String toString() {
60             StringBuilder builder = new StringBuilder(128);
61             builder.append("Receiver{");
62             builder.append(receiver);
63             builder.append(" filter=");
64             builder.append(filter);
65             if (dead) {
66                 builder.append(" DEAD");
67             }
68             builder.append("}");
69             return builder.toString();
70         }
71     }
72 
73     private static final class BroadcastRecord {
74         final Intent intent;
75         final ArrayList<ReceiverRecord> receivers;
76 
BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers)77         BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
78             intent = _intent;
79             receivers = _receivers;
80         }
81     }
82 
83     private static final String TAG = "LocalBroadcastManager";
84     private static final boolean DEBUG = false;
85 
86     private final Context mAppContext;
87 
88     private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers
89             = new HashMap<>();
90     private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
91 
92     private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
93 
94     static final int MSG_EXEC_PENDING_BROADCASTS = 1;
95 
96     private final Handler mHandler;
97 
98     private static final Object mLock = new Object();
99     private static LocalBroadcastManager mInstance;
100 
getInstance(Context context)101     public static LocalBroadcastManager getInstance(Context context) {
102         synchronized (mLock) {
103             if (mInstance == null) {
104                 mInstance = new LocalBroadcastManager(context.getApplicationContext());
105             }
106             return mInstance;
107         }
108     }
109 
LocalBroadcastManager(Context context)110     private LocalBroadcastManager(Context context) {
111         mAppContext = context;
112         mHandler = new Handler(context.getMainLooper()) {
113 
114             @Override
115             public void handleMessage(Message msg) {
116                 switch (msg.what) {
117                     case MSG_EXEC_PENDING_BROADCASTS:
118                         executePendingBroadcasts();
119                         break;
120                     default:
121                         super.handleMessage(msg);
122                 }
123             }
124         };
125     }
126 
127     /**
128      * Register a receive for any local broadcasts that match the given IntentFilter.
129      *
130      * @param receiver The BroadcastReceiver to handle the broadcast.
131      * @param filter Selects the Intent broadcasts to be received.
132      *
133      * @see #unregisterReceiver
134      */
registerReceiver(BroadcastReceiver receiver, IntentFilter filter)135     public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
136         synchronized (mReceivers) {
137             ReceiverRecord entry = new ReceiverRecord(filter, receiver);
138             ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
139             if (filters == null) {
140                 filters = new ArrayList<>(1);
141                 mReceivers.put(receiver, filters);
142             }
143             filters.add(entry);
144             for (int i=0; i<filter.countActions(); i++) {
145                 String action = filter.getAction(i);
146                 ArrayList<ReceiverRecord> entries = mActions.get(action);
147                 if (entries == null) {
148                     entries = new ArrayList<ReceiverRecord>(1);
149                     mActions.put(action, entries);
150                 }
151                 entries.add(entry);
152             }
153         }
154     }
155 
156     /**
157      * Unregister a previously registered BroadcastReceiver.  <em>All</em>
158      * filters that have been registered for this BroadcastReceiver will be
159      * removed.
160      *
161      * @param receiver The BroadcastReceiver to unregister.
162      *
163      * @see #registerReceiver
164      */
unregisterReceiver(BroadcastReceiver receiver)165     public void unregisterReceiver(BroadcastReceiver receiver) {
166         synchronized (mReceivers) {
167             final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
168             if (filters == null) {
169                 return;
170             }
171             for (int i=filters.size()-1; i>=0; i--) {
172                 final ReceiverRecord filter = filters.get(i);
173                 filter.dead = true;
174                 for (int j=0; j<filter.filter.countActions(); j++) {
175                     final String action = filter.filter.getAction(j);
176                     final ArrayList<ReceiverRecord> receivers = mActions.get(action);
177                     if (receivers != null) {
178                         for (int k=receivers.size()-1; k>=0; k--) {
179                             final ReceiverRecord rec = receivers.get(k);
180                             if (rec.receiver == receiver) {
181                                 rec.dead = true;
182                                 receivers.remove(k);
183                             }
184                         }
185                         if (receivers.size() <= 0) {
186                             mActions.remove(action);
187                         }
188                     }
189                 }
190             }
191         }
192     }
193 
194     /**
195      * Broadcast the given intent to all interested BroadcastReceivers.  This
196      * call is asynchronous; it returns immediately, and you will continue
197      * executing while the receivers are run.
198      *
199      * @param intent The Intent to broadcast; all receivers matching this
200      *     Intent will receive the broadcast.
201      *
202      * @see #registerReceiver
203      *
204      * @return Returns true if the intent has been scheduled for delivery to one or more
205      * broadcast receivers.  (Note tha delivery may not ultimately take place if one of those
206      * receivers is unregistered before it is dispatched.)
207      */
sendBroadcast(Intent intent)208     public boolean sendBroadcast(Intent intent) {
209         synchronized (mReceivers) {
210             final String action = intent.getAction();
211             final String type = intent.resolveTypeIfNeeded(
212                     mAppContext.getContentResolver());
213             final Uri data = intent.getData();
214             final String scheme = intent.getScheme();
215             final Set<String> categories = intent.getCategories();
216 
217             final boolean debug = DEBUG ||
218                     ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
219             if (debug) Log.v(
220                     TAG, "Resolving type " + type + " scheme " + scheme
221                     + " of intent " + intent);
222 
223             ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
224             if (entries != null) {
225                 if (debug) Log.v(TAG, "Action list: " + entries);
226 
227                 ArrayList<ReceiverRecord> receivers = null;
228                 for (int i=0; i<entries.size(); i++) {
229                     ReceiverRecord receiver = entries.get(i);
230                     if (debug) Log.v(TAG, "Matching against filter " + receiver.filter);
231 
232                     if (receiver.broadcasting) {
233                         if (debug) {
234                             Log.v(TAG, "  Filter's target already added");
235                         }
236                         continue;
237                     }
238 
239                     int match = receiver.filter.match(action, type, scheme, data,
240                             categories, "LocalBroadcastManager");
241                     if (match >= 0) {
242                         if (debug) Log.v(TAG, "  Filter matched!  match=0x" +
243                                 Integer.toHexString(match));
244                         if (receivers == null) {
245                             receivers = new ArrayList<ReceiverRecord>();
246                         }
247                         receivers.add(receiver);
248                         receiver.broadcasting = true;
249                     } else {
250                         if (debug) {
251                             String reason;
252                             switch (match) {
253                                 case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
254                                 case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
255                                 case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
256                                 case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
257                                 default: reason = "unknown reason"; break;
258                             }
259                             Log.v(TAG, "  Filter did not match: " + reason);
260                         }
261                     }
262                 }
263 
264                 if (receivers != null) {
265                     for (int i=0; i<receivers.size(); i++) {
266                         receivers.get(i).broadcasting = false;
267                     }
268                     mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
269                     if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
270                         mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
271                     }
272                     return true;
273                 }
274             }
275         }
276         return false;
277     }
278 
279     /**
280      * Like {@link #sendBroadcast(Intent)}, but if there are any receivers for
281      * the Intent this function will block and immediately dispatch them before
282      * returning.
283      */
sendBroadcastSync(Intent intent)284     public void sendBroadcastSync(Intent intent) {
285         if (sendBroadcast(intent)) {
286             executePendingBroadcasts();
287         }
288     }
289 
executePendingBroadcasts()290     private void executePendingBroadcasts() {
291         while (true) {
292             final BroadcastRecord[] brs;
293             synchronized (mReceivers) {
294                 final int N = mPendingBroadcasts.size();
295                 if (N <= 0) {
296                     return;
297                 }
298                 brs = new BroadcastRecord[N];
299                 mPendingBroadcasts.toArray(brs);
300                 mPendingBroadcasts.clear();
301             }
302             for (int i=0; i<brs.length; i++) {
303                 final BroadcastRecord br = brs[i];
304                 final int nbr = br.receivers.size();
305                 for (int j=0; j<nbr; j++) {
306                     final ReceiverRecord rec = br.receivers.get(j);
307                     if (!rec.dead) {
308                         rec.receiver.onReceive(mAppContext, br.intent);
309                     }
310                 }
311             }
312         }
313     }
314 }
315