blob: e2804abd752bbcadf1fc1759a23760f99de60d4b [file] [log] [blame]
Erik Klinede637722017-10-12 22:16:01 +09001/*
2 * Copyright (C) 2017 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 android.net.util;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.os.Handler;
24import android.util.Log;
25
26import java.util.concurrent.atomic.AtomicInteger;
27import java.util.function.Consumer;
28
29
30/**
31 * A utility class that runs the provided callback on the provided handler when
32 * intents matching the provided filter arrive. Intents received by a stale
33 * receiver are safely ignored.
34 *
35 * Calls to startListening() and stopListening() must happen on the same thread.
36 *
37 * @hide
38 */
39public class VersionedBroadcastListener {
40 private static final boolean DBG = false;
41
Erik Klinede637722017-10-12 22:16:01 +090042 private final String mTag;
43 private final Context mContext;
44 private final Handler mHandler;
45 private final IntentFilter mFilter;
46 private final Consumer<Intent> mCallback;
47 private final AtomicInteger mGenerationNumber;
48 private BroadcastReceiver mReceiver;
49
50 public VersionedBroadcastListener(String tag, Context ctx, Handler handler,
51 IntentFilter filter, Consumer<Intent> callback) {
52 mTag = tag;
53 mContext = ctx;
54 mHandler = handler;
55 mFilter = filter;
56 mCallback = callback;
57 mGenerationNumber = new AtomicInteger(0);
58 }
59
markchien0df2ebc42019-09-30 14:40:57 +080060 /** Start listening to intent broadcast. */
Erik Klinede637722017-10-12 22:16:01 +090061 public void startListening() {
62 if (DBG) Log.d(mTag, "startListening");
63 if (mReceiver != null) return;
64
65 mReceiver = new Receiver(mTag, mGenerationNumber, mCallback);
66 mContext.registerReceiver(mReceiver, mFilter, null, mHandler);
67 }
68
markchien0df2ebc42019-09-30 14:40:57 +080069 /** Stop listening to intent broadcast. */
Erik Klinede637722017-10-12 22:16:01 +090070 public void stopListening() {
71 if (DBG) Log.d(mTag, "stopListening");
72 if (mReceiver == null) return;
73
74 mGenerationNumber.incrementAndGet();
75 mContext.unregisterReceiver(mReceiver);
76 mReceiver = null;
77 }
78
79 private static class Receiver extends BroadcastReceiver {
80 public final String tag;
81 public final AtomicInteger atomicGenerationNumber;
82 public final Consumer<Intent> callback;
83 // Used to verify this receiver is still current.
84 public final int generationNumber;
85
markchien0df2ebc42019-09-30 14:40:57 +080086 Receiver(String tag, AtomicInteger atomicGenerationNumber, Consumer<Intent> callback) {
Erik Klinede637722017-10-12 22:16:01 +090087 this.tag = tag;
88 this.atomicGenerationNumber = atomicGenerationNumber;
89 this.callback = callback;
90 generationNumber = atomicGenerationNumber.incrementAndGet();
91 }
92
93 @Override
94 public void onReceive(Context context, Intent intent) {
95 final int currentGenerationNumber = atomicGenerationNumber.get();
96
97 if (DBG) {
markchien0df2ebc42019-09-30 14:40:57 +080098 Log.d(tag, "receiver generationNumber=" + generationNumber
99 + ", current generationNumber=" + currentGenerationNumber);
Erik Klinede637722017-10-12 22:16:01 +0900100 }
101 if (generationNumber != currentGenerationNumber) return;
102
103 callback.accept(intent);
104 }
105 }
106}