Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
implemented SMS receiving
  • Loading branch information
scintill committed Aug 31, 2017
1 parent 74d23a3 commit 8cc17da
Show file tree
Hide file tree
Showing 9 changed files with 355 additions and 20 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -8,3 +8,6 @@ lib/java/dbus/Manifest
lib/java/dbus/bin
lib/java/dbus/classes
lib/java/dbus/*.jar

lib/java/android
cyanogenmod
5 changes: 4 additions & 1 deletion Android.mk
Expand Up @@ -20,7 +20,10 @@ LOCAL_PATH := $(call my-dir)
# Build our Java RIL
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := RilOfono
LOCAL_SRC_FILES := $(call all-java-files-under,src/java) $(call all-java-files-under,lib/java)
LOCAL_SRC_FILES := $(call all-java-files-under,src/java) \
$(call all-java-files-under,lib/java/dbus) \
$(call all-java-files-under,lib/java/debug) \
$(call all-java-files-under,lib/java/ofono)
LOCAL_JAVA_LIBRARIES := telephony-common
LOCAL_CERTIFICATE := platform

Expand Down
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -36,6 +36,9 @@ The goal of this project is to write an Android RIL daemon implemented on top of
* dexopt/proguard? - see notes in Android.mk
* make sure socket operations on the main thread trigger strict mode exceptions (are Unix sockets a loophole?)
* make dbus exceptions be checked exceptions, so the compiler will find them and I have to handle them
* SMS send/receive - current implementation is limited when it comes to things like long messages. We could
concatenate/split PDUs in the RIL to map on to Ofono's nice API, but we will not be able to implement a few things
that raw PDUs can do. And duplicating the work is ugly anyway, so I think I will just patch Ofono with raw PDU APIs.

# License

Expand Down
12 changes: 12 additions & 0 deletions mount
@@ -0,0 +1,12 @@
#!/bin/bash
set -x
BASE=$(realpath $(dirname $0))

mount -t overlay overlay -o ro -olowerdir=\
$BASE/cyanogenmod/12/frameworks/base/core/java:\
$BASE/cyanogenmod/12/frameworks/base/telephony/java:\
$BASE/cyanogenmod/12/frameworks/opt/telephony/src/java:\
$BASE/cyanogenmod/12/libcore/luni/src/main/java:\
$BASE/cyanogenmod/12/packages/services/Telephony/src\
\
$BASE/lib/java/android/
20 changes: 4 additions & 16 deletions src/apk.iml
Expand Up @@ -5,21 +5,6 @@
<content url="file://$MODULE_DIR$/../cyanogenmod/12/device/samsung/serrano-common/ril">
<sourceFolder url="file://$MODULE_DIR$/../cyanogenmod/12/device/samsung/serrano-common/ril" isTestSource="false" packagePrefix="com.android.internal.telephony" />
</content>
<content url="file://$MODULE_DIR$/../cyanogenmod/12/frameworks/base/core/java">
<sourceFolder url="file://$MODULE_DIR$/../cyanogenmod/12/frameworks/base/core/java" isTestSource="false" />
</content>
<content url="file://$MODULE_DIR$/../cyanogenmod/12/frameworks/base/telephony/java">
<sourceFolder url="file://$MODULE_DIR$/../cyanogenmod/12/frameworks/base/telephony/java" isTestSource="false" />
</content>
<content url="file://$MODULE_DIR$/../cyanogenmod/12/frameworks/opt/telephony">
<sourceFolder url="file://$MODULE_DIR$/../cyanogenmod/12/frameworks/opt/telephony/src/java" isTestSource="false" />
</content>
<content url="file://$MODULE_DIR$/../cyanogenmod/12/libcore/luni/src/main/java">
<sourceFolder url="file://$MODULE_DIR$/../cyanogenmod/12/libcore/luni/src/main/java" isTestSource="false" />
</content>
<content url="file://$MODULE_DIR$/../cyanogenmod/12/packages/services/Telephony/src">
<sourceFolder url="file://$MODULE_DIR$/../cyanogenmod/12/packages/services/Telephony/src" isTestSource="false" />
</content>
<content url="file://$MODULE_DIR$/../lib/java/dbus">
<sourceFolder url="file://$MODULE_DIR$/../lib/java/dbus" isTestSource="false" />
</content>
Expand All @@ -29,6 +14,9 @@
<content url="file://$MODULE_DIR$/../lib/java/ofono">
<sourceFolder url="file://$MODULE_DIR$/../lib/java/ofono" isTestSource="false" />
</content>
<content url="file://$MODULE_DIR$/../lib/java/android">
<sourceFolder url="file://$MODULE_DIR$/../lib/java/android" isTestSource="false" />
</content>
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
Expand All @@ -37,4 +25,4 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="android-support-annotations" level="project" />
</component>
</module>
</module>
114 changes: 114 additions & 0 deletions src/java/net/scintill/ril_ofono/AospUtils.java
@@ -0,0 +1,114 @@
package net.scintill.ril_ofono;

import android.telephony.Rlog;

import static com.android.internal.telephony.SmsConstants.ENCODING_16BIT;
import static com.android.internal.telephony.SmsConstants.ENCODING_7BIT;
import static com.android.internal.telephony.SmsConstants.ENCODING_8BIT;
import static com.android.internal.telephony.SmsConstants.ENCODING_KSC5601;
import static com.android.internal.telephony.SmsConstants.ENCODING_UNKNOWN;

public class AospUtils {
private static final String LOG_TAG = "AospUtils";

/*
* Based on com.android.internal.telephony.gsm.SmsMessage in CM12
*
* Copyright (C) 2006 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.
*/
public static int getEncodingType(int dataCodingScheme) {
boolean hasMessageClass = false;
boolean userDataCompressed = false;

int encodingType = ENCODING_UNKNOWN;

// Look up the data encoding scheme
if ((dataCodingScheme & 0x80) == 0) {
userDataCompressed = (0 != (dataCodingScheme & 0x20));
hasMessageClass = (0 != (dataCodingScheme & 0x10));

if (userDataCompressed) {
Rlog.w(LOG_TAG, "4 - Unsupported SMS data coding scheme "
+ "(compression) " + (dataCodingScheme & 0xff));
} else {
switch ((dataCodingScheme >> 2) & 0x3) {
case 0: // GSM 7 bit default alphabet
encodingType = ENCODING_7BIT;
break;

case 2: // UCS 2 (16bit)
encodingType = ENCODING_16BIT;
break;

case 1: // 8 bit data
//Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet string
//that's stored in 8-bit unpacked format) characters.
encodingType = ENCODING_8BIT;
break;

case 3: // reserved
Rlog.w(LOG_TAG, "1 - Unsupported SMS data coding scheme "
+ (dataCodingScheme & 0xff));
encodingType = ENCODING_8BIT;
break;
}
}
} else if ((dataCodingScheme & 0xf0) == 0xf0) {
hasMessageClass = true;
userDataCompressed = false;

if (0 == (dataCodingScheme & 0x04)) {
// GSM 7 bit default alphabet
encodingType = ENCODING_7BIT;
} else {
// 8 bit data
encodingType = ENCODING_8BIT;
}
} else if ((dataCodingScheme & 0xF0) == 0xC0
|| (dataCodingScheme & 0xF0) == 0xD0
|| (dataCodingScheme & 0xF0) == 0xE0) {
// 3GPP TS 23.038 V7.0.0 (2006-03) section 4

// 0xC0 == 7 bit, don't store
// 0xD0 == 7 bit, store
// 0xE0 == UCS-2, store

if ((dataCodingScheme & 0xF0) == 0xE0) {
encodingType = ENCODING_16BIT;
} else {
encodingType = ENCODING_7BIT;
}

userDataCompressed = false;
boolean active = ((dataCodingScheme & 0x08) == 0x08);
// bit 0x04 reserved
} else if ((dataCodingScheme & 0xC0) == 0x80) {
// 3GPP TS 23.038 V7.0.0 (2006-03) section 4
// 0x80..0xBF == Reserved coding groups
if (dataCodingScheme == 0x84) {
// This value used for KSC5601 by carriers in Korea.
encodingType = ENCODING_KSC5601;
} else {
Rlog.w(LOG_TAG, "5 - Unsupported SMS data coding scheme "
+ (dataCodingScheme & 0xff));
}
} else {
Rlog.w(LOG_TAG, "3 - Unsupported SMS data coding scheme "
+ (dataCodingScheme & 0xff));
}

return encodingType;
}
}
111 changes: 108 additions & 3 deletions src/java/net/scintill/ril_ofono/RilOfono.java
Expand Up @@ -26,19 +26,21 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
import android.telephony.SignalStrength;
import android.telephony.SmsMessage;
import android.text.TextUtils;

