• 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 com.android.internal.telephony.domainselection;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.AsyncResult;
22 import android.os.CancellationSignal;
23 import android.os.Handler;
24 import android.os.HandlerThread;
25 import android.os.Looper;
26 import android.os.Message;
27 import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
28 import android.telephony.Annotation.DisconnectCauses;
29 import android.telephony.DomainSelectionService;
30 import android.telephony.DomainSelectionService.EmergencyScanType;
31 import android.telephony.DomainSelector;
32 import android.telephony.EmergencyRegResult;
33 import android.telephony.NetworkRegistrationInfo;
34 import android.telephony.TransportSelectorCallback;
35 import android.telephony.WwanSelectorCallback;
36 import android.util.LocalLog;
37 import android.util.Log;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.internal.infra.AndroidFuture;
41 import com.android.internal.telephony.Phone;
42 import com.android.internal.telephony.util.TelephonyUtils;
43 
44 import java.io.PrintWriter;
45 import java.util.List;
46 import java.util.concurrent.CompletableFuture;
47 import java.util.concurrent.Executor;
48 import java.util.concurrent.Executors;
49 import java.util.function.Consumer;
50 
51 
52 /**
53  * Manages the information of request and the callback binder.
54  */
55 public class DomainSelectionConnection {
56 
57     private static final boolean DBG = TelephonyUtils.IS_DEBUGGABLE;
58 
59     protected static final int EVENT_EMERGENCY_NETWORK_SCAN_RESULT = 1;
60     protected static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 2;
61 
62     /** Callback to receive responses from DomainSelectionConnection. */
63     public interface DomainSelectionConnectionCallback {
64         /**
65          * Notifies that selection has terminated because there is no decision that can be made
66          * or a timeout has occurred. The call should be terminated when this method is called.
67          *
68          * @param cause Indicates the reason.
69          */
onSelectionTerminated(@isconnectCauses int cause)70         void onSelectionTerminated(@DisconnectCauses int cause);
71     }
72 
73     /** An internal class implementing {@link TransportSelectorCallback} interface. */
74     private final class TransportSelectorCallbackWrapper implements TransportSelectorCallback {
75         @Override
onCreated(@onNull DomainSelector selector)76         public void onCreated(@NonNull DomainSelector selector) {
77             mDomainSelector = selector;
78             DomainSelectionConnection.this.onCreated();
79         }
80 
81         @Override
onWlanSelected(boolean useEmergencyPdn)82         public void onWlanSelected(boolean useEmergencyPdn) {
83             DomainSelectionConnection.this.onWlanSelected(useEmergencyPdn);
84         }
85 
86         @Override
onWwanSelected()87         public @NonNull WwanSelectorCallback onWwanSelected() {
88             if (mWwanSelectorCallback == null) {
89                 mWwanSelectorCallback = new WwanSelectorCallbackWrapper();
90             }
91             DomainSelectionConnection.this.onWwanSelected();
92             return mWwanSelectorCallback;
93         }
94 
95         @Override
onWwanSelected(final Consumer<WwanSelectorCallback> consumer)96         public void onWwanSelected(final Consumer<WwanSelectorCallback> consumer) {
97             if (mWwanSelectorCallback == null) {
98                 mWwanSelectorCallback = new WwanSelectorCallbackWrapper();
99             }
100             if (mWwanSelectedExecutor == null) {
101                 mWwanSelectedExecutor = Executors.newSingleThreadExecutor();
102             }
103             mWwanSelectedExecutor.execute(() -> {
104                 DomainSelectionConnection.this.onWwanSelected();
105                 consumer.accept(mWwanSelectorCallback);
106             });
107         }
108 
109         @Override
onSelectionTerminated(int cause)110         public void onSelectionTerminated(int cause) {
111             DomainSelectionConnection.this.onSelectionTerminated(cause);
112             dispose();
113         }
114     }
115 
116     /** An internal class implementing {@link WwanSelectorCallback} interface. */
117     private final class WwanSelectorCallbackWrapper
118             implements WwanSelectorCallback, CancellationSignal.OnCancelListener {
119         @Override
onRequestEmergencyNetworkScan(@onNull List<Integer> preferredNetworks, @EmergencyScanType int scanType, @NonNull CancellationSignal signal, @NonNull Consumer<EmergencyRegResult> consumer)120         public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks,
121                 @EmergencyScanType int scanType, @NonNull CancellationSignal signal,
122                 @NonNull Consumer<EmergencyRegResult> consumer) {
123             if (signal != null) signal.setOnCancelListener(this);
124             mResultCallback = consumer;
125             initHandler();
126             DomainSelectionConnection.this.onRequestEmergencyNetworkScan(
127                     preferredNetworks.stream().mapToInt(Integer::intValue).toArray(), scanType);
128         }
129 
130         @Override
onDomainSelected(@etworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn)131         public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
132                 boolean useEmergencyPdn) {
133             DomainSelectionConnection.this.onDomainSelected(domain, useEmergencyPdn);
134         }
135 
136         @Override
onCancel()137         public void onCancel() {
138             DomainSelectionConnection.this.onCancel();
139         }
140     }
141 
142     protected final class DomainSelectionConnectionHandler extends Handler {
DomainSelectionConnectionHandler(Looper looper)143         DomainSelectionConnectionHandler(Looper looper) {
144             super(looper);
145         }
146 
147         @Override
handleMessage(Message msg)148         public void handleMessage(Message msg) {
149             AsyncResult ar;
150             switch (msg.what) {
151                 case EVENT_EMERGENCY_NETWORK_SCAN_RESULT:
152                     mIsWaitingForScanResult = false;
153                     if (mResultCallback == null) break;
154                     ar = (AsyncResult) msg.obj;
155                     EmergencyRegResult regResult = (EmergencyRegResult) ar.result;
156                     if (DBG) logd("EVENT_EMERGENCY_NETWORK_SCAN_RESULT result=" + regResult);
157                     CompletableFuture.runAsync(
158                             () -> mResultCallback.accept(regResult),
159                             mController.getDomainSelectionServiceExecutor()).join();
160                     break;
161                 case EVENT_QUALIFIED_NETWORKS_CHANGED:
162                     onQualifiedNetworksChanged();
163                     break;
164                 default:
165                     loge("handleMessage unexpected msg=" + msg.what);
166                     break;
167             }
168         }
169     }
170 
171     protected String mTag = "DomainSelectionConnection";
172 
173     private final LocalLog mLocalLog = new LocalLog(30);
174     private final @NonNull TransportSelectorCallback mTransportSelectorCallback;
175 
176     /**
177      * Controls the communication between {@link DomainSelectionConnection} and
178      * {@link DomainSelectionService}.
179      */
180     private final @NonNull DomainSelectionController mController;
181     /** Indicates whether the requested service is for emergency services. */
182     private final boolean mIsEmergency;
183 
184     /** Interface to receive the request to trigger emergency network scan and selected domain. */
185     private @Nullable WwanSelectorCallback mWwanSelectorCallback;
186     /** Interface to return the result of emergency network scan. */
187     private @Nullable Consumer<EmergencyRegResult> mResultCallback;
188     /** Interface to the {@link DomainSelector} created for this service. */
189     private @Nullable DomainSelector mDomainSelector;
190 
191     /** The slot requested this connection. */
192     protected @NonNull Phone mPhone;
193     /** The requested domain selector type. */
194     private @DomainSelectionService.SelectorType int mSelectorType;
195 
196     /** The attributes required to determine the domain. */
197     private @Nullable DomainSelectionService.SelectionAttributes mSelectionAttributes;
198 
199     private @Nullable Looper mLooper;
200     protected @Nullable DomainSelectionConnectionHandler mHandler;
201     private boolean mRegisteredRegistrant;
202     private boolean mIsWaitingForScanResult;
203 
204     private @NonNull AndroidFuture<Integer> mOnComplete;
205 
206     private @Nullable Executor mWwanSelectedExecutor;
207 
208     /**
209      * Creates an instance.
210      *
211      * @param phone For which this service is requested.
212      * @param selectorType Indicates the type of the requested service.
213      * @param isEmergency Indicates whether this request is for emergency service.
214      * @param controller The controller to communicate with the domain selection service.
215      */
DomainSelectionConnection(@onNull Phone phone, @DomainSelectionService.SelectorType int selectorType, boolean isEmergency, @NonNull DomainSelectionController controller)216     public DomainSelectionConnection(@NonNull Phone phone,
217             @DomainSelectionService.SelectorType int selectorType, boolean isEmergency,
218             @NonNull DomainSelectionController controller) {
219         mController = controller;
220         mPhone = phone;
221         mSelectorType = selectorType;
222         mIsEmergency = isEmergency;
223 
224         mTransportSelectorCallback = new TransportSelectorCallbackWrapper();
225         mOnComplete = new AndroidFuture<>();
226     }
227 
228     /**
229      * Returns the attributes required to determine the domain for a telephony service.
230      *
231      * @return The attributes required to determine the domain.
232      */
getSelectionAttributes()233     public @Nullable DomainSelectionService.SelectionAttributes getSelectionAttributes() {
234         return mSelectionAttributes;
235     }
236 
237     /**
238      * Returns the interface for the callbacks.
239      *
240      * @return The {@link TransportSelectorCallback} interface.
241      */
242     @VisibleForTesting
getTransportSelectorCallback()243     public @NonNull TransportSelectorCallback getTransportSelectorCallback() {
244         return mTransportSelectorCallback;
245     }
246 
247     /**
248      * Returns the {@link CompletableFuture} to receive the selected domain.
249      *
250      * @return The callback to receive response.
251      */
getCompletableFuture()252     public @NonNull CompletableFuture<Integer> getCompletableFuture() {
253         return mOnComplete;
254     }
255 
256     /**
257      * Returs the {@link Phone} which requested this connection.
258      *
259      * @return The {@link Phone} instance.
260      */
getPhone()261     public @NonNull Phone getPhone() {
262         return mPhone;
263     }
264 
265     /**
266      * Requests the domain selection servic to select a domain.
267      *
268      * @param attr The attributes required to determine the domain.
269      */
270     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
selectDomain(@onNull DomainSelectionService.SelectionAttributes attr)271     public void selectDomain(@NonNull DomainSelectionService.SelectionAttributes attr) {
272         mSelectionAttributes = attr;
273         mController.selectDomain(attr, getTransportSelectorCallback());
274     }
275 
276     /**
277      * Notifies that {@link DomainSelector} instance has been created for the selection request.
278      */
onCreated()279     public void onCreated() {
280         // Can be overridden if required
281     }
282 
283     /**
284      * Notifies that WLAN transport has been selected.
285      */
onWlanSelected()286     public void onWlanSelected() {
287         // Can be overridden.
288     }
289 
290     /**
291      * Notifies that WLAN transport has been selected.
292      *
293      * @param useEmergencyPdn Indicates whether Wi-Fi emergency services use emergency PDN or not.
294      */
onWlanSelected(boolean useEmergencyPdn)295     public void onWlanSelected(boolean useEmergencyPdn) {
296         // Can be overridden.
297         onWlanSelected();
298     }
299 
300     /**
301      * Notifies that WWAN transport has been selected.
302      */
onWwanSelected()303     public void onWwanSelected() {
304         // Can be overridden.
305     }
306 
307     /**
308      * Notifies that selection has terminated because there is no decision that can be made
309      * or a timeout has occurred. The call should be terminated when this method is called.
310      *
311      * @param cause Indicates the reason.
312      */
onSelectionTerminated(@isconnectCauses int cause)313     public void onSelectionTerminated(@DisconnectCauses int cause) {
314         // Can be overridden.
315     }
316 
317     /**
318      * Requests the emergency network scan.
319      *
320      * @param preferredNetworks The ordered list of preferred networks to scan.
321      * @param scanType Indicates the scan preference, such as full service or limited service.
322      */
onRequestEmergencyNetworkScan( @onNull @adioAccessNetworkType int[] preferredNetworks, @EmergencyScanType int scanType)323     public void onRequestEmergencyNetworkScan(
324             @NonNull @RadioAccessNetworkType int[] preferredNetworks,
325             @EmergencyScanType int scanType) {
326         // Can be overridden if required
327         if (!mRegisteredRegistrant) {
328             mPhone.registerForEmergencyNetworkScan(mHandler,
329                     EVENT_EMERGENCY_NETWORK_SCAN_RESULT, null);
330             mRegisteredRegistrant = true;
331         }
332         mIsWaitingForScanResult = true;
333         mPhone.triggerEmergencyNetworkScan(preferredNetworks, scanType, null);
334     }
335 
336     /**
337      * Notifies the domain selected.
338      *
339      * @param domain The selected domain.
340      */
onDomainSelected(@etworkRegistrationInfo.Domain int domain)341     public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain) {
342         // Can be overridden if required
343         CompletableFuture<Integer> future = getCompletableFuture();
344         future.complete(domain);
345     }
346 
347     /**
348      * Notifies the domain selected.
349      *
350      * @param domain The selected domain.
351      * @param useEmergencyPdn Indicates whether emergency services use emergency PDN or not.
352      */
onDomainSelected(@etworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn)353     public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
354             boolean useEmergencyPdn) {
355         // Can be overridden if required
356         onDomainSelected(domain);
357     }
358 
359     /**
360      * Notifies that the emergency network scan is canceled.
361      */
onCancel()362     public void onCancel() {
363         // Can be overridden if required
364         onCancel(false);
365     }
366 
onCancel(boolean resetScan)367     private void onCancel(boolean resetScan) {
368         if (mIsWaitingForScanResult) {
369             mIsWaitingForScanResult = false;
370             mPhone.cancelEmergencyNetworkScan(resetScan, null);
371         }
372     }
373 
374     /**
375      * Cancels an ongoing selection operation. It is up to the {@link DomainSelectionService}
376      * to clean up all ongoing operations with the framework.
377      */
cancelSelection()378     public void cancelSelection() {
379         if (mDomainSelector == null) return;
380         mDomainSelector.cancelSelection();
381         dispose();
382     }
383 
384     /**
385      * Requests the domain selection service to reselect a domain.
386      *
387      * @param attr The attributes required to determine the domain.
388      * @return The callback to receive the response.
389      */
reselectDomain( @onNull DomainSelectionService.SelectionAttributes attr)390     public @NonNull CompletableFuture<Integer> reselectDomain(
391             @NonNull DomainSelectionService.SelectionAttributes attr) {
392         mSelectionAttributes = attr;
393         if (mDomainSelector == null) return null;
394         mOnComplete = new AndroidFuture<>();
395         mDomainSelector.reselectDomain(attr);
396         return mOnComplete;
397     }
398 
399     /**
400      * Finishes the selection procedure and cleans everything up.
401      */
finishSelection()402     public void finishSelection() {
403         if (mDomainSelector == null) return;
404         mDomainSelector.finishSelection();
405         dispose();
406     }
407 
408     /** Indicates that the service connection has been removed. */
onServiceDisconnected()409     public void onServiceDisconnected() {
410         // Can be overridden.
411         dispose();
412     }
413 
dispose()414     private void dispose() {
415         if (mRegisteredRegistrant) {
416             mPhone.unregisterForEmergencyNetworkScan(mHandler);
417             mRegisteredRegistrant = false;
418         }
419         onCancel(true);
420         mController.removeConnection(this);
421         if (mLooper != null) mLooper.quitSafely();
422         mLooper = null;
423         mHandler = null;
424     }
425 
initHandler()426     protected void initHandler() {
427         if (mLooper == null) {
428             HandlerThread handlerThread = new HandlerThread(mTag);
429             handlerThread.start();
430             mLooper = handlerThread.getLooper();
431         }
432         if (mHandler == null) mHandler = new DomainSelectionConnectionHandler(mLooper);
433     }
434 
435     /**
436      * Notifies the change of qualified networks.
437      */
onQualifiedNetworksChanged()438     protected void onQualifiedNetworksChanged() {
439         if (mIsEmergency
440                 && (mSelectorType == DomainSelectionService.SELECTOR_TYPE_CALLING)) {
441             // DomainSelectionConnection for emergency calls shall override this.
442             throw new IllegalStateException("DomainSelectionConnection for emergency calls"
443                     + " should override onQualifiedNetworksChanged()");
444         }
445     }
446 
447     /**
448      * Dumps local log.
449      */
dump(@onNull PrintWriter printWriter)450     public void dump(@NonNull PrintWriter printWriter) {
451         mLocalLog.dump(printWriter);
452     }
453 
logd(String msg)454     protected void logd(String msg) {
455         Log.d(mTag, msg);
456     }
457 
logi(String msg)458     protected void logi(String msg) {
459         Log.i(mTag, msg);
460         mLocalLog.log(msg);
461     }
462 
loge(String msg)463     protected void loge(String msg) {
464         Log.e(mTag, msg);
465         mLocalLog.log(msg);
466     }
467 }
468