/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.messaging; import android.app.Application; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.os.Handler; import android.os.Looper; import android.support.v7.mms.CarrierConfigValuesLoader; import android.support.v7.mms.MmsManager; import android.telephony.CarrierConfigManager; import com.android.messaging.datamodel.DataModel; import com.android.messaging.receiver.SmsReceiver; import com.android.messaging.sms.ApnDatabase; import com.android.messaging.sms.BugleApnSettingsLoader; import com.android.messaging.sms.BugleUserAgentInfoLoader; import com.android.messaging.sms.MmsConfig; import com.android.messaging.ui.ConversationDrawables; import com.android.messaging.util.BugleGservices; import com.android.messaging.util.BugleGservicesKeys; import com.android.messaging.util.BuglePrefs; import com.android.messaging.util.BuglePrefsKeys; import com.android.messaging.util.DebugUtils; import com.android.messaging.util.LogUtil; import com.android.messaging.util.OsUtil; import com.android.messaging.util.PhoneUtils; import com.android.messaging.util.Trace; import com.google.common.annotations.VisibleForTesting; import java.io.File; import java.lang.Thread.UncaughtExceptionHandler; /** * The application object */ public class BugleApplication extends Application implements UncaughtExceptionHandler { private static final String TAG = LogUtil.BUGLE_TAG; private UncaughtExceptionHandler sSystemUncaughtExceptionHandler; private static boolean sRunningTests = false; @VisibleForTesting protected static void setTestsRunning() { sRunningTests = true; } /** * @return true if we're running unit tests. */ public static boolean isRunningTests() { return sRunningTests; } @Override public void onCreate() { Trace.beginSection("app.onCreate"); super.onCreate(); // Note onCreate is called in both test and real application environments if (!sRunningTests) { // Only create the factory if not running tests FactoryImpl.register(getApplicationContext(), this); } else { LogUtil.e(TAG, "BugleApplication.onCreate: FactoryImpl.register skipped for test run"); } sSystemUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); Trace.endSection(); } @Override public void onConfigurationChanged(final Configuration newConfig) { super.onConfigurationChanged(newConfig); // Update conversation drawables when changing writing systems // (Right-To-Left / Left-To-Right) ConversationDrawables.get().updateDrawables(); } // Called by the "real" factory from FactoryImpl.register() (i.e. not run in tests) public void initializeSync(final Factory factory) { Trace.beginSection("app.initializeSync"); final Context context = factory.getApplicationContext(); final BugleGservices bugleGservices = factory.getBugleGservices(); final BuglePrefs buglePrefs = factory.getApplicationPrefs(); final DataModel dataModel = factory.getDataModel(); final CarrierConfigValuesLoader carrierConfigValuesLoader = factory.getCarrierConfigValuesLoader(); maybeStartProfiling(); BugleApplication.updateAppConfig(context); // Initialize MMS lib initMmsLib(context, bugleGservices, carrierConfigValuesLoader); // Initialize APN database ApnDatabase.initializeAppContext(context); // Fixup messages in flight if we crashed and send any pending dataModel.onApplicationCreated(); // Register carrier config change receiver if (OsUtil.isAtLeastM()) { registerCarrierConfigChangeReceiver(context); } Trace.endSection(); } private static void registerCarrierConfigChangeReceiver(final Context context) { context.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { LogUtil.i(TAG, "Carrier config changed. Reloading MMS config."); MmsConfig.loadAsync(); } }, new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)); } private static void initMmsLib(final Context context, final BugleGservices bugleGservices, final CarrierConfigValuesLoader carrierConfigValuesLoader) { MmsManager.setApnSettingsLoader(new BugleApnSettingsLoader(context)); MmsManager.setCarrierConfigValuesLoader(carrierConfigValuesLoader); MmsManager.setUserAgentInfoLoader(new BugleUserAgentInfoLoader(context)); MmsManager.setUseWakeLock(true); // If Gservices is configured not to use mms api, force MmsManager to always use // legacy mms sending logic MmsManager.setForceLegacyMms(!bugleGservices.getBoolean( BugleGservicesKeys.USE_MMS_API_IF_PRESENT, BugleGservicesKeys.USE_MMS_API_IF_PRESENT_DEFAULT)); bugleGservices.registerForChanges(new Runnable() { @Override public void run() { MmsManager.setForceLegacyMms(!bugleGservices.getBoolean( BugleGservicesKeys.USE_MMS_API_IF_PRESENT, BugleGservicesKeys.USE_MMS_API_IF_PRESENT_DEFAULT)); } }); } public static void updateAppConfig(final Context context) { // Make sure we set the correct state for the SMS/MMS receivers SmsReceiver.updateSmsReceiveHandler(context); } // Called from thread started in FactoryImpl.register() (i.e. not run in tests) public void initializeAsync(final Factory factory) { // Handle shared prefs upgrade & Load MMS Configuration Trace.beginSection("app.initializeAsync"); maybeHandleSharedPrefsUpgrade(factory); MmsConfig.load(); Trace.endSection(); } @Override public void onLowMemory() { super.onLowMemory(); if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { LogUtil.d(TAG, "BugleApplication.onLowMemory"); } Factory.get().reclaimMemory(); } @Override public void uncaughtException(final Thread thread, final Throwable ex) { final boolean background = getMainLooper().getThread() != thread; if (background) { LogUtil.e(TAG, "Uncaught exception in background thread " + thread, ex); final Handler handler = new Handler(getMainLooper()); handler.post(new Runnable() { @Override public void run() { sSystemUncaughtExceptionHandler.uncaughtException(thread, ex); } }); } else { sSystemUncaughtExceptionHandler.uncaughtException(thread, ex); } } private void maybeStartProfiling() { // App startup profiling support. To use it: // adb shell setprop log.tag.BugleProfile DEBUG // # Start the app, wait for a 30s, download trace file: // adb pull /data/data/com.android.messaging/cache/startup.trace /tmp // # Open trace file (using adt/tools/traceview) if (android.util.Log.isLoggable(LogUtil.PROFILE_TAG, android.util.Log.DEBUG)) { // Start method tracing with a big enough buffer and let it run for 30s. // Note we use a logging tag as we don't want to wait for gservices to start up. final File file = DebugUtils.getDebugFile("startup.trace", true); if (file != null) { android.os.Debug.startMethodTracing(file.getAbsolutePath(), 160 * 1024 * 1024); new Handler(Looper.getMainLooper()).postDelayed( new Runnable() { @Override public void run() { android.os.Debug.stopMethodTracing(); // Allow world to see trace file DebugUtils.ensureReadable(file); LogUtil.d(LogUtil.PROFILE_TAG, "Tracing complete - " + file.getAbsolutePath()); } }, 30000); } } } private void maybeHandleSharedPrefsUpgrade(final Factory factory) { final int existingVersion = factory.getApplicationPrefs().getInt( BuglePrefsKeys.SHARED_PREFERENCES_VERSION, BuglePrefsKeys.SHARED_PREFERENCES_VERSION_DEFAULT); final int targetVersion = Integer.parseInt(getString(R.string.pref_version)); if (targetVersion > existingVersion) { LogUtil.i(LogUtil.BUGLE_TAG, "Upgrading shared prefs from " + existingVersion + " to " + targetVersion); try { // Perform upgrade on application-wide prefs. factory.getApplicationPrefs().onUpgrade(existingVersion, targetVersion); // Perform upgrade on each subscription's prefs. PhoneUtils.forEachActiveSubscription(new PhoneUtils.SubscriptionRunnable() { @Override public void runForSubscription(final int subId) { factory.getSubscriptionPrefs(subId) .onUpgrade(existingVersion, targetVersion); } }); factory.getApplicationPrefs().putInt(BuglePrefsKeys.SHARED_PREFERENCES_VERSION, targetVersion); } catch (final Exception ex) { // Upgrade failed. Don't crash the app because we can always fall back to the // default settings. LogUtil.e(LogUtil.BUGLE_TAG, "Failed to upgrade shared prefs", ex); } } else if (targetVersion < existingVersion) { // We don't care about downgrade since real user shouldn't encounter this, so log it // and ignore any prefs migration. LogUtil.e(LogUtil.BUGLE_TAG, "Shared prefs downgrade requested and ignored. " + "oldVersion = " + existingVersion + ", newVersion = " + targetVersion); } } }