Loading services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java 0 → 100644 +211 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.server.net; import static android.net.NetworkTemplate.getCollapsedRatType; import android.annotation.NonNull; import android.content.Context; import android.telephony.Annotation; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import com.android.internal.util.CollectionUtils; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; /** * Helper class that watches for events that are triggered per subscription. */ // TODO (b/152176562): Write tests to verify subscription changes generate corresponding // register/unregister calls. public class NetworkStatsSubscriptionsMonitor extends SubscriptionManager.OnSubscriptionsChangedListener { /** * Interface that this monitor uses to delegate event handling to NetworkStatsService. */ public interface Delegate { /** * Notify that the collapsed RAT type has been changed for any subscription. The method * will also be triggered for any existing sub when start and stop monitoring. * * @param subscriberId IMSI of the subscription. * @param collapsedRatType collapsed RAT type. * @see android.net.NetworkTemplate#getCollapsedRatType(int). */ void onCollapsedRatTypeChanged(@NonNull String subscriberId, @Annotation.NetworkType int collapsedRatType); } private final Delegate mDelegate; /** * Receivers that watches for {@link ServiceState} changes for each subscription, to * monitor the transitioning between Radio Access Technology(RAT) types for each sub. */ @NonNull private final CopyOnWriteArrayList<RatTypeListener> mRatListeners = new CopyOnWriteArrayList<>(); @NonNull private final SubscriptionManager mSubscriptionManager; @NonNull private final TelephonyManager mTeleManager; @NonNull private final Executor mExecutor; NetworkStatsSubscriptionsMonitor(@NonNull Context context, @NonNull Executor executor, @NonNull Delegate delegate) { super(); mSubscriptionManager = (SubscriptionManager) context.getSystemService( Context.TELEPHONY_SUBSCRIPTION_SERVICE); mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); mExecutor = executor; mDelegate = delegate; } @Override public void onSubscriptionsChanged() { // Collect active subId list, hidden subId such as opportunistic subscriptions are // also needed to track CBRS. final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager); for (final int subId : newSubs) { final RatTypeListener match = CollectionUtils.find(mRatListeners, it -> it.mSubId == subId); if (match != null) continue; // Create listener for every newly added sub. Also store subscriberId into it to // prevent binder call to telephony when querying RAT. final String subscriberId = mTeleManager.getSubscriberId(subId); if (TextUtils.isEmpty(subscriberId)) { Log.wtf(NetworkStatsService.TAG, "Empty subscriberId for newly added sub: " + subId); } final RatTypeListener listener = new RatTypeListener(mExecutor, this, subId, subscriberId); mRatListeners.add(listener); // Register listener to the telephony manager that associated with specific sub. mTeleManager.createForSubscriptionId(subId) .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE); } for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) { // If the new list contains the subId of the listener, keeps it. final Integer match = CollectionUtils.find(newSubs, it -> it == listener.mSubId); if (match != null) continue; handleRemoveRatTypeListener(listener); } } @NonNull private List<Integer> getActiveSubIdList(@NonNull SubscriptionManager subscriptionManager) { final ArrayList<Integer> ret = new ArrayList<>(); final int[] ids = subscriptionManager.getActiveAndHiddenSubscriptionIdList(); for (int id : ids) ret.add(id); return ret; } /** * Get a collapsed RatType for the given subscriberId. * * @param subscriberId the target subscriberId * @return collapsed RatType for the given subscriberId */ public int getRatTypeForSubscriberId(@NonNull String subscriberId) { final RatTypeListener match = CollectionUtils.find(mRatListeners, it -> TextUtils.equals(subscriberId, it.mSubscriberId)); return match != null ? match.mLastCollapsedRatType : TelephonyManager.NETWORK_TYPE_UNKNOWN; } /** * Start monitoring events that triggered per subscription. */ public void start() { mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, this); } /** * Unregister subscription changes and all listeners for each subscription. */ public void stop() { mSubscriptionManager.removeOnSubscriptionsChangedListener(this); for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) { handleRemoveRatTypeListener(listener); } } private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) { mTeleManager.createForSubscriptionId(listener.mSubId) .listen(listener, PhoneStateListener.LISTEN_NONE); mRatListeners.remove(listener); // Removal of subscriptions doesn't generate RAT changed event, fire it for every // RatTypeListener. mDelegate.onCollapsedRatTypeChanged( listener.mSubscriberId, TelephonyManager.NETWORK_TYPE_UNKNOWN); } static class RatTypeListener extends PhoneStateListener { // Unique id for the subscription. See {@link SubscriptionInfo#getSubscriptionId}. @NonNull private final int mSubId; // IMSI to identifying the corresponding network from {@link NetworkState}. // See {@link TelephonyManager#getSubscriberId}. @NonNull private final String mSubscriberId; private volatile int mLastCollapsedRatType = TelephonyManager.NETWORK_TYPE_UNKNOWN; @NonNull private final NetworkStatsSubscriptionsMonitor mMonitor; RatTypeListener(@NonNull Executor executor, @NonNull NetworkStatsSubscriptionsMonitor monitor, int subId, @NonNull String subscriberId) { super(executor); mSubId = subId; mSubscriberId = subscriberId; mMonitor = monitor; } @Override public void onServiceStateChanged(@NonNull ServiceState ss) { final int networkType = ss.getDataNetworkType(); final int collapsedRatType = getCollapsedRatType(networkType); if (collapsedRatType == mLastCollapsedRatType) return; if (NetworkStatsService.LOGD) { Log.d(NetworkStatsService.TAG, "subtype changed for sub(" + mSubId + "): " + mLastCollapsedRatType + " -> " + collapsedRatType); } mLastCollapsedRatType = collapsedRatType; mMonitor.mDelegate.onCollapsedRatTypeChanged(mSubscriberId, mLastCollapsedRatType); } } } Loading
services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java 0 → 100644 +211 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.server.net; import static android.net.NetworkTemplate.getCollapsedRatType; import android.annotation.NonNull; import android.content.Context; import android.telephony.Annotation; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import com.android.internal.util.CollectionUtils; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; /** * Helper class that watches for events that are triggered per subscription. */ // TODO (b/152176562): Write tests to verify subscription changes generate corresponding // register/unregister calls. public class NetworkStatsSubscriptionsMonitor extends SubscriptionManager.OnSubscriptionsChangedListener { /** * Interface that this monitor uses to delegate event handling to NetworkStatsService. */ public interface Delegate { /** * Notify that the collapsed RAT type has been changed for any subscription. The method * will also be triggered for any existing sub when start and stop monitoring. * * @param subscriberId IMSI of the subscription. * @param collapsedRatType collapsed RAT type. * @see android.net.NetworkTemplate#getCollapsedRatType(int). */ void onCollapsedRatTypeChanged(@NonNull String subscriberId, @Annotation.NetworkType int collapsedRatType); } private final Delegate mDelegate; /** * Receivers that watches for {@link ServiceState} changes for each subscription, to * monitor the transitioning between Radio Access Technology(RAT) types for each sub. */ @NonNull private final CopyOnWriteArrayList<RatTypeListener> mRatListeners = new CopyOnWriteArrayList<>(); @NonNull private final SubscriptionManager mSubscriptionManager; @NonNull private final TelephonyManager mTeleManager; @NonNull private final Executor mExecutor; NetworkStatsSubscriptionsMonitor(@NonNull Context context, @NonNull Executor executor, @NonNull Delegate delegate) { super(); mSubscriptionManager = (SubscriptionManager) context.getSystemService( Context.TELEPHONY_SUBSCRIPTION_SERVICE); mTeleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); mExecutor = executor; mDelegate = delegate; } @Override public void onSubscriptionsChanged() { // Collect active subId list, hidden subId such as opportunistic subscriptions are // also needed to track CBRS. final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager); for (final int subId : newSubs) { final RatTypeListener match = CollectionUtils.find(mRatListeners, it -> it.mSubId == subId); if (match != null) continue; // Create listener for every newly added sub. Also store subscriberId into it to // prevent binder call to telephony when querying RAT. final String subscriberId = mTeleManager.getSubscriberId(subId); if (TextUtils.isEmpty(subscriberId)) { Log.wtf(NetworkStatsService.TAG, "Empty subscriberId for newly added sub: " + subId); } final RatTypeListener listener = new RatTypeListener(mExecutor, this, subId, subscriberId); mRatListeners.add(listener); // Register listener to the telephony manager that associated with specific sub. mTeleManager.createForSubscriptionId(subId) .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE); } for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) { // If the new list contains the subId of the listener, keeps it. final Integer match = CollectionUtils.find(newSubs, it -> it == listener.mSubId); if (match != null) continue; handleRemoveRatTypeListener(listener); } } @NonNull private List<Integer> getActiveSubIdList(@NonNull SubscriptionManager subscriptionManager) { final ArrayList<Integer> ret = new ArrayList<>(); final int[] ids = subscriptionManager.getActiveAndHiddenSubscriptionIdList(); for (int id : ids) ret.add(id); return ret; } /** * Get a collapsed RatType for the given subscriberId. * * @param subscriberId the target subscriberId * @return collapsed RatType for the given subscriberId */ public int getRatTypeForSubscriberId(@NonNull String subscriberId) { final RatTypeListener match = CollectionUtils.find(mRatListeners, it -> TextUtils.equals(subscriberId, it.mSubscriberId)); return match != null ? match.mLastCollapsedRatType : TelephonyManager.NETWORK_TYPE_UNKNOWN; } /** * Start monitoring events that triggered per subscription. */ public void start() { mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor, this); } /** * Unregister subscription changes and all listeners for each subscription. */ public void stop() { mSubscriptionManager.removeOnSubscriptionsChangedListener(this); for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) { handleRemoveRatTypeListener(listener); } } private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) { mTeleManager.createForSubscriptionId(listener.mSubId) .listen(listener, PhoneStateListener.LISTEN_NONE); mRatListeners.remove(listener); // Removal of subscriptions doesn't generate RAT changed event, fire it for every // RatTypeListener. mDelegate.onCollapsedRatTypeChanged( listener.mSubscriberId, TelephonyManager.NETWORK_TYPE_UNKNOWN); } static class RatTypeListener extends PhoneStateListener { // Unique id for the subscription. See {@link SubscriptionInfo#getSubscriptionId}. @NonNull private final int mSubId; // IMSI to identifying the corresponding network from {@link NetworkState}. // See {@link TelephonyManager#getSubscriberId}. @NonNull private final String mSubscriberId; private volatile int mLastCollapsedRatType = TelephonyManager.NETWORK_TYPE_UNKNOWN; @NonNull private final NetworkStatsSubscriptionsMonitor mMonitor; RatTypeListener(@NonNull Executor executor, @NonNull NetworkStatsSubscriptionsMonitor monitor, int subId, @NonNull String subscriberId) { super(executor); mSubId = subId; mSubscriberId = subscriberId; mMonitor = monitor; } @Override public void onServiceStateChanged(@NonNull ServiceState ss) { final int networkType = ss.getDataNetworkType(); final int collapsedRatType = getCollapsedRatType(networkType); if (collapsedRatType == mLastCollapsedRatType) return; if (NetworkStatsService.LOGD) { Log.d(NetworkStatsService.TAG, "subtype changed for sub(" + mSubId + "): " + mLastCollapsedRatType + " -> " + collapsedRatType); } mLastCollapsedRatType = collapsedRatType; mMonitor.mDelegate.onCollapsedRatTypeChanged(mSubscriberId, mLastCollapsedRatType); } } }