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 17 package com.android.server.net; 18 19 import static android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA; 20 import static android.net.NetworkTemplate.getCollapsedRatType; 21 22 import android.annotation.NonNull; 23 import android.content.Context; 24 import android.os.Looper; 25 import android.telephony.Annotation; 26 import android.telephony.NetworkRegistrationInfo; 27 import android.telephony.PhoneStateListener; 28 import android.telephony.ServiceState; 29 import android.telephony.SubscriptionManager; 30 import android.telephony.TelephonyManager; 31 import android.text.TextUtils; 32 import android.util.Log; 33 import android.util.Pair; 34 35 import com.android.internal.annotations.VisibleForTesting; 36 import com.android.internal.util.CollectionUtils; 37 38 import java.util.ArrayList; 39 import java.util.List; 40 import java.util.concurrent.CopyOnWriteArrayList; 41 import java.util.concurrent.Executor; 42 43 /** 44 * Helper class that watches for events that are triggered per subscription. 45 */ 46 public class NetworkStatsSubscriptionsMonitor extends 47 SubscriptionManager.OnSubscriptionsChangedListener { 48 49 /** 50 * Interface that this monitor uses to delegate event handling to NetworkStatsService. 51 */ 52 public interface Delegate { 53 /** 54 * Notify that the collapsed RAT type has been changed for any subscription. The method 55 * will also be triggered for any existing sub when start and stop monitoring. 56 * 57 * @param subscriberId IMSI of the subscription. 58 * @param collapsedRatType collapsed RAT type. 59 * @see android.net.NetworkTemplate#getCollapsedRatType(int). 60 */ onCollapsedRatTypeChanged(@onNull String subscriberId, @Annotation.NetworkType int collapsedRatType)61 void onCollapsedRatTypeChanged(@NonNull String subscriberId, 62 @Annotation.NetworkType int collapsedRatType); 63 } 64 private final Delegate mDelegate; 65 66 /** 67 * Receivers that watches for {@link ServiceState} changes for each subscription, to 68 * monitor the transitioning between Radio Access Technology(RAT) types for each sub. 69 */ 70 @NonNull 71 private final CopyOnWriteArrayList<RatTypeListener> mRatListeners = 72 new CopyOnWriteArrayList<>(); 73 74 @NonNull 75 private final SubscriptionManager mSubscriptionManager; 76 @NonNull 77 private final TelephonyManager mTeleManager; 78 79 @NonNull 80 private final Executor mExecutor; 81 NetworkStatsSubscriptionsMonitor(@onNull Context context, @NonNull Looper looper, @NonNull Executor executor, @NonNull Delegate delegate)82 NetworkStatsSubscriptionsMonitor(@NonNull Context context, @NonNull Looper looper, 83 @NonNull Executor executor, @NonNull Delegate delegate) { 84 super(looper); 85 mSubscriptionManager = (SubscriptionManager) context.getSystemService( 86 Context.TELEPHONY_SUBSCRIPTION_SERVICE); 87 mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 88 mExecutor = executor; 89 mDelegate = delegate; 90 } 91 92 @Override onSubscriptionsChanged()93 public void onSubscriptionsChanged() { 94 // Collect active subId list, hidden subId such as opportunistic subscriptions are 95 // also needed to track CBRS. 96 final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager); 97 98 // IMSI is needed for every newly added sub. Listener stores subscriberId into it to 99 // prevent binder call to telephony when querying RAT. Keep listener registration with empty 100 // IMSI is meaningless since the RAT type changed is ambiguous for multi-SIM if reported 101 // with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration. 102 final List<Pair<Integer, String>> filteredNewSubs = 103 CollectionUtils.mapNotNull(newSubs, subId -> { 104 final String subscriberId = mTeleManager.getSubscriberId(subId); 105 return TextUtils.isEmpty(subscriberId) ? null : new Pair(subId, subscriberId); 106 }); 107 108 for (final Pair<Integer, String> sub : filteredNewSubs) { 109 // Fully match listener with subId and IMSI, since in some rare cases, IMSI might be 110 // suddenly change regardless of subId, such as switch IMSI feature in modem side. 111 // If that happens, register new listener with new IMSI and remove old one later. 112 if (CollectionUtils.find(mRatListeners, 113 it -> it.equalsKey(sub.first, sub.second)) != null) { 114 continue; 115 } 116 117 final RatTypeListener listener = 118 new RatTypeListener(mExecutor, this, sub.first, sub.second); 119 mRatListeners.add(listener); 120 121 // Register listener to the telephony manager that associated with specific sub. 122 mTeleManager.createForSubscriptionId(sub.first) 123 .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE); 124 Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + sub.first); 125 } 126 127 for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) { 128 // If there is no subId and IMSI matched the listener, removes it. 129 if (CollectionUtils.find(filteredNewSubs, 130 it -> listener.equalsKey(it.first, it.second)) == null) { 131 handleRemoveRatTypeListener(listener); 132 } 133 } 134 } 135 136 @NonNull getActiveSubIdList(@onNull SubscriptionManager subscriptionManager)137 private List<Integer> getActiveSubIdList(@NonNull SubscriptionManager subscriptionManager) { 138 final ArrayList<Integer> ret = new ArrayList<>(); 139 final int[] ids = subscriptionManager.getCompleteActiveSubscriptionIdList(); 140 for (int id : ids) ret.add(id); 141 return ret; 142 } 143 144 /** 145 * Get a collapsed RatType for the given subscriberId. 146 * 147 * @param subscriberId the target subscriberId 148 * @return collapsed RatType for the given subscriberId 149 */ getRatTypeForSubscriberId(@onNull String subscriberId)150 public int getRatTypeForSubscriberId(@NonNull String subscriberId) { 151 final RatTypeListener match = CollectionUtils.find(mRatListeners, 152 it -> TextUtils.equals(subscriberId, it.mSubscriberId)); 153 return match != null ? match.mLastCollapsedRatType : TelephonyManager.NETWORK_TYPE_UNKNOWN; 154 } 155 156 /** 157 * Start monitoring events that triggered per subscription. 158 */ start()159 public void start() { 160 mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, this); 161 } 162 163 /** 164 * Unregister subscription changes and all listeners for each subscription. 165 */ stop()166 public void stop() { 167 mSubscriptionManager.removeOnSubscriptionsChangedListener(this); 168 169 for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) { 170 handleRemoveRatTypeListener(listener); 171 } 172 } 173 handleRemoveRatTypeListener(@onNull RatTypeListener listener)174 private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) { 175 mTeleManager.createForSubscriptionId(listener.mSubId) 176 .listen(listener, PhoneStateListener.LISTEN_NONE); 177 Log.d(NetworkStatsService.TAG, "RAT type listener unregistered for sub " + listener.mSubId); 178 mRatListeners.remove(listener); 179 180 // Removal of subscriptions doesn't generate RAT changed event, fire it for every 181 // RatTypeListener. 182 mDelegate.onCollapsedRatTypeChanged( 183 listener.mSubscriberId, TelephonyManager.NETWORK_TYPE_UNKNOWN); 184 } 185 186 static class RatTypeListener extends PhoneStateListener { 187 // Unique id for the subscription. See {@link SubscriptionInfo#getSubscriptionId}. 188 @NonNull 189 private final int mSubId; 190 191 // IMSI to identifying the corresponding network from {@link NetworkState}. 192 // See {@link TelephonyManager#getSubscriberId}. 193 @NonNull 194 private final String mSubscriberId; 195 196 private volatile int mLastCollapsedRatType = TelephonyManager.NETWORK_TYPE_UNKNOWN; 197 @NonNull 198 private final NetworkStatsSubscriptionsMonitor mMonitor; 199 RatTypeListener(@onNull Executor executor, @NonNull NetworkStatsSubscriptionsMonitor monitor, int subId, @NonNull String subscriberId)200 RatTypeListener(@NonNull Executor executor, 201 @NonNull NetworkStatsSubscriptionsMonitor monitor, int subId, 202 @NonNull String subscriberId) { 203 super(executor); 204 mSubId = subId; 205 mSubscriberId = subscriberId; 206 mMonitor = monitor; 207 } 208 209 @Override onServiceStateChanged(@onNull ServiceState ss)210 public void onServiceStateChanged(@NonNull ServiceState ss) { 211 // In 5G SA (Stand Alone) mode, the primary cell itself will be 5G hence telephony 212 // would report RAT = 5G_NR. 213 // However, in 5G NSA (Non Stand Alone) mode, the primary cell is still LTE and 214 // network allocates a secondary 5G cell so telephony reports RAT = LTE along with 215 // NR state as connected. In such case, attributes the data usage to NR. 216 // See b/160727498. 217 final boolean is5GNsa = (ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE 218 || ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA) 219 && ss.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED; 220 221 final int networkType = 222 (is5GNsa ? NETWORK_TYPE_5G_NSA : ss.getDataNetworkType()); 223 final int collapsedRatType = getCollapsedRatType(networkType); 224 if (collapsedRatType == mLastCollapsedRatType) return; 225 226 if (NetworkStatsService.LOGD) { 227 Log.d(NetworkStatsService.TAG, "subtype changed for sub(" + mSubId + "): " 228 + mLastCollapsedRatType + " -> " + collapsedRatType); 229 } 230 mLastCollapsedRatType = collapsedRatType; 231 mMonitor.mDelegate.onCollapsedRatTypeChanged(mSubscriberId, mLastCollapsedRatType); 232 } 233 234 @VisibleForTesting getSubId()235 public int getSubId() { 236 return mSubId; 237 } 238 equalsKey(int subId, @NonNull String subscriberId)239 boolean equalsKey(int subId, @NonNull String subscriberId) { 240 return mSubId == subId && TextUtils.equals(mSubscriberId, subscriberId); 241 } 242 } 243 } 244