1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.messaging; 18 19 import android.app.Application; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.res.Configuration; 25 import android.os.Handler; 26 import android.os.Looper; 27 import android.support.v7.mms.CarrierConfigValuesLoader; 28 import android.support.v7.mms.MmsManager; 29 import android.telephony.CarrierConfigManager; 30 31 import com.android.messaging.datamodel.DataModel; 32 import com.android.messaging.receiver.SmsReceiver; 33 import com.android.messaging.sms.ApnDatabase; 34 import com.android.messaging.sms.BugleApnSettingsLoader; 35 import com.android.messaging.sms.BugleUserAgentInfoLoader; 36 import com.android.messaging.sms.MmsConfig; 37 import com.android.messaging.ui.ConversationDrawables; 38 import com.android.messaging.util.BugleGservices; 39 import com.android.messaging.util.BugleGservicesKeys; 40 import com.android.messaging.util.BuglePrefs; 41 import com.android.messaging.util.BuglePrefsKeys; 42 import com.android.messaging.util.DebugUtils; 43 import com.android.messaging.util.LogUtil; 44 import com.android.messaging.util.OsUtil; 45 import com.android.messaging.util.PhoneUtils; 46 import com.android.messaging.util.Trace; 47 import com.google.common.annotations.VisibleForTesting; 48 49 import java.io.File; 50 import java.lang.Thread.UncaughtExceptionHandler; 51 52 /** 53 * The application object 54 */ 55 public class BugleApplication extends Application implements UncaughtExceptionHandler { 56 private static final String TAG = LogUtil.BUGLE_TAG; 57 58 private UncaughtExceptionHandler sSystemUncaughtExceptionHandler; 59 private static boolean sRunningTests = false; 60 61 @VisibleForTesting 62 protected static void setTestsRunning() { 63 sRunningTests = true; 64 } 65 66 /** 67 * @return true if we're running unit tests. 68 */ 69 public static boolean isRunningTests() { 70 return sRunningTests; 71 } 72 73 @Override 74 public void onCreate() { 75 Trace.beginSection("app.onCreate"); 76 super.onCreate(); 77 78 // Note onCreate is called in both test and real application environments 79 if (!sRunningTests) { 80 // Only create the factory if not running tests 81 FactoryImpl.register(getApplicationContext(), this); 82 } else { 83 LogUtil.e(TAG, "BugleApplication.onCreate: FactoryImpl.register skipped for test run"); 84 } 85 86 sSystemUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); 87 Thread.setDefaultUncaughtExceptionHandler(this); 88 Trace.endSection(); 89 } 90 91 @Override 92 public void onConfigurationChanged(final Configuration newConfig) { 93 super.onConfigurationChanged(newConfig); 94 95 // Update conversation drawables when changing writing systems 96 // (Right-To-Left / Left-To-Right) 97 ConversationDrawables.get().updateDrawables(); 98 } 99 100 // Called by the "real" factory from FactoryImpl.register() (i.e. not run in tests) 101 public void initializeSync(final Factory factory) { 102 Trace.beginSection("app.initializeSync"); 103 final Context context = factory.getApplicationContext(); 104 final BugleGservices bugleGservices = factory.getBugleGservices(); 105 final BuglePrefs buglePrefs = factory.getApplicationPrefs(); 106 final DataModel dataModel = factory.getDataModel(); 107 final CarrierConfigValuesLoader carrierConfigValuesLoader = 108 factory.getCarrierConfigValuesLoader(); 109 110 maybeStartProfiling(); 111 112 BugleApplication.updateAppConfig(context); 113 114 // Initialize MMS lib 115 initMmsLib(context, bugleGservices, carrierConfigValuesLoader); 116 // Initialize APN database 117 ApnDatabase.initializeAppContext(context); 118 // Fixup messages in flight if we crashed and send any pending 119 dataModel.onApplicationCreated(); 120 // Register carrier config change receiver 121 if (OsUtil.isAtLeastM()) { 122 registerCarrierConfigChangeReceiver(context); 123 } 124 125 Trace.endSection(); 126 } 127 128 private static void registerCarrierConfigChangeReceiver(final Context context) { 129 context.registerReceiver(new BroadcastReceiver() { 130 @Override 131 public void onReceive(Context context, Intent intent) { 132 LogUtil.i(TAG, "Carrier config changed. Reloading MMS config."); 133 MmsConfig.loadAsync(); 134 } 135 }, new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); 136 } 137 138 private static void initMmsLib(final Context context, final BugleGservices bugleGservices, 139 final CarrierConfigValuesLoader carrierConfigValuesLoader) { 140 MmsManager.setApnSettingsLoader(new BugleApnSettingsLoader(context)); 141 MmsManager.setCarrierConfigValuesLoader(carrierConfigValuesLoader); 142 MmsManager.setUserAgentInfoLoader(new BugleUserAgentInfoLoader(context)); 143 MmsManager.setUseWakeLock(true); 144 // If Gservices is configured not to use mms api, force MmsManager to always use 145 // legacy mms sending logic 146 MmsManager.setForceLegacyMms(!bugleGservices.getBoolean( 147 BugleGservicesKeys.USE_MMS_API_IF_PRESENT, 148 BugleGservicesKeys.USE_MMS_API_IF_PRESENT_DEFAULT)); 149 bugleGservices.registerForChanges(new Runnable() { 150 @Override 151 public void run() { 152 MmsManager.setForceLegacyMms(!bugleGservices.getBoolean( 153 BugleGservicesKeys.USE_MMS_API_IF_PRESENT, 154 BugleGservicesKeys.USE_MMS_API_IF_PRESENT_DEFAULT)); 155 } 156 }); 157 } 158 159 public static void updateAppConfig(final Context context) { 160 // Make sure we set the correct state for the SMS/MMS receivers 161 SmsReceiver.updateSmsReceiveHandler(context); 162 } 163 164 // Called from thread started in FactoryImpl.register() (i.e. not run in tests) 165 public void initializeAsync(final Factory factory) { 166 // Handle shared prefs upgrade & Load MMS Configuration 167 Trace.beginSection("app.initializeAsync"); 168 maybeHandleSharedPrefsUpgrade(factory); 169 MmsConfig.load(); 170 Trace.endSection(); 171 } 172 173 @Override 174 public void onLowMemory() { 175 super.onLowMemory(); 176 177 if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 178 LogUtil.d(TAG, "BugleApplication.onLowMemory"); 179 } 180 Factory.get().reclaimMemory(); 181 } 182 183 @Override 184 public void uncaughtException(final Thread thread, final Throwable ex) { 185 final boolean background = getMainLooper().getThread() != thread; 186 if (background) { 187 LogUtil.e(TAG, "Uncaught exception in background thread " + thread, ex); 188 189 final Handler handler = new Handler(getMainLooper()); 190 handler.post(new Runnable() { 191 192 @Override 193 public void run() { 194 sSystemUncaughtExceptionHandler.uncaughtException(thread, ex); 195 } 196 }); 197 } else { 198 sSystemUncaughtExceptionHandler.uncaughtException(thread, ex); 199 } 200 } 201 202 private void maybeStartProfiling() { 203 // App startup profiling support. To use it: 204 // adb shell setprop log.tag.BugleProfile DEBUG 205 // # Start the app, wait for a 30s, download trace file: 206 // adb pull /data/data/com.android.messaging/cache/startup.trace /tmp 207 // # Open trace file (using adt/tools/traceview) 208 if (android.util.Log.isLoggable(LogUtil.PROFILE_TAG, android.util.Log.DEBUG)) { 209 // Start method tracing with a big enough buffer and let it run for 30s. 210 // Note we use a logging tag as we don't want to wait for gservices to start up. 211 final File file = DebugUtils.getDebugFile("startup.trace", true); 212 if (file != null) { 213 android.os.Debug.startMethodTracing(file.getAbsolutePath(), 160 * 1024 * 1024); 214 new Handler(Looper.getMainLooper()).postDelayed( 215 new Runnable() { 216 @Override 217 public void run() { 218 android.os.Debug.stopMethodTracing(); 219 // Allow world to see trace file 220 DebugUtils.ensureReadable(file); 221 LogUtil.d(LogUtil.PROFILE_TAG, "Tracing complete - " 222 + file.getAbsolutePath()); 223 } 224 }, 30000); 225 } 226 } 227 } 228 229 private void maybeHandleSharedPrefsUpgrade(final Factory factory) { 230 final int existingVersion = factory.getApplicationPrefs().getInt( 231 BuglePrefsKeys.SHARED_PREFERENCES_VERSION, 232 BuglePrefsKeys.SHARED_PREFERENCES_VERSION_DEFAULT); 233 final int targetVersion = Integer.parseInt(getString(R.string.pref_version)); 234 if (targetVersion > existingVersion) { 235 LogUtil.i(LogUtil.BUGLE_TAG, "Upgrading shared prefs from " + existingVersion + 236 " to " + targetVersion); 237 try { 238 // Perform upgrade on application-wide prefs. 239 factory.getApplicationPrefs().onUpgrade(existingVersion, targetVersion); 240 // Perform upgrade on each subscription's prefs. 241 PhoneUtils.forEachActiveSubscription(new PhoneUtils.SubscriptionRunnable() { 242 @Override 243 public void runForSubscription(final int subId) { 244 factory.getSubscriptionPrefs(subId) 245 .onUpgrade(existingVersion, targetVersion); 246 } 247 }); 248 factory.getApplicationPrefs().putInt(BuglePrefsKeys.SHARED_PREFERENCES_VERSION, 249 targetVersion); 250 } catch (final Exception ex) { 251 // Upgrade failed. Don't crash the app because we can always fall back to the 252 // default settings. 253 LogUtil.e(LogUtil.BUGLE_TAG, "Failed to upgrade shared prefs", ex); 254 } 255 } else if (targetVersion < existingVersion) { 256 // We don't care about downgrade since real user shouldn't encounter this, so log it 257 // and ignore any prefs migration. 258 LogUtil.e(LogUtil.BUGLE_TAG, "Shared prefs downgrade requested and ignored. " + 259 "oldVersion = " + existingVersion + ", newVersion = " + targetVersion); 260 } 261 } 262 } 263