diff options
| author | Joey Hewitt <joey@joeyhewitt.com> | 2017-09-08 14:47:00 -0700 |
|---|---|---|
| committer | Joey Hewitt <joey@joeyhewitt.com> | 2017-09-09 17:58:44 -0700 |
| commit | 478da9efb7b4e2b4da3781b5e783fcbfd4b85416 (patch) | |
| tree | 59e404b5cb62d2c4da9c0f93a15415918d9a33c4 | |
| parent | be1f7d35e47c108bc1e3dc3829a143f74f051310 (diff) | |
| download | frameworks_opt_telephony_ril_ofono-478da9efb7b4e2b4da3781b5e783fcbfd4b85416.tar.gz frameworks_opt_telephony_ril_ofono-478da9efb7b4e2b4da3781b5e783fcbfd4b85416.tar.bz2 frameworks_opt_telephony_ril_ofono-478da9efb7b4e2b4da3781b5e783fcbfd4b85416.zip | |
improve datacall implementation
Bugfixes, work with oFono driver other than rilmodem.
* Notify VoiceNetworkStateRegistrants when we attach to the data
network -- seems to be the way to get the framework to re-poll data
registration state and start any datacalls it wants to. This should
fix the problems where the framework wouldn't even try to start a
datacall.
* Fall back on voice radio technology value if oFono doesn't provide
data technology. Do it by dependency injection of a limited interface
to minimize coupling.
* Return even inactive datacalls (not sure that it matters much).
* Return the netmask, in e.g. "/30" notation as expected by framework
code.
* Setup/teardown the network interface, using
INetworkManagementService. I guess the oFono rilmodem plugin was
doing this for us as a side-effect. I found the network management
service by searching for Java code that could do that stuff, but we
could switch to something else if needed.
Currently relying on oFono to give us the interface name (patched my
gobidev plugin to specify rmnet0), but I wonder if interface assignment
is something better done outside oFono. Does the modem care which
interface is used for which context? It seems weird, in this usecase
at least, for oFono to give us an interface, if it has no relation to
the modem. Could investigate rild's message "Interface rmnet1 will be
used for CID(2)" to see how it decides.
Some errors I was seeing early on may have come from oFono not having
an interface assigned, so if we do leave that unset on their end
maybe there's something to work around.
| -rw-r--r-- | README.md | 5 | ||||
| -rwxr-xr-x | mount | 3 | ||||
| -rw-r--r-- | src/java/net/scintill/ril_ofono/DatacallModule.java | 160 | ||||
| -rw-r--r-- | src/java/net/scintill/ril_ofono/ModemModule.java | 11 | ||||
| -rw-r--r-- | src/java/net/scintill/ril_ofono/RilOfono.java | 8 |
5 files changed, 157 insertions, 30 deletions
@@ -45,16 +45,13 @@ Nothing is thoroughly tested nor tried on a broad selection of hardware/networks * On my device, oFono seems unable to give Line ID (I think it's a RIL parcel mismatch), so numbers are displayed as "Unknown" * User-intiated USSD (special dial codes, such as `#999#` which displays balance on my carrier) * Basic data connections - * Might be a little flaky; see TODO below + * When testing, you might need to turn off wifi to ensure the data connection gets used. * Reporting of phone #, ICCID, voicemail # to Android # Resources and Credit * https://github.com/nitdroid/ofono-ril for some help on mapping ofono properties to Android RIL # Bugs and TODO -* look at and fix flaky data call setup. It seems the framework doesn't ask us to set up a connection, even if the user toggles the data slider. - * Toggling airplane mode, or restarting com.android.phone with the slider on, may help as workarounds. - * Also, turning off wifi may be needed to make sure the framework has a reason to enable mobile data * make dbus exceptions be checked exceptions, so the compiler will find them and I have to handle them * dexopt/proguard? - see notes in Android.mk * crashes in airplane mode trying to query properties on probably not-up interfaces @@ -11,6 +11,7 @@ $CMBASE/frameworks/base/telecomm/java:\ $CMBASE/frameworks/opt/telephony/src/java:\ $CMBASE/libcore/luni/src/main/java:\ $CMBASE/libcore/dalvik/src/main/java:\ -$CMBASE/packages/services/Telephony/src\ +$CMBASE/packages/services/Telephony/src:\ +$CMBASE/out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java\ \ $BASE/lib/java/android/ diff --git a/src/java/net/scintill/ril_ofono/DatacallModule.java b/src/java/net/scintill/ril_ofono/DatacallModule.java index 11a4ecd..f8c159c 100644 --- a/src/java/net/scintill/ril_ofono/DatacallModule.java +++ b/src/java/net/scintill/ril_ofono/DatacallModule.java @@ -19,6 +19,11 @@ package net.scintill.ril_ofono; +import android.net.InterfaceConfiguration; +import android.net.LinkAddress; +import android.net.NetworkUtils; +import android.os.INetworkManagementService; +import android.os.RemoteException; import android.telephony.Rlog; import android.text.TextUtils; @@ -37,16 +42,21 @@ import org.ofono.ConnectionContext; import org.ofono.ConnectionManager; import org.ofono.StructPathAndProps; +import java.net.Inet4Address; +import java.nio.ByteOrder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import libcore.io.Memory; + import static com.android.internal.telephony.CommandException.Error.MODE_NOT_SUPPORTED; import static com.android.internal.telephony.CommandException.Error.NO_SUCH_ELEMENT; import static com.android.internal.telephony.CommandException.Error.REQUEST_NOT_SUPPORTED; import static net.scintill.ril_ofono.RilOfono.RegistrantList; import static net.scintill.ril_ofono.RilOfono.notifyResultAndLog; +import static net.scintill.ril_ofono.RilOfono.privExc; import static net.scintill.ril_ofono.RilOfono.privStr; import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; @@ -55,13 +65,20 @@ import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; private static final String TAG = RilOfono.TAG; private ConnectionManager mConnMan; + private INetworkManagementService mNetworkManagementService; private RegistrantList mDataNetworkStateRegistrants; + private RegistrantList mVoiceNetworkStateRegistrants; + private VoiceRadioTechnologyGetter mVoiceRadioTechnologyGetter; private final Map<String, Variant<?>> mConnManProps = new HashMap<>(); private final Map<String, Map<String, Variant<?>>> mConnectionsProps = new HashMap<>(); + private final Map<String, String> mLastInterface = new HashMap<>(); - DatacallModule(RegistrantList dataNetworkStateRegistrants) { + DatacallModule(RegistrantList dataNetworkStateRegistrants, RegistrantList voiceNetworkStateRegistrants, VoiceRadioTechnologyGetter voiceRadioTechnologyGetter, INetworkManagementService networkManagementService) { mDataNetworkStateRegistrants = dataNetworkStateRegistrants; + mVoiceNetworkStateRegistrants = voiceNetworkStateRegistrants; + mVoiceRadioTechnologyGetter = voiceRadioTechnologyGetter; + mNetworkManagementService = networkManagementService; mConnMan = RilOfono.sInstance.getOfonoInterface(ConnectionManager.class); @@ -78,7 +95,7 @@ import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; // see e.g. GsmServiceStateTracker for the values and offsets, though some appear unused ""+(getProp(mConnManProps, "Attached", Boolean.FALSE) ? OfonoRegistrationState.registered : OfonoRegistrationState.unregistered).ts27007Creg, "", "", // unused? - ""+getProp(mConnManProps, "Bearer", OfonoNetworkTechnology._unknown).serviceStateInt, + ""+getBearerTechnology().serviceStateInt, }; } @@ -89,15 +106,15 @@ import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; } @Override - public Object setupDataCall(String radioTechnologyStr, String profile, String apnStr, String user, String password, String authType, String protocol) { - OfonoNetworkTechnology radioTechnology = OfonoNetworkTechnology.fromSetupDataCallValue(Integer.valueOf(radioTechnologyStr)); - OfonoNetworkTechnology currentRadioTechnology = getProp(mConnManProps, "Bearer", OfonoNetworkTechnology._unknown); + public Object setupDataCall(String radioTechnologyIntStr, String profile, String apnStr, String user, String password, String authType, String protocol) { + OfonoNetworkTechnology radioTechnology = OfonoNetworkTechnology.fromSetupDataCallValue(Integer.valueOf(radioTechnologyIntStr)); + OfonoNetworkTechnology currentRadioTechnology = getBearerTechnology(); - Rlog.d(TAG, "setupDataCall "+radioTechnology+"("+radioTechnologyStr+") "+privStr(profile+" "+apnStr+" "+user+" "+password+" "+authType)+" "+protocol); + Rlog.d(TAG, "setupDataCall "+radioTechnology+"("+radioTechnologyIntStr+") "+privStr(profile+" "+apnStr+" "+user+" "+password+" "+authType)+" "+protocol); // let's be stringent for now... if (radioTechnology != currentRadioTechnology) { - Rlog.e(TAG, "Unable to provide requested radio technology "+radioTechnology+"("+radioTechnologyStr+"); current is "+currentRadioTechnology); + Rlog.e(TAG, "Unable to provide requested radio technology "+radioTechnology+"("+radioTechnologyIntStr+"); current is "+currentRadioTechnology); throw new CommandException(MODE_NOT_SUPPORTED); } if (!profile.equals(String.valueOf(RILConstants.DATA_PROFILE_DEFAULT))) { @@ -135,10 +152,6 @@ import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; Map<String, Variant<?>> ipSettings = getProp(props, "Settings", new HashMap<String, Variant<?>>()); // TODO ipv6? - if (!getProp(props, "Active", Boolean.FALSE)) { - return null; - } - DataCallResponse dcr = new DataCallResponse(); // see RIL#getDataCallResponse for guidance on these values dcr.version = 11; @@ -148,13 +161,17 @@ import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; dcr.active = getProp(props, "Active", Boolean.FALSE) ? DATA_CONNECTION_ACTIVE_PH_LINK_UP : DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE; dcr.type = ""; // I don't think anything is using this, and I'm not sure the valid values - dcr.ifname = getProp(ipSettings, "Interface", ""); - if (TextUtils.isEmpty(dcr.ifname)) { - Rlog.e(TAG, "empty Interface for datacall "+dbusPath+"; skipping"); - return null; + dcr.ifname = getProp(ipSettings, "Interface", (String)null); + if (dcr.ifname != null) { + mLastInterface.put(dbusPath, dcr.ifname); } - dcr.addresses = singleStringToArray(getProp(ipSettings, "Address", "")); + if (dcr.addresses.length > 0) { + String netmask = getProp(ipSettings, "Netmask", (String)null); + if (!TextUtils.isEmpty(netmask)) { + dcr.addresses[0] += "/"+getPrefixLength((Inet4Address)NetworkUtils.numericToInetAddress(netmask)); + } + } dcr.dnses = getProp(ipSettings, "DomainNameServers", new String[0]); dcr.gateways = singleStringToArray(getProp(ipSettings, "Gateway", "")); dcr.mtu = PhoneConstants.UNSET_MTU; @@ -205,6 +222,16 @@ import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; if (handle2dPropChange(mConnectionsProps, s.getPath(), ConnectionContext.class, s.name, s.value)) { runOnMainThreadDebounced(mFnNotifyDataNetworkState, 200); } + if (s.name.equals("Settings") || s.name.equals("Active")) { + try { + Map<String, Variant<?>> connectionProps = mConnectionsProps.get(s.getPath()); + if (connectionProps != null) { // might have been deleted already in the case of going inactive + onContextIp4SettingsChange(s.getPath(), getDataCallResponse(s.getPath(), connectionProps)); + } + } catch (RemoteException e) { + Rlog.e(TAG, "Uncaught RemoteException while setting up/down data context", privExc(e)); + } + } } public void onPropChange(ConnectionManager connMan, String name, Variant<?> value) { @@ -228,8 +255,22 @@ import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; } public void handle(ConnectionManager.ContextRemoved s) { - forgetAboutUniqueId(s.path.getPath()); - mConnectionsProps.remove(s.path.getPath()); + String dbusPath = s.path.getPath(); + + // tear down interface + try { + Map<String, Variant<?>> connectionProps = mConnectionsProps.get(dbusPath); + if (connectionProps != null) { + DataCallResponse dcr = getDataCallResponse(dbusPath, connectionProps); + dcr.active = DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE; + onContextIp4SettingsChange(dbusPath, dcr); + } + } catch (Throwable e) { + Rlog.e(TAG, "handle(" + s.getClass() + ") " + dbusPath + ": Unexpected exception", privExc(e)); + } + + forgetAboutUniqueId(dbusPath); + mConnectionsProps.remove(dbusPath); runOnMainThreadDebounced(mFnNotifyDataNetworkState, 200); } @@ -237,11 +278,82 @@ import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; @Override public void run() { Object calls = getDataCallListImpl(); - Rlog.d(TAG, "notify dataNetworkState "+privStr(calls)); notifyResultAndLog("data netstate", mDataNetworkStateRegistrants, calls, true); + + // This one seems out-of-place, but as far as I can tell it's the way to get + // ServiceStateTracker to poll us and discover our data registration state. + notifyResultAndLog("voice netstate (because of data netstate)", mVoiceNetworkStateRegistrants, null, false); } }; + private boolean onContextIp4SettingsChange(String dbusPath, DataCallResponse dcr) throws RemoteException { + if (dcr == null) { + Rlog.e(TAG, "onContextIpSettingsChange: invalid DataCallResponse"); + return false; + } + + if (dcr.active == DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE) { + // dcr's iface is null at this point, so use the last one we knew about + if (mLastInterface.get(dbusPath) != null) { + String iface = mLastInterface.get(dbusPath); + mNetworkManagementService.setInterfaceDown(iface); + mNetworkManagementService.clearInterfaceAddresses(iface); + } else { + Rlog.e(TAG, "Interface unknown for context "+dbusPath+"; unable to ifdown"); + return false; + } + } else if (dcr.active == DATA_CONNECTION_ACTIVE_PH_LINK_UP) { + if (dcr.ifname == null) { + Rlog.w(TAG, "Got an active connection with no interface; ignoring (might be mid-statechange)"); + return false; + } + mNetworkManagementService.clearInterfaceAddresses(dcr.ifname); + if (dcr.addresses.length > 1) { + // we currently can't get this state from oFono + Rlog.e(TAG, "Got a multi-address interface; ignoring"); + return false; + } + + if (dcr.addresses.length == 1) { + InterfaceConfiguration ifaceCfg = new InterfaceConfiguration(); + String[] pieces = dcr.addresses[0].split("/"); + ifaceCfg.setLinkAddress(new LinkAddress( + NetworkUtils.numericToInetAddress(pieces[0]), + pieces.length == 2 ? Integer.parseInt(pieces[1]) : 32 + )); + ifaceCfg.setInterfaceUp(); + mNetworkManagementService.setInterfaceConfig(dcr.ifname, ifaceCfg); + } else { + mNetworkManagementService.setInterfaceUp(dcr.ifname); + } + if (dcr.mtu != PhoneConstants.UNSET_MTU) { + mNetworkManagementService.setMtu(dcr.ifname, dcr.mtu); + } + } else { + Rlog.w(TAG, "Ignoring context with unknown state dcr.active="+ dcr.active); + return false; + } + + return true; + } + + private int getPrefixLength(Inet4Address netmask) { + return Integer.bitCount(Memory.peekInt(netmask.getAddress(), 0, ByteOrder.BIG_ENDIAN)); + } + + /*package*/ interface VoiceRadioTechnologyGetter { + OfonoNetworkTechnology getVoiceRadioTechnology(); + } + + private OfonoNetworkTechnology getBearerTechnology() { + // ConnectionManager Bearer property is optional, so fall back on voice radio tech + OfonoNetworkTechnology tech = getProp(mConnManProps, "Bearer", OfonoNetworkTechnology._unknown); + if (tech == OfonoNetworkTechnology._unknown) { + tech = mVoiceRadioTechnologyGetter.getVoiceRadioTechnology(); + } + return tech; + } + private final Map<String, Integer> mPathToConnIdMap = new HashMap<>(); private int mNextConnId = 1; @@ -295,9 +407,12 @@ import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; OfonoAuthMethod authMethod = OfonoAuthMethod.fromRilConstant(authType); if (authMethod != null) { ctx.SetProperty("AuthenticationMethod", new Variant<>(authMethod.toString())); + ctx.SetProperty("Username", new Variant<>(username)); + ctx.SetProperty("Password", new Variant<>(password)); + } else { + ctx.SetProperty("Username", new Variant<>("")); + ctx.SetProperty("Password", new Variant<>("")); } - ctx.SetProperty("Username", new Variant<>(username)); - ctx.SetProperty("Password", new Variant<>(password)); } } @@ -307,8 +422,9 @@ import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; switch(i) { case RILConstants.SETUP_DATA_AUTH_PAP: return pap; case RILConstants.SETUP_DATA_AUTH_CHAP: return chap; + case RILConstants.SETUP_DATA_AUTH_NONE: return null; default: - Rlog.e(TAG, "unknown/non-mapping authmethod constant "+i+"; not setting method"); + Rlog.e(TAG, "unknown/unsupported authmethod constant "+i+"; not setting method"); return null; } } diff --git a/src/java/net/scintill/ril_ofono/ModemModule.java b/src/java/net/scintill/ril_ofono/ModemModule.java index bceb1b8..7c29b49 100644 --- a/src/java/net/scintill/ril_ofono/ModemModule.java +++ b/src/java/net/scintill/ril_ofono/ModemModule.java @@ -149,6 +149,7 @@ import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; public Object getNetworkSelectionMode() { String mode = getProp(mNetRegProps, "Mode", (String)null); if (mode == null) { + Rlog.w(TAG, "getNetworkSelectionMode(): oFono is not reporting the mode; returning CommandException"); throw new CommandException(GENERIC_FAILURE); } else { return new int[]{ mode.equals("manual") ? 1 : 0 }; @@ -255,10 +256,16 @@ import static net.scintill.ril_ofono.RilOfono.runOnMainThreadDebounced; } } + /*package*/ final DatacallModule.VoiceRadioTechnologyGetter mVoiceRadioTechnologyGetter = new DatacallModule.VoiceRadioTechnologyGetter() { + @Override + public OfonoNetworkTechnology getVoiceRadioTechnology() { + return getProp(mNetRegProps, "Technology", OfonoNetworkTechnology._unknown); + } + }; + private Object getVoiceRadioTechnologyImpl() { // TODO is this really the right value? - OfonoNetworkTechnology tech = getProp(mNetRegProps, "Technology", OfonoNetworkTechnology._unknown); - return new int[]{ tech.serviceStateInt }; + return new int[]{ mVoiceRadioTechnologyGetter.getVoiceRadioTechnology().serviceStateInt }; } } diff --git a/src/java/net/scintill/ril_ofono/RilOfono.java b/src/java/net/scintill/ril_ofono/RilOfono.java index 7af0847..48d0942 100644 --- a/src/java/net/scintill/ril_ofono/RilOfono.java +++ b/src/java/net/scintill/ril_ofono/RilOfono.java @@ -19,10 +19,13 @@ package net.scintill.ril_ofono; +import android.content.Context; import android.os.AsyncResult; import android.os.Handler; import android.os.HandlerThread; +import android.os.INetworkManagementService; import android.os.Message; +import android.os.ServiceManager; import android.os.StrictMode; import android.telephony.Rlog; import android.text.TextUtils; @@ -94,7 +97,10 @@ public class RilOfono implements RilMiscInterface { mRilWrapper.mSmsModule = new SmsModule(mRilWrapper.mGsmSmsRegistrants); // TODO gsm-specific mRilWrapper.mSimModule = new SimModule(mRilWrapper.mIccStatusChangedRegistrants); mRilWrapper.mVoicecallModule = new VoicecallModule(mRilWrapper.mCallStateRegistrants); - mRilWrapper.mDatacallModule = new DatacallModule(mRilWrapper.mDataNetworkStateRegistrants); + mRilWrapper.mDatacallModule = new DatacallModule( + mRilWrapper.mDataNetworkStateRegistrants, mRilWrapper.mVoiceNetworkStateRegistrants, + ((ModemModule)mRilWrapper.mModemModule).mVoiceRadioTechnologyGetter, + INetworkManagementService.Stub.asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE))); mRilWrapper.mSupplementaryServicesModule = new SupplementaryServicesModule(mRilWrapper.mUSSDRegistrants); ((ModemModule)mRilWrapper.mModemModule).onModemChange(false); // initialize starting state } catch (Throwable t) { |
