javaからCに行って、androidからメールのソースコードを理解してみます。
今日はGPRSのソースコードを探していますが、SMSのソースコードが見つかりました。メール送信機能を書いてソースコードを説明してください。
まずメールで送るプログラムをください。
まず、sendTextMessageを見に行きます。ANDROID.2.3/frame eborks/base/telephone/java/android/telephoneに預けます。
ISms.aidlを見つけました。Android Interface definition langageの略語です。これはAndroid内部プロセス通信インターフェースの記述言語です。プロセス間の通信インターフェースを定義できます。この方面の知識について調べてください。file:///usr/local/dev/android-sdk-linux/docs/guide/developing/tools/aidl.htmlを選択しますANDROID.3.2.3/frame eborks/base/telephone/java/com/android/internal/telephone/に保存します。
GSMSDis patch.java
protected Commmands Interface mCm;インターフェースが再び現れます。このインターフェースはRIL.javaと関連していますので、RIL.javaファイルを見て、ANDROID.2.3.3/framworks/base/telephone/java/com/android/internal/telephone/に預けます。
Local SocketImpl.jvaは同様にネットフォルダに保存します。
肝心なところが来ます。
JNIで呼び出しが発見されたのは、{writeba unative}、[[BIILJAVA/io/FileDescriptor]]V、(void*)socket writeba]で、socketuwritebaは呼び出しの下のこの関数です。
またdivideMessageに来てください。
まずメールで送るプログラムをください。
String number = "110";
String content = " ";
SmsManager smsManager = SmsManager.getDefault();
PendingIntent sentIntent = PendingIntent.getBroadcast(MainActivity.this, 0, new Intent(), 0);
// 70,
if (strContent.length() > 70) {
List<String> msgs = smsManager.divideMessage(content);
for (String msg : msgs) {
smsManager.sendTextMessage(number, null, msg, sentIntent, null);
}
} else {
smsManager.sendTextMessage(number, null, content, sentIntent, null);
}
この中で使われている二つの鍵の関数を説明します。sendTextMessageとdivideMessageです。まず、sendTextMessageを見に行きます。ANDROID.2.3/frame eborks/base/telephone/java/android/telephoneに預けます。
/**
* Send a text based SMS.
*
* @param destinationAddress the address to send the message to
* @param scAddress is the service center address or null to use
* the current default SMSC
* @param text the body of the message to send
* @param sentIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is sucessfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:<br>
* <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
* <code>RESULT_ERROR_RADIO_OFF</code><br>
* <code>RESULT_ERROR_NULL_PDU</code><br>
* For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
* the extra "errorCode" containing a radio technology specific value,
* generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
* @param deliveryIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is delivered to the recipient. The
* raw pdu of the status report is in the extended data ("pdu").
*
* @throws IllegalArgumentException if destinationAddress or text are empty
*/
public void sendTextMessage(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
if (TextUtils.isEmpty(destinationAddress)) {
throw new IllegalArgumentException("Invalid destinationAddress");
}
if (TextUtils.isEmpty(text)) {
throw new IllegalArgumentException("Invalid message body");
}
try {
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
if (iccISms != null) {
iccISms.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent);
}
} catch (RemoteException ex) {
// ignore it
}
}
この中で一番重要なのはISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
この言葉はISmsインターフェースの一例を得て、ismsサービスを獲得することができます。ISms.aidlを見つけました。Android Interface definition langageの略語です。これはAndroid内部プロセス通信インターフェースの記述言語です。プロセス間の通信インターフェースを定義できます。この方面の知識について調べてください。file:///usr/local/dev/android-sdk-linux/docs/guide/developing/tools/aidl.htmlを選択しますANDROID.3.2.3/frame eborks/base/telephone/java/com/android/internal/telephone/に保存します。
/** Interface for applications to access the ICC phone book.
*
* <p>The following code snippet demonstrates a static method to
* retrieve the ISms interface from Android:</p>
* <pre>private static ISms getSmsInterface()
throws DeadObjectException {
IServiceManager sm = ServiceManagerNative.getDefault();
ISms ss;
ss = ISms.Stub.asInterface(sm.getService("isms"));
return ss;
}
* </pre>
*/
interface ISms {
/**
* Send an SMS.
*
* @param smsc the SMSC to send the message through, or NULL for the
* default SMSC
* @param text the body of the message to send
* @param sentIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is sucessfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
* or one of these errors:<br>
* <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
* <code>RESULT_ERROR_RADIO_OFF</code><br>
* <code>RESULT_ERROR_NULL_PDU</code><br>
* For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
* the extra "errorCode" containing a radio technology specific value,
* generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
* @param deliveryIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is delivered to the recipient. The
* raw pdu of the status report is in the extended data ("pdu").
*/ void sendText(in String destAddr, in String scAddr, in String text,
in PendingIntent sentIntent, in PendingIntent deliveryIntent);
}
sendtext関数はこれです。IcceSms InterfaceManager.javaに保存されます。 public void sendText(String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
mPhone.getContext().enforceCallingPermission(
"android.permission.SEND_SMS",
"Sending SMS message");
if (Log.isLoggable("SMS", Log.VERBOSE)) {
log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
" text='"+ text + "' sentIntent=" +
sentIntent + " deliveryIntent=" + deliveryIntent);
}
mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
}
元はmDisplatchのsendTextを呼び出して、SMSDispather.javaファイルを見に行きました。 protected abstract void sendText(String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent);
拭いて、虚関数はどこで実現しますか?探し続けましょうGSMSDis patch.java
final class GsmSMSDispatcher extends SMSDispatcher {
...
...
...
/** {@inheritDoc} */
protected void sendText(String destAddr, String scAddr, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, text, (deliveryIntent != null));
sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
}
...
...
}
まずはget SubmitPdu関数を見てみましょう。 /**
* Get an SMS-SUBMIT PDU for a destination address and a message
*
* @param scAddress Service Centre address. Null means use default.
* @return a <code>SubmitPdu</code> containing the encoded SC
* address, if applicable, and the encoded message.
* Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message, boolean statusReportRequested) {
SubmitPduBase spb;
int activePhone = TelephonyManager.getDefault().getPhoneType();
if (PHONE_TYPE_CDMA == activePhone) {
spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, message, statusReportRequested, null);
} else {
spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
destinationAddress, message, statusReportRequested);
}
return new SubmitPdu(spb);
}
sendRawPduを見に来てください。今はsendTextではないので、注意してください。protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
PendingIntent deliveryIntent) {
if (pdu == null) {
if (sentIntent != null) {
try {
sentIntent.send(RESULT_ERROR_NULL_PDU);
} catch (CanceledException ex) {}
}
return;
}
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("smsc", smsc);
map.put("pdu", pdu);
SmsTracker tracker = new SmsTracker(map, sentIntent,
deliveryIntent);
int ss = mPhone.getServiceState().getState();
if (ss != ServiceState.STATE_IN_SERVICE) {
handleNotInService(ss, tracker);
} else {
String appName = getAppNameByIntent(sentIntent);
if (mCounter.check(appName, SINGLE_PART_SMS)) {
sendSms(tracker);
} else {
sendMessage(obtainMessage(EVENT_POST_ALERT, tracker));
}
}
}
キーはそのsendSms関数です。そしてSMSDispathcerの中にsendSmsも虚数関数です。そしてGsSMSDispatch.javaの中に実現を見つけます。 protected void sendSms(SmsTracker tracker) {
HashMap map = tracker.mData;
byte smsc[] = (byte[]) map.get("smsc");
byte pdu[] = (byte[]) map.get("pdu");
Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
mCm.sendSMS(IccUtils.bytesToHexString(smsc),
IccUtils.bytesToHexString(pdu), reply);
}
mCmとは何ですかprotected Commmands Interface mCm;インターフェースが再び現れます。このインターフェースはRIL.javaと関連していますので、RIL.javaファイルを見て、ANDROID.2.3.3/framworks/base/telephone/java/com/android/internal/telephone/に預けます。
sendSMS (String smscPDU, String pdu, Message result) {
RILRequest rr
= RILRequest.obtain(RIL_REQUEST_SEND_SMS, result);
rr.mp.writeInt(2);
rr.mp.writeString(smscPDU);
rr.mp.writeString(pdu);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
send(rr);
}
同様にRIL.javaファイル安においてsend(RILRequest rr)を見つけました。 private void
send(RILRequest rr) {
Message msg;
msg = mSender.obtainMessage(EVENT_SEND, rr);
acquireWakeLock();
msg.sendToTarget();
}
またハンドルによって、RIL.javaの中で見つけられます。class RILSender extends Handler implements Runnable {
public RILSender(Looper looper) {
super(looper);
}
// Only allocated once
byte[] dataLength = new byte[4];
//***** Runnable implementation
public void
run() {
//setup if needed
}
//***** Handler implemementation
public void
handleMessage(Message msg) {
RILRequest rr = (RILRequest)(msg.obj);
RILRequest req = null;
switch (msg.what) {
case EVENT_SEND:
/**
* mRequestMessagePending++ already happened for every
* EVENT_SEND, thus we must make sure
* mRequestMessagePending-- happens once and only once
*/
boolean alreadySubtracted = false;
try {
LocalSocket s;
s = mSocket;
if (s == null) {
rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
if (mRequestMessagesPending > 0)
mRequestMessagesPending--;
alreadySubtracted = true;
return;
}
synchronized (mRequestsList) {
mRequestsList.add(rr);
mRequestMessagesWaiting++;
}
if (mRequestMessagesPending > 0)
mRequestMessagesPending--;
alreadySubtracted = true;
byte[] data;
data = rr.mp.marshall();
rr.mp.recycle();
rr.mp = null;
if (data.length > RIL_MAX_COMMAND_BYTES) {
throw new RuntimeException(
"Parcel larger than max bytes allowed! "
+ data.length);
}
// parcel length in big endian
dataLength[0] = dataLength[1] = 0;
dataLength[2] = (byte)((data.length >> 8) & 0xff);
dataLength[3] = (byte)((data.length) & 0xff);
//Log.v(LOG_TAG, "writing packet: " + data.length + " bytes");
s.getOutputStream().write(dataLength);
s.getOutputStream().write(data);
} catch (IOException ex) {
Log.e(LOG_TAG, "IOException", ex);
req = findAndRemoveRequestFromList(rr.mSerial);
// make sure this request has not already been handled,
// eg, if RILReceiver cleared the list.
if (req != null || !alreadySubtracted) {
rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
}
} catch (RuntimeException exc) {
Log.e(LOG_TAG, "Uncaught exception ", exc);
req = findAndRemoveRequestFromList(rr.mSerial);
// make sure this request has not already been handled,
// eg, if RILReceiver cleared the list.
if (req != null || !alreadySubtracted) {
rr.onError(GENERIC_FAILURE, null);
rr.release();
}
}
if (!alreadySubtracted && mRequestMessagesPending > 0) {
mRequestMessagesPending--;
}
break;
キーコード:
s.getOutputStream().write(dataLength);
s.getOutputStream().write(data);
このようにsocketを通じて内容を出して、LocalSockett.javaはfram eweorks/base/core/java/android/netに保存します。 LocalSocketImpl impl;
public LocalSocket() {
this(new LocalSocketImpl());
isBound = false;
isConnected = false;
}
/**
* Retrieves the output stream for this instance.
*
* @return output stream
* @throws IOException if socket has been closed or cannot be created.
*/
public OutputStream getOutputStream() throws IOException {
implCreateIfNeeded();
return impl.getOutputStream();
}
/**
* It's difficult to discern from the spec when impl.create() should be
* called, but it seems like a reasonable rule is "as soon as possible,
* but not in a context where IOException cannot be thrown"
*
* @throws IOException from SocketImpl.create()
*/
private void implCreateIfNeeded() throws IOException {
if (!implCreated) {
synchronized (this) {
if (!implCreated) {
try {
impl.create(true);
} finally {
implCreated = true;
}
}
}
}
}
Local SocketImpl.jvaは同様にネットフォルダに保存します。
肝心なところが来ます。
/** {@inheritDoc} */
@Override
public void write (byte[] b, int off, int len) throws IOException {
synchronized (writeMonitor) {
FileDescriptor myFd = fd;
if (myFd == null) throw new IOException("socket closed");
if (off < 0 || len < 0 || (off + len) > b.length ) {
throw new ArrayIndexOutOfBoundsException();
}
writeba_native(b, off, len, myFd);
}
}
見ましたか writeba_native(b, off, len, myFd);
base/core/jni/android_netLocarSocketImpl.cppJNIで呼び出しが発見されたのは、{writeba unative}、[[BIILJAVA/io/FileDescriptor]]V、(void*)socket writeba]で、socketuwritebaは呼び出しの下のこの関数です。
static int socket_write_all(JNIEnv *env, jobject object, int fd,
void *buf, size_t len)
{
ssize_t ret;
struct msghdr msg;
unsigned char *buffer = (unsigned char *)buf;
memset(&msg, 0, sizeof(msg));
jobjectArray outboundFds
= (jobjectArray)env->GetObjectField(
object, field_outboundFileDescriptors);
if (env->ExceptionOccurred() != NULL) {
return -1;
}
struct cmsghdr *cmsg;
int countFds = outboundFds == NULL ? 0 : env->GetArrayLength(outboundFds);
int fds[countFds];
char msgbuf[CMSG_SPACE(countFds)];
// Add any pending outbound file descriptors to the message
if (outboundFds != NULL) {
if (env->ExceptionOccurred() != NULL) {
return -1;
}
for (int i = 0; i < countFds; i++) {
jobject fdObject = env->GetObjectArrayElement(outboundFds, i);
if (env->ExceptionOccurred() != NULL) {
return -1;
}
fds[i] = jniGetFDFromFileDescriptor(env, fdObject);
if (env->ExceptionOccurred() != NULL) {
return -1;
}
}
// See "man cmsg" really
msg.msg_control = msgbuf;
msg.msg_controllen = sizeof msgbuf;
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof fds);
memcpy(CMSG_DATA(cmsg), fds, sizeof fds);
}
// We only write our msg_control during the first write
while (len > 0) {
struct iovec iv;
memset(&iv, 0, sizeof(iv));
iv.iov_base = buffer;
iv.iov_len = len;
msg.msg_iov = &iv;
msg.msg_iovlen = 1;
do {
ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
jniThrowIOException(env, errno);
return -1;
}
buffer += ret;
len -= ret;
// Wipes out any msg_control too
memset(&msg, 0, sizeof(msg));
}
return 0;
}
やっと見つけました。これはlinuxのssizaut sendmsgというシステムの呼び出しです!!!!!またdivideMessageに来てください。
/**
* Divide a message text into several fragments, none bigger than
* the maximum SMS message size.
*
* @param text the original message. Must not be null.
* @return an <code>ArrayList</code> of strings that, in order,
* comprise the original message
*/
public ArrayList<String> divideMessage(String text) {
return SmsMessage.fragmentText(text);
}
この関数は主にリfragmentTextを呼び出す方法です。 /**
* Divide a message text into several fragments, none bigger than
* the maximum SMS message text size.
*
* @param text text, must not be null.
* @return an <code>ArrayList</code> of strings that, in order,
* comprise the original msg text
*
* @hide
*/
public static ArrayList<String> fragmentText(String text) {
int activePhone = TelephonyManager.getDefault().getPhoneType();
TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false) :
com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false);
// TODO(cleanup): The code here could be rolled into the logic
// below cleanly if these MAX_* constants were defined more
// flexibly...
int limit;
if (ted.msgCount > 1) {
limit = (ted.codeUnitSize == ENCODING_7BIT) ?
MAX_USER_DATA_SEPTETS_WITH_HEADER : MAX_USER_DATA_BYTES_WITH_HEADER;
} else {
limit = (ted.codeUnitSize == ENCODING_7BIT) ?
MAX_USER_DATA_SEPTETS : MAX_USER_DATA_BYTES;
}
int pos = 0; // Index in code units.
int textLen = text.length();
ArrayList<String> result = new ArrayList<String>(ted.msgCount);
while (pos < textLen) {
int nextPos = 0; // Counts code units.
if (ted.codeUnitSize == ENCODING_7BIT) {
if (activePhone == PHONE_TYPE_CDMA && ted.msgCount == 1) {
// For a singleton CDMA message, the encoding must be ASCII...
nextPos = pos + Math.min(limit, textLen - pos);
} else {
// For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit);
}
} else { // Assume unicode.
nextPos = pos + Math.min(limit / 2, textLen - pos);
}
if ((nextPos <= pos) || (nextPos > textLen)) {
Log.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
nextPos + " >= " + textLen + ")");
break;
}
result.add(text.substring(pos, nextPos));
pos = nextPos;
}
return result;
}
メールを送るという機能が一番重要だと思います。やはりaidlに関する知識を勉強して、プロセス間通信はとても重要です。特に私たちはアンディと接触したばかりの経験が浅い子供です。自分の理解は狭くて、能力も限られています。れんがを投げて玉を引くだけの効果があるので、みんなで自分の心得を共有してほしいです。