1 /*
2  * Copyright (C) 2018 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 package com.android.managedprovisioning.common;
17 
18 import android.content.BroadcastReceiver;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.IntentFilter;
22 import androidx.annotation.Nullable;
23 import android.util.Log;
24 
25 import java.util.concurrent.ArrayBlockingQueue;
26 import java.util.concurrent.BlockingQueue;
27 import java.util.concurrent.TimeUnit;
28 
29 /**
30  * A receiver that allows caller to wait for the broadcast synchronously. Notice that you should not
31  * reuse the instance. Usage is typically like this:
32  * <pre>
33  *     BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(context, "action");
34  *     try {
35  *         receiver.register();
36  *         Intent intent = receiver.awaitForBroadcast();
37  *         // assert the intent
38  *     } finally {
39  *         receiver.unregisterQuietly();
40  *     }
41  * </pre>
42  */
43 public class BlockingBroadcastReceiver extends BroadcastReceiver {
44     private static final String TAG = "BlockingBroadcast";
45 
46     private static final int DEFAULT_TIMEOUT_SECONDS = 10;
47 
48     private final BlockingQueue<Intent> mBlockingQueue;
49     private final String mExpectedAction;
50     private final Context mContext;
51 
BlockingBroadcastReceiver(Context context, String expectedAction)52     public BlockingBroadcastReceiver(Context context, String expectedAction) {
53         mContext = context;
54         mExpectedAction = expectedAction;
55         mBlockingQueue = new ArrayBlockingQueue<>(1);
56     }
57 
58     @Override
onReceive(Context context, Intent intent)59     public void onReceive(Context context, Intent intent) {
60         if (mExpectedAction.equals(intent.getAction())) {
61             mBlockingQueue.add(intent);
62         }
63     }
64 
register()65     public void register() {
66         mContext.registerReceiver(this, new IntentFilter(mExpectedAction),
67                 Context.RECEIVER_EXPORTED/*UNAUDITED*/);
68     }
69 
70     /**
71      * Wait until the broadcast and return the received broadcast intent. {@code null} is returned
72      * if no broadcast with expected action is received within 10 seconds.
73      */
awaitForBroadcast()74     public @Nullable Intent awaitForBroadcast() {
75         try {
76             return mBlockingQueue.poll(DEFAULT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
77         } catch (InterruptedException e) {
78             Log.e(TAG, "waitForBroadcast get interrupted: ", e);
79         }
80         return null;
81     }
82 
83     /**
84      * Wait until the broadcast and return the received broadcast intent. {@code null} is returned
85      * if no broadcast with expected action is received within the given timeout.
86      */
awaitForBroadcast(long timeoutMillis)87     public @Nullable Intent awaitForBroadcast(long timeoutMillis) {
88         try {
89             return mBlockingQueue.poll(timeoutMillis, TimeUnit.MILLISECONDS);
90         } catch (InterruptedException e) {
91             Log.e(TAG, "waitForBroadcast get interrupted: ", e);
92         }
93         return null;
94     }
95 
unregisterQuietly()96     public void unregisterQuietly() {
97         try {
98             mContext.unregisterReceiver(this);
99         } catch (Exception ex) {
100             Log.e(TAG, "Failed to unregister BlockingBroadcastReceiver: ", ex);
101         }
102     }
103 }
104