| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.car.settings.network; |
| |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.telephony.SubscriptionInfo; |
| import android.telephony.SubscriptionManager; |
| |
| import androidx.annotation.NonNull; |
| import androidx.annotation.VisibleForTesting; |
| import androidx.lifecycle.DefaultLifecycleObserver; |
| import androidx.lifecycle.LifecycleOwner; |
| |
| import com.android.car.settings.common.Logger; |
| import com.android.internal.telephony.TelephonyIntents; |
| import com.android.internal.telephony.flags.Flags; |
| import com.android.internal.util.CollectionUtils; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Objects; |
| |
| /** |
| * Listens to potential changes in subscription id and updates registered {@link |
| * MobileNetworkUpdateManager.MobileNetworkUpdateListener} with the new subscription id. |
| */ |
| public class MobileNetworkUpdateManager implements DefaultLifecycleObserver { |
| |
| /** Value to represent that the subscription id hasn't been computed yet. */ |
| static final int SUB_ID_NULL = Integer.MIN_VALUE; |
| private static final Logger LOG = new Logger(MobileNetworkUpdateManager.class); |
| |
| private final List<MobileNetworkUpdateListener> mListeners = new ArrayList<>(); |
| private final PhoneChangeReceiver mPhoneChangeReceiver; |
| private final SubscriptionManager mSubscriptionManager; |
| private List<SubscriptionInfo> mSubscriptionInfos; |
| private int mCurSubscriptionId; |
| |
| @VisibleForTesting |
| final SubscriptionManager.OnSubscriptionsChangedListener |
| mOnSubscriptionsChangeListener = |
| new SubscriptionManager.OnSubscriptionsChangedListener() { |
| @Override |
| public void onSubscriptionsChanged() { |
| if (!Objects.equals(mSubscriptionInfos, |
| mSubscriptionManager.getActiveSubscriptionInfoList( |
| /* userVisibleOnly= */ true))) { |
| updateSubscriptions(/* forceRefresh= */ false); |
| } |
| } |
| }; |
| |
| public MobileNetworkUpdateManager(Context context, int subId) { |
| mCurSubscriptionId = subId; |
| SubscriptionManager sm = context.getSystemService(SubscriptionManager.class); |
| if (Flags.workProfileApiSplit()) { |
| sm = sm.createForAllUserProfiles(); |
| } |
| mSubscriptionManager = sm; |
| mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList(); |
| |
| mPhoneChangeReceiver = new PhoneChangeReceiver(context, () -> { |
| if (mCurSubscriptionId != SUB_ID_NULL) { |
| // When the radio changes (ex: CDMA->GSM), refresh the fragment. |
| // This is very rare. |
| LOG.d("Radio change (i.e. CDMA->GSM) received for valid subscription id: " |
| + mCurSubscriptionId); |
| updateReceived(mCurSubscriptionId); |
| } |
| }); |
| } |
| |
| @VisibleForTesting |
| MobileNetworkUpdateManager(int subId, SubscriptionManager subscriptionManager, |
| PhoneChangeReceiver phoneChangeReceiver) { |
| mCurSubscriptionId = subId; |
| mSubscriptionManager = subscriptionManager; |
| mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList(); |
| |
| mPhoneChangeReceiver = phoneChangeReceiver; |
| } |
| |
| /** |
| * Registers a listener that will receive necessary updates to changes in the mobile network. |
| */ |
| public void registerListener(MobileNetworkUpdateListener listener) { |
| mListeners.add(listener); |
| } |
| |
| /** |
| * Unregisters a listener that was previously added via |
| * {@link MobileNetworkUpdateManager#registerListener(MobileNetworkUpdateListener)}. The |
| * provided argument must refer to the same object that was registered in order to securely be |
| * unregistered. |
| */ |
| public void unregisterListener(MobileNetworkUpdateListener listener) { |
| mListeners.remove(listener); |
| } |
| |
| @Override |
| public void onCreate(@NonNull LifecycleOwner owner) { |
| updateSubscriptions(/* forceRefresh= */ true); |
| } |
| |
| @Override |
| public final void onStart(@NonNull LifecycleOwner owner) { |
| mPhoneChangeReceiver.register(); |
| mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); |
| } |
| |
| @Override |
| public final void onStop(@NonNull LifecycleOwner owner) { |
| mPhoneChangeReceiver.unregister(); |
| mSubscriptionManager.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); |
| } |
| |
| private void updateSubscriptions(boolean forceRefresh) { |
| LOG.d("updateSubscriptions called"); |
| mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList(); |
| int subId = getSubscriptionId(); |
| if (forceRefresh || mCurSubscriptionId != subId) { |
| LOG.d("updateSubscriptions updated subscription id! prev: " + mCurSubscriptionId |
| + " new: " + subId); |
| mCurSubscriptionId = subId; |
| updateReceived(mCurSubscriptionId); |
| } |
| } |
| |
| private void updateReceived(int subId) { |
| for (MobileNetworkUpdateListener listener : mListeners) { |
| listener.onMobileNetworkUpdated(subId); |
| } |
| } |
| |
| private int getSubscriptionId() { |
| SubscriptionInfo subscription = getSubscription(); |
| return subscription != null ? subscription.getSubscriptionId() |
| : SubscriptionManager.INVALID_SUBSCRIPTION_ID; |
| } |
| |
| /** |
| * First, find a subscription with the id provided at construction if it exists. If not, just |
| * return the first one in the mSubscriptionInfos list since it is already sorted by sim slot. |
| */ |
| private SubscriptionInfo getSubscription() { |
| if (mCurSubscriptionId != SUB_ID_NULL) { |
| for (SubscriptionInfo subscriptionInfo : |
| mSubscriptionManager.getSelectableSubscriptionInfoList()) { |
| if (subscriptionInfo.getSubscriptionId() == mCurSubscriptionId) { |
| return subscriptionInfo; |
| } |
| } |
| } |
| |
| return CollectionUtils.isEmpty(mSubscriptionInfos) ? null : mSubscriptionInfos.get(0); |
| } |
| |
| /** |
| * Interface used by components listening to subscription id updates from {@link |
| * MobileNetworkUpdateManager}. |
| */ |
| public interface MobileNetworkUpdateListener { |
| /** Called when there is a new subscription id that other components should be aware of. */ |
| void onMobileNetworkUpdated(int subId); |
| } |
| |
| /** Broadcast receiver which observes changes in radio technology (i.e. CDMA vs GSM). */ |
| @VisibleForTesting |
| static class PhoneChangeReceiver extends BroadcastReceiver { |
| @VisibleForTesting |
| static final IntentFilter RADIO_TECHNOLOGY_CHANGED_FILTER = new IntentFilter( |
| TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); |
| |
| private Context mContext; |
| private PhoneChangeReceiver.OnChangeAction mOnChangeAction; |
| |
| /** Action to take when receiver receives a non sticky broadcast intent. */ |
| private interface OnChangeAction { |
| void onReceive(); |
| } |
| |
| PhoneChangeReceiver(Context context, PhoneChangeReceiver.OnChangeAction onChangeAction) { |
| mContext = context; |
| mOnChangeAction = onChangeAction; |
| } |
| |
| void register() { |
| mContext.registerReceiver(this, RADIO_TECHNOLOGY_CHANGED_FILTER); |
| } |
| |
| void unregister() { |
| mContext.unregisterReceiver(this); |
| } |
| |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| if (!isInitialStickyBroadcast()) { |
| mOnChangeAction.onReceive(); |
| } |
| } |
| } |
| } |