Home | History | Annotate | Download | only in util
      1 /*
      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 
     17 package android.net.util;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.os.Handler;
     24 import android.util.Log;
     25 
     26 import java.util.concurrent.atomic.AtomicInteger;
     27 import 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  */
     39 public class VersionedBroadcastListener {
     40     private static final boolean DBG = false;
     41 
     42     public interface IntentCallback {
     43         public void run(Intent intent);
     44     }
     45 
     46     private final String mTag;
     47     private final Context mContext;
     48     private final Handler mHandler;
     49     private final IntentFilter mFilter;
     50     private final Consumer<Intent> mCallback;
     51     private final AtomicInteger mGenerationNumber;
     52     private BroadcastReceiver mReceiver;
     53 
     54     public VersionedBroadcastListener(String tag, Context ctx, Handler handler,
     55             IntentFilter filter, Consumer<Intent> callback) {
     56         mTag = tag;
     57         mContext = ctx;
     58         mHandler = handler;
     59         mFilter = filter;
     60         mCallback = callback;
     61         mGenerationNumber = new AtomicInteger(0);
     62     }
     63 
     64     public void startListening() {
     65         if (DBG) Log.d(mTag, "startListening");
     66         if (mReceiver != null) return;
     67 
     68         mReceiver = new Receiver(mTag, mGenerationNumber, mCallback);
     69         mContext.registerReceiver(mReceiver, mFilter, null, mHandler);
     70     }
     71 
     72     public void stopListening() {
     73         if (DBG) Log.d(mTag, "stopListening");
     74         if (mReceiver == null) return;
     75 
     76         mGenerationNumber.incrementAndGet();
     77         mContext.unregisterReceiver(mReceiver);
     78         mReceiver = null;
     79     }
     80 
     81     private static class Receiver extends BroadcastReceiver {
     82         public final String tag;
     83         public final AtomicInteger atomicGenerationNumber;
     84         public final Consumer<Intent> callback;
     85         // Used to verify this receiver is still current.
     86         public final int generationNumber;
     87 
     88         public Receiver(
     89                 String tag, AtomicInteger atomicGenerationNumber, Consumer<Intent> callback) {
     90             this.tag = tag;
     91             this.atomicGenerationNumber = atomicGenerationNumber;
     92             this.callback = callback;
     93             generationNumber = atomicGenerationNumber.incrementAndGet();
     94         }
     95 
     96         @Override
     97         public void onReceive(Context context, Intent intent) {
     98             final int currentGenerationNumber = atomicGenerationNumber.get();
     99 
    100             if (DBG) {
    101                 Log.d(tag, "receiver generationNumber=" + generationNumber +
    102                         ", current generationNumber=" + currentGenerationNumber);
    103             }
    104             if (generationNumber != currentGenerationNumber) return;
    105 
    106             callback.accept(intent);
    107         }
    108     }
    109 }
    110