blob: 0bdf3f22ee9a904a07bd09227086452685b59dad [file] [log] [blame]
/*
* 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.getCompleteActiveSubscriptionIdList();
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);
}
}
}