• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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