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