• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.multiuser;
18 
19 import static java.util.concurrent.TimeUnit.SECONDS;
20 
21 import android.content.BroadcastReceiver;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.text.TextUtils;
27 import android.util.Log;
28 
29 import java.io.Closeable;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.concurrent.ConcurrentHashMap;
37 import java.util.concurrent.Semaphore;
38 
39 public class BroadcastWaiter implements Closeable {
40     private final Context mContext;
41     private final String mTag;
42     private final int mTimeoutInSecond;
43     private final Set<String> mActions;
44 
45     private final Set<String> mActionReceivedForUser = new HashSet<>();
46     private final List<BroadcastReceiver> mBroadcastReceivers = new ArrayList<>();
47 
48     private final Map<String, Semaphore> mSemaphoresMap = new ConcurrentHashMap<>();
getSemaphore(final String action, final int userId)49     private Semaphore getSemaphore(final String action, final int userId) {
50         final String key = action + userId;
51         return mSemaphoresMap.computeIfAbsent(key, (String absentKey) -> new Semaphore(0));
52     }
53 
BroadcastWaiter(Context context, String tag, int timeoutInSecond, String... actions)54     public BroadcastWaiter(Context context, String tag, int timeoutInSecond, String... actions) {
55         mContext = context;
56         mTag = tag + "_" + BroadcastWaiter.class.getSimpleName();
57         mTimeoutInSecond = timeoutInSecond;
58 
59         mActions = new HashSet<>(Arrays.asList(actions));
60         mActions.forEach(this::registerBroadcastReceiver);
61     }
62 
registerBroadcastReceiver(String action)63     private void registerBroadcastReceiver(String action) {
64         Log.d(mTag, "#registerBroadcastReceiver for " + action);
65 
66         final IntentFilter filter = new IntentFilter(action);
67         if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
68             filter.addDataScheme(ContentResolver.SCHEME_FILE);
69         }
70 
71         final BroadcastReceiver receiver = new BroadcastReceiver() {
72             @Override
73             public void onReceive(Context context, Intent intent) {
74                 if (action.equals(intent.getAction())) {
75                     final int userId = getSendingUserId();
76                     final String data = intent.getDataString();
77                     Log.d(mTag, "Received " + action + " for user " + userId
78                             + (!TextUtils.isEmpty(data) ? " with " + data : ""));
79                     mActionReceivedForUser.add(action + userId);
80                     getSemaphore(action, userId).release();
81                 }
82             }
83         };
84 
85         mContext.registerReceiverForAllUsers(receiver, filter, null, null);
86         mBroadcastReceivers.add(receiver);
87     }
88 
89     @Override
close()90     public void close() {
91         mBroadcastReceivers.forEach(mContext::unregisterReceiver);
92     }
93 
hasActionBeenReceivedForUser(String action, int userId)94     public boolean hasActionBeenReceivedForUser(String action, int userId) {
95         return mActionReceivedForUser.contains(action + userId);
96     }
97 
waitActionForUser(String action, int userId)98     public boolean waitActionForUser(String action, int userId) {
99         Log.d(mTag, "#waitActionForUser(action: " + action + ", userId: " + userId + ")");
100 
101         if (!mActions.contains(action)) {
102             Log.d(mTag, "No broadcast receivers registered for " + action);
103             return false;
104         }
105 
106         try {
107             if (!getSemaphore(action, userId).tryAcquire(1, mTimeoutInSecond, SECONDS)) {
108                 Log.e(mTag, action + " broadcast wasn't received for user " + userId);
109                 return false;
110             }
111         } catch (InterruptedException e) {
112             Log.e(mTag, "Interrupted while waiting " + action + " for user " + userId);
113             return false;
114         }
115         return true;
116     }
117 
waitActionForUserIfNotReceivedYet(String action, int userId)118     public boolean waitActionForUserIfNotReceivedYet(String action, int userId) {
119         return hasActionBeenReceivedForUser(action, userId)
120                 || waitActionForUser(action, userId);
121     }
122 }
123