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.settings.network.SatelliteRepository; 33 34 import com.google.common.util.concurrent.ListenableFuture; 35 36 import java.util.List; 37 import java.util.concurrent.ExecutionException; 38 import java.util.concurrent.Executor; 39 import java.util.concurrent.Executors; 40 41 /** The receiver when the slot status changes. */ 42 public class SimSlotChangeReceiver extends BroadcastReceiver { 43 private static final String TAG = "SlotChangeReceiver"; 44 45 @Override onReceive(Context context, Intent intent)46 public void onReceive(Context context, Intent intent) { 47 48 String action = intent.getAction(); 49 if (!TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED.equals(action)) { 50 Log.e(TAG, "Ignore slot changes due to unexpected action: " + action); 51 return; 52 } 53 54 SimSlotChangeService.scheduleSimSlotChange(context); 55 } 56 runOnBackgroundThread(Context context)57 public static void runOnBackgroundThread(Context context) { 58 if (shouldHandleSlotChange(context)) { 59 Log.d(TAG, "Checking satellite session status"); 60 Executor executor = Executors.newSingleThreadExecutor(); 61 ListenableFuture<Boolean> isSatelliteSessionStartedFuture = 62 new SatelliteRepository(context).requestIsSessionStarted(executor); 63 isSatelliteSessionStartedFuture.addListener(() -> { 64 boolean isSatelliteSessionStarted = false; 65 try { 66 isSatelliteSessionStarted = isSatelliteSessionStartedFuture.get(); 67 } catch (ExecutionException | InterruptedException e) { 68 Log.w(TAG, "Can't get satellite session status", e); 69 } 70 71 if (isSatelliteSessionStarted) { 72 Log.i(TAG, "Device is in a satellite session. Unable to handle SIM slot" 73 + " changes"); 74 } else { 75 Log.i(TAG, "Not in a satellite session. Handle slot changes"); 76 SimSlotChangeHandler.get().onSlotsStatusChange(context.getApplicationContext()); 77 } 78 }, executor); 79 } 80 } 81 82 // Checks whether the slot event should be handled. shouldHandleSlotChange(Context context)83 private static boolean shouldHandleSlotChange(Context context) { 84 if (!context.getResources().getBoolean(R.bool.config_handle_sim_slot_change)) { 85 Log.i(TAG, "The flag is off. Ignore slot changes."); 86 return false; 87 } 88 89 final EuiccManager euiccManager = context.getSystemService(EuiccManager.class); 90 if (euiccManager == null || !euiccManager.isEnabled()) { 91 Log.i(TAG, "Ignore slot changes because EuiccManager is disabled."); 92 return false; 93 } 94 95 if (euiccManager.getOtaStatus() == EuiccManager.EUICC_OTA_IN_PROGRESS) { 96 Log.i(TAG, "Ignore slot changes because eSIM OTA is in progress."); 97 return false; 98 } 99 100 if (!isSimSlotStateValid(context)) { 101 Log.i(TAG, "Ignore slot changes because SIM states are not valid."); 102 return false; 103 } 104 105 return true; 106 } 107 108 // Checks whether the SIM slot state is valid for slot change event. isSimSlotStateValid(Context context)109 private static boolean isSimSlotStateValid(Context context) { 110 final TelephonyManager telMgr = context.getSystemService(TelephonyManager.class); 111 UiccSlotInfo[] slotInfos = telMgr.getUiccSlotsInfo(); 112 if (slotInfos == null) { 113 Log.e(TAG, "slotInfos is null. Unable to get slot infos."); 114 return false; 115 } 116 117 boolean isAllCardStringsEmpty = true; 118 for (int i = 0; i < slotInfos.length; i++) { 119 UiccSlotInfo slotInfo = slotInfos[i]; 120 121 if (slotInfo == null) { 122 return false; 123 } 124 125 // After pSIM is inserted, there might be a short period that the status of both slots 126 // are not accurate. We drop the event if any of sim presence state is ERROR or 127 // RESTRICTED. 128 if (slotInfo.getCardStateInfo() == UiccSlotInfo.CARD_STATE_INFO_ERROR 129 || slotInfo.getCardStateInfo() == UiccSlotInfo.CARD_STATE_INFO_RESTRICTED) { 130 Log.i(TAG, "The SIM state is in an error. Drop the event. SIM info: " + slotInfo); 131 return false; 132 } 133 134 UiccCardInfo cardInfo = findUiccCardInfoBySlot(telMgr, i); 135 if (cardInfo == null) { 136 continue; 137 } 138 for (UiccPortInfo portInfo : cardInfo.getPorts()) { 139 if (!TextUtils.isEmpty(slotInfo.getCardId()) 140 || !TextUtils.isEmpty(portInfo.getIccId())) { 141 isAllCardStringsEmpty = false; 142 } 143 } 144 } 145 146 // We also drop the event if both the card strings are empty, which usually means it's 147 // between SIM slots switch the slot status is not stable at this moment. 148 if (isAllCardStringsEmpty) { 149 Log.i(TAG, "All UICC card strings are empty. Drop this event."); 150 return false; 151 } 152 153 return true; 154 } 155 156 @Nullable findUiccCardInfoBySlot(TelephonyManager telMgr, int physicalSlotIndex)157 private static UiccCardInfo findUiccCardInfoBySlot(TelephonyManager telMgr, 158 int physicalSlotIndex) { 159 List<UiccCardInfo> cardInfos = telMgr.getUiccCardsInfo(); 160 if (cardInfos == null) { 161 return null; 162 } 163 return cardInfos.stream() 164 .filter(info -> info.getPhysicalSlotIndex() == physicalSlotIndex) 165 .findFirst() 166 .orElse(null); 167 } 168 } 169