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
17package com.android.internal.util.test;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.ContextWrapper;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.os.Bundle;
25import android.os.Handler;
26import android.os.UserHandle;
27
28import java.util.ArrayList;
29import java.util.Iterator;
30import java.util.List;
31import java.util.concurrent.ExecutionException;
32import java.util.concurrent.Future;
33import java.util.concurrent.FutureTask;
34import java.util.concurrent.TimeUnit;
35import java.util.concurrent.TimeoutException;
36
37/**
38 * {@link ContextWrapper} that can attach listeners for upcoming
39 * {@link Context#sendBroadcast(Intent)}.
40 */
41public class BroadcastInterceptingContext extends ContextWrapper {
42    private static final String TAG = "WatchingContext";
43
44    private final List<BroadcastInterceptor> mInterceptors = new ArrayList<>();
45
46    public abstract class FutureIntent extends FutureTask<Intent> {
47        public FutureIntent() {
48            super(
49                () -> { throw new IllegalStateException("Cannot happen"); }
50            );
51        }
52
53        public void assertNotReceived()
54                throws InterruptedException, ExecutionException {
55            assertNotReceived(5, TimeUnit.SECONDS);
56        }
57
58        public abstract void assertNotReceived(long timeout, TimeUnit unit)
59                throws InterruptedException, ExecutionException;
60    }
61
62    public class BroadcastInterceptor extends FutureIntent {
63        private final BroadcastReceiver mReceiver;
64        private final IntentFilter mFilter;
65
66        public BroadcastInterceptor(BroadcastReceiver receiver, IntentFilter filter) {
67            mReceiver = receiver;
68            mFilter = filter;
69        }
70
71        public boolean dispatchBroadcast(Intent intent) {
72            if (mFilter.match(getContentResolver(), intent, false, TAG) > 0) {
73                if (mReceiver != null) {
74                    final Context context = BroadcastInterceptingContext.this;
75                    mReceiver.onReceive(context, intent);
76                    return false;
77                } else {
78                    set(intent);
79                    return true;
80                }
81            } else {
82                return false;
83            }
84        }
85
86        @Override
87        public Intent get() throws InterruptedException, ExecutionException {
88            try {
89                return get(5, TimeUnit.SECONDS);
90            } catch (TimeoutException e) {
91                throw new RuntimeException(e);
92            }
93        }
94
95        @Override
96        public void assertNotReceived()
97            throws InterruptedException, ExecutionException {
98            assertNotReceived(5, TimeUnit.SECONDS);
99        }
100
101        public void assertNotReceived(long timeout, TimeUnit unit)
102                throws InterruptedException, ExecutionException {
103            try {
104                final Intent intent = get(timeout, unit);
105                throw new AssertionError("Received intent: " + intent);
106            } catch (TimeoutException e) {
107            }
108        }
109    }
110
111    public BroadcastInterceptingContext(Context base) {
112        super(base);
113    }
114
115    public FutureIntent nextBroadcastIntent(String action) {
116        return nextBroadcastIntent(new IntentFilter(action));
117    }
118
119    public FutureIntent nextBroadcastIntent(IntentFilter filter) {
120        final BroadcastInterceptor interceptor = new BroadcastInterceptor(null, filter);
121        synchronized (mInterceptors) {
122            mInterceptors.add(interceptor);
123        }
124        return interceptor;
125    }
126
127    @Override
128    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
129        synchronized (mInterceptors) {
130            mInterceptors.add(new BroadcastInterceptor(receiver, filter));
131        }
132        return null;
133    }
134
135    @Override
136    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
137            String broadcastPermission, Handler scheduler) {
138        return registerReceiver(receiver, filter);
139    }
140
141    @Override
142    public void unregisterReceiver(BroadcastReceiver receiver) {
143        synchronized (mInterceptors) {
144            final Iterator<BroadcastInterceptor> i = mInterceptors.iterator();
145            while (i.hasNext()) {
146                final BroadcastInterceptor interceptor = i.next();
147                if (receiver.equals(interceptor.mReceiver)) {
148                    i.remove();
149                }
150            }
151        }
152    }
153
154    @Override
155    public void sendBroadcast(Intent intent) {
156        synchronized (mInterceptors) {
157            final Iterator<BroadcastInterceptor> i = mInterceptors.iterator();
158            while (i.hasNext()) {
159                final BroadcastInterceptor interceptor = i.next();
160                if (interceptor.dispatchBroadcast(intent)) {
161                    i.remove();
162                }
163            }
164        }
165    }
166
167    @Override
168    public void sendBroadcast(Intent intent, String receiverPermission) {
169        sendBroadcast(intent);
170    }
171
172    @Override
173    public void sendBroadcastMultiplePermissions(Intent intent, String[] receiverPermissions) {
174        sendBroadcast(intent);
175    }
176
177    @Override
178    public void sendBroadcastAsUser(Intent intent, UserHandle user) {
179        sendBroadcast(intent);
180    }
181
182    @Override
183    public void sendBroadcastAsUser(Intent intent, UserHandle user,
184            String receiverPermission) {
185        sendBroadcast(intent);
186    }
187
188    @Override
189    public void sendStickyBroadcast(Intent intent) {
190        sendBroadcast(intent);
191    }
192
193    @Override
194    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
195        sendBroadcast(intent);
196    }
197
198    @Override
199    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
200        sendBroadcast(intent);
201    }
202
203    @Override
204    public void removeStickyBroadcast(Intent intent) {
205        // ignored
206    }
207}
208