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 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 133 public boolean isAuto() { 134 return mAutoDownload; 135 } 136 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 148 public static DownloadManager getInstance() { 149 if (sInstance == null) { 150 throw new IllegalStateException("Uninitialized."); 151 } 152 return sInstance; 153 } 154 155 static boolean getAutoDownloadState(SharedPreferences prefs) { 156 return getAutoDownloadState(prefs, isRoaming()); 157 } 158 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 182 static boolean isRoaming() { 183 return isRoaming(SubscriptionManager.getDefaultSmsSubId()); 184 } 185 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 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 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 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 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