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 androidx.appcompat.mms.CarrierConfigValuesLoader; 28 import androidx.appcompat.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 setTestsRunning()62 protected static void setTestsRunning() { 63 sRunningTests = true; 64 } 65 66 /** 67 * @return true if we're running unit tests. 68 */ isRunningTests()69 public static boolean isRunningTests() { 70 return sRunningTests; 71 } 72 73 @Override onCreate()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 onConfigurationChanged(final Configuration newConfig)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) initializeSync(final Factory factory)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 registerCarrierConfigChangeReceiver(final Context context)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 Context.RECEIVER_EXPORTED/*UNAUDITED*/); 137 } 138 initMmsLib(final Context context, final BugleGservices bugleGservices, final CarrierConfigValuesLoader carrierConfigValuesLoader)139 private static void initMmsLib(final Context context, final BugleGservices bugleGservices, 140 final CarrierConfigValuesLoader carrierConfigValuesLoader) { 141 MmsManager.setApnSettingsLoader(new BugleApnSettingsLoader(context)); 142 MmsManager.setCarrierConfigValuesLoader(carrierConfigValuesLoader); 143 MmsManager.setUserAgentInfoLoader(new BugleUserAgentInfoLoader(context)); 144 MmsManager.setUseWakeLock(true); 145 // If Gservices is configured not to use mms api, force MmsManager to always use 146 // legacy mms sending logic 147 MmsManager.setForceLegacyMms(!bugleGservices.getBoolean( 148 BugleGservicesKeys.USE_MMS_API_IF_PRESENT, 149 BugleGservicesKeys.USE_MMS_API_IF_PRESENT_DEFAULT)); 150 bugleGservices.registerForChanges(new Runnable() { 151 @Override 152 public void run() { 153 MmsManager.setForceLegacyMms(!bugleGservices.getBoolean( 154 BugleGservicesKeys.USE_MMS_API_IF_PRESENT, 155 BugleGservicesKeys.USE_MMS_API_IF_PRESENT_DEFAULT)); 156 } 157 }); 158 } 159 updateAppConfig(final Context context)160 public static void updateAppConfig(final Context context) { 161 // Make sure we set the correct state for the SMS/MMS receivers 162 SmsReceiver.updateSmsReceiveHandler(context); 163 } 164 165 // Called from thread started in FactoryImpl.register() (i.e. not run in tests) initializeAsync(final Factory factory)166 public void initializeAsync(final Factory factory) { 167 // Handle shared prefs upgrade & Load MMS Configuration 168 Trace.beginSection("app.initializeAsync"); 169 maybeHandleSharedPrefsUpgrade(factory); 170 MmsConfig.load(); 171 Trace.endSection(); 172 } 173 174 @Override onLowMemory()175 public void onLowMemory() { 176 super.onLowMemory(); 177 178 if (LogUtil.isLoggable(TAG, LogUtil.DEBUG)) { 179 LogUtil.d(TAG, "BugleApplication.onLowMemory"); 180 } 181 Factory.get().reclaimMemory(); 182 } 183 184 @Override uncaughtException(final Thread thread, final Throwable ex)185 public void uncaughtException(final Thread thread, final Throwable ex) { 186 final boolean background = getMainLooper().getThread() != thread; 187 if (background) { 188 LogUtil.e(TAG, "Uncaught exception in background thread " + thread, ex); 189 190 final Handler handler = new Handler(getMainLooper()); 191 handler.post(new Runnable() { 192 193 @Override 194 public void run() { 195 sSystemUncaughtExceptionHandler.uncaughtException(thread, ex); 196 } 197 }); 198 } else { 199 sSystemUncaughtExceptionHandler.uncaughtException(thread, ex); 200 } 201 } 202 maybeStartProfiling()203 private void maybeStartProfiling() { 204 // App startup profiling support. To use it: 205 // adb shell setprop log.tag.BugleProfile DEBUG 206 // # Start the app, wait for a 30s, download trace file: 207 // adb pull /data/data/com.android.messaging/cache/startup.trace /tmp 208 // # Open trace file (using adt/tools/traceview) 209 if (android.util.Log.isLoggable(LogUtil.PROFILE_TAG, android.util.Log.DEBUG)) { 210 // Start method tracing with a big enough buffer and let it run for 30s. 211 // Note we use a logging tag as we don't want to wait for gservices to start up. 212 final File file = DebugUtils.getDebugFile("startup.trace", true); 213 if (file != null) { 214 android.os.Debug.startMethodTracing(file.getAbsolutePath(), 160 * 1024 * 1024); 215 new Handler(Looper.getMainLooper()).postDelayed( 216 new Runnable() { 217 @Override 218 public void run() { 219 android.os.Debug.stopMethodTracing(); 220 // Allow world to see trace file 221 DebugUtils.ensureReadable(file); 222 LogUtil.d(LogUtil.PROFILE_TAG, "Tracing complete - " 223 + file.getAbsolutePath()); 224 } 225 }, 30000); 226 } 227 } 228 } 229 maybeHandleSharedPrefsUpgrade(final Factory factory)230 private void maybeHandleSharedPrefsUpgrade(final Factory factory) { 231 final int existingVersion = factory.getApplicationPrefs().getInt( 232 BuglePrefsKeys.SHARED_PREFERENCES_VERSION, 233 BuglePrefsKeys.SHARED_PREFERENCES_VERSION_DEFAULT); 234 final int targetVersion = Integer.parseInt(getString(R.string.pref_version)); 235 if (targetVersion > existingVersion) { 236 LogUtil.i(LogUtil.BUGLE_TAG, "Upgrading shared prefs from " + existingVersion + 237 " to " + targetVersion); 238 try { 239 // Perform upgrade on application-wide prefs. 240 factory.getApplicationPrefs().onUpgrade(existingVersion, targetVersion); 241 // Perform upgrade on each subscription's prefs. 242 PhoneUtils.forEachActiveSubscription(new PhoneUtils.SubscriptionRunnable() { 243 @Override 244 public void runForSubscription(final int subId) { 245 factory.getSubscriptionPrefs(subId) 246 .onUpgrade(existingVersion, targetVersion); 247 } 248 }); 249 factory.getApplicationPrefs().putInt(BuglePrefsKeys.SHARED_PREFERENCES_VERSION, 250 targetVersion); 251 } catch (final Exception ex) { 252 // Upgrade failed. Don't crash the app because we can always fall back to the 253 // default settings. 254 LogUtil.e(LogUtil.BUGLE_TAG, "Failed to upgrade shared prefs", ex); 255 } 256 } else if (targetVersion < existingVersion) { 257 // We don't care about downgrade since real user shouldn't encounter this, so log it 258 // and ignore any prefs migration. 259 LogUtil.e(LogUtil.BUGLE_TAG, "Shared prefs downgrade requested and ignored. " + 260 "oldVersion = " + existingVersion + ", newVersion = " + targetVersion); 261 } 262 } 263 } 264