blob: 9a32e1c39fb906b8b6343d5e47f82fa195aabbef [file] [log] [blame]
/*
* Copyright (C) 2008 Esmertec AG.
* Copyright (C) 2008 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.im.service;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.provider.Telephony;
import static android.provider.Telephony.Sms.Intents.DATA_SMS_RECEIVED_ACTION;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Log;
import com.android.im.engine.SmsService;
public class AndroidSmsService implements SmsService {
private static final String TAG = RemoteImService.TAG;
private static final String SMS_STATUS_RECEIVED_ACTION =
"com.android.im.SmsService.SMS_STATUS_RECEIVED";
private static final int sMaxSmsLength =
SmsMessage.MAX_USER_DATA_BYTES - 7/* UDH size */;
private Context mContext;
private SmsReceiver mSmsReceiver;
private IntentFilter mIntentFilter;
private boolean mStarted;
/*package*/HashMap<Integer, ListenerList> mListeners;
/*package*/HashMap<Long, SmsSendFailureCallback> mFailureCallbacks;
public AndroidSmsService(Context context) {
mContext = context;
mSmsReceiver = new SmsReceiver();
mIntentFilter = new IntentFilter(
Telephony.Sms.Intents.DATA_SMS_RECEIVED_ACTION);
mIntentFilter.addDataScheme("sms");
mListeners = new HashMap<Integer, ListenerList>();
mFailureCallbacks = new HashMap<Long, SmsSendFailureCallback>();
}
public int getMaxSmsLength() {
return sMaxSmsLength;
}
public void sendSms(String dest, int port, byte[] data) {
sendSms(dest, port, data, null);
}
public void sendSms(String dest, int port, byte[] data,
SmsSendFailureCallback callback) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
try {
log(dest + ":" + port + " >>> " + new String(data, "UTF-8"));
} catch (UnsupportedEncodingException e) {
}
}
if (data.length > sMaxSmsLength) {
Log.e(TAG, "SMS data message can only contain " + sMaxSmsLength
+ " bytes");
return;
}
SmsManager smsManager = SmsManager.getDefault();
PendingIntent sentIntent;
if (callback == null) {
sentIntent = null;
} else {
long msgId = genMsgId();
mFailureCallbacks.put(msgId, callback);
sentIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(
SMS_STATUS_RECEIVED_ACTION,
Uri.parse("content://sms/" + msgId), /*uri*/
mContext, SmsReceiver.class),
0);
}
smsManager.sendDataMessage(dest, null/*use the default SMSC*/,
(short) port, data,
sentIntent,
null/*do not require delivery report*/);
}
public void addSmsListener(String from, int port, SmsListener listener) {
ListenerList l = mListeners.get(port);
if (l == null) {
l = new ListenerList(port);
mListeners.put(port, l);
if (Log.isLoggable(TAG, Log.DEBUG)) {
log("Register SMS receiver on port " + port);
}
// We didn't listen on the port yet, register the receiver with the
// additional port.
mIntentFilter.addDataAuthority("*", String.valueOf(port));
mContext.registerReceiver(mSmsReceiver, mIntentFilter);
synchronized (this) {
mStarted = true;
}
}
l.addListener(from, listener);
}
public void removeSmsListener(SmsListener listener) {
Iterator<ListenerList> iter = mListeners.values().iterator();
while (iter.hasNext()) {
ListenerList l = iter.next();
l.removeListener(listener);
if (l.isEmpty()) {
iter.remove();
}
}
}
public synchronized void stop() {
if (mStarted) {
mContext.unregisterReceiver(mSmsReceiver);
mStarted = false;
}
}
private static long sNextMsgId = 0;
private static synchronized long genMsgId() {
return sNextMsgId++;
}
private static void log(String msg) {
Log.d(TAG, "[SmsService]" + msg);
}
private final class SmsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (SMS_STATUS_RECEIVED_ACTION.equals(intent.getAction())) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
log("send status received");
}
long id = ContentUris.parseId(intent.getData());
SmsSendFailureCallback callback = mFailureCallbacks.get(id);
if (callback == null) {
return;
}
int resultCode = getResultCode();
if (resultCode == SmsManager.RESULT_ERROR_GENERIC_FAILURE) {
callback.onFailure(SmsSendFailureCallback.ERROR_GENERIC_FAILURE);
} else if (resultCode == SmsManager.RESULT_ERROR_RADIO_OFF) {
callback.onFailure(SmsSendFailureCallback.ERROR_RADIO_OFF);
}
mFailureCallbacks.remove(id);
} else if (DATA_SMS_RECEIVED_ACTION.equals(intent.getAction())){
Uri uri = intent.getData();
int port = uri.getPort();
if (Log.isLoggable(TAG, Log.DEBUG)) {
log("Received sms on port:" + port);
}
ListenerList listeners = mListeners.get(port);
if (listeners == null) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
log("No listener on port " + port + ", ignore");
}
return;
}
SmsMessage[] receivedSms
= Telephony.Sms.Intents.getMessagesFromIntent(intent);
for (SmsMessage msg : receivedSms) {
String from = msg.getOriginatingAddress();
byte[] data = msg.getUserData();
if (Log.isLoggable(TAG, Log.DEBUG)) {
try {
log(from + ":" + port + " <<< " + new String(data, "UTF-8"));
} catch (UnsupportedEncodingException e) {
}
}
listeners.notifySms(from, data);
}
}
}
}
private final static class ListenerList {
private int mPort;
private ArrayList<String> mAddrList;
private ArrayList<SmsListener> mListenerList;
public ListenerList(int port) {
mPort = port;
mAddrList = new ArrayList<String>();
mListenerList = new ArrayList<SmsListener>();
}
public synchronized void addListener(String addr, SmsListener listener) {
mAddrList.add(addr);
mListenerList.add(listener);
}
public synchronized void removeListener(SmsListener listener) {
int index = -1;
while ((index = mListenerList.indexOf(listener)) != -1) {
mAddrList.remove(index);
mListenerList.remove(index);
}
}
public void notifySms(String addr, byte[] data) {
int N = mListenerList.size();
for (int i = 0; i < N; i++) {
String listenAddr = mAddrList.get(i);
if (ANY_ADDRESS.equals(listenAddr)
|| addr.equals(listenAddr)
|| PhoneNumberUtils.compare(addr, listenAddr)) {
mListenerList.get(i).onIncomingSms(data);
}
}
}
public boolean isEmpty() {
return mListenerList.isEmpty();
}
public int getPort() {
return mPort;
}
}
}