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 cancelSelection()74 public void cancelSelection() { 75 logi("cancelSelection"); 76 finishSelection(); 77 } 78 79 @Override reselectDomain(@onNull SelectionAttributes attr)80 public void reselectDomain(@NonNull SelectionAttributes attr) { 81 if (isDomainSelectionRequested()) { 82 // The domain selection is already requested, 83 // so we don't need to request it again before completing the previous task. 84 logi("Domain selection is already running."); 85 return; 86 } 87 88 logi("reselectDomain"); 89 mSelectionAttributes = attr; 90 setDomainSelectionRequested(true); 91 obtainMessage(EVENT_SELECT_DOMAIN).sendToTarget(); 92 } 93 94 @Override finishSelection()95 public void finishSelection() { 96 logi("finishSelection"); 97 setDomainSelectionRequested(false); 98 mSelectionAttributes = null; 99 mTransportSelectorCallback = null; 100 mWwanSelectorCallback = null; 101 destroy(); 102 } 103 104 @Override selectDomain(SelectionAttributes attr, TransportSelectorCallback callback)105 public void selectDomain(SelectionAttributes attr, TransportSelectorCallback callback) { 106 if (isDomainSelectionRequested()) { 107 // The domain selection is already requested, 108 // so we don't need to request it again before completing the previous task. 109 logi("Domain selection is already running."); 110 return; 111 } 112 mSelectionAttributes = attr; 113 mTransportSelectorCallback = callback; 114 setDomainSelectionRequested(true); 115 mImsStateTracker.addImsStateListener(this); 116 obtainMessage(EVENT_SELECT_DOMAIN).sendToTarget(); 117 } 118 119 @Override onImsMmTelFeatureAvailableChanged()120 public void onImsMmTelFeatureAvailableChanged() { 121 sendMessageForDomainSelection(); 122 } 123 124 @Override onImsRegistrationStateChanged()125 public void onImsRegistrationStateChanged() { 126 sendMessageForDomainSelection(); 127 } 128 129 @Override onImsMmTelCapabilitiesChanged()130 public void onImsMmTelCapabilitiesChanged() { 131 sendMessageForDomainSelection(); 132 } 133 isSmsOverImsAvailable()134 protected boolean isSmsOverImsAvailable() { 135 return mImsStateTracker.isImsSmsCapable() 136 && mImsStateTracker.isImsRegistered() 137 && mImsStateTracker.isMmTelFeatureAvailable(); 138 } 139 selectDomain()140 protected void selectDomain() { 141 if (!isDomainSelectionRequested()) { 142 logi("Domain selection is not requested!"); 143 return; 144 } 145 146 logi("selectDomain: " + mImsStateTracker.imsStateToString()); 147 148 if (isSmsOverImsAvailable()) { 149 if (mImsStateTracker.isImsRegisteredOverWlan()) { 150 notifyWlanSelected(false); 151 return; 152 } 153 notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_PS, false); 154 } else { 155 notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_CS, false); 156 } 157 } 158 sendMessageForDomainSelection()159 protected void sendMessageForDomainSelection() { 160 // If the event is already queued to this handler, 161 // it will be removed first to avoid the duplicate operation. 162 removeMessages(EVENT_SELECT_DOMAIN); 163 // Since the IMS state may have already been posted, 164 // proceed with the domain selection after processing all pending messages. 165 obtainMessage(EVENT_SELECT_DOMAIN).sendToTarget(); 166 } 167 isDomainSelectionRequested()168 protected boolean isDomainSelectionRequested() { 169 return mDomainSelectionRequested; 170 } 171 setDomainSelectionRequested(boolean requested)172 protected void setDomainSelectionRequested(boolean requested) { 173 if (mDomainSelectionRequested != requested) { 174 logd("DomainSelectionRequested: " + mDomainSelectionRequested + " >> " + requested); 175 mDomainSelectionRequested = requested; 176 } 177 } 178 notifyWlanSelected(boolean useEmergencyPdn)179 protected void notifyWlanSelected(boolean useEmergencyPdn) { 180 logi("DomainSelected: WLAN, E-PDN=" + useEmergencyPdn); 181 mTransportSelectorCallback.onWlanSelected(useEmergencyPdn); 182 setDomainSelectionRequested(false); 183 } 184 notifyWwanSelected(@etworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn)185 protected void notifyWwanSelected(@NetworkRegistrationInfo.Domain int domain, 186 boolean useEmergencyPdn) { 187 if (mWwanSelectorCallback == null) { 188 mTransportSelectorCallback.onWwanSelected((callback) -> { 189 mWwanSelectorCallback = callback; 190 notifyWwanSelectedInternal(domain, useEmergencyPdn); 191 }); 192 } else { 193 notifyWwanSelectedInternal(domain, useEmergencyPdn); 194 } 195 196 setDomainSelectionRequested(false); 197 } 198 notifyWwanSelectedInternal(@etworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn)199 protected void notifyWwanSelectedInternal(@NetworkRegistrationInfo.Domain int domain, 200 boolean useEmergencyPdn) { 201 logi("DomainSelected: WWAN/" + DomainSelectionService.getDomainName(domain) 202 + ", E-PDN=" + useEmergencyPdn); 203 204 if (mWwanSelectorCallback != null) { 205 mWwanSelectorCallback.onDomainSelected(domain, useEmergencyPdn); 206 } else { 207 mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.LOCAL); 208 } 209 } 210 } 211