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.services.telephony.domainselection;
18 
19 import android.annotation.NonNull;
20 import android.content.Context;
21 import android.os.Looper;
22 import android.os.Message;
23 import android.telephony.DisconnectCause;
24 import android.telephony.DomainSelectionService;
25 import android.telephony.DomainSelectionService.SelectionAttributes;
26 import android.telephony.NetworkRegistrationInfo;
27 import android.telephony.TransportSelectorCallback;
28 
29 /**
30  * Implements SMS domain selector for sending MO SMS.
31  */
32 public class SmsDomainSelector extends DomainSelectorBase implements
33         ImsStateTracker.ImsStateListener {
34     protected static final int EVENT_SELECT_DOMAIN = 101;
35 
36     protected boolean mDestroyed = false;
37     private boolean mDomainSelectionRequested = false;
38 
SmsDomainSelector(Context context, int slotId, int subId, @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker, @NonNull DestroyListener listener)39     public SmsDomainSelector(Context context, int slotId, int subId, @NonNull Looper looper,
40             @NonNull ImsStateTracker imsStateTracker, @NonNull DestroyListener listener) {
41         this(context, slotId, subId, looper, imsStateTracker, listener, "DomainSelector-SMS");
42     }
43 
SmsDomainSelector(Context context, int slotId, int subId, @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker, @NonNull DestroyListener listener, String logTag)44     protected SmsDomainSelector(Context context, int slotId, int subId, @NonNull Looper looper,
45             @NonNull ImsStateTracker imsStateTracker, @NonNull DestroyListener listener,
46             String logTag) {
47         super(context, slotId, subId, looper, imsStateTracker, listener, logTag);
48     }
49 
50     @Override
destroy()51     public void destroy() {
52         if (mDestroyed) {
53             return;
54         }
55         logd("destroy");
56         mDestroyed = true;
57         mImsStateTracker.removeImsStateListener(this);
58         super.destroy();
59     }
60 
61     @Override
handleMessage(@onNull Message msg)62     public void handleMessage(@NonNull Message msg) {
63         switch (msg.what) {
64             case EVENT_SELECT_DOMAIN:
65                 selectDomain();
66                 break;
67             default:
68                 super.handleMessage(msg);
69                 break;
70         }
71     }
72 
73     @Override
reselectDomain(@onNull SelectionAttributes attr)74     public void reselectDomain(@NonNull SelectionAttributes attr) {
75         if (isDomainSelectionRequested()) {
76             // The domain selection is already requested,
77             // so we don't need to request it again before completing the previous task.
78             logi("Domain selection is already running.");
79             return;
80         }
81 
82         logi("reselectDomain");
83         mSelectionAttributes = attr;
84         setDomainSelectionRequested(true);
85         obtainMessage(EVENT_SELECT_DOMAIN).sendToTarget();
86     }
87 
88     @Override
finishSelection()89     public void finishSelection() {
90         logi("finishSelection");
91         setDomainSelectionRequested(false);
92         mSelectionAttributes = null;
93         mTransportSelectorCallback = null;
94         mWwanSelectorCallback = null;
95         destroy();
96     }
97 
98     @Override
selectDomain(SelectionAttributes attr, TransportSelectorCallback callback)99     public void selectDomain(SelectionAttributes attr, TransportSelectorCallback callback) {
100         if (isDomainSelectionRequested()) {
101             // The domain selection is already requested,
102             // so we don't need to request it again before completing the previous task.
103             logi("Domain selection is already running.");
104             return;
105         }
106         mSelectionAttributes = attr;
107         mTransportSelectorCallback = callback;
108         setDomainSelectionRequested(true);
109         mImsStateTracker.addImsStateListener(this);
110         obtainMessage(EVENT_SELECT_DOMAIN).sendToTarget();
111     }
112 
113     @Override
onImsMmTelFeatureAvailableChanged()114     public void onImsMmTelFeatureAvailableChanged() {
115         sendMessageForDomainSelection();
116     }
117 
118     @Override
onImsRegistrationStateChanged()119     public void onImsRegistrationStateChanged() {
120         sendMessageForDomainSelection();
121     }
122 
123     @Override
onImsMmTelCapabilitiesChanged()124     public void onImsMmTelCapabilitiesChanged() {
125         sendMessageForDomainSelection();
126     }
127 
isSmsOverImsAvailable()128     protected boolean isSmsOverImsAvailable() {
129         return mImsStateTracker.isImsSmsCapable()
130                 && mImsStateTracker.isImsRegistered()
131                 && mImsStateTracker.isMmTelFeatureAvailable();
132     }
133 
selectDomain()134     protected void selectDomain() {
135         if (!isDomainSelectionRequested()) {
136             logi("Domain selection is not requested!");
137             return;
138         }
139 
140         logi("selectDomain: " + mImsStateTracker.imsStateToString());
141 
142         if (isSmsOverImsAvailable()) {
143             if (mImsStateTracker.isImsRegisteredOverWlan()) {
144                 notifyWlanSelected(false);
145                 return;
146             }
147             notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_PS, false);
148         } else {
149             notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_CS, false);
150         }
151     }
152 
sendMessageForDomainSelection()153     protected void sendMessageForDomainSelection() {
154         // If the event is already queued to this handler,
155         // it will be removed first to avoid the duplicate operation.
156         removeMessages(EVENT_SELECT_DOMAIN);
157         // Since the IMS state may have already been posted,
158         // proceed with the domain selection after processing all pending messages.
159         obtainMessage(EVENT_SELECT_DOMAIN).sendToTarget();
160     }
161 
isDomainSelectionRequested()162     protected boolean isDomainSelectionRequested() {
163         return mDomainSelectionRequested;
164     }
165 
setDomainSelectionRequested(boolean requested)166     protected void setDomainSelectionRequested(boolean requested) {
167         if (mDomainSelectionRequested != requested) {
168             logd("DomainSelectionRequested: " + mDomainSelectionRequested + " >> " + requested);
169             mDomainSelectionRequested = requested;
170         }
171     }
172 
notifyWlanSelected(boolean useEmergencyPdn)173     protected void notifyWlanSelected(boolean useEmergencyPdn) {
174         logi("DomainSelected: WLAN, E-PDN=" + useEmergencyPdn);
175         mTransportSelectorCallback.onWlanSelected(useEmergencyPdn);
176         setDomainSelectionRequested(false);
177     }
178 
notifyWwanSelected(@etworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn)179     protected void notifyWwanSelected(@NetworkRegistrationInfo.Domain int domain,
180             boolean useEmergencyPdn) {
181         if (mWwanSelectorCallback == null) {
182             mTransportSelectorCallback.onWwanSelected((callback) -> {
183                 mWwanSelectorCallback = callback;
184                 notifyWwanSelectedInternal(domain, useEmergencyPdn);
185             });
186         } else {
187             notifyWwanSelectedInternal(domain, useEmergencyPdn);
188         }
189 
190         setDomainSelectionRequested(false);
191     }
192 
notifyWwanSelectedInternal(@etworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn)193     protected void notifyWwanSelectedInternal(@NetworkRegistrationInfo.Domain int domain,
194             boolean useEmergencyPdn) {
195         logi("DomainSelected: WWAN/" + DomainSelectionService.getDomainName(domain)
196                 + ", E-PDN=" + useEmergencyPdn);
197 
198         if (mWwanSelectorCallback != null) {
199             mWwanSelectorCallback.onDomainSelected(domain, useEmergencyPdn);
200         } else {
201             mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.LOCAL);
202         }
203     }
204 }
205