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