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