• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 com.android.server.connectivity.mdns;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.Handler;
22 import android.os.HandlerThread;
23 import android.os.Looper;
24 import android.util.Log;
25 import android.util.Pair;
26 
27 import androidx.annotation.GuardedBy;
28 
29 import com.android.net.module.util.HandlerUtils;
30 
31 import java.util.ArrayList;
32 import java.util.concurrent.Executor;
33 
34 /**
35  * A utility class to generate a handler, optionally with a looper, and to run functions on the
36  * newly created handler.
37  */
38 public class DiscoveryExecutor implements Executor {
39     private static final String TAG = DiscoveryExecutor.class.getSimpleName();
40     @Nullable
41     private final HandlerThread mHandlerThread;
42 
43     @GuardedBy("mPendingTasks")
44     @Nullable
45     private Handler mHandler;
46     // Store pending tasks and associated delay time. Each Pair represents a pending task
47     // (first) and its delay time (second).
48     @GuardedBy("mPendingTasks")
49     @NonNull
50     private final ArrayList<Pair<Runnable, Long>> mPendingTasks = new ArrayList<>();
51 
52     @GuardedBy("mPendingTasks")
53     @Nullable
54     Scheduler mScheduler;
55     @NonNull private final MdnsFeatureFlags mMdnsFeatureFlags;
56 
DiscoveryExecutor(@ullable Looper defaultLooper, @NonNull MdnsFeatureFlags mdnsFeatureFlags)57     DiscoveryExecutor(@Nullable Looper defaultLooper, @NonNull MdnsFeatureFlags mdnsFeatureFlags) {
58         mMdnsFeatureFlags = mdnsFeatureFlags;
59         if (defaultLooper != null) {
60             this.mHandlerThread = null;
61             synchronized (mPendingTasks) {
62                 this.mHandler = new Handler(defaultLooper);
63             }
64         } else {
65             this.mHandlerThread = new HandlerThread(MdnsDiscoveryManager.class.getSimpleName()) {
66                 @Override
67                 protected void onLooperPrepared() {
68                     synchronized (mPendingTasks) {
69                         mHandler = new Handler(getLooper());
70                         for (Pair<Runnable, Long> pendingTask : mPendingTasks) {
71                             executeDelayed(pendingTask.first, pendingTask.second);
72                         }
73                         mPendingTasks.clear();
74                     }
75                 }
76             };
77             this.mHandlerThread.start();
78         }
79     }
80 
81     /**
82      * Check if the current thread is the expected thread. If it is, run the given function.
83      * Otherwise, execute it using the handler.
84      */
checkAndRunOnHandlerThread(@onNull Runnable function)85     public void checkAndRunOnHandlerThread(@NonNull Runnable function) {
86         if (this.mHandlerThread == null) {
87             // Callers are expected to already be running on the handler when a defaultLooper
88             // was provided
89             function.run();
90         } else {
91             execute(function);
92         }
93     }
94 
95     /** Execute the given function */
96     @Override
execute(Runnable function)97     public void execute(Runnable function) {
98         executeDelayed(function, 0L /* delayMillis */);
99     }
100 
101     /** Execute the given function after the specified amount of time elapses. */
executeDelayed(Runnable function, long delayMillis)102     public void executeDelayed(Runnable function, long delayMillis) {
103         final Handler handler;
104         final Scheduler scheduler;
105         synchronized (mPendingTasks) {
106             if (this.mHandler == null) {
107                 mPendingTasks.add(Pair.create(function, delayMillis));
108                 return;
109             } else {
110                 handler = this.mHandler;
111                 if (mMdnsFeatureFlags.mIsAccurateDelayCallbackEnabled
112                         && this.mScheduler == null) {
113                     this.mScheduler = SchedulerFactory.createScheduler(mHandler);
114                 }
115                 scheduler = this.mScheduler;
116             }
117         }
118         if (scheduler != null) {
119             if (delayMillis == 0L) {
120                 handler.post(function);
121                 return;
122             }
123             if (HandlerUtils.isRunningOnHandlerThread(handler)) {
124                 scheduler.postDelayed(function, delayMillis);
125             } else {
126                 handler.post(() -> scheduler.postDelayed(function, delayMillis));
127             }
128         } else {
129             handler.postDelayed(function, delayMillis);
130         }
131     }
132 
133     /** Shutdown the thread if necessary. */
shutDown()134     public void shutDown() {
135         if (this.mHandlerThread != null) {
136             this.mHandlerThread.quitSafely();
137         }
138         synchronized (mPendingTasks) {
139             if (mScheduler != null) {
140                 mScheduler.close();
141             }
142         }
143     }
144 
145     /**
146      * Ensures that the current running thread is the same as the handler thread.
147      */
ensureRunningOnHandlerThread()148     public void ensureRunningOnHandlerThread() {
149         synchronized (mPendingTasks) {
150             HandlerUtils.ensureRunningOnHandlerThread(mHandler);
151         }
152     }
153 
154     /**
155      * Runs the specified task synchronously for dump method.
156      */
runWithScissorsForDumpIfReady(@onNull Runnable function)157     public void runWithScissorsForDumpIfReady(@NonNull Runnable function) {
158         final Handler handler;
159         synchronized (mPendingTasks) {
160             if (this.mHandler == null) {
161                 Log.d(TAG, "The handler is not ready. Ignore the DiscoveryManager dump");
162                 return;
163             } else {
164                 handler = this.mHandler;
165             }
166         }
167         HandlerUtils.runWithScissorsForDump(handler, function, 10_000);
168     }
169 }
170