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