1 /*
2  * Copyright (C) 2008 Esmertec AG.
3  * Copyright (C) 2008 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.mms.util;
19 
20 import android.content.BroadcastReceiver;
21 import android.content.ContentValues;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.SharedPreferences;
26 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
27 import android.database.Cursor;
28 import android.database.sqlite.SqliteWrapper;
29 import android.net.Uri;
30 import android.os.Handler;
31 import android.os.SystemProperties;
32 import android.preference.PreferenceManager;
33 import android.provider.Telephony.Mms;
34 import android.telephony.ServiceState;
35 import android.util.Log;
36 import android.widget.Toast;
37 
38 import android.telephony.SubscriptionManager;
39 import android.telephony.TelephonyManager;
40 import com.android.internal.telephony.TelephonyIntents;
41 import com.android.internal.telephony.TelephonyProperties;
42 import com.android.mms.LogTag;
43 import com.android.mms.R;
44 import com.android.mms.data.Contact;
45 import com.android.mms.ui.MessagingPreferenceActivity;
46 import com.google.android.mms.MmsException;
47 import com.google.android.mms.pdu.EncodedStringValue;
48 import com.google.android.mms.pdu.NotificationInd;
49 import com.google.android.mms.pdu.PduPersister;
50 
51 public class DownloadManager {
52     private static final String TAG = LogTag.TAG;
53     private static final boolean DEBUG = false;
54     private static final boolean LOCAL_LOGV = false;
55 
56     public static final int DEFERRED_MASK           = 0x04;
57 
58     public static final int STATE_UNKNOWN           = 0x00;
59     public static final int STATE_UNSTARTED         = 0x80;
60     public static final int STATE_DOWNLOADING       = 0x81;
61     public static final int STATE_TRANSIENT_FAILURE = 0x82;
62     public static final int STATE_PERMANENT_FAILURE = 0x87;
63     public static final int STATE_PRE_DOWNLOADING   = 0x88;
64     // TransactionService will skip downloading Mms if auto-download is off
65     public static final int STATE_SKIP_RETRYING     = 0x89;
66 
67     private final Context mContext;
68     private final Handler mHandler;
69     private final SharedPreferences mPreferences;
70     private boolean mAutoDownload;
71 
72     private final OnSharedPreferenceChangeListener mPreferencesChangeListener =
73         new OnSharedPreferenceChangeListener() {
74         public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
75             if (MessagingPreferenceActivity.AUTO_RETRIEVAL.equals(key)
76                     || MessagingPreferenceActivity.RETRIEVAL_DURING_ROAMING.equals(key)) {
77                 if (LOCAL_LOGV) {
78                     Log.v(TAG, "Preferences updated.");
79                 }
80 
81                 synchronized (sInstance) {
82                     mAutoDownload = getAutoDownloadState(prefs);
83                     if (LOCAL_LOGV) {
84                         Log.v(TAG, "mAutoDownload ------> " + mAutoDownload);
85                     }
86                 }
87             }
88         }
89     };
90 
91     private final BroadcastReceiver mRoamingStateListener =
92         new BroadcastReceiver() {
93         @Override
94         public void onReceive(Context context, Intent intent) {
95             if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(intent.getAction())) {
96                 if (LOCAL_LOGV) {
97                     Log.v(TAG, "Service state changed: " + intent.getExtras());
98                 }
99 
100                 ServiceState state = ServiceState.newFromBundle(intent.getExtras());
101                 boolean isRoaming = state.getRoaming();
102                 if (LOCAL_LOGV) {
103                     Log.v(TAG, "roaming ------> " + isRoaming);
104                 }
105                 synchronized (sInstance) {
106                     mAutoDownload = getAutoDownloadState(mPreferences, isRoaming);
107                     if (LOCAL_LOGV) {
108                         Log.v(TAG, "mAutoDownload ------> " + mAutoDownload);
109                     }
110                 }
111             }
112         }
113     };
114 
115     private static DownloadManager sInstance;
116 
DownloadManager(Context context)117     private DownloadManager(Context context) {
118         mContext = context;
119         mHandler = new Handler();
120         mPreferences = PreferenceManager.getDefaultSharedPreferences(context);
121         mPreferences.registerOnSharedPreferenceChangeListener(mPreferencesChangeListener);
122 
123         context.registerReceiver(
124                 mRoamingStateListener,
125                 new IntentFilter(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED));
126 
127         mAutoDownload = getAutoDownloadState(mPreferences);
128         if (LOCAL_LOGV) {
129             Log.v(TAG, "mAutoDownload ------> " + mAutoDownload);
130         }
131     }
132 
isAuto()133     public boolean isAuto() {
134         return mAutoDownload;
135     }
136 
init(Context context)137     public static void init(Context context) {
138         if (LOCAL_LOGV) {
139             Log.v(TAG, "DownloadManager.init()");
140         }
141 
142         if (sInstance != null) {
143             Log.w(TAG, "Already initialized.");
144         }
145         sInstance = new DownloadManager(context);
146     }
147 
getInstance()148     public static DownloadManager getInstance() {
149         if (sInstance == null) {
150             throw new IllegalStateException("Uninitialized.");
151         }
152         return sInstance;
153     }
154 
getAutoDownloadState(SharedPreferences prefs)155     static boolean getAutoDownloadState(SharedPreferences prefs) {
156         return getAutoDownloadState(prefs, isRoaming());
157     }
158 
getAutoDownloadState(SharedPreferences prefs, boolean roaming)159     static boolean getAutoDownloadState(SharedPreferences prefs, boolean roaming) {
160         boolean autoDownload = prefs.getBoolean(
161                 MessagingPreferenceActivity.AUTO_RETRIEVAL, true);
162 
163         if (LOCAL_LOGV) {
164             Log.v(TAG, "auto download without roaming -> " + autoDownload);
165         }
166 
167         if (autoDownload) {
168             boolean alwaysAuto = prefs.getBoolean(
169                     MessagingPreferenceActivity.RETRIEVAL_DURING_ROAMING, false);
170 
171             if (LOCAL_LOGV) {
172                 Log.v(TAG, "auto download during roaming -> " + alwaysAuto);
173             }
174 
175             if (!roaming || alwaysAuto) {
176                 return true;
177             }
178         }
179         return false;
180     }
181 
isRoaming()182     static boolean isRoaming() {
183         return isRoaming(SubscriptionManager.getDefaultSmsSubId());
184     }
185 
isRoaming(int subId)186     static boolean isRoaming(int subId) {
187         TelephonyManager teleMgr = TelephonyManager.getDefault();
188         if (teleMgr == null) {
189             return false;
190         }
191         return teleMgr.isNetworkRoaming(subId);
192     }
193 
markState(final Uri uri, int state)194     public void markState(final Uri uri, int state) {
195         // Notify user if the message has expired.
196         try {
197             NotificationInd nInd = (NotificationInd) PduPersister.getPduPersister(mContext)
198                     .load(uri);
199             if ((nInd.getExpiry() < System.currentTimeMillis() / 1000L)
200                     && (state == STATE_DOWNLOADING || state == STATE_PRE_DOWNLOADING)) {
201                 mHandler.post(new Runnable() {
202                     public void run() {
203                         Toast.makeText(mContext, R.string.service_message_not_found,
204                                 Toast.LENGTH_LONG).show();
205                     }
206                 });
207                 SqliteWrapper.delete(mContext, mContext.getContentResolver(), uri, null, null);
208                 return;
209             }
210         } catch(MmsException e) {
211             Log.e(TAG, e.getMessage(), e);
212             return;
213         }
214 
215         // Notify user if downloading permanently failed.
216         if (state == STATE_PERMANENT_FAILURE) {
217             mHandler.post(new Runnable() {
218                 public void run() {
219                     try {
220                         Toast.makeText(mContext, getMessage(uri),
221                                 Toast.LENGTH_LONG).show();
222                     } catch (MmsException e) {
223                         Log.e(TAG, e.getMessage(), e);
224                     }
225                 }
226             });
227         } else if (!mAutoDownload) {
228             state |= DEFERRED_MASK;
229         }
230 
231         // Use the STATUS field to store the state of downloading process
232         // because it's useless for M-Notification.ind.
233         ContentValues values = new ContentValues(1);
234         values.put(Mms.STATUS, state);
235         SqliteWrapper.update(mContext, mContext.getContentResolver(),
236                     uri, values, null, null);
237     }
238 
showErrorCodeToast(int errorStr)239     public void showErrorCodeToast(int errorStr) {
240         final int errStr = errorStr;
241         mHandler.post(new Runnable() {
242             public void run() {
243                 try {
244                     Toast.makeText(mContext, errStr, Toast.LENGTH_LONG).show();
245                 } catch (Exception e) {
246                     Log.e(TAG,"Caught an exception in showErrorCodeToast");
247                 }
248             }
249         });
250     }
251 
getMessage(Uri uri)252     private String getMessage(Uri uri) throws MmsException {
253         NotificationInd ind = (NotificationInd) PduPersister
254                 .getPduPersister(mContext).load(uri);
255 
256         EncodedStringValue v = ind.getSubject();
257         String subject = (v != null) ? v.getString()
258                 : mContext.getString(R.string.no_subject);
259 
260         v = ind.getFrom();
261         String from = (v != null)
262                 ? Contact.get(v.getString(), false).getName()
263                 : mContext.getString(R.string.unknown_sender);
264 
265         return mContext.getString(R.string.dl_failure_notification, subject, from);
266     }
267 
getState(Uri uri)268     public int getState(Uri uri) {
269         Cursor cursor = SqliteWrapper.query(mContext, mContext.getContentResolver(),
270                             uri, new String[] {Mms.STATUS}, null, null, null);
271 
272         if (cursor != null) {
273             try {
274                 if (cursor.moveToFirst()) {
275                     return cursor.getInt(0) & ~DEFERRED_MASK;
276                 }
277             } finally {
278                 cursor.close();
279             }
280         }
281         return STATE_UNSTARTED;
282     }
283 }
284