• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 package com.android.settings.sim.receivers;
17 
18 import android.content.BroadcastReceiver;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.telephony.TelephonyManager;
22 import android.telephony.UiccCardInfo;
23 import android.telephony.UiccPortInfo;
24 import android.telephony.UiccSlotInfo;
25 import android.telephony.euicc.EuiccManager;
26 import android.text.TextUtils;
27 import android.util.Log;
28 
29 import androidx.annotation.Nullable;
30 
31 import com.android.settings.R;
32 import com.android.settingslib.utils.ThreadUtils;
33 
34 import java.util.List;
35 
36 /** The receiver when the slot status changes. */
37 public class SimSlotChangeReceiver extends BroadcastReceiver {
38     private static final String TAG = "SlotChangeReceiver";
39 
40     @Override
onReceive(Context context, Intent intent)41     public void onReceive(Context context, Intent intent) {
42 
43         String action = intent.getAction();
44         if (!TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED.equals(action)) {
45             Log.e(TAG, "Ignore slot changes due to unexpected action: " + action);
46             return;
47         }
48 
49         SimSlotChangeService.scheduleSimSlotChange(context);
50     }
51 
runOnBackgroundThread(Context context)52     public static void runOnBackgroundThread(Context context) {
53         if (shouldHandleSlotChange(context)) {
54             SimSlotChangeHandler.get().onSlotsStatusChange(context.getApplicationContext());
55         }
56     }
57 
58     // Checks whether the slot event should be handled.
shouldHandleSlotChange(Context context)59     private static boolean shouldHandleSlotChange(Context context) {
60         if (!context.getResources().getBoolean(R.bool.config_handle_sim_slot_change)) {
61             Log.i(TAG, "The flag is off. Ignore slot changes.");
62             return false;
63         }
64 
65         final EuiccManager euiccManager = context.getSystemService(EuiccManager.class);
66         if (euiccManager == null || !euiccManager.isEnabled()) {
67             Log.i(TAG, "Ignore slot changes because EuiccManager is disabled.");
68             return false;
69         }
70 
71         if (euiccManager.getOtaStatus() == EuiccManager.EUICC_OTA_IN_PROGRESS) {
72             Log.i(TAG, "Ignore slot changes because eSIM OTA is in progress.");
73             return false;
74         }
75 
76         if (!isSimSlotStateValid(context)) {
77             Log.i(TAG, "Ignore slot changes because SIM states are not valid.");
78             return false;
79         }
80 
81         return true;
82     }
83 
84     // Checks whether the SIM slot state is valid for slot change event.
isSimSlotStateValid(Context context)85     private static boolean isSimSlotStateValid(Context context) {
86         final TelephonyManager telMgr = context.getSystemService(TelephonyManager.class);
87         UiccSlotInfo[] slotInfos = telMgr.getUiccSlotsInfo();
88         if (slotInfos == null) {
89             Log.e(TAG, "slotInfos is null. Unable to get slot infos.");
90             return false;
91         }
92 
93         boolean isAllCardStringsEmpty = true;
94         for (int i = 0; i < slotInfos.length; i++) {
95             UiccSlotInfo slotInfo = slotInfos[i];
96 
97             if (slotInfo == null) {
98                 return false;
99             }
100 
101             // After pSIM is inserted, there might be a short period that the status of both slots
102             // are not accurate. We drop the event if any of sim presence state is ERROR or
103             // RESTRICTED.
104             if (slotInfo.getCardStateInfo() == UiccSlotInfo.CARD_STATE_INFO_ERROR
105                     || slotInfo.getCardStateInfo() == UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) {
106                 Log.i(TAG, "The SIM state is in an error. Drop the event. SIM info: " + slotInfo);
107                 return false;
108             }
109 
110             UiccCardInfo cardInfo = findUiccCardInfoBySlot(telMgr, i);
111             if (cardInfo == null) {
112                 continue;
113             }
114             for (UiccPortInfo portInfo : cardInfo.getPorts()) {
115                 if (!TextUtils.isEmpty(slotInfo.getCardId())
116                         || !TextUtils.isEmpty(portInfo.getIccId())) {
117                     isAllCardStringsEmpty = false;
118                 }
119             }
120         }
121 
122         // We also drop the event if both the card strings are empty, which usually means it's
123         // between SIM slots switch the slot status is not stable at this moment.
124         if (isAllCardStringsEmpty) {
125             Log.i(TAG, "All UICC card strings are empty. Drop this event.");
126             return false;
127         }
128 
129         return true;
130     }
131 
132     @Nullable
findUiccCardInfoBySlot(TelephonyManager telMgr, int physicalSlotIndex)133     private static UiccCardInfo findUiccCardInfoBySlot(TelephonyManager telMgr,
134             int physicalSlotIndex) {
135         List<UiccCardInfo> cardInfos = telMgr.getUiccCardsInfo();
136         if (cardInfos == null) {
137             return null;
138         }
139         return cardInfos.stream()
140                 .filter(info -> info.getPhysicalSlotIndex() == physicalSlotIndex)
141                 .findFirst()
142                 .orElse(null);
143     }
144 }
145