blob: 808a5d60dacf674594097f966a50f50a79171ed4 [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
2 * Copyright (C) 2006 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
17package com.android.phone;
18
19import android.app.Activity;
20import android.app.KeyguardManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070021import android.app.ProgressDialog;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070022import android.content.BroadcastReceiver;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070023import android.content.ContentResolver;
24import android.content.Context;
25import android.content.ContextWrapper;
26import android.content.Intent;
27import android.content.IntentFilter;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070028import android.media.AudioManager;
Santos Cordone18902f2016-03-22 17:16:04 -070029import android.net.ConnectivityManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070030import android.net.Uri;
31import android.os.AsyncResult;
Junda Liu605148f2015-04-28 15:23:40 -070032import android.os.Bundle;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070033import android.os.Handler;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070034import android.os.Message;
Jonathan Basseric31f1f32015-05-12 10:13:03 -070035import android.os.PersistableBundle;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070036import android.os.PowerManager;
Brad Ebinger3fa43462016-04-12 16:06:48 -070037import android.os.ServiceManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070038import android.os.SystemClock;
39import android.os.SystemProperties;
Brad Ebinger3fa43462016-04-12 16:06:48 -070040import android.os.SystemService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070041import android.os.UpdateLock;
Brad Ebinger3fa43462016-04-12 16:06:48 -070042import android.os.UserManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070043import android.preference.PreferenceManager;
Sanket Padawe4c699232016-02-09 11:07:22 -080044import android.provider.Settings;
Junda Liu12f7d802015-05-01 12:06:44 -070045import android.telephony.CarrierConfigManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070046import android.telephony.ServiceState;
Andrew Lee385019f2014-11-24 14:19:50 -080047import android.telephony.SubscriptionManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070048import android.util.Log;
Santos Cordone18902f2016-03-22 17:16:04 -070049import android.widget.Toast;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070050
51import com.android.internal.telephony.Call;
52import com.android.internal.telephony.CallManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070053import com.android.internal.telephony.IccCardConstants;
54import com.android.internal.telephony.MmiCode;
55import com.android.internal.telephony.Phone;
56import com.android.internal.telephony.PhoneConstants;
57import com.android.internal.telephony.PhoneFactory;
58import com.android.internal.telephony.TelephonyCapabilities;
59import com.android.internal.telephony.TelephonyIntents;
Santos Cordon352ff652014-05-30 01:41:45 -070060import com.android.phone.common.CallLogAsync;
Andrew Leefb7f92e2015-02-26 16:23:32 -080061import com.android.phone.settings.SettingsConstants;
Brad Ebinger3fa43462016-04-12 16:06:48 -070062import com.android.server.sip.SipService;
Santos Cordon76aaf482015-04-08 10:58:27 -070063import com.android.services.telephony.activation.SimActivationManager;
Brad Ebinger3fa43462016-04-12 16:06:48 -070064import com.android.services.telephony.sip.SipUtil;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070065
66/**
67 * Global state for the telephony subsystem when running in the primary
68 * phone process.
69 */
Sailesh Nepalbf900542014-07-15 16:18:32 -070070public class PhoneGlobals extends ContextWrapper {
Andrew Lee83383e42014-10-31 12:42:28 -070071 public static final String LOG_TAG = "PhoneApp";
Santos Cordon7d4ddf62013-07-10 11:58:08 -070072
73 /**
74 * Phone app-wide debug level:
75 * 0 - no debug logging
76 * 1 - normal debug logging if ro.debuggable is set (which is true in
77 * "eng" and "userdebug" builds but not "user" builds)
78 * 2 - ultra-verbose debug logging
79 *
80 * Most individual classes in the phone app have a local DBG constant,
81 * typically set to
82 * (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1)
83 * or else
84 * (PhoneApp.DBG_LEVEL >= 2)
85 * depending on the desired verbosity.
86 *
87 * ***** DO NOT SUBMIT WITH DBG_LEVEL > 0 *************
88 */
Andrew Lee88b51e22014-10-29 15:48:51 -070089 public static final int DBG_LEVEL = 0;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070090
91 private static final boolean DBG =
92 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
93 private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
94
95 // Message codes; see mHandler below.
96 private static final int EVENT_SIM_NETWORK_LOCKED = 3;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070097 private static final int EVENT_SIM_STATE_CHANGED = 8;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070098 private static final int EVENT_DATA_ROAMING_DISCONNECTED = 10;
99 private static final int EVENT_DATA_ROAMING_OK = 11;
100 private static final int EVENT_UNSOL_CDMA_INFO_RECORD = 12;
Brad Ebinger3fa43462016-04-12 16:06:48 -0700101 private static final int EVENT_RESTART_SIP = 13;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700102
103 // The MMI codes are also used by the InCallScreen.
104 public static final int MMI_INITIATE = 51;
105 public static final int MMI_COMPLETE = 52;
106 public static final int MMI_CANCEL = 53;
107 // Don't use message codes larger than 99 here; those are reserved for
108 // the individual Activities of the Phone UI.
109
Santos Cordone18902f2016-03-22 17:16:04 -0700110 public static final int AIRPLANE_ON = 1;
111 public static final int AIRPLANE_OFF = 0;
112
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700113 /**
114 * Allowable values for the wake lock code.
115 * SLEEP means the device can be put to sleep.
116 * PARTIAL means wake the processor, but we display can be kept off.
117 * FULL means wake both the processor and the display.
118 */
119 public enum WakeState {
120 SLEEP,
121 PARTIAL,
122 FULL
123 }
124
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700125 private static PhoneGlobals sMe;
126
127 // A few important fields we expose to the rest of the package
128 // directly (rather than thru set/get methods) for efficiency.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700129 CallController callController;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700130 CallManager mCM;
Santos Cordon63a84242013-07-23 13:32:52 -0700131 CallNotifier notifier;
132 CallerInfoCache callerInfoCache;
Santos Cordon63a84242013-07-23 13:32:52 -0700133 NotificationMgr notificationMgr;
Andrew Lee9431b832015-03-09 18:46:45 -0700134 public PhoneInterfaceManager phoneMgr;
Santos Cordon76aaf482015-04-08 10:58:27 -0700135 public SimActivationManager simActivationManager;
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800136 CarrierConfigLoader configLoader;
Santos Cordon63a84242013-07-23 13:32:52 -0700137
Santos Cordon69a69192013-08-22 14:25:42 -0700138 private CallGatewayManager callGatewayManager;
Sandeep Kuntade73a6a2014-10-15 18:45:56 +0530139 private Phone phoneInEcm;
Santos Cordon63a84242013-07-23 13:32:52 -0700140
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700141 static boolean sVoiceCapable = true;
142
Santos Cordonc593d002015-06-03 15:41:15 -0700143 // TODO: Remove, no longer used.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700144 CdmaPhoneCallState cdmaPhoneCallState;
145
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700146 // The currently-active PUK entry activity and progress dialog.
147 // Normally, these are the Emergency Dialer and the subsequent
148 // progress dialog. null if there is are no such objects in
149 // the foreground.
150 private Activity mPUKEntryActivity;
151 private ProgressDialog mPUKEntryProgressDialog;
152
Yorke Lee4d2db1c2014-11-06 11:37:09 -0800153 private boolean mDataDisconnectedDueToRoaming = false;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700154
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700155 private WakeState mWakeState = WakeState.SLEEP;
156
157 private PowerManager mPowerManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700158 private PowerManager.WakeLock mWakeLock;
159 private PowerManager.WakeLock mPartialWakeLock;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700160 private KeyguardManager mKeyguardManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700161
162 private UpdateLock mUpdateLock;
163
164 // Broadcast receiver for various intent broadcasts (see onCreate())
165 private final BroadcastReceiver mReceiver = new PhoneAppBroadcastReceiver();
166
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700167 /**
168 * The singleton OtaUtils instance used for OTASP calls.
169 *
170 * The OtaUtils instance is created lazily the first time we need to
171 * make an OTASP call, regardless of whether it's an interactive or
172 * non-interactive OTASP call.
173 */
174 public OtaUtils otaUtils;
175
176 // Following are the CDMA OTA information Objects used during OTA Call.
177 // cdmaOtaProvisionData object store static OTA information that needs
178 // to be maintained even during Slider open/close scenarios.
179 // cdmaOtaConfigData object stores configuration info to control visiblity
180 // of each OTA Screens.
181 // cdmaOtaScreenState object store OTA Screen State information.
182 public OtaUtils.CdmaOtaProvisionData cdmaOtaProvisionData;
183 public OtaUtils.CdmaOtaConfigData cdmaOtaConfigData;
184 public OtaUtils.CdmaOtaScreenState cdmaOtaScreenState;
185 public OtaUtils.CdmaOtaInCallScreenUiState cdmaOtaInCallScreenUiState;
186
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700187 Handler mHandler = new Handler() {
188 @Override
189 public void handleMessage(Message msg) {
190 PhoneConstants.State phoneState;
191 switch (msg.what) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700192 // TODO: This event should be handled by the lock screen, just
193 // like the "SIM missing" and "Sim locked" cases (bug 1804111).
194 case EVENT_SIM_NETWORK_LOCKED:
Jonathan Basseri9504c6b2015-06-04 14:23:32 -0700195 if (getCarrierConfig().getBoolean(
196 CarrierConfigManager.KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL)) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700197 // Some products don't have the concept of a "SIM network lock"
198 Log.i(LOG_TAG, "Ignoring EVENT_SIM_NETWORK_LOCKED event; "
199 + "not showing 'SIM network unlock' PIN entry screen");
200 } else {
201 // Normal case: show the "SIM network unlock" PIN entry screen.
202 // The user won't be able to do anything else until
203 // they enter a valid SIM network PIN.
204 Log.i(LOG_TAG, "show sim depersonal panel");
Tyler Gunn52a37072015-08-24 14:23:19 -0700205 IccNetworkDepersonalizationPanel.showDialog();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700206 }
207 break;
208
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700209 case EVENT_DATA_ROAMING_DISCONNECTED:
210 notificationMgr.showDataDisconnectedRoaming();
211 break;
212
213 case EVENT_DATA_ROAMING_OK:
214 notificationMgr.hideDataDisconnectedRoaming();
215 break;
216
217 case MMI_COMPLETE:
218 onMMIComplete((AsyncResult) msg.obj);
219 break;
220
221 case MMI_CANCEL:
Stuart Scottdcf40a92014-12-09 10:45:01 -0800222 PhoneUtils.cancelMmiCode(mCM.getFgPhone());
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700223 break;
224
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700225 case EVENT_SIM_STATE_CHANGED:
226 // Marks the event where the SIM goes into ready state.
227 // Right now, this is only used for the PUK-unlocking
228 // process.
229 if (msg.obj.equals(IccCardConstants.INTENT_VALUE_ICC_READY)) {
230 // when the right event is triggered and there
231 // are UI objects in the foreground, we close
232 // them to display the lock panel.
233 if (mPUKEntryActivity != null) {
234 mPUKEntryActivity.finish();
235 mPUKEntryActivity = null;
236 }
237 if (mPUKEntryProgressDialog != null) {
238 mPUKEntryProgressDialog.dismiss();
239 mPUKEntryProgressDialog = null;
240 }
241 }
242 break;
243
244 case EVENT_UNSOL_CDMA_INFO_RECORD:
245 //TODO: handle message here;
246 break;
Brad Ebinger3fa43462016-04-12 16:06:48 -0700247 case EVENT_RESTART_SIP:
248 // This should only run if the Phone process crashed and was restarted. We do
249 // not want this running if the device is still in the FBE encrypted state.
250 // This is the same procedure that is triggered in the SipBroadcastReceiver
251 // upon BOOT_COMPLETED.
252 UserManager userManager = UserManager.get(sMe);
253 if (userManager != null && userManager.isUserUnlocked()) {
254 SipUtil.startSipService();
255 }
256 break;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700257 }
258 }
259 };
260
261 public PhoneGlobals(Context context) {
262 super(context);
263 sMe = this;
264 }
265
266 public void onCreate() {
267 if (VDBG) Log.v(LOG_TAG, "onCreate()...");
268
269 ContentResolver resolver = getContentResolver();
270
271 // Cache the "voice capable" flag.
272 // This flag currently comes from a resource (which is
273 // overrideable on a per-product basis):
274 sVoiceCapable =
275 getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
276 // ...but this might eventually become a PackageManager "system
277 // feature" instead, in which case we'd do something like:
278 // sVoiceCapable =
279 // getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS);
280
Stuart Scottdcf40a92014-12-09 10:45:01 -0800281 if (mCM == null) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700282 // Initialize the telephony framework
283 PhoneFactory.makeDefaultPhones(this);
284
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700285 // Start TelephonyDebugService After the default phone is created.
286 Intent intent = new Intent(this, TelephonyDebugService.class);
287 startService(intent);
288
289 mCM = CallManager.getInstance();
Stuart Scottdcf40a92014-12-09 10:45:01 -0800290 for (Phone phone : PhoneFactory.getPhones()) {
291 mCM.registerPhone(phone);
Stuart Scottdcf40a92014-12-09 10:45:01 -0800292 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700293
294 // Create the NotificationMgr singleton, which is used to display
295 // status bar icons and control other status bar behavior.
296 notificationMgr = NotificationMgr.init(this);
297
Brad Ebinger3fa43462016-04-12 16:06:48 -0700298 // If PhoneGlobals has crashed and is being restarted, then restart.
299 mHandler.sendEmptyMessage(EVENT_RESTART_SIP);
300
Anthony Lee03ebdfc2015-07-27 08:12:02 -0700301 // Create an instance of CdmaPhoneCallState and initialize it to IDLE
302 cdmaPhoneCallState = new CdmaPhoneCallState();
303 cdmaPhoneCallState.CdmaPhoneCallStateInit();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700304
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700305 // before registering for phone state changes
306 mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
307 mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, LOG_TAG);
308 // lock used to keep the processor awake, when we don't care for the display.
309 mPartialWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
310 | PowerManager.ON_AFTER_RELEASE, LOG_TAG);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700311
312 mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
313
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700314 // Get UpdateLock to suppress system-update related events (e.g. dialog show-up)
315 // during phone calls.
316 mUpdateLock = new UpdateLock("phone");
317
318 if (DBG) Log.d(LOG_TAG, "onCreate: mUpdateLock: " + mUpdateLock);
319
320 CallLogger callLogger = new CallLogger(this, new CallLogAsync());
321
Jay Shrauner21a75342013-11-25 16:14:43 -0800322 callGatewayManager = CallGatewayManager.getInstance();
Santos Cordon69a69192013-08-22 14:25:42 -0700323
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700324 // Create the CallController singleton, which is the interface
325 // to the telephony layer for user-initiated telephony functionality
326 // (like making outgoing calls.)
Santos Cordon69a69192013-08-22 14:25:42 -0700327 callController = CallController.init(this, callLogger, callGatewayManager);
328
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700329 // Create the CallerInfoCache singleton, which remembers custom ring tone and
330 // send-to-voicemail settings.
331 //
332 // The asynchronous caching will start just after this call.
333 callerInfoCache = CallerInfoCache.init(this);
334
Stuart Scottdcf40a92014-12-09 10:45:01 -0800335 phoneMgr = PhoneInterfaceManager.init(this, PhoneFactory.getDefaultPhone());
Santos Cordon406c0342013-08-28 00:07:47 -0700336
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800337 configLoader = CarrierConfigLoader.init(this);
338
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700339 // Create the CallNotifer singleton, which handles
340 // asynchronous events from the telephony layer (like
341 // launching the incoming-call UI when an incoming call comes
342 // in.)
Brad Ebingera9c6b6d2016-01-07 17:24:16 -0800343 notifier = CallNotifier.init(this);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700344
Stuart Scottdcf40a92014-12-09 10:45:01 -0800345 PhoneUtils.registerIccStatus(mHandler, EVENT_SIM_NETWORK_LOCKED);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700346
347 // register for MMI/USSD
348 mCM.registerForMmiComplete(mHandler, MMI_COMPLETE, null);
349
350 // register connection tracking to PhoneUtils
351 PhoneUtils.initializeConnectionHandler(mCM);
352
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700353 // Register for misc other intent broadcasts.
354 IntentFilter intentFilter =
355 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700356 intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700357 intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
358 intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
359 intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
360 intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700361 registerReceiver(mReceiver, intentFilter);
362
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700363 //set the default values for the preferences in the phone.
364 PreferenceManager.setDefaultValues(this, R.xml.network_setting, false);
365
366 PreferenceManager.setDefaultValues(this, R.xml.call_feature_setting, false);
367
368 // Make sure the audio mode (along with some
369 // audio-mode-related state of our own) is initialized
370 // correctly, given the current state of the phone.
371 PhoneUtils.setAudioMode(mCM);
372 }
373
Santos Cordon52bc81b2014-10-07 19:55:12 -0700374 cdmaOtaProvisionData = new OtaUtils.CdmaOtaProvisionData();
375 cdmaOtaConfigData = new OtaUtils.CdmaOtaConfigData();
376 cdmaOtaScreenState = new OtaUtils.CdmaOtaScreenState();
377 cdmaOtaInCallScreenUiState = new OtaUtils.CdmaOtaInCallScreenUiState();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700378
Santos Cordon76aaf482015-04-08 10:58:27 -0700379 simActivationManager = new SimActivationManager();
380
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700381 // XXX pre-load the SimProvider so that it's ready
382 resolver.getType(Uri.parse("content://icc/adn"));
383
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700384 // TODO: Register for Cdma Information Records
385 // phone.registerCdmaInformationRecord(mHandler, EVENT_UNSOL_CDMA_INFO_RECORD, null);
386
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700387 // Read HAC settings and configure audio hardware
388 if (getResources().getBoolean(R.bool.hac_enabled)) {
Stuart Scottdcf40a92014-12-09 10:45:01 -0800389 int hac = android.provider.Settings.System.getInt(
390 getContentResolver(),
391 android.provider.Settings.System.HEARING_AID,
392 0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700393 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
Andrew Leefb7f92e2015-02-26 16:23:32 -0800394 audioManager.setParameter(SettingsConstants.HAC_KEY,
395 hac == SettingsConstants.HAC_ENABLED
396 ? SettingsConstants.HAC_VAL_ON : SettingsConstants.HAC_VAL_OFF);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700397 }
Santos Cordonff506f52013-11-21 19:13:19 -0800398 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700399
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700400 /**
401 * Returns the singleton instance of the PhoneApp.
402 */
Sailesh Nepal1eaf22b2014-02-22 17:00:49 -0800403 public static PhoneGlobals getInstance() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700404 if (sMe == null) {
405 throw new IllegalStateException("No PhoneGlobals here!");
406 }
407 return sMe;
408 }
409
410 /**
411 * Returns the singleton instance of the PhoneApp if running as the
412 * primary user, otherwise null.
413 */
414 static PhoneGlobals getInstanceIfPrimary() {
415 return sMe;
416 }
417
418 /**
Stuart Scottdcf40a92014-12-09 10:45:01 -0800419 * Returns the default phone.
420 *
Andrew Lee385019f2014-11-24 14:19:50 -0800421 * WARNING: This method should be used carefully, now that there may be multiple phones.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700422 */
Andrew Lee83383e42014-10-31 12:42:28 -0700423 public static Phone getPhone() {
Stuart Scottdcf40a92014-12-09 10:45:01 -0800424 return PhoneFactory.getDefaultPhone();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700425 }
426
Andrew Lee2fcb6c32014-12-04 14:52:35 -0800427 public static Phone getPhone(int subId) {
428 return PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId));
Andrew Lee385019f2014-11-24 14:19:50 -0800429 }
430
Santos Cordonde10b752013-09-19 04:11:33 -0700431 /* package */ CallManager getCallManager() {
432 return mCM;
433 }
434
Chris Manton4e9fa912015-06-19 11:26:57 -0700435 public PersistableBundle getCarrierConfig() {
Shishir Agrawald3480e02016-01-25 13:05:49 -0800436 return getCarrierConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
Jonathan Basseri89b0ab42015-05-01 10:52:40 -0700437 }
438
Chris Manton4e9fa912015-06-19 11:26:57 -0700439 public PersistableBundle getCarrierConfigForSubId(int subId) {
Jonathan Basseri89b0ab42015-05-01 10:52:40 -0700440 return configLoader.getConfigForSubId(subId);
Junda Liu605148f2015-04-28 15:23:40 -0700441 }
442
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700443 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700444 * Handles OTASP-related events from the telephony layer.
445 *
446 * While an OTASP call is active, the CallNotifier forwards
447 * OTASP-related telephony events to this method.
448 */
449 void handleOtaspEvent(Message msg) {
450 if (DBG) Log.d(LOG_TAG, "handleOtaspEvent(message " + msg + ")...");
451
452 if (otaUtils == null) {
453 // We shouldn't be getting OTASP events without ever
454 // having started the OTASP call in the first place!
455 Log.w(LOG_TAG, "handleOtaEvents: got an event but otaUtils is null! "
456 + "message = " + msg);
457 return;
458 }
459
460 otaUtils.onOtaProvisionStatusChanged((AsyncResult) msg.obj);
461 }
462
463 /**
464 * Similarly, handle the disconnect event of an OTASP call
465 * by forwarding it to the OtaUtils instance.
466 */
467 /* package */ void handleOtaspDisconnect() {
468 if (DBG) Log.d(LOG_TAG, "handleOtaspDisconnect()...");
469
470 if (otaUtils == null) {
471 // We shouldn't be getting OTASP events without ever
472 // having started the OTASP call in the first place!
473 Log.w(LOG_TAG, "handleOtaspDisconnect: otaUtils is null!");
474 return;
475 }
476
477 otaUtils.onOtaspDisconnect();
478 }
479
480 /**
481 * Sets the activity responsible for un-PUK-blocking the device
482 * so that we may close it when we receive a positive result.
483 * mPUKEntryActivity is also used to indicate to the device that
484 * we are trying to un-PUK-lock the phone. In other words, iff
485 * it is NOT null, then we are trying to unlock and waiting for
486 * the SIM to move to READY state.
487 *
488 * @param activity is the activity to close when PUK has
489 * finished unlocking. Can be set to null to indicate the unlock
490 * or SIM READYing process is over.
491 */
492 void setPukEntryActivity(Activity activity) {
493 mPUKEntryActivity = activity;
494 }
495
496 Activity getPUKEntryActivity() {
497 return mPUKEntryActivity;
498 }
499
500 /**
501 * Sets the dialog responsible for notifying the user of un-PUK-
502 * blocking - SIM READYing progress, so that we may dismiss it
503 * when we receive a positive result.
504 *
505 * @param dialog indicates the progress dialog informing the user
506 * of the state of the device. Dismissed upon completion of
507 * READYing process
508 */
509 void setPukEntryProgressDialog(ProgressDialog dialog) {
510 mPUKEntryProgressDialog = dialog;
511 }
512
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700513 /**
514 * Controls whether or not the screen is allowed to sleep.
515 *
516 * Once sleep is allowed (WakeState is SLEEP), it will rely on the
517 * settings for the poke lock to determine when to timeout and let
518 * the device sleep {@link PhoneGlobals#setScreenTimeout}.
519 *
520 * @param ws tells the device to how to wake.
521 */
522 /* package */ void requestWakeState(WakeState ws) {
523 if (VDBG) Log.d(LOG_TAG, "requestWakeState(" + ws + ")...");
524 synchronized (this) {
525 if (mWakeState != ws) {
526 switch (ws) {
527 case PARTIAL:
528 // acquire the processor wake lock, and release the FULL
529 // lock if it is being held.
530 mPartialWakeLock.acquire();
531 if (mWakeLock.isHeld()) {
532 mWakeLock.release();
533 }
534 break;
535 case FULL:
536 // acquire the full wake lock, and release the PARTIAL
537 // lock if it is being held.
538 mWakeLock.acquire();
539 if (mPartialWakeLock.isHeld()) {
540 mPartialWakeLock.release();
541 }
542 break;
543 case SLEEP:
544 default:
545 // release both the PARTIAL and FULL locks.
546 if (mWakeLock.isHeld()) {
547 mWakeLock.release();
548 }
549 if (mPartialWakeLock.isHeld()) {
550 mPartialWakeLock.release();
551 }
552 break;
553 }
554 mWakeState = ws;
555 }
556 }
557 }
558
559 /**
560 * If we are not currently keeping the screen on, then poke the power
561 * manager to wake up the screen for the user activity timeout duration.
562 */
563 /* package */ void wakeUpScreen() {
564 synchronized (this) {
565 if (mWakeState == WakeState.SLEEP) {
566 if (DBG) Log.d(LOG_TAG, "pulse screen lock");
Dianne Hackborn148769b2015-07-13 17:55:47 -0700567 mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.phone:WAKE");
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700568 }
569 }
570 }
571
572 /**
573 * Sets the wake state and screen timeout based on the current state
574 * of the phone, and the current state of the in-call UI.
575 *
576 * This method is a "UI Policy" wrapper around
577 * {@link PhoneGlobals#requestWakeState} and {@link PhoneGlobals#setScreenTimeout}.
578 *
579 * It's safe to call this method regardless of the state of the Phone
580 * (e.g. whether or not it's idle), and regardless of the state of the
581 * Phone UI (e.g. whether or not the InCallScreen is active.)
582 */
583 /* package */ void updateWakeState() {
584 PhoneConstants.State state = mCM.getState();
585
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700586 // True if the speakerphone is in use. (If so, we *always* use
587 // the default timeout. Since the user is obviously not holding
588 // the phone up to his/her face, we don't need to worry about
589 // false touches, and thus don't need to turn the screen off so
590 // aggressively.)
591 // Note that we need to make a fresh call to this method any
592 // time the speaker state changes. (That happens in
593 // PhoneUtils.turnOnSpeaker().)
594 boolean isSpeakerInUse = (state == PhoneConstants.State.OFFHOOK) && PhoneUtils.isSpeakerOn(this);
595
596 // TODO (bug 1440854): The screen timeout *might* also need to
597 // depend on the bluetooth state, but this isn't as clear-cut as
598 // the speaker state (since while using BT it's common for the
599 // user to put the phone straight into a pocket, in which case the
600 // timeout should probably still be short.)
601
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700602 // Decide whether to force the screen on or not.
603 //
604 // Force the screen to be on if the phone is ringing or dialing,
605 // or if we're displaying the "Call ended" UI for a connection in
606 // the "disconnected" state.
607 // However, if the phone is disconnected while the user is in the
608 // middle of selecting a quick response message, we should not force
609 // the screen to be on.
610 //
611 boolean isRinging = (state == PhoneConstants.State.RINGING);
Stuart Scottdcf40a92014-12-09 10:45:01 -0800612 boolean isDialing = (mCM.getFgPhone().getForegroundCall().getState() == Call.State.DIALING);
Jay Shrauner6fe8fd62013-09-16 19:39:30 -0700613 boolean keepScreenOn = isRinging || isDialing;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700614 // keepScreenOn == true means we'll hold a full wake lock:
615 requestWakeState(keepScreenOn ? WakeState.FULL : WakeState.SLEEP);
616 }
617
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700618 KeyguardManager getKeyguardManager() {
619 return mKeyguardManager;
620 }
621
622 private void onMMIComplete(AsyncResult r) {
623 if (VDBG) Log.d(LOG_TAG, "onMMIComplete()...");
624 MmiCode mmiCode = (MmiCode) r.result;
Stuart Scottdcf40a92014-12-09 10:45:01 -0800625 PhoneUtils.displayMMIComplete(mmiCode.getPhone(), getInstance(), mmiCode, null, null);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700626 }
627
Stuart Scottdcf40a92014-12-09 10:45:01 -0800628 private void initForNewRadioTechnology(int phoneId) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700629 if (DBG) Log.d(LOG_TAG, "initForNewRadioTechnology...");
630
Stuart Scottdcf40a92014-12-09 10:45:01 -0800631 final Phone phone = PhoneFactory.getPhone(phoneId);
Santos Cordonc593d002015-06-03 15:41:15 -0700632 if (phone == null || !TelephonyCapabilities.supportsOtasp(phone)) {
633 // Clean up OTA for non-CDMA since it is only valid for CDMA.
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700634 clearOtaState();
635 }
636
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700637 notifier.updateCallNotifierRegistrationsAfterRadioTechnologyChange();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700638 }
639
Chris Mantona09f3632016-02-09 14:48:42 -0800640 private void handleAirplaneModeChange(Context context, int newMode) {
641 int cellState = Settings.Global.getInt(context.getContentResolver(),
642 Settings.Global.CELL_ON, PhoneConstants.CELL_ON_FLAG);
643 boolean isAirplaneNewlyOn = (newMode == 1);
644 switch (cellState) {
645 case PhoneConstants.CELL_OFF_FLAG:
646 // Airplane mode does not affect the cell radio if user
647 // has turned it off.
648 break;
649 case PhoneConstants.CELL_ON_FLAG:
650 maybeTurnCellOff(context, isAirplaneNewlyOn);
651 break;
652 case PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG:
653 maybeTurnCellOn(context, isAirplaneNewlyOn);
654 break;
655 }
656 }
657
658 /*
659 * Returns true if the radio must be turned off when entering airplane mode.
660 */
661 private boolean isCellOffInAirplaneMode(Context context) {
662 String airplaneModeRadios = Settings.Global.getString(context.getContentResolver(),
663 Settings.Global.AIRPLANE_MODE_RADIOS);
664 return airplaneModeRadios == null
665 || airplaneModeRadios.contains(Settings.Global.RADIO_CELL);
666 }
667
668 private void setRadioPowerOff(Context context) {
669 Log.i(LOG_TAG, "Turning radio off - airplane");
670 Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
671 PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG);
672 Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT, 0);
673 PhoneUtils.setRadioPower(false);
674 }
675
676 private void setRadioPowerOn(Context context) {
677 Log.i(LOG_TAG, "Turning radio on - airplane");
678 Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
679 PhoneConstants.CELL_ON_FLAG);
680 Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT,
681 1);
682 PhoneUtils.setRadioPower(true);
683 }
684
685 private void maybeTurnCellOff(Context context, boolean isAirplaneNewlyOn) {
686 if (isAirplaneNewlyOn) {
Santos Cordone18902f2016-03-22 17:16:04 -0700687 // If we are trying to turn off the radio, make sure there are no active
688 // emergency calls. If there are, switch airplane mode back to off.
689 if (PhoneUtils.isInEmergencyCall(mCM)) {
690 // Switch airplane mode back to off.
691 ConnectivityManager.from(this).setAirplaneMode(false);
692 Toast.makeText(this, R.string.radio_off_during_emergency_call, Toast.LENGTH_LONG)
693 .show();
694 Log.i(LOG_TAG, "Ignoring airplane mode: emergency call. Turning airplane off");
Chris Mantona09f3632016-02-09 14:48:42 -0800695 } else if (isCellOffInAirplaneMode(context)) {
696 setRadioPowerOff(context);
Santos Cordone18902f2016-03-22 17:16:04 -0700697 } else {
Chris Mantona09f3632016-02-09 14:48:42 -0800698 Log.i(LOG_TAG, "Ignoring airplane mode: settings prevent cell radio power off");
Santos Cordone18902f2016-03-22 17:16:04 -0700699 }
Chris Mantona09f3632016-02-09 14:48:42 -0800700 }
701 }
702
703 private void maybeTurnCellOn(Context context, boolean isAirplaneNewlyOn) {
704 if (!isAirplaneNewlyOn) {
705 setRadioPowerOn(context);
Santos Cordone18902f2016-03-22 17:16:04 -0700706 }
707 }
708
Santos Cordon593ab382013-08-06 21:58:23 -0700709 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700710 * Receiver for misc intent broadcasts the Phone app cares about.
711 */
712 private class PhoneAppBroadcastReceiver extends BroadcastReceiver {
713 @Override
714 public void onReceive(Context context, Intent intent) {
715 String action = intent.getAction();
716 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
Santos Cordone18902f2016-03-22 17:16:04 -0700717 int airplaneMode = Settings.Global.getInt(getContentResolver(),
718 Settings.Global.AIRPLANE_MODE_ON, AIRPLANE_OFF);
719 // Treat any non-OFF values as ON.
720 if (airplaneMode != AIRPLANE_OFF) {
721 airplaneMode = AIRPLANE_ON;
722 }
Chris Mantona09f3632016-02-09 14:48:42 -0800723 handleAirplaneModeChange(context, airplaneMode);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700724 } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
Stuart Scottdcf40a92014-12-09 10:45:01 -0800725 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
726 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
727 int phoneId = SubscriptionManager.getPhoneId(subId);
728 String state = intent.getStringExtra(PhoneConstants.STATE_KEY);
729 if (VDBG) {
730 Log.d(LOG_TAG, "mReceiver: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED");
731 Log.d(LOG_TAG, "- state: " + state);
732 Log.d(LOG_TAG, "- reason: "
733 + intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY));
734 Log.d(LOG_TAG, "- subId: " + subId);
735 Log.d(LOG_TAG, "- phoneId: " + phoneId);
736 }
737 Phone phone = SubscriptionManager.isValidPhoneId(phoneId) ?
738 PhoneFactory.getPhone(phoneId) : PhoneFactory.getDefaultPhone();
Santos Cordonc593d002015-06-03 15:41:15 -0700739
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700740 // The "data disconnected due to roaming" notification is shown
741 // if (a) you have the "data roaming" feature turned off, and
742 // (b) you just lost data connectivity because you're roaming.
743 boolean disconnectedDueToRoaming =
744 !phone.getDataRoamingEnabled()
Stuart Scottdcf40a92014-12-09 10:45:01 -0800745 && PhoneConstants.DataState.DISCONNECTED.equals(state)
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700746 && Phone.REASON_ROAMING_ON.equals(
747 intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY));
Yorke Lee4d2db1c2014-11-06 11:37:09 -0800748 if (mDataDisconnectedDueToRoaming != disconnectedDueToRoaming) {
749 mDataDisconnectedDueToRoaming = disconnectedDueToRoaming;
750 mHandler.sendEmptyMessage(disconnectedDueToRoaming
751 ? EVENT_DATA_ROAMING_DISCONNECTED : EVENT_DATA_ROAMING_OK);
752 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700753 } else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) &&
754 (mPUKEntryActivity != null)) {
755 // if an attempt to un-PUK-lock the device was made, while we're
756 // receiving this state change notification, notify the handler.
757 // NOTE: This is ONLY triggered if an attempt to un-PUK-lock has
758 // been attempted.
759 mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED,
760 intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)));
761 } else if (action.equals(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED)) {
762 String newPhone = intent.getStringExtra(PhoneConstants.PHONE_NAME_KEY);
Stuart Scottdcf40a92014-12-09 10:45:01 -0800763 int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY,
764 SubscriptionManager.INVALID_PHONE_INDEX);
765 Log.d(LOG_TAG, "Radio technology switched. Now " + newPhone + " (" + phoneId
766 + ") is active.");
767 initForNewRadioTechnology(phoneId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700768 } else if (action.equals(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED)) {
769 handleServiceStateChanged(intent);
770 } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
Sandeep Kuntade73a6a2014-10-15 18:45:56 +0530771 int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY, 0);
772 phoneInEcm = getPhone(phoneId);
773 Log.d(LOG_TAG, "Emergency Callback Mode. phoneId:" + phoneId);
774 if (phoneInEcm != null) {
775 if (TelephonyCapabilities.supportsEcm(phoneInEcm)) {
776 Log.d(LOG_TAG, "Emergency Callback Mode arrived in PhoneApp.");
777 // Start Emergency Callback Mode service
778 if (intent.getBooleanExtra("phoneinECMState", false)) {
779 context.startService(new Intent(context,
780 EmergencyCallbackModeService.class));
781 } else {
782 phoneInEcm = null;
783 }
784 } else {
785 // It doesn't make sense to get ACTION_EMERGENCY_CALLBACK_MODE_CHANGED
786 // on a device that doesn't support ECM in the first place.
787 Log.e(LOG_TAG, "Got ACTION_EMERGENCY_CALLBACK_MODE_CHANGED, but "
788 + "ECM isn't supported for phone: " + phoneInEcm.getPhoneName());
789 phoneInEcm = null;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700790 }
791 } else {
Sandeep Kuntade73a6a2014-10-15 18:45:56 +0530792 Log.w(LOG_TAG, "phoneInEcm is null.");
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700793 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700794 }
795 }
796 }
797
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700798 private void handleServiceStateChanged(Intent intent) {
799 /**
800 * This used to handle updating EriTextWidgetProvider this routine
801 * and and listening for ACTION_SERVICE_STATE_CHANGED intents could
802 * be removed. But leaving just in case it might be needed in the near
803 * future.
804 */
805
806 // If service just returned, start sending out the queued messages
Santos Cordonc593d002015-06-03 15:41:15 -0700807 Bundle extras = intent.getExtras();
808 if (extras != null) {
809 ServiceState ss = ServiceState.newFromBundle(extras);
810 if (ss != null) {
811 int state = ss.getState();
812 notificationMgr.updateNetworkSelection(state);
813 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700814 }
815 }
816
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700817 // it is safe to call clearOtaState() even if the InCallScreen isn't active
818 public void clearOtaState() {
819 if (DBG) Log.d(LOG_TAG, "- clearOtaState ...");
Jay Shrauner6fe8fd62013-09-16 19:39:30 -0700820 if (otaUtils != null) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700821 otaUtils.cleanOtaScreen(true);
822 if (DBG) Log.d(LOG_TAG, " - clearOtaState clears OTA screen");
823 }
824 }
825
826 // it is safe to call dismissOtaDialogs() even if the InCallScreen isn't active
827 public void dismissOtaDialogs() {
828 if (DBG) Log.d(LOG_TAG, "- dismissOtaDialogs ...");
Jay Shrauner6fe8fd62013-09-16 19:39:30 -0700829 if (otaUtils != null) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700830 otaUtils.dismissAllOtaDialogs();
831 if (DBG) Log.d(LOG_TAG, " - dismissOtaDialogs clears OTA dialogs");
832 }
833 }
834
Sandeep Kuntade73a6a2014-10-15 18:45:56 +0530835 public Phone getPhoneInEcm() {
836 return phoneInEcm;
837 }
838
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700839 /**
Tyler Gunn9c1071f2014-12-09 10:07:54 -0800840 * Triggers a refresh of the message waiting (voicemail) indicator.
841 *
842 * @param subId the subscription id we should refresh the notification for.
843 */
844 public void refreshMwiIndicator(int subId) {
845 notificationMgr.refreshMwi(subId);
846 }
847
848 /**
Nancy Chenbb49d412015-07-23 13:54:16 -0700849 * Dismisses the message waiting (voicemail) indicator.
850 *
851 * @param subId the subscription id we should dismiss the notification for.
852 */
853 public void clearMwiIndicator(int subId) {
854 notificationMgr.updateMwi(subId, false);
855 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700856}