Home | History | Annotate | Download | only in messaging
      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