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.services.telephony.domainselection; 18 19 import static android.telephony.SubscriptionManager.EXTRA_SLOT_INDEX; 20 import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; 21 import static android.telephony.TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED; 22 import static android.telephony.TelephonyManager.EXTRA_PHONE_IN_ECM_STATE; 23 24 import android.annotation.NonNull; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.os.Handler; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.os.SystemProperties; 32 import android.telephony.AccessNetworkConstants; 33 import android.telephony.CarrierConfigManager; 34 import android.telephony.PreciseDataConnectionState; 35 import android.telephony.SubscriptionManager; 36 import android.telephony.TelephonyCallback; 37 import android.telephony.TelephonyManager; 38 import android.telephony.data.ApnSetting; 39 import android.util.ArrayMap; 40 import android.util.Log; 41 42 /** Helper class to cache emergency data connection state. */ 43 public class DataConnectionStateHelper extends Handler { 44 private static final String TAG = "DataConnectionStateHelper"; 45 private static final boolean DBG = (SystemProperties.getInt("ro.debuggable", 0) == 1); 46 47 /** 48 * TelephonyCallback used to monitor ePDN state. 49 */ 50 private static final class DataConnectionStateListener extends TelephonyCallback 51 implements TelephonyCallback.PreciseDataConnectionStateListener { 52 53 private final Handler mHandler; 54 private final TelephonyManager mTelephonyManager; 55 private final DataConnectionStateHelper mOwner; 56 private final int mSubId; 57 private final int mSlotIndex; 58 private int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID; 59 private int mState = TelephonyManager.DATA_UNKNOWN; 60 DataConnectionStateListener(Handler handler, TelephonyManager tm, DataConnectionStateHelper owner, int subId, int slotIndex)61 DataConnectionStateListener(Handler handler, TelephonyManager tm, 62 DataConnectionStateHelper owner, int subId, int slotIndex) { 63 mHandler = handler; 64 mTelephonyManager = tm; 65 mOwner = owner; 66 mSubId = subId; 67 mSlotIndex = slotIndex; 68 } 69 70 @Override onPreciseDataConnectionStateChanged( @onNull PreciseDataConnectionState dataConnectionState)71 public void onPreciseDataConnectionStateChanged( 72 @NonNull PreciseDataConnectionState dataConnectionState) { 73 ApnSetting apnSetting = dataConnectionState.getApnSetting(); 74 if ((apnSetting == null) 75 || ((apnSetting.getApnTypeBitmask() & ApnSetting.TYPE_EMERGENCY) == 0)) { 76 return; 77 } 78 mTransportType = dataConnectionState.getTransportType(); 79 mState = dataConnectionState.getState(); 80 mOwner.notifyDataConnectionStateChange(mSlotIndex, mState); 81 Log.i(TAG, "onPreciseDataConnectionStateChanged ePDN state=" + mState 82 + ", transport=" + mTransportType + ", subId=" + mSubId); 83 } 84 registerTelephonyCallback()85 public void registerTelephonyCallback() { 86 Log.i(TAG, "registerTelephonyCallback subId=" + mSubId); 87 TelephonyManager tm = mTelephonyManager.createForSubscriptionId(mSubId); 88 tm.registerTelephonyCallback(mHandler::post, this); 89 } 90 unregisterTelephonyCallback()91 public void unregisterTelephonyCallback() { 92 Log.i(TAG, "unregisterTelephonyCallback subId=" + mSubId); 93 mTelephonyManager.unregisterTelephonyCallback(this); 94 } 95 getSubId()96 public int getSubId() { 97 return mSubId; 98 } 99 getTransportType()100 public int getTransportType() { 101 return mTransportType; 102 } 103 getState()104 public int getState() { 105 return mState; 106 } 107 } 108 109 private final Context mContext; 110 private final TelephonyManager mTelephonyManager; 111 private final CarrierConfigManager mConfigManager; 112 113 private final ArrayMap<Integer, DataConnectionStateListener> 114 mDataConnectionStateListeners = new ArrayMap<>(); 115 116 private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener = 117 (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigChanged( 118 slotIndex, subId, carrierId); 119 120 private EmergencyCallDomainSelector mSelector; 121 122 /** 123 * Creates an instance. 124 * 125 * @param context The Context this is associated with. 126 * @param looper The Looper to run the DataConnectionStateHelper. 127 */ DataConnectionStateHelper(@onNull Context context, @NonNull Looper looper)128 public DataConnectionStateHelper(@NonNull Context context, @NonNull Looper looper) { 129 super(looper); 130 131 mContext = context; 132 mTelephonyManager = context.getSystemService(TelephonyManager.class); 133 mConfigManager = context.getSystemService(CarrierConfigManager.class); 134 mConfigManager.registerCarrierConfigChangeListener(this::post, 135 mCarrierConfigChangeListener); 136 } 137 138 /** 139 * Returns whether it is in emergency callback mode. 140 * 141 * @param slotIndex The logical SIM slot index. 142 * @return true if it is in emergency callback mode. 143 */ isInEmergencyCallbackMode(int slotIndex)144 public boolean isInEmergencyCallbackMode(int slotIndex) { 145 Intent intent = mContext.registerReceiver(null, 146 new IntentFilter(ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)); 147 if (intent != null 148 && ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(intent.getAction())) { 149 boolean inEcm = intent.getBooleanExtra(EXTRA_PHONE_IN_ECM_STATE, false); 150 int index = intent.getIntExtra(EXTRA_SLOT_INDEX, INVALID_SIM_SLOT_INDEX); 151 Log.i(TAG, "isInEmergencyCallbackMode inEcm=" + inEcm + ", slotIndex=" + index); 152 return inEcm && (slotIndex == index); 153 } 154 return false; 155 } 156 157 /** 158 * Returns the transport type of emergency data connection. 159 * 160 * @param slotIndex The logical SIM slot index. 161 * @return the transport type of emergency data connection. 162 */ getTransportType(int slotIndex)163 public int getTransportType(int slotIndex) { 164 DataConnectionStateListener listener = 165 mDataConnectionStateListeners.get(Integer.valueOf(slotIndex)); 166 if (listener == null) return AccessNetworkConstants.TRANSPORT_TYPE_INVALID; 167 Log.i(TAG, "getTransportType " + listener.getTransportType()); 168 return listener.getTransportType(); 169 } 170 171 /** 172 * Returns the data connection state. 173 * 174 * @param slotIndex The logical SIM slot index. 175 * @return the data connection state. 176 */ getDataConnectionState(int slotIndex)177 public int getDataConnectionState(int slotIndex) { 178 DataConnectionStateListener listener = 179 mDataConnectionStateListeners.get(Integer.valueOf(slotIndex)); 180 if (listener == null) return TelephonyManager.DATA_UNKNOWN; 181 Log.i(TAG, "getDataConnectionState " + listener.getState()); 182 return listener.getState(); 183 } 184 185 /** 186 * Sets the EmergencyCallDomainSelector instance. 187 * 188 * @param selector The instance of {@link EmergencyCallDomainSelector}. 189 */ setEmergencyCallDomainSelector(EmergencyCallDomainSelector selector)190 public void setEmergencyCallDomainSelector(EmergencyCallDomainSelector selector) { 191 mSelector = selector; 192 } 193 notifyDataConnectionStateChange(int slotIndex, int state)194 private void notifyDataConnectionStateChange(int slotIndex, int state) { 195 EmergencyCallDomainSelector selector = mSelector; 196 if (selector != null) { 197 Log.i(TAG, "notifyDataConnectionStateChange slot=" + slotIndex + ", state=" + state); 198 selector.notifyDataConnectionStateChange(slotIndex, state); 199 } 200 } 201 202 203 @Override handleMessage(Message msg)204 public void handleMessage(Message msg) { 205 switch(msg.what) { 206 default: 207 super.handleMessage(msg); 208 break; 209 } 210 } 211 onCarrierConfigChanged(int slotIndex, int subId, int carrierId)212 private void onCarrierConfigChanged(int slotIndex, int subId, int carrierId) { 213 Log.i(TAG, "onCarrierConfigChanged slotIndex=" + slotIndex 214 + ", subId=" + subId + ", carrierId=" + carrierId); 215 216 if (slotIndex < 0) { 217 return; 218 } 219 220 DataConnectionStateListener listener = 221 mDataConnectionStateListeners.get(Integer.valueOf(slotIndex)); 222 223 // Remove stale listener. 224 if (listener != null && listener.getSubId() != subId) { 225 listener.unregisterTelephonyCallback(); 226 mDataConnectionStateListeners.remove(Integer.valueOf(slotIndex)); 227 listener = null; 228 } 229 230 if (listener == null 231 && SubscriptionManager.isValidSubscriptionId(subId)) { 232 listener = new DataConnectionStateListener(this, mTelephonyManager, 233 this, subId, slotIndex); 234 listener.registerTelephonyCallback(); 235 mDataConnectionStateListeners.put(Integer.valueOf(slotIndex), listener); 236 } 237 } 238 239 /** Destroys the instance. */ destroy()240 public void destroy() { 241 if (DBG) Log.d(TAG, "destroy"); 242 mConfigManager.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener); 243 mDataConnectionStateListeners.forEach((k, v) -> v.unregisterTelephonyCallback()); 244 } 245 } 246