import com.android.internal.telephony.BaseCommands;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.SmsConstants;
import com.android.internal.telephony.SmsResponse;
import com.android.internal.telephony.UUSInfo;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsMessage;
import com.android.internal.telephony.uicc.AdnRecord;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccCardStatus;
Expand All @@ -65,11 +67,16 @@
import org.ofono.SimManager;
import org.ofono.Struct1;

import java.io.ByteArrayOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;

import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_EDGE;
Expand Down Expand Up @@ -110,7 +117,7 @@ public class RilOfono extends BaseCommands implements CommandsInterface {

public RilOfono(Context context, int preferredNetworkType, int cdmaSubscription, Integer instanceId) {
super(context);
Rlog.d(TAG, String.format("RilOfono %d starting", BUILD_NUMBER));
Rlog.d(TAG, "RilOfono "+BUILD_NUMBER+" starting");

mPhoneType = RILConstants.NO_PHONE;

Expand All @@ -136,6 +143,8 @@ public void run() {
mDbus.addSigHandler(NetworkRegistration.PropertyChanged.class, sigHandler);
mDbus.addSigHandler(SimManager.PropertyChanged.class, sigHandler);
mDbus.addSigHandler(org.ofono.Message.PropertyChanged.class, sigHandler);
mDbus.addSigHandler(MessageManager.IncomingMessage.class, sigHandler);
mDbus.addSigHandler(MessageManager.ImmediateMessage.class, sigHandler);
initProps();
onModemChange(false); // initialize starting state
} catch (DBusException e) {
Expand All @@ -144,6 +153,8 @@ public void run() {
}
}
});

