1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.mms.transaction;
18
19import android.app.IntentService;
20import android.app.Service;
21import android.content.ContentUris;
22import android.content.ContentValues;
23import android.content.Context;
24import android.content.Intent;
25import android.database.Cursor;
26import android.database.sqlite.SqliteWrapper;
27import android.net.Uri;
28import android.provider.Telephony.Sms;
29import android.provider.Telephony.Sms.Inbox;
30import android.telephony.SmsMessage;
31import android.util.Log;
32
33import com.android.mms.LogTag;
34import com.android.mms.MmsConfig;
35
36/**
37 * Service that gets started by the MessageStatusReceiver when a message status report is
38 * received.
39 */
40public class MessageStatusService extends IntentService {
41    private static final String[] ID_PROJECTION = new String[] { Sms._ID };
42    private static final String LOG_TAG = LogTag.TAG;
43    private static final Uri STATUS_URI = Uri.parse("content://sms/status");
44
45    public MessageStatusService() {
46        // Class name will be the thread name.
47        super(MessageStatusService.class.getName());
48
49        // Intent should be redelivered if the process gets killed before completing the job.
50        setIntentRedelivery(true);
51    }
52
53    @Override
54    protected void onHandleIntent(Intent intent) {
55        if (!MmsConfig.isSmsEnabled(this)) {
56            Log.d(LOG_TAG, "MessageStatusService: is not the default sms app");
57            return;
58        }
59        // This method is called on a worker thread.
60
61        Uri messageUri = intent.getData();
62        byte[] pdu = intent.getByteArrayExtra("pdu");
63        String format = intent.getStringExtra("format");
64
65        SmsMessage message = updateMessageStatus(this, messageUri, pdu, format);
66
67        // Called on a background thread, so it's OK to block.
68        if (message != null && message.getStatus() < Sms.STATUS_PENDING) {
69            MessagingNotification.blockingUpdateNewMessageIndicator(this,
70                    MessagingNotification.THREAD_NONE, message.isStatusReportMessage());
71        }
72    }
73
74    private SmsMessage updateMessageStatus(Context context, Uri messageUri, byte[] pdu,
75            String format) {
76        SmsMessage message = SmsMessage.createFromPdu(pdu, format);
77        if (message == null) {
78            return null;
79        }
80        // Create a "status/#" URL and use it to update the
81        // message's status in the database.
82        Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
83                            messageUri, ID_PROJECTION, null, null, null);
84
85        try {
86            if (cursor.moveToFirst()) {
87                int messageId = cursor.getInt(0);
88
89                Uri updateUri = ContentUris.withAppendedId(STATUS_URI, messageId);
90                int status = message.getStatus();
91                boolean isStatusReport = message.isStatusReportMessage();
92                ContentValues contentValues = new ContentValues(2);
93
94                if (Log.isLoggable(LogTag.TAG, Log.DEBUG)) {
95                    log("updateMessageStatus: msgUrl=" + messageUri + ", status=" + status +
96                            ", isStatusReport=" + isStatusReport);
97                }
98
99                contentValues.put(Sms.STATUS, status);
100                contentValues.put(Inbox.DATE_SENT, System.currentTimeMillis());
101                SqliteWrapper.update(context, context.getContentResolver(),
102                                    updateUri, contentValues, null, null);
103            } else {
104                error("Can't find message for status update: " + messageUri);
105            }
106        } finally {
107            cursor.close();
108        }
109        return message;
110    }
111
112    private void error(String message) {
113        Log.e(LOG_TAG, "[MessageStatusReceiver] " + message);
114    }
115
116    private void log(String message) {
117        Log.d(LOG_TAG, "[MessageStatusReceiver] " + message);
118    }
119}
120