1 /* 2 * Copyright (C) 2019 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.car.settings.network; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.telephony.SubscriptionInfo; 24 import android.telephony.SubscriptionManager; 25 26 import androidx.annotation.NonNull; 27 import androidx.annotation.VisibleForTesting; 28 import androidx.lifecycle.DefaultLifecycleObserver; 29 import androidx.lifecycle.LifecycleOwner; 30 31 import com.android.car.settings.common.Logger; 32 import com.android.internal.telephony.TelephonyIntents; 33 import com.android.internal.telephony.flags.Flags; 34 import com.android.internal.util.CollectionUtils; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.Objects; 39 40 /** 41 * Listens to potential changes in subscription id and updates registered {@link 42 * MobileNetworkUpdateManager.MobileNetworkUpdateListener} with the new subscription id. 43 */ 44 public class MobileNetworkUpdateManager implements DefaultLifecycleObserver { 45 46 /** Value to represent that the subscription id hasn't been computed yet. */ 47 static final int SUB_ID_NULL = Integer.MIN_VALUE; 48 private static final Logger LOG = new Logger(MobileNetworkUpdateManager.class); 49 50 private final List<MobileNetworkUpdateListener> mListeners = new ArrayList<>(); 51 private final PhoneChangeReceiver mPhoneChangeReceiver; 52 private final SubscriptionManager mSubscriptionManager; 53 private List<SubscriptionInfo> mSubscriptionInfos; 54 private int mCurSubscriptionId; 55 56 @VisibleForTesting 57 final SubscriptionManager.OnSubscriptionsChangedListener 58 mOnSubscriptionsChangeListener = 59 new SubscriptionManager.OnSubscriptionsChangedListener() { 60 @Override 61 public void onSubscriptionsChanged() { 62 if (!Objects.equals(mSubscriptionInfos, 63 mSubscriptionManager.getActiveSubscriptionInfoList( 64 /* userVisibleOnly= */ true))) { 65 updateSubscriptions(/* forceRefresh= */ false); 66 } 67 } 68 }; 69 MobileNetworkUpdateManager(Context context, int subId)70 public MobileNetworkUpdateManager(Context context, int subId) { 71 mCurSubscriptionId = subId; 72 SubscriptionManager sm = context.getSystemService(SubscriptionManager.class); 73 if (Flags.workProfileApiSplit()) { 74 sm = sm.createForAllUserProfiles(); 75 } 76 mSubscriptionManager = sm; 77 mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList(); 78 79 mPhoneChangeReceiver = new PhoneChangeReceiver(context, () -> { 80 if (mCurSubscriptionId != SUB_ID_NULL) { 81 // When the radio changes (ex: CDMA->GSM), refresh the fragment. 82 // This is very rare. 83 LOG.d("Radio change (i.e. CDMA->GSM) received for valid subscription id: " 84 + mCurSubscriptionId); 85 updateReceived(mCurSubscriptionId); 86 } 87 }); 88 } 89 90 @VisibleForTesting MobileNetworkUpdateManager(int subId, SubscriptionManager subscriptionManager, PhoneChangeReceiver phoneChangeReceiver)91 MobileNetworkUpdateManager(int subId, SubscriptionManager subscriptionManager, 92 PhoneChangeReceiver phoneChangeReceiver) { 93 mCurSubscriptionId = subId; 94 mSubscriptionManager = subscriptionManager; 95 mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList(); 96 97 mPhoneChangeReceiver = phoneChangeReceiver; 98 } 99 100 /** 101 * Registers a listener that will receive necessary updates to changes in the mobile network. 102 */ registerListener(MobileNetworkUpdateListener listener)103 public void registerListener(MobileNetworkUpdateListener listener) { 104 mListeners.add(listener); 105 } 106 107 /** 108 * Unregisters a listener that was previously added via 109 * {@link MobileNetworkUpdateManager#registerListener(MobileNetworkUpdateListener)}. The 110 * provided argument must refer to the same object that was registered in order to securely be 111 * unregistered. 112 */ unregisterListener(MobileNetworkUpdateListener listener)113 public void unregisterListener(MobileNetworkUpdateListener listener) { 114 mListeners.remove(listener); 115 } 116 117 @Override onCreate(@onNull LifecycleOwner owner)118 public void onCreate(@NonNull LifecycleOwner owner) { 119 updateSubscriptions(/* forceRefresh= */ true); 120 } 121 122 @Override onStart(@onNull LifecycleOwner owner)123 public final void onStart(@NonNull LifecycleOwner owner) { 124 mPhoneChangeReceiver.register(); 125 mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); 126 } 127 128 @Override onStop(@onNull LifecycleOwner owner)129 public final void onStop(@NonNull LifecycleOwner owner) { 130 mPhoneChangeReceiver.unregister(); 131 mSubscriptionManager.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); 132 } 133 updateSubscriptions(boolean forceRefresh)134 private void updateSubscriptions(boolean forceRefresh) { 135 LOG.d("updateSubscriptions called"); 136 mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList(); 137 int subId = getSubscriptionId(); 138 if (forceRefresh || mCurSubscriptionId != subId) { 139 LOG.d("updateSubscriptions updated subscription id! prev: " + mCurSubscriptionId 140 + " new: " + subId); 141 mCurSubscriptionId = subId; 142 updateReceived(mCurSubscriptionId); 143 } 144 } 145 updateReceived(int subId)146 private void updateReceived(int subId) { 147 for (MobileNetworkUpdateListener listener : mListeners) { 148 listener.onMobileNetworkUpdated(subId); 149 } 150 } 151 getSubscriptionId()152 private int getSubscriptionId() { 153 SubscriptionInfo subscription = getSubscription(); 154 return subscription != null ? subscription.getSubscriptionId() 155 : SubscriptionManager.INVALID_SUBSCRIPTION_ID; 156 } 157 158 /** 159 * First, find a subscription with the id provided at construction if it exists. If not, just 160 * return the first one in the mSubscriptionInfos list since it is already sorted by sim slot. 161 */ getSubscription()162 private SubscriptionInfo getSubscription() { 163 if (mCurSubscriptionId != SUB_ID_NULL) { 164 for (SubscriptionInfo subscriptionInfo : 165 mSubscriptionManager.getSelectableSubscriptionInfoList()) { 166 if (subscriptionInfo.getSubscriptionId() == mCurSubscriptionId) { 167 return subscriptionInfo; 168 } 169 } 170 } 171 172 return CollectionUtils.isEmpty(mSubscriptionInfos) ? null : mSubscriptionInfos.get(0); 173 } 174 175 /** 176 * Interface used by components listening to subscription id updates from {@link 177 * MobileNetworkUpdateManager}. 178 */ 179 public interface MobileNetworkUpdateListener { 180 /** Called when there is a new subscription id that other components should be aware of. */ onMobileNetworkUpdated(int subId)181 void onMobileNetworkUpdated(int subId); 182 } 183 184 /** Broadcast receiver which observes changes in radio technology (i.e. CDMA vs GSM). */ 185 @VisibleForTesting 186 static class PhoneChangeReceiver extends BroadcastReceiver { 187 @VisibleForTesting 188 static final IntentFilter RADIO_TECHNOLOGY_CHANGED_FILTER = new IntentFilter( 189 TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); 190 191 private Context mContext; 192 private PhoneChangeReceiver.OnChangeAction mOnChangeAction; 193 194 /** Action to take when receiver receives a non sticky broadcast intent. */ 195 private interface OnChangeAction { onReceive()196 void onReceive(); 197 } 198 PhoneChangeReceiver(Context context, PhoneChangeReceiver.OnChangeAction onChangeAction)199 PhoneChangeReceiver(Context context, PhoneChangeReceiver.OnChangeAction onChangeAction) { 200 mContext = context; 201 mOnChangeAction = onChangeAction; 202 } 203 register()204 void register() { 205 mContext.registerReceiver(this, RADIO_TECHNOLOGY_CHANGED_FILTER); 206 } 207 unregister()208 void unregister() { 209 mContext.unregisterReceiver(this); 210 } 211 212 @Override onReceive(Context context, Intent intent)213 public void onReceive(Context context, Intent intent) { 214 if (!isInitialStickyBroadcast()) { 215 mOnChangeAction.onReceive(); 216 } 217 } 218 } 219 } 220