//mMainHandler.postDelayed(new Tests(this), 10000);
}

@Override
Expand Down Expand Up @@ -520,6 +531,36 @@ public void sendSMSExpectMore(String smscPDU, String pdu, Message result) {
sendSMS(smscPDU, pdu, result);
}

private void handleIncomingMessage(String content, Map<String, Variant> info) {
String dateStr = (String) info.get("SentTime").getValue();
String sender = (String) info.get("Sender").getValue();

Rlog.d(TAG, "handleIncomingMessage "+sender+" "+dateStr+" "+content); // TODO sensitive

Date date = Utils.parseOfonoDate(dateStr);
if (date == null) {
Rlog.e(TAG, "error parsing SMS date "+dateStr);
date = new Date();
}

final Object msg = createReceivedMessage(sender, content, date, getProp(info, "Immediate", false));
mMainHandler.post(new Runnable() {
@Override
public void run() {
mGsmSmsRegistrant.notifyResult(msg);
}
});
}

public void handle(MessageManager.IncomingMessage s) {
handleIncomingMessage(s.message, s.info);
}

public void handle(MessageManager.ImmediateMessage s) {
s.info.put("Immediate", new Variant<>(true));
handleIncomingMessage(s.message, s.info);
}

@Override
public void sendCdmaSms(byte[] pdu, Message response) {
genericTrace(); // XXX NYI
Expand Down Expand Up @@ -1459,13 +1500,57 @@ private SmsMessage parseSmsPduStrs(String smscPDUStr, String pduStr) {
smscPDUStr = "00"; // see PduParser; means no smsc
}
try {
return SmsMessage.createFromPdu(IccUtils.hexStringToBytes(smscPDUStr + pduStr));
return SmsMessage.createFromPdu(IccUtils.hexStringToBytes(smscPDUStr + pduStr), SmsConstants.FORMAT_3GPP);
} catch (Throwable t) {
// SmsMessage should have logged information about the error
return null;
}
}

