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