12d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen/*
22d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen * Copyright (C) 2014 The Android Open Source Project
32d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen *
42d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen * Licensed under the Apache License, Version 2.0 (the "License");
52d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen * you may not use this file except in compliance with the License.
62d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen * You may obtain a copy of the License at
72d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen *
82d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen *      http://www.apache.org/licenses/LICENSE-2.0
92d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen *
102d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen * Unless required by applicable law or agreed to in writing, software
112d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen * distributed under the License is distributed on an "AS IS" BASIS,
122d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen * See the License for the specific language governing permissions and
142d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen * limitations under the License.
152d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen */
162d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen
172d860e9479ac9390efeb1ddaf94217f478666a54Ye Wenpackage com.android.mms.service;
182d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen
1918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport com.google.android.mms.MmsException;
2018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport com.google.android.mms.pdu.DeliveryInd;
2118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport com.google.android.mms.pdu.GenericPdu;
2218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport com.google.android.mms.pdu.NotificationInd;
2318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport com.google.android.mms.pdu.PduComposer;
2418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport com.google.android.mms.pdu.PduParser;
2518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport com.google.android.mms.pdu.PduPersister;
2618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport com.google.android.mms.pdu.ReadOrigInd;
2718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport com.google.android.mms.pdu.RetrieveConf;
2818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport com.google.android.mms.pdu.SendReq;
2918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport com.google.android.mms.util.SqliteWrapper;
3018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
3149b04614ece9f1a46acc5f69141e7e2d6736a5fbYe Wenimport com.android.internal.telephony.IMms;
328c027a60c84d23672647a3775190ee3fa7655b34Ye Wenimport com.android.internal.telephony.SmsApplication;
332d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen
34b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wenimport android.app.Activity;
352d860e9479ac9390efeb1ddaf94217f478666a54Ye Wenimport android.app.PendingIntent;
362d860e9479ac9390efeb1ddaf94217f478666a54Ye Wenimport android.app.Service;
37b83f2faa04dc275b6779644308384459ffcff63fJulian Odellimport android.content.ContentResolver;
3818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport android.content.ContentUris;
3918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport android.content.ContentValues;
40b83f2faa04dc275b6779644308384459ffcff63fJulian Odellimport android.content.Context;
412d860e9479ac9390efeb1ddaf94217f478666a54Ye Wenimport android.content.Intent;
428c027a60c84d23672647a3775190ee3fa7655b34Ye Wenimport android.content.SharedPreferences;
4318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport android.database.Cursor;
4418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport android.database.sqlite.SQLiteException;
4518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport android.net.Uri;
4618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport android.os.Binder;
478a79dc0267f021de60f321e621691370b79cb81aShri Bordeimport android.os.Bundle;
48c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wenimport android.os.Handler;
49c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wenimport android.os.HandlerThread;
502d860e9479ac9390efeb1ddaf94217f478666a54Ye Wenimport android.os.IBinder;
51c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wenimport android.os.Looper;
52b83f2faa04dc275b6779644308384459ffcff63fJulian Odellimport android.os.ParcelFileDescriptor;
530527dc4edc5e8be417edc74031a209070a28547aYe Wenimport android.os.Process;
54c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wenimport android.os.Message;
552d860e9479ac9390efeb1ddaf94217f478666a54Ye Wenimport android.os.RemoteException;
5618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport android.provider.Telephony;
5718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport android.telephony.SmsManager;
5818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wenimport android.text.TextUtils;
592d860e9479ac9390efeb1ddaf94217f478666a54Ye Wenimport android.util.Log;
602d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen
61b83f2faa04dc275b6779644308384459ffcff63fJulian Odellimport java.io.IOException;
62b83f2faa04dc275b6779644308384459ffcff63fJulian Odellimport java.util.Arrays;
63b83f2faa04dc275b6779644308384459ffcff63fJulian Odellimport java.util.concurrent.Callable;
64b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wenimport java.util.concurrent.ConcurrentHashMap;
65b83f2faa04dc275b6779644308384459ffcff63fJulian Odellimport java.util.concurrent.ExecutorService;
66b83f2faa04dc275b6779644308384459ffcff63fJulian Odellimport java.util.concurrent.Executors;
67b83f2faa04dc275b6779644308384459ffcff63fJulian Odellimport java.util.concurrent.Future;
68b83f2faa04dc275b6779644308384459ffcff63fJulian Odellimport java.util.concurrent.TimeUnit;
69b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen
702d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen/**
712d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen * System service to process MMS API requests
722d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen */
73b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wenpublic class MmsService extends Service implements MmsRequest.RequestManager {
74c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    public static final String TAG = "MmsService";
75b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen
76b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen    public static final int QUEUE_INDEX_SEND = 0;
77b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen    public static final int QUEUE_INDEX_DOWNLOAD = 1;
78b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen
798c027a60c84d23672647a3775190ee3fa7655b34Ye Wen    private static final String SHARED_PREFERENCES_NAME = "mmspref";
808c027a60c84d23672647a3775190ee3fa7655b34Ye Wen    private static final String PREF_AUTO_PERSISTING = "autopersisting";
818c027a60c84d23672647a3775190ee3fa7655b34Ye Wen
82b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    // Maximum time to spend waiting to read data from a content provider before failing with error.
83b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    private static final int TASK_TIMEOUT_MS = 30 * 1000;
84b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    // Maximum size of MMS service supports - used on occassions when MMS messages are processed
85b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    // in a carrier independent manner (for example for imports and drafts) and the carrier
86b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    // specific size limit should not be used (as it could be lower on some carriers).
87b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    private static final int MAX_MMS_FILE_SIZE = 8 * 1024 * 1024;
88b83f2faa04dc275b6779644308384459ffcff63fJulian Odell
89b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen    // Pending requests that are currently executed by carrier app
90b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen    // TODO: persist this in case MmsService crashes
91b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen    private final ConcurrentHashMap<Integer, MmsRequest> mPendingRequests =
92b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            new ConcurrentHashMap<Integer, MmsRequest>();
93b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen
94b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    private final ExecutorService mExecutor = Executors.newCachedThreadPool();
95b83f2faa04dc275b6779644308384459ffcff63fJulian Odell
96b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen    @Override
97b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen    public void addPending(int key, MmsRequest request) {
98b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen        mPendingRequests.put(key, request);
99b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen    }
100c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen
101c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    /**
102c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen     * A thread-based request queue for executing the MMS requests in serial order
103c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen     */
104c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    private class RequestQueue extends Handler {
105c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        public RequestQueue(Looper looper) {
106c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen            super(looper);
107c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        }
108c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen
109c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        @Override
110c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        public void handleMessage(Message msg) {
111c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen            final MmsRequest request = (MmsRequest) msg.obj;
112c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen            if (request != null) {
113c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen                request.execute(MmsService.this, mMmsNetworkManager);
114c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen            }
115c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        }
116c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    }
117c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen
1180527dc4edc5e8be417edc74031a209070a28547aYe Wen    private void enforceSystemUid() {
1190527dc4edc5e8be417edc74031a209070a28547aYe Wen        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
1200527dc4edc5e8be417edc74031a209070a28547aYe Wen            throw new SecurityException("Only system can call this service");
1210527dc4edc5e8be417edc74031a209070a28547aYe Wen        }
1220527dc4edc5e8be417edc74031a209070a28547aYe Wen    }
1230527dc4edc5e8be417edc74031a209070a28547aYe Wen
1242d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen    private IMms.Stub mStub = new IMms.Stub() {
1252d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen        @Override
126b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        public void sendMessage(long subId, String callingPkg, Uri contentUri,
1273e40f4c4a8c82e795a5cea235d52eacd177c0a69Ye Wen                String locationUrl, Bundle configOverrides, PendingIntent sentIntent)
128b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                        throws RemoteException {
129c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen            Log.d(TAG, "sendMessage");
1300527dc4edc5e8be417edc74031a209070a28547aYe Wen            enforceSystemUid();
131b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            final SendRequest request = new SendRequest(MmsService.this, subId, contentUri,
132c984707ecf54d545a4a5809c6ce1d18bf7cee61eYe Wen                    null/*messageUri*/, locationUrl, sentIntent, callingPkg, configOverrides);
1338c027a60c84d23672647a3775190ee3fa7655b34Ye Wen            if (SmsApplication.shouldWriteMessageForPackage(callingPkg, MmsService.this)) {
1348c027a60c84d23672647a3775190ee3fa7655b34Ye Wen                // Store the message in outbox first before sending
1358c027a60c84d23672647a3775190ee3fa7655b34Ye Wen                request.storeInOutbox(MmsService.this);
1368c027a60c84d23672647a3775190ee3fa7655b34Ye Wen            }
137b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            // Try sending via carrier app
138b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            request.trySendingByCarrierApp(MmsService.this);
1392d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen        }
1402d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen
1412d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen        @Override
14218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        public void downloadMessage(long subId, String callingPkg, String locationUrl,
1433e40f4c4a8c82e795a5cea235d52eacd177c0a69Ye Wen                Uri contentUri, Bundle configOverrides,
144b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                PendingIntent downloadedIntent) throws RemoteException {
145c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen            Log.d(TAG, "downloadMessage: " + locationUrl);
1460527dc4edc5e8be417edc74031a209070a28547aYe Wen            enforceSystemUid();
147b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            final DownloadRequest request = new DownloadRequest(MmsService.this, subId,
148b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    locationUrl, contentUri, downloadedIntent, callingPkg, configOverrides);
149b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            // Try downloading via carrier app
150b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            request.tryDownloadingByCarrierApp(MmsService.this);
151b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen        }
152b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen
153b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen        @Override
154f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang        public void updateMmsSendStatus(int messageRef, byte[] pdu, int status) {
155f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang            Log.d(TAG, "updateMmsSendStatus: ref=" + messageRef
156f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang                    + ", pdu=" + (pdu == null ? null : pdu.length) + ", status=" + status);
1570527dc4edc5e8be417edc74031a209070a28547aYe Wen            enforceSystemUid();
158b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            final MmsRequest request = mPendingRequests.get(messageRef);
159b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            if (request != null) {
160f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang                if (status != SmsManager.MMS_ERROR_RETRY) {
161f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang                    // Sent completed (maybe success or fail) by carrier app, finalize the request.
162f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang                    request.processResult(MmsService.this, status, pdu);
163b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen                } else {
164b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen                    // Failed, try sending via carrier network
165b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen                    addRunning(request);
166b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen                }
167b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            } else {
168b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen                // Really wrong here: can't find the request to update
169b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen                Log.e(TAG, "Failed to find the request to update send status");
170b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            }
171b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen        }
172b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen
173b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen        @Override
174f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang        public void updateMmsDownloadStatus(int messageRef, int status) {
175f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang            Log.d(TAG, "updateMmsDownloadStatus: ref=" + messageRef + ", status=" + status);
1760527dc4edc5e8be417edc74031a209070a28547aYe Wen            enforceSystemUid();
177b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            final MmsRequest request = mPendingRequests.get(messageRef);
178b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            if (request != null) {
179f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang                if (status != SmsManager.MMS_ERROR_RETRY) {
180f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang                    // Downloaded completed (maybe success or fail) by carrier app, finalize the
181f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang                    // request.
182f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang                    request.processResult(MmsService.this, status, null/*response*/);
183b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen                } else {
184b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen                    // Failed, try downloading via the carrier network
185b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen                    addRunning(request);
186b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen                }
187b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            } else {
188b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen                // Really wrong here: can't find the request to update
189b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen                Log.e(TAG, "Failed to find the request to update download status");
190b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            }
1912d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen        }
19218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
19318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        @Override
1948a79dc0267f021de60f321e621691370b79cb81aShri Borde        public Bundle getCarrierConfigValues(long subId) {
1958a79dc0267f021de60f321e621691370b79cb81aShri Borde            Log.d(TAG, "getCarrierConfigValues");
19676b0e8693a8004001663044f5188d5b9fe4203e8Tom Taylor            final MmsConfig mmsConfig = MmsConfigManager.getInstance().getMmsConfigBySubId(subId);
19776b0e8693a8004001663044f5188d5b9fe4203e8Tom Taylor            if (mmsConfig == null) {
1988a79dc0267f021de60f321e621691370b79cb81aShri Borde                return new Bundle();
19976b0e8693a8004001663044f5188d5b9fe4203e8Tom Taylor            }
2008a79dc0267f021de60f321e621691370b79cb81aShri Borde            return mmsConfig.getCarrierConfigValues();
20118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
20218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
20318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        @Override
20418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        public Uri importTextMessage(String callingPkg, String address, int type, String text,
20518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                long timestampMillis, boolean seen, boolean read) {
20618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.d(TAG, "importTextMessage");
2070527dc4edc5e8be417edc74031a209070a28547aYe Wen            enforceSystemUid();
20818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return importSms(address, type, text, timestampMillis, seen, read, callingPkg);
20918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
21018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
21118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        @Override
212b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        public Uri importMultimediaMessage(String callingPkg, Uri contentUri,
213b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                String messageId, long timestampSecs, boolean seen, boolean read) {
21418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.d(TAG, "importMultimediaMessage");
2150527dc4edc5e8be417edc74031a209070a28547aYe Wen            enforceSystemUid();
216b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            return importMms(contentUri, messageId, timestampSecs, seen, read, callingPkg);
21718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
21818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
21918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        @Override
22018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
22118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                throws RemoteException {
22218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.d(TAG, "deleteStoredMessage " + messageUri);
2230527dc4edc5e8be417edc74031a209070a28547aYe Wen            enforceSystemUid();
22418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (!isSmsMmsContentUri(messageUri)) {
22518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "deleteStoredMessage: invalid message URI: " + messageUri.toString());
22618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                return false;
22718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
228c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen            // Clear the calling identity and query the database using the phone user id
229c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen            // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
230c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen            // between the calling uid and the package uid
23118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final long identity = Binder.clearCallingIdentity();
23218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            try {
23318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                if (getContentResolver().delete(
23418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                        messageUri, null/*where*/, null/*selectionArgs*/) != 1) {
23518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    Log.e(TAG, "deleteStoredMessage: failed to delete");
23618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    return false;
23718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                }
23818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            } catch (SQLiteException e) {
23918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "deleteStoredMessage: failed to delete", e);
24018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            } finally {
24118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Binder.restoreCallingIdentity(identity);
24218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
24318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return true;
24418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
24518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
24618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        @Override
24718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        public boolean deleteStoredConversation(String callingPkg, long conversationId)
24818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                throws RemoteException {
24918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.d(TAG, "deleteStoredConversation " + conversationId);
2500527dc4edc5e8be417edc74031a209070a28547aYe Wen            enforceSystemUid();
25118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (conversationId == -1) {
25218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "deleteStoredConversation: invalid thread id");
25318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                return false;
25418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
25518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final Uri uri = ContentUris.withAppendedId(
25618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    Telephony.Threads.CONTENT_URI, conversationId);
257c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen            // Clear the calling identity and query the database using the phone user id
258c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen            // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
259c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen            // between the calling uid and the package uid
26018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final long identity = Binder.clearCallingIdentity();
26118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            try {
26218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                if (getContentResolver().delete(uri, null, null) != 1) {
26318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    Log.e(TAG, "deleteStoredConversation: failed to delete");
26418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    return false;
26518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                }
26618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            } catch (SQLiteException e) {
26718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "deleteStoredConversation: failed to delete", e);
26818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            } finally {
26918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Binder.restoreCallingIdentity(identity);
27018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
27118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return true;
27218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
27318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
27418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        @Override
27518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
27618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                ContentValues statusValues) throws RemoteException {
27718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.d(TAG, "updateStoredMessageStatus " + messageUri);
2780527dc4edc5e8be417edc74031a209070a28547aYe Wen            enforceSystemUid();
27918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return updateMessageStatus(messageUri, statusValues);
28018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
28118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
28218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        @Override
2832704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen        public boolean archiveStoredConversation(String callingPkg, long conversationId,
2842704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen                boolean archived) throws RemoteException {
2852704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen            Log.d(TAG, "archiveStoredConversation " + conversationId + " " + archived);
2862704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen            if (conversationId == -1) {
2872704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen                Log.e(TAG, "archiveStoredConversation: invalid thread id");
2882704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen                return false;
2892704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen            }
2902704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen            return archiveConversation(conversationId, archived);
2912704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen        }
2922704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen
2932704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen        @Override
29418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        public Uri addTextMessageDraft(String callingPkg, String address, String text)
29518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                throws RemoteException {
29618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.d(TAG, "addTextMessageDraft");
2970527dc4edc5e8be417edc74031a209070a28547aYe Wen            enforceSystemUid();
29818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return addSmsDraft(address, text, callingPkg);
29918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
30018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
30118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        @Override
302b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
30318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                throws RemoteException {
30418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.d(TAG, "addMultimediaMessageDraft");
3050527dc4edc5e8be417edc74031a209070a28547aYe Wen            enforceSystemUid();
306b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            return addMmsDraft(contentUri, callingPkg);
30718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
30818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
30918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        @Override
31018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        public void sendStoredMessage(long subId, String callingPkg, Uri messageUri,
3113e40f4c4a8c82e795a5cea235d52eacd177c0a69Ye Wen                Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
312f6d88a72cd01ecaf9f0319249e239afc008f1ac8Cheuksan Wang            throw new UnsupportedOperationException();
31318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
3146f31811156a8aeef55011749c25d7c98d7a7a7ddShishir Agrawal
3158c027a60c84d23672647a3775190ee3fa7655b34Ye Wen        @Override
3168c027a60c84d23672647a3775190ee3fa7655b34Ye Wen        public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
3178c027a60c84d23672647a3775190ee3fa7655b34Ye Wen            Log.d(TAG, "setAutoPersisting " + enabled);
3180527dc4edc5e8be417edc74031a209070a28547aYe Wen            enforceSystemUid();
3198c027a60c84d23672647a3775190ee3fa7655b34Ye Wen            final SharedPreferences preferences = getSharedPreferences(
3208c027a60c84d23672647a3775190ee3fa7655b34Ye Wen                    SHARED_PREFERENCES_NAME, MODE_PRIVATE);
3218c027a60c84d23672647a3775190ee3fa7655b34Ye Wen            final SharedPreferences.Editor editor = preferences.edit();
3228c027a60c84d23672647a3775190ee3fa7655b34Ye Wen            editor.putBoolean(PREF_AUTO_PERSISTING, enabled);
3238c027a60c84d23672647a3775190ee3fa7655b34Ye Wen            editor.apply();
3248c027a60c84d23672647a3775190ee3fa7655b34Ye Wen        }
3258c027a60c84d23672647a3775190ee3fa7655b34Ye Wen
3268c027a60c84d23672647a3775190ee3fa7655b34Ye Wen        @Override
3278c027a60c84d23672647a3775190ee3fa7655b34Ye Wen        public boolean getAutoPersisting() throws RemoteException {
3288c027a60c84d23672647a3775190ee3fa7655b34Ye Wen            Log.d(TAG, "getAutoPersisting");
3298c027a60c84d23672647a3775190ee3fa7655b34Ye Wen            return getAutoPersistingPref();
3308c027a60c84d23672647a3775190ee3fa7655b34Ye Wen        }
3312d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen    };
3322d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen
333c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    // Request queue threads
334c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    // 0: send queue
335c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    // 1: download queue
336c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    private final RequestQueue[] mRequestQueues = new RequestQueue[2];
337c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen
338c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    // Manages MMS connectivity related stuff
339c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    private final MmsNetworkManager mMmsNetworkManager = new MmsNetworkManager(this);
340c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen
341c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    /**
342c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen     * Lazy start the request queue threads
343c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen     *
344c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen     * @param queueIndex index of the queue to start
345c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen     */
346c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    private void startRequestQueueIfNeeded(int queueIndex) {
347c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        if (queueIndex < 0 || queueIndex >= mRequestQueues.length) {
348c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen            return;
349c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        }
350c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        synchronized (this) {
351c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen            if (mRequestQueues[queueIndex] == null) {
352c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen                final HandlerThread thread =
353c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen                        new HandlerThread("MmsService RequestQueue " + queueIndex);
354c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen                thread.start();
355c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen                mRequestQueues[queueIndex] = new RequestQueue(thread.getLooper());
356c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen            }
357c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        }
358c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    }
359c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen
360b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen    @Override
361b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen    public void addRunning(MmsRequest request) {
362b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen        if (request == null) {
363b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen            return;
364b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen        }
365b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen        final int queue = request.getRunningQueue();
366b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen        startRequestQueueIfNeeded(queue);
367c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        final Message message = Message.obtain();
368c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        message.obj = request;
369b786d3ea3daf4a91119ea06c532fe7ef5835944cYe Wen        mRequestQueues[queue].sendMessage(message);
370c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    }
371c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen
3722d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen    @Override
3732d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen    public IBinder onBind(Intent intent) {
3742d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen        return mStub;
3752d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen    }
3762d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen
3772d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen    public final IBinder asBinder() {
3782d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen        return mStub;
3792d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen    }
3802d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen
3812d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen    @Override
3822d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen    public void onCreate() {
3832d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen        super.onCreate();
384c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        Log.d(TAG, "onCreate");
385c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        // Load mms_config
386c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen        // TODO (ywen): make sure we start request queues after mms_config is loaded
38776b0e8693a8004001663044f5188d5b9fe4203e8Tom Taylor        MmsConfigManager.getInstance().init(this);
388c91cc9d4a5aa12a570a3b35a12b3e34a6a9eeb51Ye Wen    }
38918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
39018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    private Uri importSms(String address, int type, String text, long timestampMillis,
39118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            boolean seen, boolean read, String creator) {
39218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        Uri insertUri = null;
39318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        switch (type) {
39418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            case SmsManager.SMS_TYPE_INCOMING:
39518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                insertUri = Telephony.Sms.Inbox.CONTENT_URI;
39618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
39718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                break;
39818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            case SmsManager.SMS_TYPE_OUTGOING:
39918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                insertUri = Telephony.Sms.Sent.CONTENT_URI;
40018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                break;
40118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
40218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        if (insertUri == null) {
40318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "importTextMessage: invalid message type for importing: " + type);
40418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return null;
40518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
40618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        final ContentValues values = new ContentValues(6);
40718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        values.put(Telephony.Sms.ADDRESS, address);
40818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        values.put(Telephony.Sms.DATE, timestampMillis);
40918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        values.put(Telephony.Sms.SEEN, seen ? 1 : 0);
41018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        values.put(Telephony.Sms.READ, read ? 1 : 0);
41118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        values.put(Telephony.Sms.BODY, text);
41218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        if (!TextUtils.isEmpty(creator)) {
41318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            values.put(Telephony.Mms.CREATOR, creator);
41418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
415c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Clear the calling identity and query the database using the phone user id
416c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
417c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // between the calling uid and the package uid
41818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        final long identity = Binder.clearCallingIdentity();
41918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        try {
42018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return getContentResolver().insert(insertUri, values);
42118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } catch (SQLiteException e) {
42218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "importTextMessage: failed to persist imported text message", e);
42318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } finally {
42418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Binder.restoreCallingIdentity(identity);
42518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
42618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        return null;
42718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    }
42818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
429b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    private Uri importMms(Uri contentUri, String messageId, long timestampSecs,
43018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            boolean seen, boolean read, String creator) {
431b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        byte[] pduData = readPduFromContentUri(contentUri, MAX_MMS_FILE_SIZE);
43218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        if (pduData == null || pduData.length < 1) {
43318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "importMessage: empty PDU");
43418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return null;
43518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
436c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Clear the calling identity and query the database using the phone user id
437c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
438c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // between the calling uid and the package uid
43918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        final long identity = Binder.clearCallingIdentity();
44018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        try {
44118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final GenericPdu pdu = (new PduParser(pduData)).parse();
44218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (pdu == null) {
44318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "importMessage: can't parse input PDU");
44418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                return null;
44518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
44618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Uri insertUri = null;
44718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (pdu instanceof SendReq) {
44818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                insertUri = Telephony.Mms.Sent.CONTENT_URI;
44918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            } else if (pdu instanceof RetrieveConf ||
45018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    pdu instanceof NotificationInd ||
45118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    pdu instanceof DeliveryInd ||
45218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    pdu instanceof ReadOrigInd) {
45318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                insertUri = Telephony.Mms.Inbox.CONTENT_URI;
45418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
45518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (insertUri == null) {
45618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "importMessage; invalid MMS type: " + pdu.getClass().getCanonicalName());
45718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                return null;
45818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
45918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final PduPersister persister = PduPersister.getPduPersister(this);
46018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final Uri uri = persister.persist(
46118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    pdu,
46218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    insertUri,
46318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    true/*createThreadId*/,
46418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    true/*groupMmsEnabled*/,
46518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    null/*preOpenedFiles*/);
46618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (uri == null) {
46718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "importMessage: failed to persist message");
46818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                return null;
46918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
47018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final ContentValues values = new ContentValues(5);
47118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (!TextUtils.isEmpty(messageId)) {
47218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                values.put(Telephony.Mms.MESSAGE_ID, messageId);
47318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
47418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (timestampSecs != -1) {
47518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                values.put(Telephony.Mms.DATE, timestampSecs);
47618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
47718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            values.put(Telephony.Mms.READ, seen ? 1 : 0);
47818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            values.put(Telephony.Mms.SEEN, read ? 1 : 0);
47918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (!TextUtils.isEmpty(creator)) {
48018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                values.put(Telephony.Mms.CREATOR, creator);
48118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
48218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (SqliteWrapper.update(this, getContentResolver(), uri, values,
48318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    null/*where*/, null/*selectionArg*/) != 1) {
48418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "importMessage: failed to update message");
48518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
48618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return uri;
48718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } catch (RuntimeException e) {
48818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "importMessage: failed to parse input PDU", e);
48918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } catch (MmsException e) {
49018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "importMessage: failed to persist message", e);
49118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } finally {
49218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Binder.restoreCallingIdentity(identity);
49318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
49418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        return null;
49518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    }
49618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
49718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    private static boolean isSmsMmsContentUri(Uri uri) {
49818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        final String uriString = uri.toString();
49918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        if (!uriString.startsWith("content://sms/") && !uriString.startsWith("content://mms/")) {
50018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return false;
50118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
50218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        if (ContentUris.parseId(uri) == -1) {
50318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return false;
50418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
50518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        return true;
50618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    }
50718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
50818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    private boolean updateMessageStatus(Uri messageUri, ContentValues statusValues) {
50918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        if (!isSmsMmsContentUri(messageUri)) {
51018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "updateMessageStatus: invalid messageUri: " + messageUri.toString());
51118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return false;
51218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
51318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        if (statusValues == null) {
51418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.w(TAG, "updateMessageStatus: empty values to update");
51518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return false;
51618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
51718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        final ContentValues values = new ContentValues();
51818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        if (statusValues.containsKey(SmsManager.MESSAGE_STATUS_READ)) {
51918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final Integer val = statusValues.getAsInteger(SmsManager.MESSAGE_STATUS_READ);
52018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (val != null) {
52118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                // MMS uses the same column name
52218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                values.put(Telephony.Sms.READ, val);
52318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
52418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } else if (statusValues.containsKey(SmsManager.MESSAGE_STATUS_SEEN)) {
52518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final Integer val = statusValues.getAsInteger(SmsManager.MESSAGE_STATUS_SEEN);
52618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (val != null) {
52718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                // MMS uses the same column name
52818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                values.put(Telephony.Sms.SEEN, val);
52918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
53018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
53118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        if (values.size() < 1) {
53218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.w(TAG, "updateMessageStatus: no value to update");
53318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return false;
53418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
535c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Clear the calling identity and query the database using the phone user id
536c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
537c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // between the calling uid and the package uid
53818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        final long identity = Binder.clearCallingIdentity();
53918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        try {
54018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (getContentResolver().update(
54118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    messageUri, values, null/*where*/, null/*selectionArgs*/) != 1) {
54218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "updateMessageStatus: failed to update database");
54318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                return false;
54418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
54518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return true;
54618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } catch (SQLiteException e) {
54718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "updateMessageStatus: failed to update database", e);
54818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } finally {
54918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Binder.restoreCallingIdentity(identity);
55018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
55118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        return false;
55218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    }
55318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
5542704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen    private static final String ARCHIVE_CONVERSATION_SELECTION = Telephony.Threads._ID + "=?";
5552704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen    private boolean archiveConversation(long conversationId, boolean archived) {
5562704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen        final ContentValues values = new ContentValues(1);
5572704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen        values.put(Telephony.Threads.ARCHIVED, archived ? 1 : 0);
558c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Clear the calling identity and query the database using the phone user id
559c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
560c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // between the calling uid and the package uid
5612704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen        final long identity = Binder.clearCallingIdentity();
5622704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen        try {
5632704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen            if (getContentResolver().update(
5642704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen                    Telephony.Threads.CONTENT_URI,
5652704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen                    values,
5662704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen                    ARCHIVE_CONVERSATION_SELECTION,
5672704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen                    new String[] { Long.toString(conversationId)}) != 1) {
5682704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen                Log.e(TAG, "archiveConversation: failed to update database");
5692704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen                return false;
5702704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen            }
5712704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen            return true;
5722704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen        } catch (SQLiteException e) {
5732704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen            Log.e(TAG, "archiveConversation: failed to update database", e);
5742704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen        } finally {
5752704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen            Binder.restoreCallingIdentity(identity);
5762704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen        }
5772704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen        return false;
5782704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen    }
5792704c4bf1d52cb7a5d6d77c042b9448f38d3ca3cYe Wen
58018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    private Uri addSmsDraft(String address, String text, String creator) {
58118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        final ContentValues values = new ContentValues(5);
58218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        values.put(Telephony.Sms.ADDRESS, address);
58318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        values.put(Telephony.Sms.BODY, text);
58418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        values.put(Telephony.Sms.READ, 1);
58518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        values.put(Telephony.Sms.SEEN, 1);
58618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        if (!TextUtils.isEmpty(creator)) {
58718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            values.put(Telephony.Mms.CREATOR, creator);
58818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
589c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Clear the calling identity and query the database using the phone user id
590c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
591c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // between the calling uid and the package uid
59218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        final long identity = Binder.clearCallingIdentity();
59318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        try {
59418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return getContentResolver().insert(Telephony.Sms.Draft.CONTENT_URI, values);
59518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } catch (SQLiteException e) {
59618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "addSmsDraft: failed to store draft message", e);
59718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } finally {
59818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Binder.restoreCallingIdentity(identity);
59918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
60018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        return null;
60118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    }
60218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
603b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    private Uri addMmsDraft(Uri contentUri, String creator) {
604b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        byte[] pduData = readPduFromContentUri(contentUri, MAX_MMS_FILE_SIZE);
60518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        if (pduData == null || pduData.length < 1) {
60618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "addMmsDraft: empty PDU");
60718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return null;
60818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
609c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Clear the calling identity and query the database using the phone user id
610c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
611c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // between the calling uid and the package uid
61218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        final long identity = Binder.clearCallingIdentity();
61318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        try {
61418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final GenericPdu pdu = (new PduParser(pduData)).parse();
61518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (pdu == null) {
61618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "addMmsDraft: can't parse input PDU");
61718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                return null;
61818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
61918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (!(pdu instanceof SendReq)) {
62018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "addMmsDraft; invalid MMS type: " + pdu.getClass().getCanonicalName());
62118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                return null;
62218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
62318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final PduPersister persister = PduPersister.getPduPersister(this);
62418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final Uri uri = persister.persist(
62518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    pdu,
62618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    Telephony.Mms.Draft.CONTENT_URI,
62718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    true/*createThreadId*/,
62818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    true/*groupMmsEnabled*/,
62918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    null/*preOpenedFiles*/);
63018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (uri == null) {
63118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "addMmsDraft: failed to persist message");
63218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                return null;
63318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
63418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final ContentValues values = new ContentValues(3);
63518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            values.put(Telephony.Mms.READ, 1);
63618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            values.put(Telephony.Mms.SEEN, 1);
63718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (!TextUtils.isEmpty(creator)) {
63818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                values.put(Telephony.Mms.CREATOR, creator);
63918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
64018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (SqliteWrapper.update(this, getContentResolver(), uri, values,
64118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    null/*where*/, null/*selectionArg*/) != 1) {
64218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "addMmsDraft: failed to update message");
64318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
64418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return uri;
64518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } catch (RuntimeException e) {
64618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "addMmsDraft: failed to parse input PDU", e);
64718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } catch (MmsException e) {
64818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "addMmsDraft: failed to persist message", e);
64918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } finally {
65018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Binder.restoreCallingIdentity(identity);
65118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
65218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        return null;
65318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    }
65418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
65518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    private boolean isFailedOrDraft(Uri messageUri) {
656c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Clear the calling identity and query the database using the phone user id
657c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
658c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // between the calling uid and the package uid
659c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        final long identity = Binder.clearCallingIdentity();
66018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        Cursor cursor = null;
66118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        try {
66218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            cursor = getContentResolver().query(
66318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    messageUri,
66418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    new String[]{ Telephony.Mms.MESSAGE_BOX },
66518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    null/*selection*/,
66618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    null/*selectionArgs*/,
66718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                    null/*sortOrder*/);
66818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (cursor != null && cursor.moveToFirst()) {
66918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                final int box = cursor.getInt(0);
67018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                return box == Telephony.Mms.MESSAGE_BOX_DRAFTS
67118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                        || box == Telephony.Mms.MESSAGE_BOX_FAILED;
67218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
67318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } catch (SQLiteException e) {
67418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "isFailedOrDraft: query message type failed", e);
67518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } finally {
67618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (cursor != null) {
67718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                cursor.close();
67818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
679c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen            Binder.restoreCallingIdentity(identity);
68018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
68118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        return false;
68218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    }
68318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
68418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    private byte[] loadPdu(Uri messageUri) {
685c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Clear the calling identity and query the database using the phone user id
686c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // Otherwise the AppOps check in TelephonyProvider would complain about mismatch
687c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        // between the calling uid and the package uid
688c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        final long identity = Binder.clearCallingIdentity();
68918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        try {
69018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final PduPersister persister = PduPersister.getPduPersister(this);
69118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final GenericPdu pdu = persister.load(messageUri);
69218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            if (pdu == null) {
69318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                Log.e(TAG, "loadPdu: failed to load PDU from " + messageUri.toString());
69418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                return null;
69518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
69618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            final PduComposer composer = new PduComposer(this, pdu);
69718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            return composer.make();
69818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } catch (MmsException e) {
69918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "loadPdu: failed to load PDU from " + messageUri.toString(), e);
70018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        } catch (RuntimeException e) {
70118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            Log.e(TAG, "loadPdu: failed to serialize PDU", e);
702c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen        } finally {
703c0c6d5e594fc05e3790555d2470f8f05dc055ba0Ye Wen            Binder.restoreCallingIdentity(identity);
70418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
70518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        return null;
70618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    }
70718aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen
70818aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    private void returnUnspecifiedFailure(PendingIntent pi) {
70918aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        if (pi != null) {
71018aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            try {
71118aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                pi.send(SmsManager.MMS_ERROR_UNSPECIFIED);
71218aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            } catch (PendingIntent.CanceledException e) {
71318aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen                // ignore
71418aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen            }
71518aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen        }
71618aabe2742cbaffc3c8293cfb3ce2841fe82326dYe Wen    }
7178c027a60c84d23672647a3775190ee3fa7655b34Ye Wen
7188c027a60c84d23672647a3775190ee3fa7655b34Ye Wen    @Override
7198c027a60c84d23672647a3775190ee3fa7655b34Ye Wen    public boolean getAutoPersistingPref() {
7208c027a60c84d23672647a3775190ee3fa7655b34Ye Wen        final SharedPreferences preferences = getSharedPreferences(
7218c027a60c84d23672647a3775190ee3fa7655b34Ye Wen                SHARED_PREFERENCES_NAME, MODE_PRIVATE);
7228c027a60c84d23672647a3775190ee3fa7655b34Ye Wen        return preferences.getBoolean(PREF_AUTO_PERSISTING, false);
7238c027a60c84d23672647a3775190ee3fa7655b34Ye Wen    }
724b83f2faa04dc275b6779644308384459ffcff63fJulian Odell
725b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    /**
726b83f2faa04dc275b6779644308384459ffcff63fJulian Odell     * Read pdu from content provider uri
727b83f2faa04dc275b6779644308384459ffcff63fJulian Odell     * @param contentUri content provider uri from which to read
728b83f2faa04dc275b6779644308384459ffcff63fJulian Odell     * @param maxSize maximum number of bytes to read
729b83f2faa04dc275b6779644308384459ffcff63fJulian Odell     * @return pdu bytes if succeeded else null
730b83f2faa04dc275b6779644308384459ffcff63fJulian Odell     */
731b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    public byte[] readPduFromContentUri(final Uri contentUri, final int maxSize) {
732b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        Callable<byte[]> copyPduToArray = new Callable<byte[]>() {
733b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            public byte[] call() {
734b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                ParcelFileDescriptor.AutoCloseInputStream inStream = null;
735b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                try {
736b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    ContentResolver cr = MmsService.this.getContentResolver();
737b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    ParcelFileDescriptor pduFd = cr.openFileDescriptor(contentUri, "r");
738b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    inStream = new ParcelFileDescriptor.AutoCloseInputStream(pduFd);
739b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    // Request one extra byte to make sure file not bigger than maxSize
740b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    byte[] tempBody = new byte[maxSize+1];
741b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    int bytesRead = inStream.read(tempBody, 0, maxSize+1);
742b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    if (bytesRead == 0) {
743b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                        Log.e(MmsService.TAG, "MmsService.readPduFromContentUri: empty PDU");
744b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                        return null;
745b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    }
746b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    if (bytesRead <= maxSize) {
747b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                        return Arrays.copyOf(tempBody, bytesRead);
748b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    }
749b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    Log.e(MmsService.TAG, "MmsService.readPduFromContentUri: PDU too large");
750b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    return null;
751b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                } catch (IOException ex) {
752b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    Log.e(MmsService.TAG,
753b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                            "MmsService.readPduFromContentUri: IO exception reading PDU", ex);
754b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    return null;
755b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                } finally {
756b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    if (inStream != null) {
757b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                        try {
758b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                            inStream.close();
759b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                        } catch (IOException ex) {
760b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                        }
761b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    }
762b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                }
763b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            }
764b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        };
765b83f2faa04dc275b6779644308384459ffcff63fJulian Odell
766b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        Future<byte[]> pendingResult = mExecutor.submit(copyPduToArray);
767b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        try {
768b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            byte[] pdu = pendingResult.get(TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
769b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            return pdu;
770b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        } catch (Exception e) {
771b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            // Typically a timeout occurred - cancel task
772b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            pendingResult.cancel(true);
773b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        }
774b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        return null;
775b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    }
776b83f2faa04dc275b6779644308384459ffcff63fJulian Odell
777b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    /**
778b83f2faa04dc275b6779644308384459ffcff63fJulian Odell     * Write pdu bytes to content provider uri
779b83f2faa04dc275b6779644308384459ffcff63fJulian Odell     * @param contentUri content provider uri to which bytes should be written
780b83f2faa04dc275b6779644308384459ffcff63fJulian Odell     * @param pdu Bytes to write
781b83f2faa04dc275b6779644308384459ffcff63fJulian Odell     * @return true if all bytes successfully written else false
782b83f2faa04dc275b6779644308384459ffcff63fJulian Odell     */
783b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    public boolean writePduToContentUri(final Uri contentUri, final byte[] pdu) {
784b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        Callable<Boolean> copyDownloadedPduToOutput = new Callable<Boolean>() {
785b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            public Boolean call() {
786b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                ParcelFileDescriptor.AutoCloseOutputStream outStream = null;
787b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                try {
788b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    ContentResolver cr = MmsService.this.getContentResolver();
789b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    ParcelFileDescriptor pduFd = cr.openFileDescriptor(contentUri, "w");
790b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    outStream = new ParcelFileDescriptor.AutoCloseOutputStream(pduFd);
791b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    outStream.write(pdu);
792b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    return Boolean.TRUE;
793b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                } catch (IOException ex) {
794b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    return Boolean.FALSE;
795b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                } finally {
796b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    if (outStream != null) {
797b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                        try {
798b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                            outStream.close();
799b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                        } catch (IOException ex) {
800b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                        }
801b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                    }
802b83f2faa04dc275b6779644308384459ffcff63fJulian Odell                }
803b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            }
804b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        };
805b83f2faa04dc275b6779644308384459ffcff63fJulian Odell
806b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        Future<Boolean> pendingResult = mExecutor.submit(copyDownloadedPduToOutput);
807b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        try {
808b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            Boolean succeeded = pendingResult.get(TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
809b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            return succeeded == Boolean.TRUE;
810b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        } catch (Exception e) {
811b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            // Typically a timeout occurred - cancel task
812b83f2faa04dc275b6779644308384459ffcff63fJulian Odell            pendingResult.cancel(true);
813b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        }
814b83f2faa04dc275b6779644308384459ffcff63fJulian Odell        return false;
815b83f2faa04dc275b6779644308384459ffcff63fJulian Odell    }
8162d860e9479ac9390efeb1ddaf94217f478666a54Ye Wen}
817