diff options
-rwxr-xr-x | core/java/android/provider/Telephony.java | 175 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 17 | ||||
-rwxr-xr-x | core/res/res/values/strings.xml | 9 | ||||
-rw-r--r-- | telephony/java/android/telephony/CellBroadcastMessage.java | 422 |
4 files changed, 623 insertions, 0 deletions
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 9612151..19d8d5c 100755 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -1816,6 +1816,181 @@ public final class Telephony { public static final String BEARER = "bearer"; } + /** + * Contains received SMS cell broadcast messages. + */ + public static final class CellBroadcasts implements BaseColumns { + + /** Not instantiable. */ + private CellBroadcasts() {} + + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = + Uri.parse("content://cellbroadcasts"); + + /** + * Message geographical scope. + * <P>Type: INTEGER</P> + */ + public static final String GEOGRAPHICAL_SCOPE = "geo_scope"; + + /** + * Message serial number. + * <P>Type: INTEGER</P> + */ + public static final String SERIAL_NUMBER = "serial_number"; + + /** + * PLMN of broadcast sender. (SERIAL_NUMBER + PLMN + LAC + CID) uniquely identifies a + * broadcast for duplicate detection purposes. + * <P>Type: TEXT</P> + */ + public static final String PLMN = "plmn"; + + /** + * Location Area (GSM) or Service Area (UMTS) of broadcast sender. Unused for CDMA. + * Only included if Geographical Scope of message is not PLMN wide (01). + * <P>Type: INTEGER</P> + */ + public static final String LAC = "lac"; + + /** + * Cell ID of message sender (GSM/UMTS). Unused for CDMA. Only included when the + * Geographical Scope of message is cell wide (00 or 11). + * <P>Type: INTEGER</P> + */ + public static final String CID = "cid"; + + /** + * Message code (OBSOLETE: merged into SERIAL_NUMBER). + * <P>Type: INTEGER</P> + */ + public static final String V1_MESSAGE_CODE = "message_code"; + + /** + * Message identifier (OBSOLETE: renamed to SERVICE_CATEGORY). + * <P>Type: INTEGER</P> + */ + public static final String V1_MESSAGE_IDENTIFIER = "message_id"; + + /** + * Service category (GSM/UMTS message identifier, CDMA service category). + * <P>Type: INTEGER</P> + */ + public static final String SERVICE_CATEGORY = "service_category"; + + /** + * Message language code. + * <P>Type: TEXT</P> + */ + public static final String LANGUAGE_CODE = "language"; + + /** + * Message body. + * <P>Type: TEXT</P> + */ + public static final String MESSAGE_BODY = "body"; + + /** + * Message delivery time. + * <P>Type: INTEGER (long)</P> + */ + public static final String DELIVERY_TIME = "date"; + + /** + * Has the message been viewed? + * <P>Type: INTEGER (boolean)</P> + */ + public static final String MESSAGE_READ = "read"; + + /** + * Message format (3GPP or 3GPP2). + * <P>Type: INTEGER</P> + */ + public static final String MESSAGE_FORMAT = "format"; + + /** + * Message priority (including emergency). + * <P>Type: INTEGER</P> + */ + public static final String MESSAGE_PRIORITY = "priority"; + + /** + * ETWS warning type (ETWS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String ETWS_WARNING_TYPE = "etws_warning_type"; + + /** + * CMAS message class (CMAS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String CMAS_MESSAGE_CLASS = "cmas_message_class"; + + /** + * CMAS category (CMAS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String CMAS_CATEGORY = "cmas_category"; + + /** + * CMAS response type (CMAS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String CMAS_RESPONSE_TYPE = "cmas_response_type"; + + /** + * CMAS severity (CMAS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String CMAS_SEVERITY = "cmas_severity"; + + /** + * CMAS urgency (CMAS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String CMAS_URGENCY = "cmas_urgency"; + + /** + * CMAS certainty (CMAS alerts only). + * <P>Type: INTEGER</P> + */ + public static final String CMAS_CERTAINTY = "cmas_certainty"; + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = DELIVERY_TIME + " DESC"; + + /** + * Query columns for instantiating {@link android.telephony.CellBroadcastMessage} objects. + */ + public static final String[] QUERY_COLUMNS = { + _ID, + GEOGRAPHICAL_SCOPE, + PLMN, + LAC, + CID, + SERIAL_NUMBER, + SERVICE_CATEGORY, + LANGUAGE_CODE, + MESSAGE_BODY, + DELIVERY_TIME, + MESSAGE_READ, + MESSAGE_FORMAT, + MESSAGE_PRIORITY, + ETWS_WARNING_TYPE, + CMAS_MESSAGE_CLASS, + CMAS_CATEGORY, + CMAS_RESPONSE_TYPE, + CMAS_SEVERITY, + CMAS_URGENCY, + CMAS_CERTAINTY + }; + } + public static final class Intents { private Intents() { // Not instantiable diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b7ee2ec..4443bc8 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -202,6 +202,23 @@ android:label="@string/permlab_receiveEmergencyBroadcast" android:description="@string/permdesc_receiveEmergencyBroadcast" /> + <!-- Allows an application to read previously received cell broadcast + messages and to register a content observer to get notifications when + a cell broadcast has been received and added to the database. For + emergency alerts, the database is updated immediately after the + alert dialog and notification sound/vibration/speech are presented. + The "read" column is then updated after the user dismisses the alert. + This enables supplementary emergency assistance apps to start loading + additional emergency information (if Internet access is available) + when the alert is first received, and to delay presenting the info + to the user until after the initial alert dialog is dismissed. + @hide Pending API council approval --> + <permission android:name="android.permission.READ_CELL_BROADCASTS" + android:permissionGroup="android.permission-group.MESSAGES" + android:protectionLevel="dangerous" + android:label="@string/permlab_readCellBroadcasts" + android:description="@string/permdesc_readCellBroadcasts" /> + <!-- Allows an application to read SMS messages. --> <permission android:name="android.permission.READ_SMS" android:permissionGroup="android.permission-group.MESSAGES" diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 7df24a2..149a78c 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -474,6 +474,15 @@ and process emergency broadcast messages. This permission is only available to system apps.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_readCellBroadcasts">read cell broadcast messages</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_readCellBroadcasts">Allows the app to read + cell broadcast messages received by your device. Cell broadcast alerts + are delivered in some locations to warn you of emergency situations. + Malicious apps may interfere with the performance or operation of your + device when an emergency cell broadcast is received.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_sendSms">send SMS messages</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> diff --git a/telephony/java/android/telephony/CellBroadcastMessage.java b/telephony/java/android/telephony/CellBroadcastMessage.java new file mode 100644 index 0000000..36c238d --- /dev/null +++ b/telephony/java/android/telephony/CellBroadcastMessage.java @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2011 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 android.telephony; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Typeface; +import android.os.Parcel; +import android.os.Parcelable; +import android.provider.Telephony; +import android.telephony.SmsCbCmasInfo; +import android.telephony.SmsCbEtwsInfo; +import android.telephony.SmsCbLocation; +import android.telephony.SmsCbMessage; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.format.DateUtils; +import android.text.style.StyleSpan; + +/** + * Application wrapper for {@link SmsCbMessage}. This is Parcelable so that + * decoded broadcast message objects can be passed between running Services. + * New broadcasts are received by the CellBroadcastReceiver app, which exports + * the database of previously received broadcasts at "content://cellbroadcasts/". + * The "android.permission.READ_CELL_BROADCASTS" permission is required to read + * from the ContentProvider, and writes to the database are not allowed.<p> + * + * Use {@link #createFromCursor} to create CellBroadcastMessage objects from rows + * in the database cursor returned by the ContentProvider. + * + * {@hide} + */ +public class CellBroadcastMessage implements Parcelable { + + /** Identifier for getExtra() when adding this object to an Intent. */ + public static final String SMS_CB_MESSAGE_EXTRA = + "com.android.cellbroadcastreceiver.SMS_CB_MESSAGE"; + + /** SmsCbMessage. */ + private final SmsCbMessage mSmsCbMessage; + + private final long mDeliveryTime; + private boolean mIsRead; + + public CellBroadcastMessage(SmsCbMessage message) { + mSmsCbMessage = message; + mDeliveryTime = System.currentTimeMillis(); + mIsRead = false; + } + + private CellBroadcastMessage(SmsCbMessage message, long deliveryTime, boolean isRead) { + mSmsCbMessage = message; + mDeliveryTime = deliveryTime; + mIsRead = isRead; + } + + private CellBroadcastMessage(Parcel in) { + mSmsCbMessage = new SmsCbMessage(in); + mDeliveryTime = in.readLong(); + mIsRead = (in.readInt() != 0); + } + + /** Parcelable: no special flags. */ + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + mSmsCbMessage.writeToParcel(out, flags); + out.writeLong(mDeliveryTime); + out.writeInt(mIsRead ? 1 : 0); + } + + public static final Parcelable.Creator<CellBroadcastMessage> CREATOR + = new Parcelable.Creator<CellBroadcastMessage>() { + public CellBroadcastMessage createFromParcel(Parcel in) { + return new CellBroadcastMessage(in); + } + + public CellBroadcastMessage[] newArray(int size) { + return new CellBroadcastMessage[size]; + } + }; + + /** + * Create a CellBroadcastMessage from a row in the database. + * @param cursor an open SQLite cursor pointing to the row to read + * @return the new CellBroadcastMessage + * @throws IllegalArgumentException if one of the required columns is missing + */ + public static CellBroadcastMessage createFromCursor(Cursor cursor) { + int geoScope = cursor.getInt( + cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE)); + int serialNum = cursor.getInt( + cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERIAL_NUMBER)); + int category = cursor.getInt( + cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERVICE_CATEGORY)); + String language = cursor.getString( + cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.LANGUAGE_CODE)); + String body = cursor.getString( + cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_BODY)); + int format = cursor.getInt( + cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_FORMAT)); + int priority = cursor.getInt( + cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_PRIORITY)); + + String plmn; + int plmnColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.PLMN); + if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) { + plmn = cursor.getString(plmnColumn); + } else { + plmn = null; + } + + int lac; + int lacColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.LAC); + if (lacColumn != -1 && !cursor.isNull(lacColumn)) { + lac = cursor.getInt(lacColumn); + } else { + lac = -1; + } + + int cid; + int cidColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.CID); + if (cidColumn != -1 && !cursor.isNull(cidColumn)) { + cid = cursor.getInt(cidColumn); + } else { + cid = -1; + } + + SmsCbLocation location = new SmsCbLocation(plmn, lac, cid); + + SmsCbEtwsInfo etwsInfo; + int etwsWarningTypeColumn = cursor.getColumnIndex( + Telephony.CellBroadcasts.ETWS_WARNING_TYPE); + if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) { + int warningType = cursor.getInt(etwsWarningTypeColumn); + etwsInfo = new SmsCbEtwsInfo(warningType, false, false, null); + } else { + etwsInfo = null; + } + + SmsCbCmasInfo cmasInfo; + int cmasMessageClassColumn = cursor.getColumnIndex( + Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS); + if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) { + int messageClass = cursor.getInt(cmasMessageClassColumn); + + int cmasCategory; + int cmasCategoryColumn = cursor.getColumnIndex( + Telephony.CellBroadcasts.CMAS_CATEGORY); + if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) { + cmasCategory = cursor.getInt(cmasCategoryColumn); + } else { + cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN; + } + + int responseType; + int cmasResponseTypeColumn = cursor.getColumnIndex( + Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE); + if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) { + responseType = cursor.getInt(cmasResponseTypeColumn); + } else { + responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN; + } + + int severity; + int cmasSeverityColumn = cursor.getColumnIndex( + Telephony.CellBroadcasts.CMAS_SEVERITY); + if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) { + severity = cursor.getInt(cmasSeverityColumn); + } else { + severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; + } + + int urgency; + int cmasUrgencyColumn = cursor.getColumnIndex( + Telephony.CellBroadcasts.CMAS_URGENCY); + if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) { + urgency = cursor.getInt(cmasUrgencyColumn); + } else { + urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; + } + + int certainty; + int cmasCertaintyColumn = cursor.getColumnIndex( + Telephony.CellBroadcasts.CMAS_CERTAINTY); + if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) { + certainty = cursor.getInt(cmasCertaintyColumn); + } else { + certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; + } + + cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity, + urgency, certainty); + } else { + cmasInfo = null; + } + + SmsCbMessage msg = new SmsCbMessage(format, geoScope, serialNum, location, category, + language, body, priority, etwsInfo, cmasInfo); + + long deliveryTime = cursor.getLong(cursor.getColumnIndexOrThrow( + Telephony.CellBroadcasts.DELIVERY_TIME)); + boolean isRead = (cursor.getInt(cursor.getColumnIndexOrThrow( + Telephony.CellBroadcasts.MESSAGE_READ)) != 0); + + return new CellBroadcastMessage(msg, deliveryTime, isRead); + } + + /** + * Return a ContentValues object for insertion into the database. + * @return a new ContentValues object containing this object's data + */ + public ContentValues getContentValues() { + ContentValues cv = new ContentValues(16); + SmsCbMessage msg = mSmsCbMessage; + cv.put(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE, msg.getGeographicalScope()); + SmsCbLocation location = msg.getLocation(); + if (location.getPlmn() != null) { + cv.put(Telephony.CellBroadcasts.PLMN, location.getPlmn()); + } + if (location.getLac() != -1) { + cv.put(Telephony.CellBroadcasts.LAC, location.getLac()); + } + if (location.getCid() != -1) { + cv.put(Telephony.CellBroadcasts.CID, location.getCid()); + } + cv.put(Telephony.CellBroadcasts.SERIAL_NUMBER, msg.getSerialNumber()); + cv.put(Telephony.CellBroadcasts.SERVICE_CATEGORY, msg.getServiceCategory()); + cv.put(Telephony.CellBroadcasts.LANGUAGE_CODE, msg.getLanguageCode()); + cv.put(Telephony.CellBroadcasts.MESSAGE_BODY, msg.getMessageBody()); + cv.put(Telephony.CellBroadcasts.DELIVERY_TIME, mDeliveryTime); + cv.put(Telephony.CellBroadcasts.MESSAGE_READ, mIsRead); + cv.put(Telephony.CellBroadcasts.MESSAGE_FORMAT, msg.getMessageFormat()); + cv.put(Telephony.CellBroadcasts.MESSAGE_PRIORITY, msg.getMessagePriority()); + + SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); + if (etwsInfo != null) { + cv.put(Telephony.CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType()); + } + + SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo(); + if (cmasInfo != null) { + cv.put(Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass()); + cv.put(Telephony.CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory()); + cv.put(Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType()); + cv.put(Telephony.CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity()); + cv.put(Telephony.CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency()); + cv.put(Telephony.CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty()); + } + + return cv; + } + + /** + * Set or clear the "read message" flag. + * @param isRead true if the message has been read; false if not + */ + public void setIsRead(boolean isRead) { + mIsRead = isRead; + } + + public String getLanguageCode() { + return mSmsCbMessage.getLanguageCode(); + } + + public int getServiceCategory() { + return mSmsCbMessage.getServiceCategory(); + } + + public long getDeliveryTime() { + return mDeliveryTime; + } + + public String getMessageBody() { + return mSmsCbMessage.getMessageBody(); + } + + public boolean isRead() { + return mIsRead; + } + + public int getSerialNumber() { + return mSmsCbMessage.getSerialNumber(); + } + + public SmsCbCmasInfo getCmasWarningInfo() { + return mSmsCbMessage.getCmasWarningInfo(); + } + + public SmsCbEtwsInfo getEtwsWarningInfo() { + return mSmsCbMessage.getEtwsWarningInfo(); + } + + /** + * Return whether the broadcast is an emergency (PWS) message type. + * This includes lower priority test messages and Amber alerts. + * + * All public alerts show the flashing warning icon in the dialog, + * but only emergency alerts play the alert sound and speak the message. + * + * @return true if the message is PWS type; false otherwise + */ + public boolean isPublicAlertMessage() { + return mSmsCbMessage.isEmergencyMessage(); + } + + /** + * Returns whether the broadcast is an emergency (PWS) message type, + * including test messages, but excluding lower priority Amber alert broadcasts. + * + * @return true if the message is PWS type, excluding Amber alerts + */ + public boolean isEmergencyAlertMessage() { + if (!mSmsCbMessage.isEmergencyMessage()) { + return false; + } + SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo(); + if (cmasInfo != null && + cmasInfo.getMessageClass() == SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY) { + return false; + } + return true; + } + + /** + * Return whether the broadcast is an ETWS emergency message type. + * @return true if the message is ETWS emergency type; false otherwise + */ + public boolean isEtwsMessage() { + return mSmsCbMessage.isEtwsMessage(); + } + + /** + * Return whether the broadcast is a CMAS emergency message type. + * @return true if the message is CMAS emergency type; false otherwise + */ + public boolean isCmasMessage() { + return mSmsCbMessage.isCmasMessage(); + } + + /** + * Return the CMAS message class. + * @return the CMAS message class, e.g. {@link SmsCbCmasInfo#CMAS_CLASS_SEVERE_THREAT}, or + * {@link SmsCbCmasInfo#CMAS_CLASS_UNKNOWN} if this is not a CMAS alert + */ + public int getCmasMessageClass() { + if (mSmsCbMessage.isCmasMessage()) { + return mSmsCbMessage.getCmasWarningInfo().getMessageClass(); + } else { + return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN; + } + } + + /** + * Return whether the broadcast is an ETWS popup alert. + * This method checks the message ID and the message code. + * @return true if the message indicates an ETWS popup alert + */ + public boolean isEtwsPopupAlert() { + SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); + return etwsInfo != null && etwsInfo.isPopupAlert(); + } + + /** + * Return whether the broadcast is an ETWS emergency user alert. + * This method checks the message ID and the message code. + * @return true if the message indicates an ETWS emergency user alert + */ + public boolean isEtwsEmergencyUserAlert() { + SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); + return etwsInfo != null && etwsInfo.isEmergencyUserAlert(); + } + + /** + * Return whether the broadcast is an ETWS test message. + * @return true if the message is an ETWS test message; false otherwise + */ + public boolean isEtwsTestMessage() { + SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); + return etwsInfo != null && + etwsInfo.getWarningType() == SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE; + } + + /** + * Return the abbreviated date string for the message delivery time. + * @param context the context object + * @return a String to use in the broadcast list UI + */ + public String getDateString(Context context) { + int flags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_SHOW_TIME | + DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE | + DateUtils.FORMAT_CAP_AMPM; + return DateUtils.formatDateTime(context, mDeliveryTime, flags); + } + + /** + * Return the date string for the message delivery time, suitable for text-to-speech. + * @param context the context object + * @return a String for populating the list item AccessibilityEvent for TTS + */ + public String getSpokenDateString(Context context) { + int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE; + return DateUtils.formatDateTime(context, mDeliveryTime, flags); + } +} |