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 com.android.internal.telephony.TelephonyIntents; 39 import com.android.internal.telephony.TelephonyProperties; 40 import com.android.mms.R; 41 import com.android.mms.data.Contact; 42 import com.android.mms.ui.MessagingPreferenceActivity; 43 import com.google.android.mms.MmsException; 44 import com.google.android.mms.pdu.EncodedStringValue; 45 import com.google.android.mms.pdu.NotificationInd; 46 import com.google.android.mms.pdu.PduPersister; 47 48 public class DownloadManager { 49 private static final String TAG = "DownloadManager"; 50 private static final boolean DEBUG = false; 51 private static final boolean LOCAL_LOGV = false; 52 53 public static final int DEFERRED_MASK = 0x04; 54 55 public static final int STATE_UNKNOWN = 0x00; 56 public static final int STATE_UNSTARTED = 0x80; 57 public static final int STATE_DOWNLOADING = 0x81; 58 public static final int STATE_TRANSIENT_FAILURE = 0x82; 59 public static final int STATE_PERMANENT_FAILURE = 0x87; 60 public static final int STATE_PRE_DOWNLOADING = 0x88; 61 62 private final Context mContext; 63 private final Handler mHandler; 64 private final SharedPreferences mPreferences; 65 private boolean mAutoDownload; 66 67 private final OnSharedPreferenceChangeListener mPreferencesChangeListener = 68 new OnSharedPreferenceChangeListener() { 69 public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { 70 if (MessagingPreferenceActivity.AUTO_RETRIEVAL.equals(key) 71 || MessagingPreferenceActivity.RETRIEVAL_DURING_ROAMING.equals(key)) { 72 if (LOCAL_LOGV) { 73 Log.v(TAG, "Preferences updated."); 74 } 75 76 synchronized (sInstance) { 77 mAutoDownload = getAutoDownloadState(prefs); 78 if (LOCAL_LOGV) { 79 Log.v(TAG, "mAutoDownload ------> " + mAutoDownload); 80 } 81 } 82 } 83 } 84 }; 85 86 private final BroadcastReceiver mRoamingStateListener = 87 new BroadcastReceiver() { 88 @Override 89 public void onReceive(Context context, Intent intent) { 90 if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(intent.getAction())) { 91 if (LOCAL_LOGV) { 92 Log.v(TAG, "Service state changed: " + intent.getExtras()); 93 } 94 95 ServiceState state = ServiceState.newFromBundle(intent.getExtras()); 96 boolean isRoaming = state.getRoaming(); 97 if (LOCAL_LOGV) { 98 Log.v(TAG, "roaming ------> " + isRoaming); 99 } 100 synchronized (sInstance) { 101 mAutoDownload = getAutoDownloadState(mPreferences, isRoaming); 102 if (LOCAL_LOGV) { 103 Log.v(TAG, "mAutoDownload ------> " + mAutoDownload); 104 } 105 } 106 } 107 } 108 }; 109 110 private static DownloadManager sInstance; 111 112 private DownloadManager(Context context) { 113 mContext = context; 114 mHandler = new Handler(); 115 mPreferences = PreferenceManager.getDefaultSharedPreferences(context); 116 mPreferences.registerOnSharedPreferenceChangeListener(mPreferencesChangeListener); 117 118 context.registerReceiver( 119 mRoamingStateListener, 120 new IntentFilter(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)); 121 122 mAutoDownload = getAutoDownloadState(mPreferences); 123 if (LOCAL_LOGV) { 124 Log.v(TAG, "mAutoDownload ------> " + mAutoDownload); 125 } 126 } 127 128 public boolean isAuto() { 129 return mAutoDownload; 130 } 131 132 public static void init(Context context) { 133 if (LOCAL_LOGV) { 134 Log.v(TAG, "DownloadManager.init()"); 135 } 136 137 if (sInstance != null) { 138 Log.w(TAG, "Already initialized."); 139 } 140 sInstance = new DownloadManager(context); 141 } 142 143 public static DownloadManager getInstance() { 144 if (sInstance == null) { 145 throw new IllegalStateException("Uninitialized."); 146 } 147 return sInstance; 148 } 149 150 static boolean getAutoDownloadState(SharedPreferences prefs) { 151 return getAutoDownloadState(prefs, isRoaming()); 152 } 153 154 static boolean getAutoDownloadState(SharedPreferences prefs, boolean roaming) { 155 boolean autoDownload = prefs.getBoolean( 156 MessagingPreferenceActivity.AUTO_RETRIEVAL, true); 157 158 if (LOCAL_LOGV) { 159 Log.v(TAG, "auto download without roaming -> " + autoDownload); 160 } 161 162 if (autoDownload) { 163 boolean alwaysAuto = prefs.getBoolean( 164 MessagingPreferenceActivity.RETRIEVAL_DURING_ROAMING, false); 165 166 if (LOCAL_LOGV) { 167 Log.v(TAG, "auto download during roaming -> " + alwaysAuto); 168 } 169 170 if (!roaming || alwaysAuto) { 171 return true; 172 } 173 } 174 return false; 175 } 176 177 static boolean isRoaming() { 178 // TODO: fix and put in Telephony layer 179 String roaming = SystemProperties.get( 180 TelephonyProperties.PROPERTY_OPERATOR_ISROAMING, null); 181 if (LOCAL_LOGV) { 182 Log.v(TAG, "roaming ------> " + roaming); 183 } 184 return "true".equals(roaming); 185 } 186 187 public void markState(final Uri uri, int state) { 188 // Notify user if the message has expired. 189 try { 190 NotificationInd nInd = (NotificationInd) PduPersister.getPduPersister(mContext) 191 .load(uri); 192 if ((nInd.getExpiry() < System.currentTimeMillis() / 1000L) 193 && (state == STATE_DOWNLOADING || state == STATE_PRE_DOWNLOADING)) { 194 mHandler.post(new Runnable() { 195 public void run() { 196 Toast.makeText(mContext, R.string.service_message_not_found, 197 Toast.LENGTH_LONG).show(); 198 } 199 }); 200 SqliteWrapper.delete(mContext, mContext.getContentResolver(), uri, null, null); 201 return; 202 } 203 } catch(MmsException e) { 204 Log.e(TAG, e.getMessage(), e); 205 return; 206 } 207 208 // Notify user if downloading permanently failed. 209 if (state == STATE_PERMANENT_FAILURE) { 210 mHandler.post(new Runnable() { 211 public void run() { 212 try { 213 Toast.makeText(mContext, getMessage(uri), 214 Toast.LENGTH_LONG).show(); 215 } catch (MmsException e) { 216 Log.e(TAG, e.getMessage(), e); 217 } 218 } 219 }); 220 } else if (!mAutoDownload) { 221 state |= DEFERRED_MASK; 222 } 223 224 // Use the STATUS field to store the state of downloading process 225 // because it's useless for M-Notification.ind. 226 ContentValues values = new ContentValues(1); 227 values.put(Mms.STATUS, state); 228 SqliteWrapper.update(mContext, mContext.getContentResolver(), 229 uri, values, null, null); 230 } 231 232 public void showErrorCodeToast(int errorStr) { 233 final int errStr = errorStr; 234 mHandler.post(new Runnable() { 235 public void run() { 236 try { 237 Toast.makeText(mContext, errStr, Toast.LENGTH_LONG).show(); 238 } catch (Exception e) { 239 Log.e(TAG,"Caught an exception in showErrorCodeToast"); 240 } 241 } 242 }); 243 } 244 245 private String getMessage(Uri uri) throws MmsException { 246 NotificationInd ind = (NotificationInd) PduPersister 247 .getPduPersister(mContext).load(uri); 248 249 EncodedStringValue v = ind.getSubject(); 250 String subject = (v != null) ? v.getString() 251 : mContext.getString(R.string.no_subject); 252 253 v = ind.getFrom(); 254 String from = (v != null) 255 ? Contact.get(v.getString(), false).getName() 256 : mContext.getString(R.string.unknown_sender); 257 258 return mContext.getString(R.string.dl_failure_notification, subject, from); 259 } 260 261 public int getState(Uri uri) { 262 Cursor cursor = SqliteWrapper.query(mContext, mContext.getContentResolver(), 263 uri, new String[] {Mms.STATUS}, null, null, null); 264 265 if (cursor != null) { 266 try { 267 if (cursor.moveToFirst()) { 268 return cursor.getInt(0) & ~DEFERRED_MASK; 269 } 270 } finally { 271 cursor.close(); 272 } 273 } 274 return STATE_UNSTARTED; 275 } 276 } 277