blob: a606ad62d724a0a6bb021788154c1cec9238eee7 [file] [log] [blame]
/*
* Copyright (C) 2016 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.internal.telephony.dataconnection;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import android.net.NetworkCapabilities;
import android.net.NetworkFactory;
import android.net.NetworkRequest;
import android.net.StringNetworkSpecifier;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.Rlog;
import android.util.LocalLog;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneSwitcher;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.SubscriptionMonitor;
import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
public class TelephonyNetworkFactory extends NetworkFactory {
public final String LOG_TAG;
protected static final boolean DBG = true;
private static final int REQUEST_LOG_SIZE = 40;
private static final int ACTION_NO_OP = 0;
private static final int ACTION_REQUEST = 1;
private static final int ACTION_RELEASE = 2;
private final PhoneSwitcher mPhoneSwitcher;
private final SubscriptionController mSubscriptionController;
private final SubscriptionMonitor mSubscriptionMonitor;
private final DcTracker mDcTracker;
private final LocalLog mLocalLog = new LocalLog(REQUEST_LOG_SIZE);
// Key: network request. Value: whether it's applied to DcTracker.
private final HashMap<NetworkRequest, Boolean> mNetworkRequests = new HashMap();
private final Phone mPhone;
private int mSubscriptionId;
private final static int TELEPHONY_NETWORK_SCORE = 50;
private final Handler mInternalHandler;
private static final int EVENT_ACTIVE_PHONE_SWITCH = 1;
private static final int EVENT_SUBSCRIPTION_CHANGED = 2;
private static final int EVENT_NETWORK_REQUEST = 3;
private static final int EVENT_NETWORK_RELEASE = 4;
public TelephonyNetworkFactory(SubscriptionMonitor subscriptionMonitor, Looper looper,
Phone phone) {
super(looper, phone.getContext(), "TelephonyNetworkFactory[" + phone.getPhoneId()
+ "]", null);
mPhone = phone;
mInternalHandler = new InternalHandler(looper);
mSubscriptionController = SubscriptionController.getInstance();
setCapabilityFilter(makeNetworkFilter(mSubscriptionController, mPhone.getPhoneId()));
setScoreFilter(TELEPHONY_NETWORK_SCORE);
mPhoneSwitcher = PhoneSwitcher.getInstance();
mSubscriptionMonitor = subscriptionMonitor;
LOG_TAG = "TelephonyNetworkFactory[" + mPhone.getPhoneId() + "]";
// TODO: Will need to dynamically route network requests to the corresponding DcTracker in
// the future. For now we route everything to WWAN.
mDcTracker = mPhone.getDcTracker(TransportType.WWAN);
mPhoneSwitcher.registerForActivePhoneSwitch(mInternalHandler, EVENT_ACTIVE_PHONE_SWITCH,
null);
mSubscriptionId = INVALID_SUBSCRIPTION_ID;
mSubscriptionMonitor.registerForSubscriptionChanged(mPhone.getPhoneId(), mInternalHandler,
EVENT_SUBSCRIPTION_CHANGED, null);
register();
}
private NetworkCapabilities makeNetworkFilter(SubscriptionController subscriptionController,
int phoneId) {
final int subscriptionId = subscriptionController.getSubIdUsingPhoneId(phoneId);
return makeNetworkFilter(subscriptionId);
}
private NetworkCapabilities makeNetworkFilter(int subscriptionId) {
NetworkCapabilities nc = new NetworkCapabilities();
nc.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
nc.setNetworkSpecifier(new StringNetworkSpecifier(String.valueOf(subscriptionId)));
return nc;
}
private class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_ACTIVE_PHONE_SWITCH: {
onActivePhoneSwitch();
break;
}
case EVENT_SUBSCRIPTION_CHANGED: {
onSubIdChange();
break;
}
case EVENT_NETWORK_REQUEST: {
onNeedNetworkFor(msg);
break;
}
case EVENT_NETWORK_RELEASE: {
onReleaseNetworkFor(msg);
break;
}
}
}
}
private void applyRequestsOnActivePhoneSwitch(NetworkRequest networkRequest,
boolean cleanUpOnRelease, int action) {
if (action == ACTION_NO_OP) return;
String logStr = "onActivePhoneSwitch: " + ((action == ACTION_REQUEST)
? "Requesting" : "Releasing") + " network request " + networkRequest;
mLocalLog.log(logStr);
if (action == ACTION_REQUEST) {
mDcTracker.requestNetwork(networkRequest, mLocalLog);
} else if (action == ACTION_RELEASE) {
mDcTracker.releaseNetwork(networkRequest, mLocalLog, cleanUpOnRelease);
}
}
private static int getAction(boolean wasActive, boolean isActive) {
if (!wasActive && isActive) {
return ACTION_REQUEST;
} else if (wasActive && !isActive) {
return ACTION_RELEASE;
} else {
return ACTION_NO_OP;
}
}
// apply or revoke requests if our active-ness changes
private void onActivePhoneSwitch() {
for (HashMap.Entry<NetworkRequest, Boolean> entry : mNetworkRequests.entrySet()) {
NetworkRequest networkRequest = entry.getKey();
boolean applied = entry.getValue();
boolean shouldApply = mPhoneSwitcher.shouldApplyNetworkRequest(
networkRequest, mPhone.getPhoneId());
applyRequestsOnActivePhoneSwitch(networkRequest, true,
getAction(applied, shouldApply));
mNetworkRequests.put(networkRequest, shouldApply);
}
}
// watch for phone->subId changes, reapply new filter and let
// that flow through to apply/revoke of requests
private void onSubIdChange() {
final int newSubscriptionId = mSubscriptionController.getSubIdUsingPhoneId(
mPhone.getPhoneId());
if (mSubscriptionId != newSubscriptionId) {
if (DBG) log("onSubIdChange " + mSubscriptionId + "->" + newSubscriptionId);
mSubscriptionId = newSubscriptionId;
setCapabilityFilter(makeNetworkFilter(mSubscriptionId));
}
}
@Override
public void needNetworkFor(NetworkRequest networkRequest, int score) {
Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_REQUEST);
msg.obj = networkRequest;
msg.sendToTarget();
}
private void onNeedNetworkFor(Message msg) {
NetworkRequest networkRequest = (NetworkRequest)msg.obj;
boolean shouldApply = mPhoneSwitcher.shouldApplyNetworkRequest(
networkRequest, mPhone.getPhoneId());
mNetworkRequests.put(networkRequest, shouldApply);
String s = "onNeedNetworkFor " + networkRequest + " shouldApply " + shouldApply;
log(s);
mLocalLog.log(s);
if (shouldApply) {
mDcTracker.requestNetwork(networkRequest, mLocalLog);
}
}
@Override
public void releaseNetworkFor(NetworkRequest networkRequest) {
Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_RELEASE);
msg.obj = networkRequest;
msg.sendToTarget();
}
private void onReleaseNetworkFor(Message msg) {
NetworkRequest networkRequest = (NetworkRequest)msg.obj;
boolean applied = mNetworkRequests.get(networkRequest);
mNetworkRequests.remove(networkRequest);
String s = "onReleaseNetworkFor " + networkRequest + " applied " + applied;
log(s);
mLocalLog.log(s);
if (applied) {
mDcTracker.releaseNetwork(networkRequest, mLocalLog, false);
}
}
protected void log(String s) {
Rlog.d(LOG_TAG, s);
}
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
pw.println("Network Requests:");
pw.increaseIndent();
for (HashMap.Entry<NetworkRequest, Boolean> entry : mNetworkRequests.entrySet()) {
NetworkRequest nr = entry.getKey();
boolean applied = entry.getValue();
pw.println((applied ? "Applied: " : "Not applied: ") + nr);
}
mLocalLog.dump(fd, pw, args);
pw.decreaseIndent();
}
}