private SmsMessage createReceivedMessage(String sender, String contentText, Date date, boolean immediate) {
try {
// see SmsMessage#parsePdu
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(new byte[] {
0x00, // null sc address
0x00, // deliver type. no reply path or user header
});
byte[] bcdSender = PhoneNumberUtils.networkPortionToCalledPartyBCD(sender);
os.write((bcdSender.length - 1) * 2); // BCD digit count, excluding TOA.
os.write(bcdSender);

// build a submit pdu so it will encode the message for us
// it turned out to not be as convenient as I hoped, but probably still better than
// writing/copying here
com.android.internal.telephony.gsm.SmsMessage.SubmitPdu submitPduOb =
com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(null, "0", contentText, false);
byte[] submitPdu = new byte[1 + submitPduOb.encodedMessage.length];
submitPdu[0] = 0x00; // null sc adddr, so it will parse below
System.arraycopy(submitPduOb.encodedMessage, 0, submitPdu, 1, submitPduOb.encodedMessage.length);
com.android.internal.telephony.gsm.SmsMessage msg =
com.android.internal.telephony.gsm.SmsMessage.createFromPdu(submitPdu);
if (msg == null) throw new RuntimeException("unable to parse submit pdu to create deliver pdu");

// finish writing the deliver
int dataCodingScheme = callPrivateMethod(msg, Integer.class, "getDataCodingScheme");
os.write(new byte[]{
0x00, // protocol identifier
(byte) (dataCodingScheme | (immediate ? 0x10 : 0))
});
os.write(getScTimestamp(date));
byte[] payload = msg.getUserData();
os.write(AospUtils.getEncodingType(dataCodingScheme) != SmsConstants.ENCODING_7BIT ?
payload.length : // octet length
// septet length - we can't tell how many meaningful septets there are, but
// I think in the case of 7bit it will always be the length of the string
contentText.length());
os.write(payload);
return SmsMessage.createFromPdu(os.toByteArray(), SmsConstants.FORMAT_3GPP);
} catch (Throwable t) {
return null;
}
}

// opposite of IccUtils#bcdToString
private byte[] stringToBcd(String str) {
byte[] ret = new byte[(str.length() / 2) + 1];
Expand All @@ -1490,4 +1575,24 @@ public String toDebugString(Object o) {
}
}

@SuppressWarnings("unchecked")
private static <T> T callPrivateMethod(Object o, Class<T> returnClass, String methodName)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {

Method m = o.getClass().getDeclaredMethod(methodName);
m.setAccessible(true);
return (T) m.invoke(o);
}

private static byte[] getScTimestamp(Date d) {
// opposite of SmsMessage#getSCTimestampMillis()
// value is BCD nibble-swapped ymdhmszz (z = zone)
SimpleDateFormat fmt = new SimpleDateFormat("ssmmHHddMMyy", Locale.US);
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
StringBuilder b = new StringBuilder(fmt.format(d));
b.reverse();
b.append("00"); // TODO preserve real tz?
return IccUtils.hexStringToBytes(b.toString());
}

}

0 comments on commit 8cc17da

Please sign in to comment.